diff --git a/0-StartHere.txt b/0-StartHere.txt new file mode 100644 index 0000000..00d07b4 --- /dev/null +++ b/0-StartHere.txt @@ -0,0 +1,220 @@ +$Id: 0-StartHere.txt,v 1.1 2001/07/14 06:17:00 tundra Exp $ + + INTRODUCTION TO HB + ================== + +(BIG HINT: You do not care about HB as a useful program (probably), +but if you're trying to learn Python, it may help.) + + +What Is HB? +=========== + +HB is a fairly simple, but not trivial, Home Budget management program +written in the Python programming language. (You'll need a version of +Python for your operating system to run hb.py, the actual HB program. +You can get this at www.python.org.) HB is not going to replace +Quicken or MS-Money any time soon, but as the next section explains, I +had a good reason or two for writing it. + + +Why Did I Write It? +=================== + +Two Reasons: + +Years ago - decades, actually - one of the very first programs I wrote +in BASIC was the inevitable Home Budget program. In those days it was +written in MBASIC on a TRS-80 Model 1 talking to cassette tape for +storage. Over the years the program evolved first to floppy disk, +then to MS-DOS, then to compiled QuickBASIC. It was never a very well +written program in the first place and all that evolving didn't make +it any prettier. It was a pain to maintain, but I actually *liked* +using it. You see, I hate all the complexity that GUIs brought to +personal computing. My finances are not like John D. Rockerfeller's +and I don't need a whiz-bang GUI, scratches-your-back-while-it-does- +your-taxes program just to figure out how much money I'm going to owe +at the end of the month, fer cryin' out loud. So, one reason I did HB +was to replace my old piece-of-crap BASIC program. + +The (much more important) other reason I wrote HB was because I wanted +to learn the Python programming language. This is the reason you care +about HB at all, if you do (because only 1 out of 280 million +Americans (me) is actually going to use it for its intended purpose). + +I'm a Computer Scientist by training and a lot of graduate school +(1 - M.S. and 1/2 Ph.D. ;) stuff I did involved languages and compiler +design and I just *love* fiddling around with new computer languages. +Over the years I've programmed some or a lot in: BASIC, Assembly +Languages, C, C++, LISP, FORTRAN, PL/M, PL/1, AWK, Perl, Visual BASIC, +Java, and Forth. When I first started playing with Python, I realized +that this was one of the very coolest languages I'd ever seen. It is +good for just about everything (except, probably, writing operating +systems), promotes good programming style, causes you to write lots of +good code *fast*, and, well, it's just plain FUN. + +The problem is that, while you can learn the basic syntactic elements +and get a sense of semantics of a language from a tutorial, you really +don't "get it" until you write a non-trivial program. In my +experience, this is the only way to being learning the "idioms" of a +language. OTOH, if the program you start with is trying to solve too +complex a problem, you'll end up spending all your time on the problem +not the language. For instance, you probably do not want to make your +first Python program do non-linear differential equations (unless you +happen to be a whiz at them, in which case, you have my condolences ;). + +So, I decided to reimplement my old home budget program in Python. +The program is solving an intellectually simple problem, but it is +non-trivial in the sense that it requires I/O (disk, printer, +keyboard), needs to manage a display space, needs to parse user input, +and so on. Along the way, I added some "nifty" features like unlimited +Undo capability and a table-driven command interpreter that's kinda cool, +if I do say so myself. + + +Why Am I Distributing It? +========================= + + +As I was writing (and rewriting, and rewriting, and ...) HB, it +occured to me that other people might be able to learn Python a little +faster if they had a complete application to look at. So, I went back +and heavily commented the code (about 50% of hb.py is *comments*), +wrote these docs and decided to make it available to anyone who wanted +to paw through it. + +It also occurs to me that, having misspent my youth in academics, +people teaching Python programming might want to use HB for class +work. This would make me very happy. Too much of what is taught +involves "toy problems" that never get much beyond the trivial example +stage. + +IMPORTANT NOTE: This is not intended to "teach" you Python or +programming or anything else. The idea is that you already know how +to program and have a basic understanding of how Python does things. +The point here is to "Put It All Together" and show an application of +all the pieces as an integrated whole. + + +Program Features +================ + +This is a pretty straighforward Budget Management system. Nothing too +esoteric here, really: + +Unlimited Different Budgets + Up to the limit of your disk space. + +Unlimited Accounts + Well, the limit is what you can squeeze on a single screen. + +Unlimited UnDo + Limited by memory - if you run out of UnDo stack, + you're using HB for WAY too complicated budgets ;) + +Prints Reports To Printer (Win32 Only) Or Disk File (All OS) + +Handles Monthly "Burn Rate" For Each Account + +Pay, Debit, Credit, And Transfer Funds + +Balance The Budget + +Read And Write Budget Files + + +Code Features +============= + +This is probably what you really care about: + +Uses Every Major Python Data Structure + Tuples, Tuples Of Tuples, Tuples Of Tuples Of Tuples, Lists, + Dictionaries, and Objects + +Has Mixture Of OO And Procedural Code + Python is *really* good at this. + +Uses A Regular Expression For Input Validation + +Uses 'eval' To Demonstrate Runtime String Execution + +(Some) Exception Handling + +Disk, Keyboard, And Device I/O + +Code Is Completely "UnStrung" + There are no literals (other than newlines) embedded in the code + which makes maintenance and internationalization very simple. + +Completely Table-Driven Command Interpreter + Kinda cool, actually. A little complicated at first, but it makes + adding new features a snap AND prepares the way for mating the program + to a GUI if you must have one in your life. + +Stack-Based Context Management And UnDo Facilities + +LOADED With Explanatory Comments So You Understand What's Going On + + +Caveats And Acknowledgements +============================ + +I am an experienced programmer, but not an experienced *Python* +programmer. This means that not everything in HB is necessarily good +Python - I hope it is, it probably mostly is, but it probably also has +some warts. If you *are* an experienced "Pythonista" and you see +something in HB that makes you just wince, send me an email and let me +know what needs fixin'. + +I also want to take a moment and thank the many friendly people of +comp.lang.python who answer my many stupid questions promptly and +correctly both on the newsgroup and privately. + +Also, a big "Thank You" to www.diveintopython.org for one of the best +language tutorials I've ever seen. It is a work-in-progress, but a +"must" if you are learning Python. + +Finally, a huge thanks to GVR and the many people who designed, +implemented, and refine one of the slickest programming languages +around... + + +How To Proceed +============== + +All the files in HB documentation directory begin with a number. All +you have to do is read them in order. This document begins with '0-', +so the next thing you need to read is the one starting with '1-' (the +HB Licensing document - you MUST read and agree to those terms to make +use of HB in any way)... and so on. + + +Political Statement +=================== + +Some people view Open Source and Freeware as Marxists: + + "Code should be made available because it belongs to everyone + and there is a moral imperative to make it freely available." + +In other words, "From each according to their ability, to each according +to their need." (This really worked well in the 20th Century, don't you +think?) + +This is drivel of the worst kind. If you don't understand why, read +"Economics In One Lesson" by Hazlitt, "The Road To Serfdom" by Hayek, and +"Capitalism, The Unknown Ideal" by Rand. I view HB as a Libertarian: + + "HB is my property (well, the property of my corporation). I + created it on my own, so it belongs to me. I make it freely + available because *I can* and *I want to*. The *only* way I + make it available is if you agree to my licensing terms. If + you do not, don't use HB. I have no moral obligation to + make it available at all, I do it because it suits me." + + +Questions Or Comments? +===================== + +Send 'em to: tundra@tundraware.com diff --git a/1-HB-License.txt b/1-HB-License.txt new file mode 100644 index 0000000..eee81f3 --- /dev/null +++ b/1-HB-License.txt @@ -0,0 +1,84 @@ +$Id: 1-HB-License.txt,v 1.1 2001/07/14 06:17:00 tundra Exp $ + +In order to use,study, modify, or copy HB, you must read and agree to +all the licensing terms below. If you do not agree with or understand +*ANYTHING* you see in this document, you are NOT granted a license to +use, study, modify, or copy HB. By using, studying, modifying, or +copying HB, you are agreeing to all the terms of the HB LICENSE below +in their entirety. + + + HB LICENSE AGREEMENT + ==================== + +1) DEFINITIONS + +Throughout this Agreement the term "HB" is used to mean: + +The program, hb.py. All its documentation and support files. +Anything included in the HB software distribution from TundraWare Inc. + +Throughout this Agreement the term "User" is used to mean: + +Any person who enagaged in any of the following activities: + + Uses the HB program in any way. + + Reads the HB documentation. + + Studies the HB program source code or supporting files. + + Makes use of any part of the HB software distribution for any + purpose. + + Duplicates and/or distributes the HB software distribution. + +2) OWNERSHIP + +HB Is Copyright (c) 2001, TundraWare Inc., All Rights Reserved. + + +3) TERMS + +Permission is hereby granted to the User for the duplication and use +of HB so long as ALL the following conditions are met: + + 1) The User of HB understands and agrees that this is EXPERIMENTAL + SOFTWARE which is provided "AS-IS" with no warranties expressed + or implied by TundraWare Inc. + + 2) The User acknowledges HB has NOT been tested for: + + a) Correct operation + b) Freedom from unintended consequences + c) Any operation or condition which might cause damage to the + User's or other machines, software, networks, or data, or + which might cause any breach of system security of the + User's system(s) or any other systems. + + 3) By using HB in any way, the User does so at their own risk and + agrees to hold TundraWare Inc. harmless for any damage, + direct or indirect, that this software may or does cause to + the User's computational environment, including, but not + limited to, the User's or others' hardware, software, network, + or data. THE USER FURTHER AGREES TO HOLD TUNDRAWARE + INC. HARMLESS FOR ANY ECONOMIC DAMAGE OR ANY OTHER ADVERSE + CONSEQUENCE, DIRECT OR INDIRECT, CAUSED BY THE USE OF HB. + + 4) If duplicated and/or distributed, no fee beyond reasonable + duplication charges may be charged for HB. No commercial + use of HB which involves any remuneration beyond these + duplication charges is permitted. + + 5) Any distributed copies of HB must include all the originally + provided software, documentation, and licensing information in + their original distribution format and packaging without any + modifications. + + + + +IF YOU DO NOT UNDERSTAND, OR CANNOT ABIDE BY ANY OF THESE CONDITIONS, +DO NOT USE HB. + +To report bugs or suggest improvements, contact: tundra@tundraware.com diff --git a/2-HowToUse.txt b/2-HowToUse.txt new file mode 100644 index 0000000..ece8dfe --- /dev/null +++ b/2-HowToUse.txt @@ -0,0 +1,147 @@ +$Id: 2-HowToUse.txt,v 1.1 2001/07/14 06:17:00 tundra Exp $ + + HOW TO USE HB + ============= + +Before you dive into the code, it's useful to get a basic understanding +of how HB works from a user's point-of-view. + +Budget Files +============ + +Before starting HB, you have to create a budget file with any old +editor (one of the ways you might extend HB as a programming exercise +would be adding the ability to create new budgets). This file +is organized like this: + +Cash 3000 2343.44 +Books 100 55.41 +Creditcards 0.0 562.8 +Dsl 39.0 0.0 +Rent 1000.0 0.0 +Savings 200.0 4320 +Utilities 160.0 0.0 + + +The first column is the the account Name. + +The second column is the amount you add to that account (the "Burn Rate") +every month. For example, the Cash account has a burn rate of 3000 - +that is presumably how much money you bring in to that budget every +month. + +The third column is the current account Balance. You can either set +this to be 0 intitially, and enter the amounts through HB, or prime +the Budget with the amount you want. + +The columns should be separated with spaces and terminate with a +single new line. HB does very little checking on the sanity of the +budget file, so mis-formatting this file will blow up the program. +Waddya want fer free? + +You then have to make one edit to the hb.py file and tell it where +it can find its budget files. At the top of the program a very few +variables that users should change. You'll see something like this: + +BASEDIR = "./" +BUDFIL = "hb.txt" + +This says that the directory which holds HB budget files is the +current directory, and the default budget files is called 'hb.txt' +which is what HB will load when it starts up. + + +Running The Program +=================== + +Once it starts up, HB will show you a screen something like this: + +------ + HomeBudget - Version 1.71 - Fri Jul 13 23:10:33 2001 + Copyright (c) 2001, TundraWare Inc. All Rights Reserved. + Budget: hb.txt + + + + +Books 55.41 Creditcards 562.8 Dsl 0.0 +Rent 0.0 Savings 4320.0 Utilities 0.0 +------------------------------------------------------------------------------- +Total Cash: 2343.44 Total Debits: 4938.21 Available Cash: -2594.77 + + + +abandon alance redit ebit oad onthly +

