[opensuse-buildservice] [gsoc] osc2 client - summary of week 11
Hi, here's a small summary of the 11th (coding) week. Last week I worked on implementing the new commandline interface. While doing so I faced several "issues": * How to combine argparse and our oscargs url-like syntax? Basically we have to run our oscargs parser on the result which is returned by argparse.ArgumentParser's parse_args method. The problem is that both parsers have a different "syntax" that is using a naive approach will lead to some redundancies (we specify the ui twice: one time for argparse and one time for oscargs). In order to avoid this we need some "interface" to which the oscargs syntax is passed and which configures the argparse parser accordingly. * How to support custom commands? We also have to provide an "easy" way to specify custom commands. Additionally it might be handy if existing commands can be enhanced (either by adding additional options etc. or by adding a new subcommand etc.). The best would be if the user simply drop his/her plugins in a specific directory and osc will scan this directory and use the new plugins/commands. * Specifying the ui programmatically is somehow confusing/cluttered. It would be much better if the ui can be specified in a more "declarative" way without the syntactic "overhead" (well that's a bit exaggerated) which is needed to configure the parser. Additionally it would be nice to have a convenient way to specify a multi line description for a command (hardcoding the str into the source makes the code "harder" to read). Finally I ended up with a small DSL which can be used to specify the ui in a "declarative" way (the initial idea + syntax is taken from the django framework (see [1])). Example: Assume we want to implement a request command which consists (for the sake of simplicity) of 2 subcommands "list" and "accept". This can be specified like the following: # file: osc/cli/request/ui.py class Request(CommandDescription, OscCommand): """Show and modify requests.""" cmd = 'request' class RequestList(CommandDescription, Request): """List requests. By default open requests for a specific project or package will be listed. Examples: osc request list api:// osc request list api://project osc request list api://project/package """ cmd = 'list' args = 'api://project?/package?' opt_user = Option('U', 'user', 'list only requests for USER') opt_group = Option('G', 'group', 'list only requests for GROUP') opt_state = Option('s', 'state', 'list only requests with state STATE', choices=['new', 'review', 'accepted', 'revoked', 'declined', 'superseded'], action='append') func = request_list class RequestAccept(CommandDescription, Request): """Accept a specific request. ... """ cmd = 'accept' args = 'api://reqid' func = request_accept In order to add the request command it is sufficient to add an import osc.cli.request.ui statement to the main cli module. This produces the following output: marcus@linux:~/osc2/osc/cli> python cli.py request -h usage: cli.py request [-h] {list,accept} ... Show and modify requests. positional arguments: {list,accept} list List requests. accept Accept a specific request. optional arguments: -h, --help show this help message and exit marcus@linux:~/osc2/osc/cli> and marcus@linux:~/osc2/osc/cli> python cli.py request list -h usage: cli.py request list [-h] [-s {new,review,accepted,revoked,declined,superseded}] [-G GROUP] [-U USER] api://project?/package? List requests. By default open requests for a specific project or package will be listed. Examples: osc request list api:// osc request list api://project osc request list api://project/package positional arguments: api://project?/package? optional arguments: -h, --help show this help message and exit -s {new,review,accepted,revoked,declined,superseded}, --state {new,review,accepted,revoked,declined,superseded} list only requests with state STATE -G GROUP, --group GROUP list only requests for GROUP -U USER, --user USER list only requests for USER marcus@linux:~/osc2/osc/cli> How does it work? First of all each class which defines a command or subcommand has to inherit from class "CommandDescription". If a subcommand is to be defined it also has to inherit from the "parent" command (that is in our example "RequestList" and "RequestAccept" inherit from class "Request" (which in turn inherits from class "OscCommand" (from this class all toplevel commands have to inherit))). In short: with the help of the inheritance hierarchy it is possible to define a command <- subcommand hierarchy. Note: actually the classes "RequestList" and "RequestAccept" only inherit from "CommandDescription". The "parent" command base class is only needed for a "marking" purpose (it is filtered out with the help of a metaclass when the concrete class is "constructed" - I'll leave out the details for now and may write a dedicated blogpost about it). Now the remaining task is to define and implement the commands (note: we will definitely not finish the project on the "suggested pencils down" date and use the week until the "firm pencils down" date for coding...). Marcus [1] https://docs.djangoproject.com/en/1.4/topics/db/models Here's a small example how to modify an existing command: # plugins/myrequestaccept.py from osc.cli.description import Option import osc.cli.request.ui class MyRequestAccept(osc.cli.request.ui.RequestAccept): # add a new option opt_foo = Option('f', 'foo', help='foo option') This leads to marcus@linux:~/osc2/osc/cli> python cli.py request accept -h usage: cli.py request accept [-h] [-f FOO] api://reqid positional arguments: api://reqid optional arguments: -h, --help show this help message and exit -f FOO, --foo FOO foo option marcus@linux:~/osc2/osc/cli> -- To unsubscribe, e-mail: opensuse-buildservice+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-buildservice+owner@opensuse.org
participants (1)
-
Marcus Hüwe