Example #1
0
def test_func_name_partials_vs_human():
    assert base.func_name(fnt.partialmethod(_Foo.foo), human=1,
                          partials=0) == "foo"
    assert base.func_name(fnt.partialmethod(_Foo.foo), human=None,
                          partials=0) == "foo"
    assert (base.func_name(fnt.partialmethod(_Foo.foo), human=1,
                           partials=1) == "foo(...)")
    assert (base.func_name(fnt.partialmethod(_Foo.foo), human=None,
                           partials=1) == "foo(...)")
    assert (base.func_name(fnt.partialmethod(_Foo.foo), human=1,
                           partials=None) == "foo(...)")
Example #2
0
    def harvest(self,
                *items: Any,
                base_modules=...) -> List[Tuple[str, Callable]]:
        """
        Collect any callable `items` and children, respecting `base_modules`, `excludes` etc.

        :param items:
            module fqdn (if already imported), items with ``__name__``, like
            modules, classes, functions, or partials (without ``__name__``).

            If nothing is given, `attr:`baseModules` is used in its place.

            .. Note::
                This parameter works differently from :attr:`base_modules`, that is,
                harvesting is not limited to those modules only, recursing to
                any imported ones from `items`.

        :return:
            the :attr:`collected`
        """
        old_base_modules = self.base_modules
        try:
            if base_modules is not ...:
                self.base_modules = base_modules

            if not items:
                items = self.base_modules  # type: ignore

            for bi in items:
                if isinstance(bi, str):
                    bi, name_path = sys.modules[bi], bi
                else:
                    name_path = tuple(
                        func_name(bi, mod=0, fqdn=0, human=0,
                                  partials=1).split("."))
                self._harvest(name_path, (bi, ))

            return self.collected
        finally:
            self.base_modules = old_base_modules
Example #3
0
def _make_py_item_url(fn):
    if not inspect.isbuiltin(fn):
        fn_name = func_name(fn, None, mod=1, fqdn=1, human=0)
        if fn_name:
            return f"../reference.html#{fn_name}"