ay save+uit pint ave ransfer ndo + +Command? +----- + +The 'hotkey' to invoke a command is delimited by <>. + +If you key in an EOF at any point, the program will exit w/o saving + +If you key in a Ctrl-C, it does nothing under Unix, and aborts the +program under Win32. + +If you hit Enter to any prompt (blank line) the program will abort +the transaction underway and take you back to this main screen prompt. + +The columns above the separator line are your accounts. The sum of +all these accounts is your Total Debits. + +Available Cash = Cash-Debits In this case you're running a big deficit, +so you better either get more Cash, or quit reserving so much in Savings ;) + + +Here's roughly what the commands do: + +abandon Exit w/o saving and recalculate budget. + +alance If you have more Cash than Debits, put the excess in Savings. + If you have more Debits than Cash, move money from Savings into + Cash. This feature requires there be a Savings account. If + there is not one, the feature will not appear on the toolbar. + +redit Add funds to an account. + +ebit Remove funds from an account. + +oad Load a new budget. If you just hit Enter at the prompt, + it will reload the budget you are working on. + +onthly Increase each account by its monthly Burn Rate amount. + You typically do this at the beginning of a month in anticipation + of getting paid and having your recurring bills show up. + +

ay Each account reflects an indebtedness or liability. + When you pay from an account, the account balance is + reduced by the amount you select and Cash is decremented + accordingly (Unless your try to pay from the Cash account - + then it only subtracts the amount once.) When prompted + for an amount to pay, if you just hit Enter, HB understands + this to mean you want to pay the entire amount in that account + and does so. + +save+uit Ummm, it saves the current budget and quits the program. + +pint Print a report to the last used reporting file/device. + Initially, by default, under Win32, this is LPT1: Under + any other OS, it is a file called 'HB-Report.txt'. But, + when you select this option it will prompt you for the + name of an output file or device. If you just hit Enter, + it takes the default, otherwise you can enter whatever + you like. + +ave Save the current budget, but don't exit. + +ransfer Move funds from one account to another. + +ndo UnDo the last thing you did. You can keep UnDoing things + until HB is back to the point at which it started. UnDo + has no effect for Saves or Prints, but it does return to + the state prior to loading a budget. Try it - it's fun. + + + + diff --git a/3-UnderTheHood.txt b/3-UnderTheHood.txt new file mode 100644 index 0000000..e76367f --- /dev/null +++ b/3-UnderTheHood.txt @@ -0,0 +1,221 @@ +$Id: 3-UnderTheHood.txt,v 1.1 2001/07/14 06:17:00 tundra Exp $ + + UNDER THE HOOD: COMMENTARY ON HOW HB WORKS + ========================================== + +The hb.py source file is full of (hopefully) helpful comments. You'll +probably get the most help from reading that source. Just so you can +kind of see the overall picture, this document is intended to give you +more of a "Thousand Foot View" of the code. At the end, I talk a bit +about some of the design decisions I made, and why. + + +Code Structure +-------------- + +The code is laid out like this: + + Variables Available For User Modification + + Imports + + Aliases & Redefinitions + + Constants & Literal Strings + + Prompts & Strings Used In The Application + + The Command Table + This is where all available commands are described in + tabular fashion. This is the heart of how the Command + Interpreter knows what to do when the user presses a key. + + Global Variables & Data Structures + + Class Definitions + + Main Handler Function Definitions + There is one of these for each feature the user can + select. So there is a Main Handler for Print, Transfer, + Save, and so on. + + Support Function Definitions + Functions we need to do the "housekeeping" inside HB. + + Startup Code + (This is where the program actually begins execution.) + This initializes some variables and objects, loads the + default budget and generally gets things ready to go. + + Command Interpreter Loop + Sits and waits for the user to key in a command and + then does something about it. + + +Comments On The Command Interpreter +----------------------------------- + +The Command Interpreter is the most complicated part of hb.py, but +it's pretty well commented in the code. Here's the Big Picture: + + +Setting Up The Command Interpreter +---------------------------------- + +The Options table at the top of the program has a description +for each feature (like Save or Transfer) that the user can invoke. +This "description" consists of two parts: + + 1) The feature *name*, and the *name* of the Main Handling Function + for that feature. + + 2) A list (a tuple, really) of all the functions that need to + be run to gather arguments from the user in order for + the feature's Main Handling Function to actually be run. + For example, before we can run Transfer(), we have to discover + the "From Account", the "To Account", and "How Much". + +When you want to add a new feature you have to: + + 1) Write a Main Handler Function for it + 2) Write any needed Support Functions + 3) Put an entry in Options for it. + +These descriptions are all ***STRINGS***. Every time HB loads a new +budget, it reads these strings to dynamically construct a so-called +"Jump Table" (sometimes these are called "Dispatch Tables"). This +Jump Table is what the Command Interpreter uses to figure out what to +do when the user asks for something. + +You would think that you only need to build the Jump Table once and be +done with it, but that won't work. Some features may only be available +if certain accounts are present in a given budget. For example, +Balancing an account depends on the presence of another account +named by the BALACCT variable (in the user variable section at the +top of the code). If this account is not present in a new budget, +then HB is smart enough to suppress showing that option on the toolbar. +That's why you have to build the JumpTable every time you Load a budget. + + +Running The Command Interpreter +------------------------------- + +Here, roughly, is what the Command Intepreter is doing in pseudo-code: + +If we're not done: + + Display the state of the budget to the user. + + Wait for the user to keyin a legitimate hotkey requesting a feature. + A blank line just means to skip it and go back to the top of this + whole Command Interpreter loop. + + Use the hotkey to index into the Jump Table (a Python dictionary) + and retrieve the dispatch information for that feature. + + From this dispatch information we get: + + The name of the Main Handler Function that is responsible + for that feature. + + The names of all the Support Functions (and their user + prompts, if any) that we have to run to build a list of + arguments to send to the Main Handler Function. + + Run each of the Support Functions we found and add their return + values to args list. + + Create an Invocation Record - a list consisting of two things, + a reference to the Main Handler Function, and the args we're sending + it - and push it onto the UnDo stack. We skip this step if the + requested feature was UnDo - you cannot UnDo and Undo in HB. + + Now, call the Main Handler Function and pass it the args list we + just built. + + +Why The Two Step Process From Options-> JumpTable ->Command Interpreter? +Why Not Just Code The Jump Table Directly? +------------------------------------------------------------------------ + +Actually that's how I started out. I directly encoded a Jump Table +something like this: + +JumpTable = { + "B" : ("alance", Balance, ((BalAmount, ""),)) + ... And so on + } + + +There were several reasons I didn't like this approach: + +1) Minor Issue: With a static Jump Table, I'd have to add a flag to + determine whether the feature was enabled or not (To supress things + like Balance when BALACCT is not present in the budget). Not a big + deal and, in retrospect, probably a little cleaner than the way + I ended up doing it. + +2) Major Issue: Notice that in this version of reality Balance and + BalAmount are *object references* NOT strings. (A function in + Python is an object - *everything* in Python is an object. So a + function reference is really an object reference.) This means that + the JumpTable would have to appear in the code *after* these + functions were defined. (Python hates forward references - there's + probably some way around this, but I've not bothered to figure it + out.) This would have meant having literal strings in two places + in the code - at the top, where most of the literals are defined + and right after all the function definitions. I take this + Destringing and Maintenance stuff pretty seriously - it is the way + things are gonna be from here forward. Putting stuff that needs to + be changed in different places in the code it just begging for + trouble later on. + +3) I wanted to play around with the 'eval' feature of Python. + It works very nicely, BTW ;)) + + +Why Bother With All This Hocus-Pocus? +------------------------------------- + +Yeah! Why not just have a bunch of nested if-elif pairs to do the same thing. +Several reasons, Grasshopper: + +1) This way keeps all the literal strings out of the code. Destringing is + pretty important as the globe gets smaller and smaller. Not everyone + speaks and reads English (which is one of the reasons Python has Unicode + character support). If you code is littered with strings, translating it + to another language is a real pain and likely to be buggy. This way, every + string you may ever have to change sits happily at the top of the code + before even the first class definition. Repeat after me: + + SEPARATE & ISOLATE LITERALS FROM LOGIC + SEPARATE & ISOLATE PRESENTATION FROM COMPUTATION + SEPARATE & ISOLATE DATABASE FROM DATA STORAGE INFRASTRUCTURE + SEPARATE & ISOLATE APPLICATIONS FROM SYSTEMS INFRASTRUCTURE + SEPARATE & ISOLATE SYNCHRONOUS EXECUTION SEMANTICS + + The failure to do these things is why the Internet is such a buggy mess, + why scale is so hard to achieve, why security is tough to do.... + (Ooops, I just regressed back into Systems Architect mode. Sorry 'bout + that, I'm all better now.) + +2) Changing things and general maintenance is much simpler. It's relatively + easy to maintain a table and some functions. It is VERY painful to maintain + nested conditionals as the program logic evolves. + +3) Adding new features is a snap. Try it and see. Just add a trivial feature + that, say, displays how many accounts are in the current budget. Once you + understand what's going on here, it takes about 5 minutes. + +4) More structure means less bugs, means less testing (hopefully). + +5) This style of event-driven, table-lookup coding is well suited for + GUIs. Someday, I'm gonna break down and learn Tkinter and/or + wxPython. When I do, this code structure should change very little. + I'll have to go to new routines for keyboard input and display, but + the rest of it should pretty much stay the same way. + + + + + diff --git a/4-Limitations-Enhancements.txt b/4-Limitations-Enhancements.txt new file mode 100644 index 0000000..4287f15 --- /dev/null +++ b/4-Limitations-Enhancements.txt @@ -0,0 +1,65 @@ +$Id: 4-Limitations-Enhancements.txt,v 1.1 2001/07/14 06:17:28 tundra Exp $ + + KNOWN LIMITATIONS AND PROBLEMS OF HB + ==================================== + +- For some reason, I cannot get the Win32 version of Python 2.1 to + properly trap a KeyboardInterrupt. The code is there and it works + on Unix (at least FreeBSD 4.3), but it just plain does not want to + cooperate under WinDoze. + +- There are several places where I don't bother to check for error + conditions which can cause the Python runtime to burp and end with a + traceback. + +- There is almost no checking for sanity in the budget file being + loaded, so it is easy to blow up HB with a bad budget. + + + + ENHANCEMENTS TO HB YOU MIGHT TRY YOURSELF + ========================================= + +- Check correctness and/or be more forgiving about reading in budget + files. + +- Use the getopt library to add command line processing features to + HB. You could, for instance, use the command line to name the + initial budget file or default print device/file. + +- Add a new user feature of your own. This means adding the correct + entry in Options, writing a Main Function Handler for the new + feature and any needed support functions to compute arguments. + Remember, keep literal strings out of the code and in the tables at + the beginning of the program. A good place to start would be + features for creating new budgets, and adding or deleting accounts + within a budget. + +- Modify the Print() Main Function Handler to properly print under + Unix or MacOS. + +- Get the program running using curses on Unix or a similar screen + management system for Win32. + +- GUIify HB with Tkinter or wxPython. The code is already setup for + it to be event driven (more or less) in a GUI. + +- Gut the Jump Table and Command Interpreter logic out of HB and write + your own application using this approach. + +- Rewrite all the arithmetic and currency objects in HB to use BCD or + the FixedPoint library to avoid all the rounding error garbage + floats give you. + +- Modify the Command Interpreter to accept multiple arguments at once + to avoid the endless input dialog. For example: + + t Sav Ph 300 + + Would mean to transfer 300 (Dollars/Pesos/Marks ...) from the Savings + to to Phone Accounts. + +- Replace the (really lame) ABORTINPUT global flag mechanism with + a custom exception object to abort the current input dialog. + Python lets you define your own exception types, and this is a + good application of same.