Skip to content

Commit

Permalink
Adding examples and tuning interfaces.
Browse files Browse the repository at this point in the history
  • Loading branch information
mayfield committed Aug 28, 2015
1 parent 1237c79 commit df57d66
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 34 deletions.
9 changes: 4 additions & 5 deletions examples/hello_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ class Hello(shellish.Command):

name = 'hello'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_subcommand(World)

def run(self, args):
shellish.Shell(self).cmdloop()
""" Just run the shell if a subcommand was not given. """
self.shell()


class World(shellish.Command):
Expand All @@ -21,5 +18,7 @@ class World(shellish.Command):
def run(self, args):
print('Hello World')


hello = Hello()
hello.add_subcommand(World)
hello()
34 changes: 34 additions & 0 deletions examples/simple_and_advanced.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
A Frankenstein combo of advanced style and simple (autocommand).
"""

import shellish


@shellish.autocommand
def subcommand1(firstarg, second, *args, optional=1, optionalstr:str=None,
mustbeint:int=None, **kwargs:bool):
print("Hi from sub1", firstarg, second, args, optional, optionalstr,
mustbeint, kwargs)


@shellish.autocommand
def subcommand2(firstarg, second, *args, optional=1, optionalstr:str=None,
mustbeint:int=None, **kwargs:bool):
print("Hi from sub2", firstarg, second, args, optional, optionalstr,
mustbeint, kwargs)


class Root(shellish.Command):
""" Shellify some autocommands. """

def __init__(self):
super().__init__()
self.add_subcommand(subcommand1)
self.add_subcommand(subcommand2)

def run(self, args):
self.shell()

root = Root()
root()
19 changes: 19 additions & 0 deletions examples/simple_nesting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
Nesting commands using simple syntax.
"""

import shellish


@shellish.autocommand
def main():
print("Default Action")


@shellish.autocommand
def sub(option=None):
print("Hi from sub", option)


main.add_subcommand(sub)
main()
66 changes: 66 additions & 0 deletions examples/skin_a_cat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
Demo of different ways to skin a cat.
"""

import shellish

##############
# Decorators #
##############
@shellish.autocommand
def cat1():
cat1.shell()

@shellish.autocommand
def sub1(optional:int=1):
print("ran subcommand1", optional)

cat1.add_subcommand(sub1)


###############
# Composition #
###############
cat2 = shellish.Command(name='cat2', doc='composition cat')
cat2.run = lambda args: cat2.shell()

sub2 = shellish.Command(name='sub2', doc='composition cat sub')
sub2.add_argument('--optional', type=int, default=2)
sub2.run = lambda args: print("ran subcommand2", args.optional)
cat2.add_subcommand(sub2)


###############
# Inheritance #
###############
class Cat3(shellish.Command):
""" Inheritance cat. """

name = 'cat3'

def run(self, args):
self.shell()

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_subcommand(Sub3)


class Sub3(shellish.Command):
""" Inheritance cat sub. """

name = 'sub3'

def setup_args(self, parser):
self.add_argument('--optional', type=int, default=3)

def run(self, args):
print("ran subcommand3", args.optional)


# Putting it together for a demo..
main = shellish.Command(name='main', doc='harness')
main.add_subcommand(cat1)
main.add_subcommand(cat2)
main.add_subcommand(Cat3)
main.shell()
18 changes: 18 additions & 0 deletions examples/tabcompletion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
Demo how to add your auto tab completers.
"""

import random
import shellish

def thing_completer(prefix):
letters = 'qwertyuiopasdfghjklzxcvbnm'
word = lambda: ''.join(random.sample(letters, random.randint(1, 16)))
return set(word() for x in range(random.randint(1, 1000)))

thing = shellish.Command(name='thing', doc='Demo Tab Completion')
thing.add_argument('--choices', choices=['one', 'two', 'three'])
thing.add_argument('--function', complete=thing_completer)
root = shellish.Command()
root.add_subcommand(thing)
root.shell()
16 changes: 5 additions & 11 deletions shellish/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@
Public interface.
"""

from . import shell, completer, command
import importlib


def export(module, symbol):
globals()[symbol] = getattr(module, symbol)

for x in shell.__public__:
export(shell, x)
for x in completer.__public__:
export(completer, x)
for x in command.__public__:
export(command, x)
for x in ['shell', 'completer', 'command']:
module = importlib.import_module('.%s' % x, 'shellish')
for sym in module.__public__:
globals()[sym] = getattr(module, sym)
21 changes: 12 additions & 9 deletions shellish/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ def run(self, args):
self.argparser.print_usage()
raise SystemExit(1)

def __init__(self, parent=None, **context):
def __init__(self, parent=None, doc=None, name=None, **context):
self.doc = doc or self.__doc__
if name:
self.name = name
self.subcommands = []
self.default_subcommand = None
self.context_keys = set()
Expand Down Expand Up @@ -147,9 +150,9 @@ def clean_docstring(self):
""" Return sanitized docstring from this class.
The first line of the docstring is the title, and remaining lines are
the details, aka git style. """
if not self.__doc__:
if not self.doc:
raise SyntaxError('Docstring missing for: %s' % self)
doc = [x.strip() for x in self.__doc__.splitlines()]
doc = [x.strip() for x in self.doc.splitlines()]
if not doc[0]:
doc.pop(0) # Some people leave the first line blank.
title = doc.pop(0)
Expand Down Expand Up @@ -320,6 +323,10 @@ class AutoCommand(Command):
to map the function signature to a parser configuration. Use the
@autocommand decorator to use it. """

def __init__(self, *args, func=None, **kwargs):
self.func = func
super().__init__(*args, **kwargs)

def run(self, args):
""" Convert the unordered args into function arguments. """
args = vars(args)
Expand Down Expand Up @@ -401,9 +408,5 @@ def autocommand(func):
""" A simplified decorator for making a single function a Command
instance. In the future this will leverage PEP0484 to do really smart
function parsing and conversion to argparse actions. """

class FuncCommand(AutoCommand):
__doc__ = func.__doc__ or 'Auto command for: %s' % func.__name__
name = func.__name__

return FuncCommand(func=func)
doc = func.__doc__ or 'Auto command for: %s' % func.__name__
return AutoCommand(doc=doc, name=func.__name__, func=func)
25 changes: 16 additions & 9 deletions test/autocommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Tests for the decorator that converts a function to a command.
"""

import json
import shlex
from shellish import autocommand
import unittest

Expand Down Expand Up @@ -217,13 +219,18 @@ def f(one:float):
with self.assertRaises(SystemExit):
f(argv=x)

def test_annotation_list(self):

class Nesting(unittest.TestCase):

def test_one_level(self):
@autocommand
def f(one:list):
self.assertIsInstance(one, list)
return one
for x in [0, 1, 1.1]:
self.assertEquals(f(argv=str(x)), x)
for x in ['nope']:
with self.assertRaises(SystemExit):
f(argv=x)
def main():
return 'main'

@autocommand
def sub():
return 'sub'

main.add_subcommand(sub)
self.assertEquals(main(argv=''), 'main')
self.assertEquals(main(argv='sub'), 'sub')

0 comments on commit df57d66

Please sign in to comment.