Git Product home page Git Product logo

Comments (5)

AbeDillon avatar AbeDillon commented on September 24, 2024
def main(argv=None):
    argv = sys.argv[1:] if argv is None else argv
    outer_argv = argv[:1]
    inner_argv = argv[1:]

    def defopt(path: str="", *args):
        """
        run any suitably documented function from the command-line

        Args:
            path: the absolute, python-style (dot-notation) path to a callable object
            args (List[str]): arguments to be passed to the callable specified by path
        """
        # NOTE: *args is not actually used directly, it's just added so the documentation
        #       reflects the intended interface
        inner = import_object(path)
        run(inner, argv=inner_argv)

    run(defopt, argv=outer_argv)

def import_object(path, _full_path=None):
    """
    Import an object from a python dot-notation path. Similar to import_module
    from importlib, but allows the path to contain objects other than packages,
    modules, and classes.

    Args:
        path (str): The absolute python-style path to an object

    Returns: the object

    Examples:
        >>> import_object("string.digits")
        '0123456789'

        >>> import_object("string.digits.__class__.__name__.upper")()
        'STR'

        >>> import_object("round")(1.234, 2)
        1.23
    """
    if (not _full_path) and ("." not in path):
        return globals().get(path, import_object("builtins." + path, path))
    _full_path = _full_path or path
    try:
        mod_path, name = path.rsplit('.', 1)
    except ValueError as e:
        raise ValueError("%r is not a valid object path" % _full_path) from e
    try:
        mod = importlib.import_module(mod_path)
    except ImportError:
        mod = import_object(mod_path, _full_path)  # allow for nested objects
    return getattr(mod, name)

if __name__ == "__main__":
    main()

from defopt.

evanunderscore avatar evanunderscore commented on September 24, 2024

Perhaps we could reuse whatever mechanism setuptools uses for entry points? The only difference would be that you need to use a colon to denote which part needs to be imported, e.g. package.module:object.attribute, which I don't mind as it's more explicit.

from defopt.

anntzer avatar anntzer commented on September 24, 2024
--- a/defopt.py
+++ b/defopt.py
@@ -10,10 +10,11 @@ import contextlib
 import functools
 import inspect
 import re
+import pydoc
 import sys
 import typing
 from argparse import (
-    SUPPRESS, ArgumentError, ArgumentTypeError, ArgumentParser,
+    REMAINDER, SUPPRESS, ArgumentError, ArgumentTypeError, ArgumentParser,
     RawTextHelpFormatter, _AppendAction, _StoreAction)
 from collections import defaultdict, namedtuple, Counter
 from enum import Enum
@@ -703,3 +704,17 @@ def _make_store_tuple_action_class(make_tuple, member_types, parsers):
             return super(_StoreTupleAction, self).__call__(
                 parser, namespace, value, option_string)
     return _StoreTupleAction
+
+
+if __name__ == "__main__":
+    def main(argv=None):
+        parser = ArgumentParser()
+        parser.add_argument("function")
+        parser.add_argument("args", nargs=REMAINDER)
+        args = parser.parse_args(argv)
+        argparse_kwargs = (
+            {"prog": " ".join(sys.argv[:2])} if argv is None else {})
+        retval = run(pydoc.locate(args.function), argv=args.args,
+                     argparse_kwargs=argparse_kwargs)
+        sys.displayhook(retval)
+
+    main()

works nicely, including -h/--help (pydoc.locate is useful here), but I'm not sure if there's an easy way to load annotations from typeshed...

from defopt.

anntzer avatar anntzer commented on September 24, 2024

I'm going to close this even though a patch is relatively simple (per above) because

  • even though defopt uses extremely "natural" annotations, in practice the callable, its parameters, and their annotations typically still need to be carefully designed to really work as CLI; e.g. the initially suggested round would not work because the second parameter is not keyword-only.
  • it's not clear we can easily load typeshed hints at runtime.

May reopen if real use cases show up.

from defopt.

anntzer avatar anntzer commented on September 24, 2024

As it turns out I had a need for this feature (to make a script defopt-runnable without declaring a dependency on it, to simplify redistribution), so I just pushed it :)

from defopt.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.