Commits

Andy Mikhailenko committed e9416e0

Fix issue #34: class members (instance/class/static methods) as commands

  • Participants
  • Parent commits 07122ab

Comments (0)

Files changed (4)

File argh/assembling.py

 
 Functions and classes to properly assemble your commands in a parser.
 """
+import argparse
 import sys
-import argparse
 
 from argh.completion import COMPLETION_ENABLED
 from argh.constants import (ATTR_ALIASES, ATTR_ARGS, ATTR_NAME,
                             ATTR_INFER_ARGS_FROM_SIGNATURE,
                             ATTR_EXPECTS_NAMESPACE_OBJECT)
-from argh.utils import get_subparsers
+from argh.utils import get_subparsers, get_arg_names
 from argh import compat
 
 
         return
 
     spec = compat.getargspec(function)
+    names = get_arg_names(function)
 
-    kwargs = dict(zip(*[reversed(x) for x in (spec.args, spec.defaults or [])]))
+    kwargs = dict(zip(*[reversed(x) for x in (names, spec.defaults or [])]))
 
     if sys.version_info < (3,0):
         annotations = {}
 
     # define the list of conflicting option strings
     # (short forms, i.e. single-character ones)
-    chars = [a[0] for a in spec.args]
+    chars = [a[0] for a in names]
     char_counts = dict((char, chars.count(char)) for char in set(chars))
     conflicting_opts = tuple(char for char in char_counts
                              if 1 < char_counts[char])
 
-    for name in spec.args:
+    for name in names:
         flags = []    # name_or_flags
         akwargs = {}  # keyword arguments for add_argument()
 

File argh/dispatching.py

 ~~~~~~~~~~~
 """
 import argparse
-import inspect
 import sys
 from types import GeneratorType
 
 from argh.completion import autocomplete
 from argh.assembling import add_commands, set_default_command
 from argh.exceptions import CommandError
+from argh.utils import get_arg_names
 
 
 __all__ = ['dispatch', 'dispatch_command', 'dispatch_commands']
             # actual function will pass
 
             spec = compat.getargspec(args.function)
+            names = get_arg_names(args.function)
 
-            positional = [all_input[k] for k in spec.args]
+            positional = [all_input[k] for k in names]
             keywords = {}
 
             # *args

File argh/utils.py

 ~~~~~~~~~
 """
 import argparse
+import inspect
+
+from argh import compat
 
 
 def get_subparsers(parser, create=False):
     else:
         if create:
             return parser.add_subparsers()
+
+
+def get_arg_names(function):
+    """Returns argument names for given function.  Omits special arguments
+    of instance methods (`self`) and static methods (usually `cls` or something
+    like this).
+    """
+    spec = compat.getargspec(function)
+    names = spec.args
+
+    if not names:
+        return []
+
+    if inspect.ismethod(function):
+        return names[1:]
+    else:
+        return names

File test/test_integration.py

     p.set_default_command(func)
 
     assert p._actions[-1].completer == 'STUB'
+
+
+def test_class_members():
+    "Issue #34: class members as commands"
+
+    class Controller:
+        var = 123
+
+        def instance_meth(self, value):
+            return value, self.var
+
+        @classmethod
+        def class_meth(cls, value):
+            return value, cls.var
+
+        @staticmethod
+        def static_meth(value):
+            return value, 'w00t?'
+
+        @staticmethod
+        def static_meth2(value):
+            return value, 'huh!'
+
+    controller = Controller()
+
+    p = DebugArghParser()
+    p.add_commands([
+        controller.instance_meth,
+        controller.class_meth,
+        controller.static_meth,
+        Controller.static_meth2,
+    ])
+
+    assert run(p, 'instance-meth foo') == 'foo\n123\n'
+    assert run(p, 'class-meth foo') == 'foo\n123\n'
+    assert run(p, 'static-meth foo') == 'foo\nw00t?\n'
+    assert run(p, 'static-meth2 foo') == 'foo\nhuh!\n'