Example #4
0
    def yield_wrapped_ops(
        self,
        fn: Union[Callable, Tuple[Union[str, Collection[str]],
                                  Union[Callable, Collection[Callable]]], ],
        exclude=(),
        domain: Union[str, int, Collection] = None,
    ) -> Iterable[FnOp]:
        """
        Convert a (possibly **@autographed**) function into an graphtik **FnOperations**,

        respecting any configured overrides

        :param fn:
            either a callable, or a 2-tuple(`name-path`, `fn-path`) for::

                [module[, class, ...]] callable

            - If `fn` is an operation, yielded as is (found also in 2-tuple).
            - Both tuple elements may be singulars, and are auto-tuple-zed.
            - The `name-path` may (or may not) correspond to the given `fn-path`,
              and is used to derrive the operation-name;  If not given, the function
              name is inspected.
            - The last elements of the `name-path` are overridden by names in decorations;
              if the decor-name is the "default" (`None`), the `name-path` becomes
              the op-name.
            - The `name-path` is not used when matching overrides.

        :param exclude:
            a list of decor-names to exclude, as stored in decors.
            Ignored if `fn` already an operation.
        :param domain:
            if given, overrides :attr:`domain` for :func:`.autographed` decorators
            to search.
            List-ified if a single str, :func:`autographed` decors for the 1st one
            matching are used.

        :return:
            one or more :class:`FnOp` instances (if more than one name is defined
            when the given function was :func:`autographed`).

        Overriddes order: my-args, self.overrides, autograph-decorator, inspection

        See also: David Brubeck Quartet, "40 days"
        """
        if isinstance(fn, tuple):
            name_path, fn_path = fn
        else:
            name_path, fn_path = (), fn

        fun_path = cast(Tuple[Callable, ...], astuple(fn_path, None))
        fun = fun_path[-1]

        if isinstance(fun, Operation):
            ## pass-through operations
            yield fun
            return

        def param_to_modifier(name: str, param: inspect.Parameter) -> str:
            return (optional(name)
                    # is optional?
                    if param.default is not inspect._empty  # type: ignore
                    else keyword(name)
                    if param.kind == Parameter.KEYWORD_ONLY else name)

        given_name_path = astuple(name_path, None)

        decors_by_name = get_autograph_decors(fun, {}, domain or self.domain)

        for decor_name, decors in decors_by_name.items() or ((None, {}), ):
            if given_name_path and not decor_name:
                name_path = decor_path = given_name_path
            else:  # Name in decors was "default"(None).
                name_path = decor_path = astuple(
                    (decor_name
                     if decor_name else func_name(fun, fqdn=1)).split("."),
                    None,
                )
                assert decor_path, locals()

                if given_name_path:
                    # Overlay `decor_path` over `named_path`, right-aligned.
                    name_path = tuple(*name_path[:-len(decor_path)],
                                      *decor_path)

            fn_name = str(name_path[-1])
            if fn_name in exclude:
                continue
            overrides = self._from_overrides(decor_path)

            op_data = (ChainMap(overrides, decors) if (overrides and decors)
                       else overrides if overrides else decors)
            if op_data:
                log.debug("Autograph overrides for %r: %s", name_path, op_data)

            op_props = "needs provides renames, inp_sideffects out_sideffects".split(
            )
            needs, provides, override_renames, inp_sideffects, out_sideffects = (
                op_data.get(a, _unset) for a in op_props)

            sig = None
            if needs is _unset:
                sig = inspect.signature(fun)
                needs = [
                    param_to_modifier(name, param)
                    for name, param in sig.parameters.items() if name != "self"
                    and param.kind is not Parameter.VAR_KEYWORD
                ]
                ## Insert object as 1st need for object-methods.
                #
                if len(fun_path) > 1:
                    clazz = fun_path[-2]
                    # TODO: respect autograph decorator for object-names.
                    class_name = name_path[-2] if len(
                        name_path) > 1 else clazz.__name__
                    if is_regular_class(class_name, clazz):
                        log.debug("Object-method %s.%s", class_name, fn_name)
                        needs.insert(0, camel_2_snake_case(class_name))

            needs = aslist(needs, "needs")
            if ... in needs:
                if sig is None:
                    sig = inspect.signature(fun)
                needs = [
                    arg_name if n is ... else n
                    for n, arg_name in zip(needs, sig.parameters)
                ]

            if provides is _unset:
                if is_regular_class(fn_name, fun):
                    ## Convert class-name into object variable.
                    provides = camel_2_snake_case(fn_name)
                elif self.out_patterns:
                    provides = self._deduce_provides_from_fn_name(
                        fn_name) or _unset
                if provides is _unset:
                    provides = ()
            provides = aslist(provides, "provides")

            needs, provides = self._apply_renames(
                (override_renames, self.renames), (needs, provides))

            if inp_sideffects is not _unset:
                needs.extend((i if is_sfx(i) else sfxed(
                    *i) if isinstance(i, tuple) else sfx(i))
                             for i in aslist(inp_sideffects, "inp_sideffects"))

            if out_sideffects is not _unset:
                provides.extend(
                    (i if is_sfx(i) else sfxed(
                        *i) if isinstance(i, tuple) else sfx(i))
                    for i in aslist(out_sideffects, "out_sideffects"))

            if self.full_path_names:
                fn_name = self._join_path_names(*name_path)

            op_kws = self._collect_rest_op_args(decors)

            yield FnOp(fn=fun,
                       name=fn_name,
                       needs=needs,
                       provides=provides,
                       **op_kws)
Example #5
0
def test_func_name_lambda_local(kw, exp):
    assert base.func_name(lambda: None, **kw) == exp
Example #6
0
def test_func_name_object_method(kw, exp):
    assert base.func_name(_Foo().foo, **kw) == exp
Example #7
0
def test_func_name_partial_method(kw, exp):
    assert base.func_name(fnt.partialmethod(_Foo.foo), **kw) == exp
Example #8
0
def test_func_name_class_method(kw, exp):
    assert base.func_name(_Foo.foo, **kw) == exp
Example #9
0
def test_func_name_partial_x2(kw, exp):
    assert base.func_name(fnt.partial(fnt.partial(_foo, 1, a=2), b=3),
                          **kw) == exp
Example #10
0
def test_func_name_partial_empty(kw, exp):
    assert base.func_name(fnt.partial(_foo), **kw) == exp
Example #11
0
def test_func_name_non_partial(kw, exp):
    assert base.func_name(_foo, **kw) == exp
Example #12
0
def test_func_name_builtin(kw, exp):
    assert base.func_name(eval, **kw) == exp