GitXplorerGitXplorer
a

parsedcmd

public
7 stars
1 forks
0 issues

Commits

List of commits on branch master.
Unverified
f2b2b5f3d44b21dae6414b0ac90b61e6b4af2e0d

Make tests work properly for Python 2.6+.

aanntzer committed 12 years ago
Unverified
2fbd5c1caacf9c403deb49eddf0c7af5ca291747

Autohelp from signature. Respect self.stdout.

aanntzer committed 12 years ago
Unverified
9c28b61af2bb4153feec6806a6c294423e378963

Actually tested on Python 2.6; renamed tests.

aanntzer committed 12 years ago
Unverified
738dbf0c4f3387ead162aa225b6cfad56008b83c

Nose tests, bugfixes, README example that works.

aanntzer committed 12 years ago
Unverified
f854e6cb0a409cd3713279154c79c045ca51751f

Renamed to parsedcmd. Debugged getcallargs.

aanntzer committed 12 years ago
Unverified
45a88da3171e65fcb4b03f3c3ff34d16f4b29ede

First public version.

aanntzer committed 13 years ago

README

The README file for this repository.

ParsedCmd - A Cmd with argument list parsing

ParsedCmd is an extension built around the excellent cmd module of the standard library. Cmd allows one to build simple custom shells using do_* methods, taking care in particular of the REPL loop and the interactive help. However, no facility is given for parsing the argument line (do_* methods are passed the rest of the line as a single string argument).

With ParsedCmd, do_* methods can be type-annotated, either using Python 3's function annotation syntax, or with the ad-hoc annotate decorator, allowing the dispatcher to parse the argument list for them. Arguments can also be marked as keyword-only, either using Python 3's dedicated syntax, or with the ad-hoc kw_only decorator, in which case they will be assigned only if given as explicit arguments, i.e. method -option opt translates into do_method(option=opt) if option is keyword-only.

These annotations can also used to enhance the output of the default do_help method, by setting the show_usage attribute of the ParsedCmd object to True.

Example (Python 2.6-2.7)

from parsedcmd import *

class UI(ParsedCmd):
    # Non-annotated arguments default to str.
    # boolean is a utility function, that casts every string to True,
    # except "f", "false", "off" and "0" (case-insensitive).
    @annotate(flag=boolean, repeat=int)
    @kw_only("flag", "repeat")
    def do_print(self, line="abc", flag=True, repeat=1):
        """Print a given string (defaults to "abc").
        Print nothing if -flag is set to false.
        Print multiple copies if -repeat N option is given.
        """
        if flag:
            for i in range(repeat):
                print(line, file=self.stdout)

    # *args can also be annotated.
    # Python 2's usual limitations about mixing keyword arguments and *args
    # applies.
    @annotate(mul=int, nums=int)
    def do_multiply(self, mul, *nums):
        """Print `mul` times the numbers given.
        """
        for num in nums:
            print(mul * num, file=self.stdout)

    # Do not parse the argument line for do_shell.
    @gets_raw
    def do_shell(self, line):
        """Evaluates the given line.
        """
        eval(line)

Example (Python 3)

from parsedcmd import *

class UI(ParsedCmd):
    def do_print(self, line="abc", *, flag: boolean=True, repeat: int=1):
        """Print a given string (defaults to "abc").
        Print nothing if -flag is set to false.
        Print multiple copies if -repeat N option is given.
        """
        if flag:
            for i in range(repeat):
                print(line, file=self.stdout)

    def do_multiply(self, mul: int, *nums: int):
        """Print `mul` times the numbers given.
        """
        for num in nums:
            print(mul * num, file=self.stdout)

    @gets_raw
    def do_shell(self, line):
        """Evaluates the given line.
        """
        eval(line)

Remarks

The parsing is done in the following steps:

  • the input line is passed to the split() method (by default shlex.split()), and the result is bound to the argument list of the do_* method.
  • initial options (-opt val) are assigned to keyword-only arguments (which can be simulated in Python 2 using the @kw_only decorator).
  • each value bound to an argument annotated with a callable, either through @annotate([arg=callable]*), or through Python 3's function annotation syntax (f(arg[=default]: callable)), is passed to it; however, this does not affect default values),
  • if do_* has an annotated *args argument, then each element of args / each value in kwargs is casted.
  • in theory, **kwargs are also parsed and cast but there is currently effectively no way to assign to them.

ParsedCmd interacts imperfectly with decorated functions. Currently, it follows the __wrapped__ attribute until finding a function that either doesn't have this attribute or is decorated with @use_my_annotations, uses the signature and the annotations of this function to create the argument list, which is then passed to the wrapper function. In particular, ParsedCmd provides a wraps function that works like the one provided in functools, but also sets the __wrapped__ attribute (as in Python 3.3 or higher).

Testing

Just run py.test in the source folder.