| 1 | ======================= |
|---|
| 2 | Commandline framework |
|---|
| 3 | ======================= |
|---|
| 4 | |
|---|
| 5 | Overview |
|---|
| 6 | ======== |
|---|
| 7 | |
|---|
| 8 | pkgcore's own commandline tools and ideally also most external tools |
|---|
| 9 | use a couple of utilities from pkgcore.util.commandline to enforce a |
|---|
| 10 | consistent interface and reduce boilerplate. There are also some |
|---|
| 11 | helpers for writing tests for scripts using the utilities. Finally, |
|---|
| 12 | pkgcore's own scripts are started through a single wrapper (just to |
|---|
| 13 | reduce boilerplate). |
|---|
| 14 | |
|---|
| 15 | Writing a script |
|---|
| 16 | ================ |
|---|
| 17 | |
|---|
| 18 | Whether your script is intended for inclusion with pkgcore itself or |
|---|
| 19 | not, the first things you should write are a commandline.OptionParser |
|---|
| 20 | subclass (unless your script takes no commandline arguments) and a |
|---|
| 21 | main function. The OptionParser is a lightly customized |
|---|
| 22 | optparse.OptionParser, so the standard `optparse documentation`_ |
|---|
| 23 | applies. Differences include: |
|---|
| 24 | |
|---|
| 25 | * A couple of standard options and defaults are added. Some of this |
|---|
| 26 | uses __init__.py, so if you override that (which you will) remember |
|---|
| 27 | to call the base class (with any keyword arguments you received). |
|---|
| 28 | * The "Values" object used is a subclass, with a "config" property |
|---|
| 29 | that autoloads the user's configuration. You should access this as |
|---|
| 30 | late as possible for a more responsive ui. |
|---|
| 31 | * check_values applies some minor cleanups, see the module for |
|---|
| 32 | details. Remember to call the base method (you will usually want to |
|---|
| 33 | do some things here). |
|---|
| 34 | |
|---|
| 35 | The "main" function takes an optparse "values" object generated by |
|---|
| 36 | your option parser and two pkgcore.util.formatters.Formatter |
|---|
| 37 | instances, one for stdout and one for stderr. This one should do the |
|---|
| 38 | actual work your script does. |
|---|
| 39 | |
|---|
| 40 | The return value of the main function is your script's exit status. |
|---|
| 41 | Returning None is the same thing as returning 0 (success). |
|---|
| 42 | |
|---|
| 43 | If you have used optparse before you might wonder why main only |
|---|
| 44 | receives an optparse values object, not the remaining arguments. This |
|---|
| 45 | is handled a bit differently in pkgcore: if you handle arguments you |
|---|
| 46 | should sanity-check them in check_values and store them on the values |
|---|
| 47 | object. check_values should always return an empty tuple as second |
|---|
| 48 | argument, either because no arguments were passed or because they were |
|---|
| 49 | all accepted by check_values. We believe this makes more sense, since |
|---|
| 50 | it stores everything learned from the commandline on a single object. |
|---|
| 51 | |
|---|
| 52 | All output *has* to go through the formatter. If you use "print" |
|---|
| 53 | directly the formatter will lose track of where it is in the line, |
|---|
| 54 | which will cause weird output if you use the "wrap" option of the |
|---|
| 55 | formatter. The test helpers also rely on all output going through the |
|---|
| 56 | formatters. |
|---|
| 57 | |
|---|
| 58 | To actually run your script you call pkgcore.util.commandline.main |
|---|
| 59 | (do not confuse this with your own script's main function, the two are |
|---|
| 60 | quite different). The simplest (and most common) call is |
|---|
| 61 | ``commandline.main({None: (yourscript.OptionParser, yourscript.main)})``. |
|---|
| 62 | The weird dict is used for subcommands_. The recommended place to put |
|---|
| 63 | this call is in a tiny script that just imports your actual script |
|---|
| 64 | module and calls commandline.main. Making your script an actual module |
|---|
| 65 | you can import means it can be tested (and it can be useful in |
|---|
| 66 | interactive python or for quick hacky scripts). |
|---|
| 67 | |
|---|
| 68 | commandline.main takes care of a couple of things, including setting |
|---|
| 69 | up a reporter for the standard library's logging_ package and |
|---|
| 70 | swallowing exceptions from the configuration system. It does *not* |
|---|
| 71 | swallow any other exceptions your script might raise (although this might |
|---|
| 72 | become an option in the future). |
|---|
| 73 | |
|---|
| 74 | check_values and main: what goes where |
|---|
| 75 | -------------------------------------- |
|---|
| 76 | |
|---|
| 77 | The idea (as you can guess from the names) is that check_values makes |
|---|
| 78 | sure everything passed on the commandline makes sense, but no more |
|---|
| 79 | than that. |
|---|
| 80 | |
|---|
| 81 | * The best way to report incorrect commandline parameters is by |
|---|
| 82 | calling ``error("error message goes here")`` on the option parser. |
|---|
| 83 | You cannot do this from main, since it has no access to the option |
|---|
| 84 | parser. Please do not try to print something similar through the |
|---|
| 85 | ``err`` formatter here, shift the code to check_values. |
|---|
| 86 | * check_values does not have access to the out or err formatter. The |
|---|
| 87 | only way it should "communicate" is through the error (or possibly |
|---|
| 88 | exit) methods. If you want to produce different kinds of output, do |
|---|
| 89 | it in main. (it is possible the option parser will grow a |
|---|
| 90 | ``warning`` method at some point, if this would be useful let us |
|---|
| 91 | know (file a trac ticket). |
|---|
| 92 | * Use common sense. If it is part of your script's main task it should |
|---|
| 93 | be in main. If it changes the filesystem it should definitely be in |
|---|
| 94 | main. |
|---|
| 95 | |
|---|
| 96 | Subcommands |
|---|
| 97 | =========== |
|---|
| 98 | |
|---|
| 99 | The main function recently gained some support for subcommands (which |
|---|
| 100 | you probably know from most version control systems). If you find |
|---|
| 101 | yourself trying to reimplement this kind of interface with optparse, |
|---|
| 102 | or one similar to emerge with a couple of mutually exclusive switches |
|---|
| 103 | selecting a mode (--depclean, --sync etc.) then you should try using |
|---|
| 104 | this subcommand system instead. |
|---|
| 105 | |
|---|
| 106 | To use it, simply define a separate OptionParser and main function for |
|---|
| 107 | every subcommand and use the subcommand name as the key in the dict |
|---|
| 108 | passed to commandline.main. The key ``None`` used for "no subcommand" |
|---|
| 109 | can still be used too, but this is probably not a good idea. |
|---|
| 110 | |
|---|
| 111 | If there is no parser/main pair with the key ``None`` and an |
|---|
| 112 | unrecognized subcommand is passed (including ``--help``) an overview |
|---|
| 113 | of subcommands is printed. This uses the docstring of the __main__ |
|---|
| 114 | function, so put something useful there. If there is a ``None`` parser |
|---|
| 115 | you should include the valid subcommands in its ``--help`` output, |
|---|
| 116 | since there is no way to get at commandline.main's autogenerated |
|---|
| 117 | subcommand help if a ``None`` parser is present. |
|---|
| 118 | |
|---|
| 119 | pwrapper |
|---|
| 120 | ======== |
|---|
| 121 | |
|---|
| 122 | Because having a dozen of different scripts each just calling |
|---|
| 123 | commandline.main would be silly pkgcore's own scripts are all symlinks |
|---|
| 124 | to a single wrapper which imports the right actual script based on the |
|---|
| 125 | ``sys.argv[0]`` it is called with and runs it. The script module needs |
|---|
| 126 | to define either a commandline_commands dict (for a script with |
|---|
| 127 | subcommands) or a class called OptionParser and function called main |
|---|
| 128 | for this to work. |
|---|
| 129 | |
|---|
| 130 | The script used in the source tree also takes care of inserting the |
|---|
| 131 | right pkgcore package on sys.path. Installed pkgcore uses a different |
|---|
| 132 | wrapper without this magic. |
|---|
| 133 | |
|---|
| 134 | If you write a new script that should go into pkgcore itself, use the |
|---|
| 135 | wrapper. If you maintain it externally and do not have a lot of |
|---|
| 136 | scripts, don't bother duplicating this wrapper system. Don't bother |
|---|
| 137 | duplicating the path manipulation either: if you put your script in |
|---|
| 138 | the same directory your actual package or module is in (no separate |
|---|
| 139 | "bin" directory) and don't run it as root no path manipulation is |
|---|
| 140 | required. |
|---|
| 141 | |
|---|
| 142 | Tests |
|---|
| 143 | ===== |
|---|
| 144 | |
|---|
| 145 | Because additions to the default options pkgcore uses can make your |
|---|
| 146 | script unrunnable it is *critical* to have at least rudimentary tests |
|---|
| 147 | that just instantiate your parser. Because optparse defaults to |
|---|
| 148 | calling sys.exit for a parse failure and the pkgcore version also |
|---|
| 149 | likes to load the user's configuration files, writing those tests is |
|---|
| 150 | slightly tricky. ``pkgcore.test.scripts.helpers`` tries to make it |
|---|
| 151 | easier. It contains a mangle_parser function that takes an |
|---|
| 152 | OptionParser instance and makes it raise exceptions instead of |
|---|
| 153 | exiting. It also contains a mixin with some extra assert methods that |
|---|
| 154 | check if your option parser and main function have the desired effect |
|---|
| 155 | on various arguments and configurations. See the docstrings for more |
|---|
| 156 | information. |
|---|
| 157 | |
|---|
| 158 | .. _`optparse documentation`: |
|---|
| 159 | http://docs.python.org/lib/module-optparse.html |
|---|
| 160 | .. _logging: http://docs.python.org/lib/module-logging.html |
|---|