Пример #1
0
 def prepare_args(raw_response) -> Mapping[str, Parameter]:
     args = re.findall(r"{(\d+)[^:}]*(:[^.}]*)?[^}]*\}", raw_response)
     default = [("ctx", Parameter("ctx", Parameter.POSITIONAL_OR_KEYWORD))]
     if not args:
         return OrderedDict(default)
     allowed_builtins = {
         "bool": bool,
         "complex": complex,
         "float": float,
         "frozenset": frozenset,
         "int": int,
         "list": list,
         "set": set,
         "str": str,
         "tuple": tuple,
         "query": quote_plus,
     }
     indices = [int(a[0]) for a in args]
     low = min(indices)
     indices = [a - low for a in indices]
     high = max(indices)
     if high > 9:
         raise ArgParseError(_("Too many arguments!"))
     gaps = set(indices).symmetric_difference(range(high + 1))
     if gaps:
         raise ArgParseError(
             _("Arguments must be sequential. Missing arguments: ")
             + ", ".join(str(i + low) for i in gaps)
         )
     fin = [Parameter("_" + str(i), Parameter.POSITIONAL_OR_KEYWORD) for i in range(high + 1)]
     for arg in args:
         index = int(arg[0]) - low
         anno = arg[1][1:]  # strip initial colon
         if anno.lower().endswith("converter"):
             anno = anno[:-9]
         if not anno or anno.startswith("_"):  # public types only
             name = "{}_{}".format("text", index if index < high else "final")
             fin[index] = fin[index].replace(name=name)
             continue
         # allow type hinting only for discord.py and builtin types
         try:
             anno = getattr(discord, anno)
             # force an AttributeError if there's no discord.py converter
             getattr(commands, anno.__name__ + "Converter")
         except AttributeError:
             anno = allowed_builtins.get(anno.lower(), Parameter.empty)
         if (
             anno is not Parameter.empty
             and fin[index].annotation is not Parameter.empty
             and anno != fin[index].annotation
         ):
             raise ArgParseError(
                 _(
                     'Conflicting colon notation for argument {index}: "{name1}" and "{name2}".'
                 ).format(
                     index=index + low,
                     name1=fin[index].annotation.__name__,
                     name2=anno.__name__,
                 )
             )
         if anno is not Parameter.empty:
             fin[index] = fin[index].replace(annotation=anno)
     # consume rest
     fin[-1] = fin[-1].replace(kind=Parameter.KEYWORD_ONLY)
     # name the parameters for the help text
     for i, param in enumerate(fin):
         anno = param.annotation
         name = "{}_{}".format(
             "text" if anno is Parameter.empty else anno.__name__.lower(),
             i if i < high else "final",
         )
         fin[i] = fin[i].replace(name=name)
     # insert ctx parameter for discord.py parsing
     fin = default + [(p.name, p) for p in fin]
     return OrderedDict(fin)
Пример #2
0
def _get_setter_fun(
        cls,  # type: Type
        property_name,  # type: str
        type_hint,  # type: Any
        default_value,  # type: Any
        private_property_name,  # type: str
        overridden_setter=AUTO  # type: Callable
):
    """
    Utility method to find the overridden setter function for a given property, or generate a new one

    :param cls:
    :param property_name:
    :param type_hint:
    :param default_value:
    :param private_property_name:
    :param overridden_setter: an already found overridden setter to use. If AUTO is provided (default), the class will
        be inspected to find them
    :return:
    """
    if overridden_setter is AUTO:
        # If not provided - look for an overridden setter in the class
        overridden_setters = getmembers(cls,
                                        predicate=_has_annotation(
                                            __SETTER_OVERRIDE_ANNOTATION,
                                            property_name))
        if len(overridden_setters) > 1:
            raise DuplicateOverrideError(
                'Setter is overridden more than once for attribute name : %s' %
                property_name)
        else:
            try:
                overridden_setter = overridden_setters[0][1]
            except IndexError:
                pass

    if overridden_setter is not None:
        # --use the overridden setter found/provided
        setter_fun = overridden_setter
        try:  # python 2 - possibly unbind the function
            setter_fun = setter_fun.im_func
        except AttributeError:
            pass

        # --find the parameter name and check the signature
        s = signature(setter_fun)
        p = [
            attribute_name for attribute_name, param in s.parameters.items()
            if attribute_name is not 'self'
        ]
        if len(p) != 1:
            raise IllegalSetterSignatureException(
                'overridden setter %s should have 1 and only 1 non-self argument, '
                'found %s' % (setter_fun.__name__, s))
        actual_arg_name = p[0]
    else:
        # --create the setter: Dynamically compile a wrapper with correct argument name
        sig = Signature(parameters=[
            Parameter('self', kind=Parameter.POSITIONAL_OR_KEYWORD),
            Parameter(property_name,
                      kind=Parameter.POSITIONAL_OR_KEYWORD,
                      annotation=type_hint,
                      default=default_value)
        ])

        @with_signature(sig)
        def autoprops_generated_setter(self, **kwargs):
            """ generated by `autoprops` - setter for a property """
            setattr(self, private_property_name, kwargs[property_name])

        setter_fun = autoprops_generated_setter
        actual_arg_name = property_name

    return setter_fun, actual_arg_name
Пример #3
0
def make_sig(*names):
    parms = [
        Parameter(name, Parameter.POSITIONAL_OR_KEYWORD) for name in names
    ]
    return Signature(parms)
Пример #4
0
#         end = time.time()
#         print(func.__name__, end - start)
#         return r
#     return wrapper
#
# @timethis
# def countdown(n):
#     while n > 0:
#         n -= 1
#
# countdown(100000)

from inspect import Signature, Parameter

parms = [
    Parameter('x', Parameter.POSITIONAL_OR_KEYWORD),
    Parameter('y', Parameter.POSITIONAL_OR_KEYWORD, default=42),
    Parameter('z', Parameter.KEYWORD_ONLY, default=None)
]

sig = Signature(parms)
print(sig)


def func(*args, **kwargs):
    bound_values = sig.bind(*args, **kwargs)
    for name, value in bound_values.arguments.items():
        print(name, value)


func(1, 2, z=3)
Пример #5
0
    def parameters(cls):
        ''' Generate Python ``Parameter`` values suitable for functions that are
        derived from the glyph.

        Returns:
            list(Parameter)

        '''
        arg_params = []
        no_more_defaults = False

        for arg in reversed(cls._args):
            descriptor = cls.lookup(arg)
            default = descriptor.class_default(cls)
            if default is None:
                no_more_defaults = True

            # simplify field(x) defaults to just present the column name
            if isinstance(default, dict) and set(default) == {"field"}:
                default = default["field"]

            # make sure we don't hold on to references to actual Models
            if isinstance(default, Model):
                default = _ModelRepr(default)

            param = Parameter(
                name=arg,
                kind=Parameter.POSITIONAL_OR_KEYWORD,
                # For positional arg properties, default=None means no default.
                default=Parameter.empty if no_more_defaults else default)
            if default:
                del default
            typ = type_link(descriptor.property)
            arg_params.insert(0, (param, typ, descriptor.__doc__))

        # these are not really useful, and should also really be private, just skip them
        omissions = {
            'js_event_callbacks', 'js_property_callbacks', 'subscribed_events'
        }

        kwarg_params = []

        kws = set(cls.properties()) - set(cls._args) - omissions
        for kw in kws:
            descriptor = cls.lookup(kw)
            default = descriptor.class_default(cls)

            # simplify field(x) defaults to just present the column name
            if isinstance(default, dict) and set(default) == {"field"}:
                default = default["field"]

            # make sure we don't hold on to references to actual Models
            if isinstance(default, Model):
                default = _ModelRepr(default)

            param = Parameter(name=kw,
                              kind=Parameter.KEYWORD_ONLY,
                              default=default)
            del default
            typ = type_link(descriptor.property)
            kwarg_params.append((param, typ, descriptor.__doc__))

        for kw, (typ, doc) in cls._extra_kws.items():
            param = Parameter(
                name=kw,
                kind=Parameter.KEYWORD_ONLY,
            )
            kwarg_params.append((param, typ, doc))

        kwarg_params.sort(key=lambda x: x[0].name)

        return arg_params + kwarg_params
Пример #6
0
def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
    """Create a Signature object from AST *node*."""
    args = node.args
    defaults = list(args.defaults)
    params = []
    if hasattr(args, "posonlyargs"):
        posonlyargs = len(args.posonlyargs)  # type: ignore
        positionals = posonlyargs + len(args.args)
    else:
        posonlyargs = 0
        positionals = len(args.args)

    for _ in range(len(defaults), positionals):
        defaults.insert(0, Parameter.empty)

    if hasattr(args, "posonlyargs"):
        for i, arg in enumerate(args.posonlyargs):  # type: ignore
            if defaults[i] is Parameter.empty:
                default = Parameter.empty
            else:
                default = ast_unparse(defaults[i])

            annotation = ast_unparse(arg.annotation) or Parameter.empty
            params.append(
                Parameter(arg.arg,
                          Parameter.POSITIONAL_ONLY,
                          default=default,
                          annotation=annotation))

    for i, arg in enumerate(args.args):
        if defaults[i + posonlyargs] is Parameter.empty:
            default = Parameter.empty
        else:
            default = ast_unparse(defaults[i + posonlyargs])

        annotation = ast_unparse(arg.annotation) or Parameter.empty
        params.append(
            Parameter(arg.arg,
                      Parameter.POSITIONAL_OR_KEYWORD,
                      default=default,
                      annotation=annotation))

    if args.vararg:
        annotation = ast_unparse(args.vararg.annotation) or Parameter.empty
        params.append(
            Parameter(args.vararg.arg,
                      Parameter.VAR_POSITIONAL,
                      annotation=annotation))

    for i, arg in enumerate(args.kwonlyargs):
        default = ast_unparse(args.kw_defaults[i]) or Parameter.empty
        annotation = ast_unparse(arg.annotation) or Parameter.empty
        params.append(
            Parameter(arg.arg,
                      Parameter.KEYWORD_ONLY,
                      default=default,
                      annotation=annotation))

    if args.kwarg:
        annotation = ast_unparse(args.kwarg.annotation) or Parameter.empty
        params.append(
            Parameter(args.kwarg.arg,
                      Parameter.VAR_KEYWORD,
                      annotation=annotation))

    return_annotation = ast_unparse(node.returns) or Parameter.empty

    return inspect.Signature(params, return_annotation=return_annotation)
Пример #7
0
 def _make_signature(names):
     return Signature(
         Parameter(name, Parameter.POSITIONAL_OR_KEYWORD) for name in names)
def test_instance_method():
    r"""Ensure instance methods' signature."""
    assert hasattr(ResLSTMBlock, '__init__')
    assert inspect.signature(ResLSTMBlock.__init__) == Signature(
        parameters=[
            Parameter(
                name='self',
                kind=Parameter.POSITIONAL_OR_KEYWORD,
                default=Parameter.empty,
            ),
            Parameter(
                name='d_hid',
                kind=Parameter.KEYWORD_ONLY,
                annotation=int,
                default=Parameter.empty,
            ),
            Parameter(
                name='n_hid_lyr',
                kind=Parameter.KEYWORD_ONLY,
                annotation=int,
                default=Parameter.empty,
            ),
            Parameter(
                name='p_hid',
                kind=Parameter.KEYWORD_ONLY,
                annotation=float,
                default=Parameter.empty,
            ),
            Parameter(
                name='kwargs',
                kind=Parameter.VAR_KEYWORD,
                annotation=Optional[Dict],
            ),
        ],
        return_annotation=Signature.empty,
    )

    assert hasattr(ResLSTMModel, '__init__')
    assert inspect.signature(ResLSTMModel.__init__) == Signature(
        parameters=[
            Parameter(
                name='self',
                kind=Parameter.POSITIONAL_OR_KEYWORD,
                default=Parameter.empty,
            ),
            Parameter(
                name='d_emb',
                kind=Parameter.KEYWORD_ONLY,
                annotation=int,
                default=Parameter.empty,
            ),
            Parameter(
                name='d_hid',
                kind=Parameter.KEYWORD_ONLY,
                annotation=int,
                default=Parameter.empty,
            ),
            Parameter(
                name='n_hid_lyr',
                kind=Parameter.KEYWORD_ONLY,
                annotation=int,
                default=Parameter.empty,
            ),
            Parameter(
                name='n_post_hid_lyr',
                kind=Parameter.KEYWORD_ONLY,
                annotation=int,
                default=Parameter.empty,
            ),
            Parameter(
                name='n_pre_hid_lyr',
                kind=Parameter.KEYWORD_ONLY,
                annotation=int,
                default=Parameter.empty,
            ),
            Parameter(
                name='p_emb',
                kind=Parameter.KEYWORD_ONLY,
                annotation=float,
                default=Parameter.empty,
            ),
            Parameter(
                name='p_hid',
                kind=Parameter.KEYWORD_ONLY,
                annotation=float,
                default=Parameter.empty,
            ),
            Parameter(
                name='tknzr',
                kind=Parameter.KEYWORD_ONLY,
                annotation=BaseTknzr,
                default=Parameter.empty,
            ),
            Parameter(
                name='kwargs',
                kind=Parameter.VAR_KEYWORD,
                annotation=Optional[Dict],
            ),
        ],
        return_annotation=Signature.empty,
    )
Пример #9
0
def test_wasi():
    # .__init__
    assert Wasi.__init__.__text_signature__ == "($self, /, *args, **kwargs)"
    # .arguments
    assert Wasi.arguments.__text_signature__ == "($self, arguments)"
    assert signature(Wasi.arguments) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("arguments", Parameter.POSITIONAL_OR_KEYWORD),
    ])
    # .argument
    assert Wasi.argument.__text_signature__ == "($self, argument)"
    assert signature(Wasi.argument) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("argument", Parameter.POSITIONAL_OR_KEYWORD),
    ])
    # .environments
    assert Wasi.environments.__text_signature__ == "($self, environments)"
    assert signature(Wasi.environments) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("environments", Parameter.POSITIONAL_OR_KEYWORD),
    ])
    # .environment
    assert Wasi.environment.__text_signature__ == "($self, key, value)"
    assert signature(Wasi.environment) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("key", Parameter.POSITIONAL_OR_KEYWORD),
        Parameter("value", Parameter.POSITIONAL_OR_KEYWORD),
    ])
    # .preopen_directories
    assert Wasi.preopen_directories.__text_signature__ == "($self, preopen_directories)"
    assert signature(Wasi.preopen_directories) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("preopen_directories", Parameter.POSITIONAL_OR_KEYWORD),
    ])
    # .preopen_directory
    assert Wasi.preopen_directory.__text_signature__ == "($self, preopen_directory)"
    assert signature(Wasi.preopen_directory) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("preopen_directory", Parameter.POSITIONAL_OR_KEYWORD),
    ])
    # .map_directories
    assert Wasi.map_directories.__text_signature__ == "($self, map_directories)"
    assert signature(Wasi.map_directories) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("map_directories", Parameter.POSITIONAL_OR_KEYWORD),
    ])
    # .map_directory
    assert Wasi.map_directory.__text_signature__ == "($self, alias, directory)"
    assert signature(Wasi.map_directory) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("alias", Parameter.POSITIONAL_OR_KEYWORD),
        Parameter("directory", Parameter.POSITIONAL_OR_KEYWORD),
    ])
    # .generate_import_object_for_module
    assert Wasi.generate_import_object_for_module.__text_signature__ == "($self, module, version=0)"
    assert signature(Wasi.generate_import_object_for_module) == Signature([
        Parameter("self", Parameter.POSITIONAL_ONLY),
        Parameter("module", Parameter.POSITIONAL_OR_KEYWORD),
        Parameter("version", Parameter.POSITIONAL_OR_KEYWORD, default=0),
    ])
Пример #10
0
        def auth_endpoint(func):
            send_token = False
            send_request = False
            func_sig = signature(func)
            params = list(func_sig.parameters.values())
            for ind, param in enumerate(params.copy()):
                if param.name == 'request' and param._annotation == Request:
                    send_request = True
                if param.name == 'token' and param.annotation == str:
                    send_token = True
                    params.pop(ind)

            token_parameter = Parameter('token',
                                        kind=Parameter.POSITIONAL_OR_KEYWORD,
                                        default=Depends(self.oauth2_scheme),
                                        annotation=str)
            if not send_request:
                request_parameter = Parameter(
                    'request',
                    kind=Parameter.POSITIONAL_OR_KEYWORD,
                    annotation=Request)

            args_index = [str(p) for p in params]
            kwarg_index = None
            for i, v in enumerate(args_index):
                if '**' in v:
                    kwarg_index = i
            arg_index = None
            for i, v in enumerate(args_index):
                if '*' in v and not i == kwarg_index:
                    arg_index = i

            if arg_index:
                if not send_request:
                    params.insert(0, request_parameter)
                params.insert(arg_index - 1, token_parameter)
            elif not kwarg_index:
                if not send_request:
                    params.insert(0, request_parameter)
                params.append(token_parameter)
            ## ** kwargs
            else:
                if not send_request:
                    params.insert(0, request_parameter)
                params.insert(kwarg_index - 1, token_parameter)

            new_sig = func_sig.replace(parameters=params)

            @wraps(func, new_sig=new_sig)
            async def mock_function(*args, **kwargs):
                request = kwargs['request']
                token = kwargs['token']
                if token == 'NO_TOKEN':
                    if response_class is HTMLResponse or 'text/html' in request.headers[
                            'accept']:
                        response = HTMLResponse(self.admin.login_page(
                            welcome_message='Login Required'),
                                                status_code=401)
                        response.set_cookie('token', 'INVALID')
                        response.set_cookie('ref',
                                            request.__dict__['scope']['path'])
                        return response

                try:
                    token = self.decode_token(token)[1]
                    self.log.debug(f"decoded token: {token}")
                except Exception:
                    self.log.exception(f"error decoding token")
                    if response_class is HTMLResponse:
                        response = HTMLResponse(self.admin.login_page(
                            welcome_message='Login Required'),
                                                status_code=401)
                        response.set_cookie('token', 'INVALID')
                        response.set_cookie('ref',
                                            request.__dict__['scope']['path'])
                        return response
                    raise HTTPException(
                        status_code=401,
                        detail=f"not authorized, invalid or expired")

                allowed = False
                for auth_type, values in permissions.items():
                    if not auth_type in token['permissions']:
                        self.log.warning(f"{auth_type} is required")
                        continue
                    for value in values:
                        if value in token['permissions'][auth_type]:
                            allowed = True
                            break
                if not allowed:
                    if response_class is HTMLResponse:
                        response = HTMLResponse(self.admin.forbidden_page(),
                                                status_code=403)
                        response.set_cookie('token', 'INVALID')
                        return response
                    raise HTTPException(
                        status_code=403,
                        detail=
                        f"not authorized, permissions required: {permissions}")

                if 'access_token' in kwargs:
                    kwargs['access_token'] = token

                if not send_token:
                    del kwargs['token']
                if not send_request:
                    del kwargs['request']

                result = func(*args, **kwargs)
                if asyncio.iscoroutine(result):
                    return await result
                return result

            mock_function.__name__ = func.__name__

            route = getattr(self.server, method)

            route(path, *args, **kwargs)(mock_function)
            return mock_function
Пример #11
0
except ImportError:  # py2
    from funcsigs import Signature, Parameter
import pytest

__all__ = ['simplified_test_function', 'ignore_warnings', 'expect_warnings']


# Pytest determines the signature of the test function by unpacking any
# wrapped functions (this is the default of the signature() function it
# uses. We correct this behavior by setting the __signature__ attribute of
# the wrapper function to its correct signature. To do that, we cannot use
# signature() because its follow_wrapped parameter was introduced only in
# Python 3.5. Instead, we build the signature manually.
TESTFUNC_SIGNATURE = Signature(
    parameters=[
        Parameter('desc', Parameter.POSITIONAL_OR_KEYWORD),
        Parameter('kwargs', Parameter.POSITIONAL_OR_KEYWORD),
        Parameter('exp_exc_types', Parameter.POSITIONAL_OR_KEYWORD),
        Parameter('exp_warn_types', Parameter.POSITIONAL_OR_KEYWORD),
        Parameter('condition', Parameter.POSITIONAL_OR_KEYWORD),
    ]
)


def simplified_test_function(test_func):
    """
    A decorator for test functions that simplifies the test function by
    handling a number of things:

    * Skipping the test if the `condition` item in the testcase is `False`,
    * Invoking the Python debugger if the `condition` item in the testcase is
Пример #12
0
def make_signature(names):  # Build a function signature object
    return Signature(
        Parameter(name, Parameter.POSITIONAL_OR_KEYWORD) for name in names)
Пример #13
0
    def steps_decorator(test_func):
        """
        The test function decorator. When a function is decorated it
         - checks that the function is a generator
         - checks that the function signature does not contain our private name `GENERATOR_MODE_STEP_ARGNAME` "by
           chance"
         - wraps the function
        :param test_func:
        :return:
        """

        # ------VALIDATION -------
        # Check if function is a generator
        if not isgeneratorfunction(test_func):
            raise ValueError(
                "Decorated function is not a generator. You either forgot to add `yield` statements in "
                "its body, or you are using mode='generator' instead of mode='parametrizer' or 'auto'."
                "See help(pytest_steps) for details")

        # check that our name for the additional 'test step' parameter is valid (it does not exist in f signature)
        f_sig = signature(test_func)
        test_step_argname = GENERATOR_MODE_STEP_ARGNAME
        if test_step_argname in f_sig.parameters:
            raise ValueError(
                "Your test function relies on arg name %s that is needed by @test_steps in generator "
                "mode" % test_step_argname)

        # ------CORE -------
        # Transform the steps into ids if needed
        step_ids = [create_pytest_param_str_id(f) for f in steps]

        # Create the container that will hold all execution monitors for this function
        # TODO maybe have later a single 'monitor' instance at plugin level... like in pytest-benchmark
        all_monitors = StepMonitorsContainer(test_func, step_ids)

        # Create the function wrapper.
        # We will expose a new signature with additional 'request' arguments if needed, and the test step
        orig_sig = signature(test_func)
        func_needs_request = 'request' in orig_sig.parameters
        additional_params = ((Parameter(
            test_step_argname, kind=Parameter.POSITIONAL_OR_KEYWORD), ) + (
                (Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD), )
                if not func_needs_request else ()))
        # add request parameter last, as first may be 'self'
        new_sig = add_signature_parameters(orig_sig, last=additional_params)

        # -- first create the logic
        @wraps(test_func, new_sig=new_sig)
        def wrapped_test_function(*args, **kwargs):
            step_name = kwargs.pop(test_step_argname)
            request = kwargs['request'] if func_needs_request else kwargs.pop(
                'request')
            if request is None:
                # we are manually called outside of pytest. let's execute all steps at nce
                if step_name is None:
                    # print("@test_steps - decorated function '%s' is being called manually. The `%s` parameter is set "
                    #       "to None so all steps will be executed in order" % (f, test_step_argname))
                    step_names = step_ids
                else:
                    # print("@test_steps - decorated function '%s' is being called manually. The `%s` parameter is set "
                    #       "to %s so only these steps will be executed in order. Note that the order should be
                    #       feasible"
                    #       "" % (f, test_step_argname, step_name))
                    if not isinstance(step_name, (list, tuple)):
                        step_names = [create_pytest_param_str_id(step_name)]
                    else:
                        step_names = [
                            create_pytest_param_str_id(f) for f in step_name
                        ]
                steps_monitor = StepsMonitor(step_ids, test_func, args, kwargs)
                for i, (step_name,
                        ref_step_name) in enumerate(zip(step_names, step_ids)):
                    if step_name != ref_step_name:
                        raise ValueError(
                            "Incorrect sequence of steps provided for manual execution. Step #%s should"
                            " be named '%s', found '%s'" %
                            (i + 1, ref_step_name, step_name))
                    steps_monitor.execute(step_name, args, kwargs)
            else:
                # Retrieve or create the corresponding execution monitor
                steps_monitor = all_monitors.get_execution_monitor(
                    request.node, args, kwargs)

                # execute the step
                # print("DEBUG - executing step %s" % step_name)
                steps_monitor.execute(step_name, args, kwargs)

        # With this hack we will be ordered correctly by pytest https://github.com/pytest-dev/pytest/issues/4429
        wrapped_test_function.place_as = test_func

        # Parametrize the wrapper function with the test step ids
        parametrizer = pytest.mark.parametrize(test_step_argname,
                                               step_ids,
                                               ids=str)

        # finally apply parametrizer
        parametrized_step_function_wrapper = parametrizer(
            wrapped_test_function)
        return parametrized_step_function_wrapper
Пример #14
0
from inspect import signature, Signature, Parameter


def func(a, b=42, *args, d=None, **kwargs):
    pass


if __name__ == '__main__':
    sig1 = signature(func)
    print(sig1)

    parms = [
        Parameter('a', Parameter.POSITIONAL_OR_KEYWORD),
        Parameter('b', Parameter.POSITIONAL_OR_KEYWORD, default=42),
        Parameter('args', Parameter.VAR_POSITIONAL),
        Parameter('d', Parameter.KEYWORD_ONLY, default=None),
        Parameter('kwargs', Parameter.VAR_KEYWORD)
    ]

    sig2 = Signature(parms)
    print(sig2)

    print(f'sig1 == sig2: {sig1 == sig2}')
Пример #15
0
def saved_fixture(
        store='fixture_store',  # type: Union[str, Dict[str, Any]]
        key=None,  # type: str
        views=None,  # type: Dict[str, Callable[[Any], Any]]
        save_raw=None,  # type: bool
        fixture_fun=DECORATED):
    """
    Decorates a fixture so that it is saved in `store`. `store` can be a dict-like variable or a string
    representing a fixture name to a dict-like session-scoped fixture. By default it uses the global 'fixture_store'
    fixture provided by this plugin.

    After executing all tests, `<store>` will contain a new item under key `<key>` (default is the name of the fixture).
    This item is a dictionary <test_id>: <fixture_value> for each test node where the fixture was setup.

    ```python
    import pytest
    from pytest_harvest import saved_fixture

    @pytest.fixture
    @saved_fixture
    def dummy():
        return 1

    def test_dummy(dummy):
        pass

    def test_synthesis(fixture_store):
        print(fixture_store['dummy'])
    ```

    Note that for session-scoped and module-scoped fixtures, not all test ids will appear in the store - only those
    for which the fixture was (re)created.

    Users can save additional views created from the fixture instance by applying transforms (callable functions). To
    do this, users can provide a dictionary under the `views` argument, containing a `{<key>: <procedure>}` dict-like.
    For each entry, `<procedure>` will be applied on the fixture instance, and the result will be stored under `<key>`.
    `save_raw` controls whether the fixture instance should still be saved in this case (default: `True` if
    `views is None`, `False` otherwise).

    :param store: a dict-like object or a fixture name corresponding to a dict-like object. in this dictionary, a new
        entry will be added for the fixture. This entry will contain a dictionary <test_id>: <fixture_value> for each
        test node. By default fixtures are stored in the `fixture_store``fixture.
    :param key: the name associated with the stored fixture in the store. By default this is the fixture name.
    :param views: an optional dictionary that can be provided to store views created from the fixture, rather than (or
        in addition to, if `save_raw=True`) the fixture itself. The dict should contain a `{<key>: <procedure>}`
        dict-like. For each entry, `<procedure>` will be applied on the fixture instance, and the result will be stored
        under `<key>`.
    :param save_raw: controls whether the fixture instance should be saved. `None` (Default) is an automatic behaviour
        meaning "`True` if `views is None`, `False` otherwise".
    :return: a fixture that will be stored
    """
    # default: if views is None, we save the raw fixture. If user wants to save views, we do not save the raw
    if save_raw is None:
        save_raw = views is None

    # Simply get the function name
    fixture_name = getattr(fixture_fun, '__name__', None) or str(fixture_fun)

    # Name to use for storage
    key = key or fixture_name

    # is the store a fixture or an object ?
    store_is_a_fixture = isinstance(store, string_types)

    # if the store object is already available, we can ensure that it is initialized. Otherwise trust pytest for that
    if not store_is_a_fixture:
        if key in store.keys():
            raise ValueError(
                "Key '%s' already exists in store object. Please make sure that your store object is "
                "properly initialized as an empty dict-like object, and/or provide a different custom "
                "`name` if two stored fixtures share the same storage key name."
            )

    # Note: we can not init the storage[key] entry here because when storage is a fixture, it does not yet exist.

    def _init_and_check(request, store):
        """Performs a few checks and returns the key to use for saving (the node id)"""
        # find the current node id depending on the scope
        scope = get_scope(request)
        if scope == 'function':
            nodeid = request.node.nodeid
        else:
            # session- or module-scope
            # raise Exception("The `@saved_fixture` decorator is only applicable to function-scope fixtures. `%s`"
            #                 " seems to have scope='%s'. Consider removing `@saved_fixture` or changing "
            #                 "the scope to 'function'." % (fixture_fun, scope))
            nodeid = request._pyfuncitem.nodeid

        # Init storage if needed
        if save_raw:
            # init
            if key not in store:
                store[key] = OrderedDict()
            # Check that the node id is unique
            if nodeid in store[key]:
                raise KeyError(
                    "Internal Error - This fixture '%s' was already "
                    "stored for test id '%s'" % (key, nodeid))

        if views is not None:
            for k in views.keys():
                if k not in store:
                    store[k] = OrderedDict()
                # Check that the node id is unique
                if nodeid in store[k]:
                    raise KeyError(
                        "Internal Error - This fixture view '%s' was already "
                        "stored for test id '%s'" % (k, nodeid))

        return nodeid

    # We will expose a new signature with additional arguments
    orig_sig = signature(fixture_fun)
    func_needs_request = 'request' in orig_sig.parameters
    new_args = ((Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD),) if not func_needs_request else ()) \
               + ((Parameter(store, kind=Parameter.POSITIONAL_OR_KEYWORD),) if store_is_a_fixture else ())
    new_sig = add_signature_parameters(orig_sig, first=new_args)

    # Wrap the fixture in the correct mode (generator or not)
    if not isgeneratorfunction(fixture_fun):

        @wraps(fixture_fun, new_sig=new_sig)
        def stored_fixture_function(*args, **kwargs):
            """Wraps a fixture so as to store it before it is returned"""
            # get the actual store object
            if store_is_a_fixture:
                store_ = kwargs.pop(store)  # read and remove it
            else:
                # use the variable from outer scope (from `make_saved_fixture`)
                store_ = store
            request = kwargs['request'] if func_needs_request else kwargs.pop(
                'request')
            nodeid = _init_and_check(request, store_)
            fixture_value = fixture_fun(*args, **kwargs)  # Get the fixture
            _store_fixture_and_views(store_, nodeid, key, fixture_value, views,
                                     save_raw)  # Store it
            return fixture_value  # Return it

    else:

        @wraps(fixture_fun, new_sig=new_sig)
        def stored_fixture_function(*args, **kwargs):
            """Wraps a fixture so as to store it before it is returned (generator mode)"""
            # get the actual store object
            if store_is_a_fixture:
                store_ = kwargs.pop(store)
            else:
                # use the variable from outer scope (from `make_saved_fixture`)
                store_ = store
            request = kwargs['request'] if func_needs_request else kwargs.pop(
                'request')
            nodeid = _init_and_check(request, store_)
            gen = fixture_fun(*args, **kwargs)
            fixture_value = next(gen)  # Get the fixture
            _store_fixture_and_views(store_, nodeid, key, fixture_value, views,
                                     save_raw)  # Store it
            yield fixture_value  # Return it

            # Make sure to terminate the underlying generator
            try:
                next(gen)
            except StopIteration:
                pass

    return stored_fixture_function
Пример #16
0
def helper_single_param(func, *, param_name: str):
    """Check text_signature and signature for function with one parameter (except `self`)"""

    assert func.__text_signature__ == "({})".format(param_name)
    assert signature(func) == Signature(
        [Parameter(param_name, Parameter.POSITIONAL_OR_KEYWORD)])
def ufunc(np_ufunc, return_type_override=None):
    if not isinstance(np_ufunc, np.ufunc):
        raise TypeError("Must be an instance of `np.ufunc`, got {}".format(
            type(np_ufunc)))

    def wf_ufunc(*args):
        if len(args) != np_ufunc.nin:
            raise TypeError(
                "Invalid number of arguments to function `{}`".format(
                    np_ufunc.__name__))

        # Since typecheck_promote doesn't support variadic arguments, manually
        # attempt to promote each argument to an Array or scalar
        promoted = []
        for i, arg in enumerate(args):
            try:
                if isinstance(arg, BaseArray):
                    promoted.append(arg)
                elif isinstance(arg, np.ma.core.MaskedArray):
                    promoted.append(MaskedArray._promote(arg))
                elif isinstance(arg, np.ndarray):
                    promoted.append(Array._promote(arg))
                else:
                    promoted.append(
                        _promote(arg, (Bool, Int, Float, Scalar), i,
                                 np_ufunc.__name__))
                    # TODO(gabe) not great to be relying on internal `_promote` here
            except (ProxyTypeError, TypeError):
                raise ProxyTypeError(
                    "Argument {} to function {} must be a Workflows Array, Scalar, Int, Float,"
                    "Bool, or a type promotable to one of those, not {}".
                    format(i + 1, np_ufunc.__name__, type(arg)))

        return_type = _ufunc_result_type(
            *promoted, return_type_override=return_type_override)

        return return_type._from_apply("wf.numpy." + np_ufunc.__name__,
                                       *promoted)

    HANDLED_UFUNCS[np_ufunc.__name__] = wf_ufunc

    copy_docstring_from_numpy(wf_ufunc, np_ufunc)

    # create an inspect.Signature object
    return_annotation = (return_type_override if return_type_override
                         is not None else Union[Array, MaskedArray, Scalar,
                                                Int, Float, Bool])

    if np_ufunc.nin == 2:
        signature = Signature(
            [
                Parameter(
                    "x1",
                    Parameter.POSITIONAL_OR_KEYWORD,
                    annotation=Union[Array, MaskedArray, Scalar, Int, Float,
                                     Bool],
                ),
                Parameter(
                    "x2",
                    Parameter.POSITIONAL_OR_KEYWORD,
                    annotation=Union[Array, MaskedArray, Scalar, Int, Float,
                                     Bool],
                ),
            ],
            return_annotation=return_annotation,
        )
    else:
        signature = Signature(
            [
                Parameter(
                    "x",
                    Parameter.POSITIONAL_OR_KEYWORD,
                    annotation=Union[Array, MaskedArray, Scalar, Int, Float,
                                     Bool],
                )
            ],
            return_annotation=return_annotation,
        )

    # set __annotations__
    if "sphinx" not in sys.modules:
        wf_ufunc.__annotations__ = {
            name: param.annotation
            for name, param in signature.parameters.items()
        }
        wf_ufunc.__annotations__["return"] = signature.return_annotation

    # set __signature__ to the stringified version
    wf_ufunc.__signature__ = stringify_signature(signature)

    # forward attributes
    wf_ufunc.nin = np_ufunc.nin
    wf_ufunc.nargs = np_ufunc.nargs
    wf_ufunc.nout = np_ufunc.nout
    wf_ufunc.ntypes = np_ufunc.ntypes
    wf_ufunc.identity = np_ufunc.identity
    wf_ufunc.signature = np_ufunc.signature
    wf_ufunc.types = np_ufunc.types

    # NOTE: we currently don't support other attributes of ufuncs (besides `__call__`)
    wf_ufunc.reduce = lambda *args: raise_(
        NotImplementedError(
            "The `reduce` ufunc method is not supported by Workflows types"))
    wf_ufunc.reduceat = lambda *args: raise_(
        NotImplementedError(
            "The `reduceat` ufunc method is not supported by Workflows types"))
    wf_ufunc.accumulate = lambda *args: raise_(
        NotImplementedError(
            "The `accumulate` ufunc method is not supported by Workflows types"
        ))
    wf_ufunc.outer = lambda *args: raise_(
        NotImplementedError(
            "The `outer` ufunc method is not supported by Workflows types"))

    return wf_ufunc
Пример #18
0
def helper_self(func):
    assert func.__text_signature__ == "($self)"
    assert signature(func) == Signature(
        [Parameter("self", Parameter.POSITIONAL_ONLY)])
Пример #19
0
    def __init__(self, backends: Sequence[BaseAuthentication],
                 user_db: BaseUserDatabase):
        self.backends = backends
        self.user_db = user_db

        parameters = [
            Parameter(name=name_to_variable_name(backend.name),
                      kind=Parameter.POSITIONAL_OR_KEYWORD,
                      default=Depends(backend.scheme))
            for backend in self.backends
        ]

        signature = Signature(parameters)

        @with_signature(signature, func_name='get_optional_current_user')
        async def get_optional_current_user(*args, **kwargs):
            return await self.authenticate(*args, **kwargs)

        @with_signature(signature,
                        func_name='get_optional_current_active_user')
        async def get_optional_current_active_user(*args, **kwargs):
            user = await get_optional_current_user(*args, **kwargs)

            if not user or not user.is_active:
                return None

            return user

        @with_signature(signature, func_name='get_optional_current_superuser')
        async def get_optional_current_superuser(*args, **kwargs):
            user = await get_optional_current_active_user(*args, **kwargs)

            if not user or not user.is_superuser:
                return None

            return user

        @with_signature(signature, func_name='get_current_user')
        async def get_current_user(*args, **kwargs):
            user = await get_optional_current_user(*args, **kwargs)

            if user is None:
                raise HTTPException(status_code=403,
                                    detail='You do not have the permission')

            return user

        @with_signature(signature, func_name='get_current_active_user')
        async def get_current_active_user(*args, **kwargs):
            user = await get_optional_current_active_user(*args, **kwargs)

            if user is None:
                raise HTTPException(status_code=403,
                                    detail='You do not have the permission')

            return user

        @with_signature(signature, func_name='get_current_superuser')
        async def get_current_superuser(*args, **kwargs):
            user = await get_optional_current_active_user(*args, **kwargs)

            if user is None:
                raise HTTPException(status_code=403,
                                    detail='You do not have the permission')

            if not user.is_superuser:
                raise HTTPException(status_code=403,
                                    detail='You need to be superuser')

            return user

        self.get_current_user = get_current_user
        self.get_current_active_user = get_current_active_user
        self.get_optional_current_active_user = get_optional_current_active_user
        self.get_optional_current_superuser = get_optional_current_superuser
        self.get_optional_current_user = get_optional_current_user
        self.get_current_superuser = get_current_superuser
Пример #20
0
 def helper_offset(func):
     assert func.__text_signature__ == "($self, offset=0)"
     assert signature(func) == Signature([
         Parameter("self", Parameter.POSITIONAL_ONLY),
         Parameter("offset", Parameter.POSITIONAL_OR_KEYWORD, default=0),
     ])
Пример #21
0
class Sample:
    __signature__ = Signature(parameters=[Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
Пример #22
0
def register_single_type_tests(plugin, list_of_params):
    for generator in list_of_params:
        for idx, param in enumerate(generator, 1):
            action_id = f'test_{generator.__name__}_{idx}'
            function_parameters = []
            qiime_annotations = {}
            defaults = {}

            # Required
            param_name = param.base_name
            function_parameters.append(
                Parameter(param_name,
                          Parameter.POSITIONAL_OR_KEYWORD,
                          annotation=param.view_type))
            qiime_annotations[param_name] = param.qiime_type

            # Optional
            param_name = f'optional_{param.base_name}'
            function_parameters.append(
                Parameter(param_name,
                          Parameter.POSITIONAL_OR_KEYWORD,
                          annotation=param.view_type,
                          default=None))
            qiime_annotations[param_name] = param.qiime_type
            defaults[param_name] = None

            if (not is_semantic_type(param.qiime_type)
                    and not is_metadata_type(param.qiime_type)):
                # Defaults
                for i_, default in enumerate(param.domain, 1):
                    param_name = f'default{i_}_{param.base_name}'
                    function_parameters.append(
                        Parameter(param_name,
                                  Parameter.POSITIONAL_OR_KEYWORD,
                                  annotation=param.view_type,
                                  default=default))
                    qiime_annotations[param_name] = param.qiime_type
                    defaults[param_name] = default

            func = function_template_1output
            disguise_function(func, action_id, function_parameters, 1)

            qiime_inputs = {}
            qiime_parameters = {}
            qiime_outputs = [('only_output', EchoOutput)]
            for name, val in qiime_annotations.items():
                if is_semantic_type(val):
                    qiime_inputs[name] = val
                else:
                    qiime_parameters[name] = val

            usage_examples = {}
            for i, arg in enumerate(param.domain):
                usage_examples[f'example_{i}'] = UsageInstantiator(
                    action_id, {param.base_name: param},
                    {param.base_name: arg}, qiime_outputs, defaults)

            # one more with defaults all passed a different value
            new_inputs = {
                # the last iteration is fine, we aren't testing this param
                param.base_name:
                arg
            }
            shifted_args = deque([arg, *param.domain])
            shifted_args.rotate(1)
            for key, arg in zip(defaults, shifted_args):
                new_inputs[key] = arg

            usage_examples[f'example_{i+1}'] = UsageInstantiator(
                action_id,
                {k: param
                 for k in chain([param.base_name], defaults)}, new_inputs,
                qiime_outputs)

            # prove we can still pass defaults manually
            usage_examples[f'example_{i+2}'] = UsageInstantiator(
                action_id,
                {k: param
                 for k in chain([param.base_name], defaults)}, {
                     param.base_name: arg,
                     **defaults
                 }, qiime_outputs)

            plugin.methods.register_function(
                function=func,
                inputs=qiime_inputs,
                parameters=qiime_parameters,
                outputs=qiime_outputs,
                input_descriptions={},
                parameter_descriptions={},
                output_descriptions={},
                name=f'Phrase describing {action_id.replace("_", "-")}',
                description=LOREM_IPSUM,
                examples=usage_examples)
Пример #23
0
def _mk_param(nm, **kwargs):
    return Parameter(nm, kind=Parameter.POSITIONAL_OR_KEYWORD, **kwargs)
This script shows how to check the passed arguments
to see if they match a specific signature when using
general purpose functions using kwargs and args.

To do this you can use the signature features in the 'inspect'
module. The classes to use are Signature and
Parameter.

Below an example of a function signature is shown
"""
from inspect import Signature, Parameter
import inspect

# Make a signature for a func(x, y=42, *, z=None)
parms = [
    Parameter("x", Parameter.POSITIONAL_OR_KEYWORD),
    Parameter("y", Parameter.POSITIONAL_OR_KEYWORD, default=42),
    Parameter("z", Parameter.KEYWORD_ONLY, default=None),
]

sig = Signature(parms)
print(sig)

# Bind signature to *args and **kwargs using bind()


def func(*args, **kwargs):
    bound_vales = sig.bind(*args, **kwargs)
    for name, value in bound_vales.arguments.items():
        print(name, value)
Пример #25
0
    def decorator(func):
        # Build new signature.
        original_sig = signature(func)
        var_keyword = '' 
        var_positional = '' 
        parameters = OrderedDict()
        original_positional = OrderedDict()
        # Add positional arguments and keywords first.
        for name, param in original_sig.parameters.items():
            if param.kind == param.VAR_KEYWORD:
                var_keyword = name
            elif param.kind == param.VAR_POSITIONAL:
                var_positional = name
            else:
                default = default_kwargs.get(name, param.default)
                param = Parameter(name, param.kind, default=default)
                parameters[name] = param
                original_positional[name] = param
        # Add var positional.
        if var_positional:
            parameters[var_positional] = Parameter(var_positional, 
                                                   Parameter.VAR_POSITIONAL)
        # Add remainder defualt_kwargs as keyword only variables.
        for name, value in default_kwargs.items():
            if name not in parameters:
                param = Parameter(name, Parameter.KEYWORD_ONLY, default=value)
                parameters[name] = param
        # Add var keyword.
        if var_keyword:
            parameters[var_keyword] = Parameter(var_keyword,
                                                Parameter.VAR_KEYWORD)
        # Build our inner wrapper signature.
        wrapper_sig = original_sig.replace(parameters=parameters.values())
        # Remove cloaked var arguments.
        if var_positional and hide_var_positional:
            parameters.pop(var_positional)
        if var_keyword and hide_var_keyword:
            parameters.pop(var_keyword)
        cloak_sig = original_sig.replace(parameters=parameters.values())

        # Wrapper function.
        function_defaults = set(original_sig.parameters)
        override_defaults = set(default_kwargs).intersection(function_defaults)
        def wrapper(*args, **kwargs):
            # Build new kwargs and args.
            arguments = OrderedDict(wrapper_sig.bind(*args, **kwargs).arguments)
            kwargs = {}
            updates = {}
            
            # Build the positional arguments. Override func's default values.
            ordered_args = OrderedDict()
            for name in original_positional:
                if name in arguments:
                    ordered_args[name] = arguments[name]
                    if name in default_kwargs:
                        updates[name] = arguments[name]
                elif name in default_kwargs:
                    ordered_args[name] = default_kwargs[name]
                    updates[name] = default_kwargs[name]
            
            # Now handle the keyword only and var arguments
            args = list(ordered_args.values())
            for name in set(arguments).difference(ordered_args):
                value = arguments[name]
                if name == var_positional:
                    args.extend(value)
                elif name == var_keyword: 
                    for k, v in value.items():
                        kwargs[k] = v
                else:
                    # Update keyword only values
                    if name in default_kwargs:
                        updates[name] = value
                    kwargs[name] = value

            default_kwargs.update(updates)
            if var_keyword:
                # Add default_kwargs keyword values not defined in kwargs.
                for k in set(default_kwargs).difference(kwargs):
                    if k not in original_positional:
                        kwargs[k] = default_kwargs[k]
            else:
                # Remove kwargs that func doesn't have defined.
                for k in set(kwargs).difference(function_defaults):
                    kwargs.pop(k)
            return func(*args, **kwargs)

        # Return wrapped up func with the cloaked signature. 
        functools.update_wrapper(wrapper, func)
        wrapper.__signature__ = cloak_sig
        return wrapper
Пример #26
0
def _decorate_fixture_plus(
        fixture_func,
        scope="function",  # type: str
        autouse=False,  # type: bool
        name=None,  # type: str
        unpack_into=None,  # type: Iterable[str]
        hook=None,  # type: Callable[[Callable], Callable]
        _caller_module_offset_when_unpack=3,  # type: int
        **kwargs):
    """ decorator to mark a fixture factory function.

    Identical to `@pytest.fixture` decorator, except that

     - it supports multi-parametrization with `@pytest.mark.parametrize` as requested in
       https://github.com/pytest-dev/pytest/issues/3960. As a consequence it does not support the `params` and `ids`
       arguments anymore.

     - it supports a new argument `unpack_into` where you can provide names for fixtures where to unpack this fixture
       into.

    :param scope: the scope for which this fixture is shared, one of "function" (default), "class", "module" or
        "session".
    :param autouse: if True, the fixture func is activated for all tests that can see it.  If False (the default) then
        an explicit reference is needed to activate the fixture.
    :param name: the name of the fixture. This defaults to the name of the decorated function. Note: If a fixture is
        used in the same module in which it is defined, the function name of the fixture will be shadowed by the
        function arg that requests the fixture; one way to resolve this is to name the decorated function
        ``fixture_<fixturename>`` and then use ``@pytest.fixture(name='<fixturename>')``.
    :param unpack_into: an optional iterable of names, or string containing coma-separated names, for additional
        fixtures to create to represent parts of this fixture. See `unpack_fixture` for details.
    :param hook: an optional hook to apply to each fixture function that is created during this call. The hook function
        will be called everytime a fixture is about to be created. It will receive a single argument (the function
        implementing the fixture) and should return the function to use. For example you can use `saved_fixture` from
        `pytest-harvest` as a hook in order to save all such created fixtures in the fixture store.
    :param kwargs: other keyword arguments for `@pytest.fixture`
    """
    if name is not None:
        # Compatibility for the 'name' argument
        if PYTEST3_OR_GREATER:
            # pytest version supports "name" keyword argument
            kwargs['name'] = name
        elif name is not None:
            # 'name' argument is not supported in this old version, use the __name__ trick.
            fixture_func.__name__ = name

    # if unpacking is requested, do it first
    if unpack_into is not None:
        # get the future fixture name if needed
        if name is None:
            name = fixture_func.__name__

        # get caller module to create the symbols
        caller_module = get_caller_module(
            frame_offset=_caller_module_offset_when_unpack)
        _make_unpack_fixture(caller_module, unpack_into, name, hook=hook)

    # (1) Collect all @pytest.mark.parametrize markers (including those created by usage of @cases_data)
    parametrizer_marks = get_pytest_parametrize_marks(fixture_func)
    if len(parametrizer_marks) < 1:
        # make the fixture union-aware
        wrapped_fixture_func = ignore_unused(fixture_func)

        # resolve possibly infinite generators of ids here
        if 'params' in kwargs and 'ids' in kwargs:
            kwargs['ids'] = resolve_ids(kwargs['ids'],
                                        kwargs['params'],
                                        full_resolve=False)

        # transform the created wrapper into a fixture
        return pytest_fixture(scope=scope,
                              autouse=autouse,
                              hook=hook,
                              **kwargs)(wrapped_fixture_func)

    else:
        if 'params' in kwargs:
            raise ValueError(
                "With `fixture_plus` you cannot mix usage of the keyword argument `params` and of "
                "the pytest.mark.parametrize marks")

    # (2) create the huge "param" containing all params combined
    # --loop (use the same order to get it right)
    params_names_or_name_combinations = []
    params_values = []
    params_ids = []
    params_marks = []
    for pmark in parametrizer_marks:
        # -- pmark is a single @pytest.parametrize mark. --

        # check number of parameter names in this parameterset
        if len(pmark.param_names) < 1:
            raise ValueError(
                "Fixture function '%s' decorated with '@fixture_plus' has an empty parameter "
                "name in a @pytest.mark.parametrize mark")

        # remember the argnames
        params_names_or_name_combinations.append(pmark.param_names)

        # separate specific configuration (pytest.param()) from the values
        custom_pids, _pmarks, _pvalues = extract_parameterset_info(
            pmark.param_names, pmark.param_values, check_nb=True)

        # get the ids by merging/creating the various possibilities
        _paramids = make_test_ids(argnames=pmark.param_names,
                                  argvalues=_pvalues,
                                  global_ids=pmark.param_ids,
                                  id_marks=custom_pids)

        # Finally store the ids, marks, and values for this parameterset
        params_ids.append(_paramids)
        params_marks.append(tuple(_pmarks))
        params_values.append(tuple(_pvalues))

    # (3) generate the ids and values, possibly reapplying marks
    if len(params_names_or_name_combinations) == 1:
        # we can simplify - that will be more readable
        final_ids = params_ids[0]
        final_marks = params_marks[0]
        final_values = list(params_values[0])

        # reapply the marks
        for i, marks in enumerate(final_marks):
            if marks is not None:
                final_values[i] = make_marked_parameter_value(
                    (final_values[i], ), marks=marks)
    else:
        final_values = list(product(*params_values))
        final_ids = combine_ids(product(*params_ids))
        final_marks = tuple(product(*params_marks))

        # reapply the marks
        for i, marks in enumerate(final_marks):
            ms = [m for mm in marks if mm is not None for m in mm]
            if len(ms) > 0:
                final_values[i] = make_marked_parameter_value(
                    (final_values[i], ), marks=ms)

    if len(final_values) != len(final_ids):
        raise ValueError(
            "Internal error related to fixture parametrization- please report")

    # (4) wrap the fixture function so as to remove the parameter names and add 'request' if needed
    all_param_names = tuple(v for pnames in params_names_or_name_combinations
                            for v in pnames)

    # --create the new signature that we want to expose to pytest
    old_sig = signature(fixture_func)
    for p in all_param_names:
        if p not in old_sig.parameters:
            raise ValueError(
                "parameter '%s' not found in fixture signature '%s%s'"
                "" % (p, fixture_func.__name__, old_sig))
    new_sig = remove_signature_parameters(old_sig, *all_param_names)
    # add request if needed
    func_needs_request = 'request' in old_sig.parameters
    if not func_needs_request:
        new_sig = add_signature_parameters(
            new_sig,
            first=Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD))

    # --common routine used below. Fills kwargs with the appropriate names and values from fixture_params
    def _map_arguments(*_args, **_kwargs):
        request = _kwargs['request'] if func_needs_request else _kwargs.pop(
            'request')

        # populate the parameters
        if len(params_names_or_name_combinations) == 1:
            _params = [request.param]  # remove the simplification
        else:
            _params = request.param
        for p_names, fixture_param_value in zip(
                params_names_or_name_combinations, _params):
            if len(p_names) == 1:
                # a single parameter for that generated fixture (@pytest.mark.parametrize with a single name)
                _kwargs[p_names[0]] = get_lazy_args(fixture_param_value,
                                                    request)
            else:
                # several parameters for that generated fixture (@pytest.mark.parametrize with several names)
                # unpack all of them and inject them in the kwargs
                for old_p_name, old_p_value in zip(p_names,
                                                   fixture_param_value):
                    _kwargs[old_p_name] = get_lazy_args(old_p_value, request)

        return _args, _kwargs

    # --Finally create the fixture function, a wrapper of user-provided fixture with the new signature
    if not isgeneratorfunction(fixture_func):
        # normal function with return statement
        @wraps(fixture_func, new_sig=new_sig)
        def wrapped_fixture_func(*_args, **_kwargs):
            if not is_used_request(_kwargs['request']):
                return NOT_USED
            else:
                _args, _kwargs = _map_arguments(*_args, **_kwargs)
                return fixture_func(*_args, **_kwargs)

    else:
        # generator function (with a yield statement)
        @wraps(fixture_func, new_sig=new_sig)
        def wrapped_fixture_func(*_args, **_kwargs):
            if not is_used_request(_kwargs['request']):
                yield NOT_USED
            else:
                _args, _kwargs = _map_arguments(*_args, **_kwargs)
                for res in fixture_func(*_args, **_kwargs):
                    yield res

    # transform the created wrapper into a fixture
    _make_fix = pytest_fixture(scope=scope,
                               params=final_values,
                               autouse=autouse,
                               hook=hook,
                               ids=final_ids,
                               **kwargs)
    return _make_fix(wrapped_fixture_func)
Пример #27
0
    def _create_builder(cls, element, completions):
        def builder(cls, spec=None, **kws):
            spec = element if spec is None else '%s.%s' % (element, spec)
            prefix = 'In opts.{element}(...), '.format(element=element)
            backend = kws.get('backend', None)
            keys = set(kws.keys())
            if backend:
                allowed_kws = cls._element_keywords(backend,
                                                    elements=[element
                                                              ])[element]
                invalid = keys - set(allowed_kws)
            else:
                mismatched = {}
                all_valid_kws = set()
                for loaded_backend in Store.loaded_backends():
                    valid = set(
                        cls._element_keywords(loaded_backend).get(element, []))
                    all_valid_kws |= set(valid)
                    if keys <= valid:  # Found a backend for which all keys are valid
                        return Options(spec, **kws)
                    mismatched[loaded_backend] = list(keys - valid)

                invalid = keys - all_valid_kws  # Keys not found for any backend
                if mismatched and not invalid:  # Keys found across multiple backends
                    msg = (
                        '{prefix} keywords supplied are mixed across backends. '
                        'Keyword(s) {info}')
                    info = ', '.join('%s are invalid for %s' %
                                     (', '.join(repr(el) for el in v), k)
                                     for k, v in mismatched.items())
                    raise ValueError(msg.format(info=info, prefix=prefix))
                allowed_kws = completions

            reraise = False
            if invalid:
                try:
                    cls._options_error(
                        list(invalid)[0], element, backend, allowed_kws)
                except ValueError as e:
                    msg = str(e)[0].lower() + str(e)[1:]
                    reraise = True

                if reraise:
                    raise ValueError(prefix + msg)

            return Options(spec, **kws)

        filtered_keywords = [
            k for k in completions if k not in cls._no_completion
        ]
        sorted_kw_set = sorted(set(filtered_keywords))
        if sys.version_info.major == 2:
            kws = ', '.join('{opt}=None'.format(opt=opt)
                            for opt in sorted_kw_set)
            builder.__doc__ = '{element}({kws})'.format(element=element,
                                                        kws=kws)
        else:
            from inspect import Parameter, Signature
            signature = Signature(
                [Parameter('spec', Parameter.POSITIONAL_OR_KEYWORD)] + [
                    Parameter(kw, Parameter.KEYWORD_ONLY)
                    for kw in sorted_kw_set
                ])
            builder.__signature__ = signature
        return classmethod(builder)
Пример #28
0
    def wrap_class(cls, params: Tuple[str, ...]):
        """
        Wraps a class to comply with the bindable decoration: wraps all methods and adds bind & unbind functionality
        :param cls: Class to decorate
        :param params: Parameters to allow binding for
        :return: Decorated class
        """
        implicit_resolution: ImplicitResolution = ImplicitResolution(
            Bindable.implicitly_resolve, *params)
        for func_name, (func, bound_func) in variables(cls).items():
            # Check function is not reserved
            if func_name in ('bind', 'unbind', 'bind_like', 'bound_params'):
                continue

            # Decorate all non-protected functions
            if callable(bound_func) and (not func_name.startswith('_') or getattr(func, 'override_protection', False))\
                    and not isinstance(bound_func, staticmethod):
                setattr(cls, func_name, implicit_resolution(func))

        # Update params to include previous bindings
        params: Tuple[str, ...] = tuple(
            set(getattr(cls, '_bindable_params', ()) + params))
        setattr(cls, '_bindable_params', params)

        original_init = getattr(cls, '__init__',
                                lambda self, *args, **kwargs: None)

        @wraps(original_init)
        def new_init(self, *args, **kwargs):
            # Add _bound_params dictionary to instance
            original_init(self, *args, **kwargs)
            self._bound_params = {}

        setattr(cls, '__init__', new_init)

        # Many possible bound parameters
        def bind(self: T, **kwargs):
            """Binds (the) parameters {0} to the instance"""
            problem: str = next(
                (kwarg for kwarg in kwargs if kwarg not in params), None)
            if problem:
                raise Exception(
                    f"Cannot bind parameter [{problem}], was not declared bindable"
                )

            for k, v in kwargs.items():
                self._bound_params[k] = v

        def bind_like(self: T, *others: T):
            """Binds this instance like another instance"""
            if len(others) == 0:
                self._bound_params = {}
                return

            self._bound_params = getattr(others[0], '_bound_params', {}).copy()
            for other in others[1:]:
                for key, value in getattr(other, 'bound_params', {}).items():
                    if key in self._bound_params and self._bound_params.get(
                            key) != value:
                        self._bound_params.pop(key)

        def unbind(self: T, *names: str):
            """Unbinds (the) parameters {0} from the instance, pass no names to unbind all"""
            problem: str = next((name for name in names if name not in params),
                                None)
            if problem:
                raise Exception(
                    f"Cannot unbind parameter [{problem}], was not declared bindable"
                )

            if len(names) == 0:
                names = tuple(self._bound_params.keys())

            for name in names:
                if name in self._bound_params:
                    self._bound_params.pop(name)

        @cached_property
        def bound_params(_self):
            class Proxy(Mapping):
                def __contains__(self, key: str):
                    return key in getattr(_self, '_bound_params', {})

                def __getitem__(self, key: str):
                    return getattr(_self, '_bound_params', {})[key]

                def __iter__(self):
                    return iter(getattr(_self, '_bound_params', {}))

                def __len__(self) -> int:
                    return len(list(iter(self)))

            return Proxy()

        bind.__doc__ = bind.__doc__.format(params)
        unbind.__doc__ = unbind.__doc__.format(params)

        # Update binding signature to match possible bound parameters
        bind.__signature__ = signature(bind).replace(parameters=[
            Parameter(name='self', kind=Parameter.POSITIONAL_OR_KEYWORD)
        ] + [
            Parameter(name=name, kind=Parameter.KEYWORD_ONLY)
            for name in params
        ])
        # Set name for cached property
        bound_params.__set_name__(cls, 'bound_params')

        # Add bind & unbind to the class
        setattr(cls, 'bind', bind)
        setattr(cls, 'unbind', unbind)
        setattr(cls, 'bind_like', bind_like)
        setattr(cls, 'bound_params', bound_params)

        return cls
Пример #29
0
def _preprocess_data(func=None, *, replace_names=None, label_namer=None):
    """
    A decorator to add a 'data' kwarg to a function.

    When applied::

        @_preprocess_data()
        def func(ax, *args, **kwargs): ...

    the signature is modified to ``decorated(ax, *args, data=None, **kwargs)``
    with the following behavior:

    - if called with ``data=None``, forward the other arguments to ``func``;
    - otherwise, *data* must be a mapping; for any argument passed in as a
      string ``name``, replace the argument by ``data[name]`` (if this does not
      throw an exception), then forward the arguments to ``func``.

    In either case, any argument that is a `MappingView` is also converted to a
    list.

    Parameters
    ----------
    replace_names : list of str or None, default: None
        The list of parameter names for which lookup into *data* should be
        attempted. If None, replacement is attempted for all arguments.
    label_namer : str, default: None
        If set e.g. to "namer" (which must be a kwarg in the function's
        signature -- not as ``**kwargs``), if the *namer* argument passed in is
        a (string) key of *data* and no *label* kwarg is passed, then use the
        (string) value of the *namer* as *label*. ::

            @_preprocess_data(label_namer="foo")
            def func(foo, label=None): ...

            func("key", data={"key": value})
            # is equivalent to
            func.__wrapped__(value, label="key")
    """

    if func is None:  # Return the actual decorator.
        return functools.partial(
            _preprocess_data,
            replace_names=replace_names, label_namer=label_namer)

    sig = inspect.signature(func)
    varargs_name = None
    varkwargs_name = None
    arg_names = []
    params = list(sig.parameters.values())
    for p in params:
        if p.kind is Parameter.VAR_POSITIONAL:
            varargs_name = p.name
        elif p.kind is Parameter.VAR_KEYWORD:
            varkwargs_name = p.name
        else:
            arg_names.append(p.name)
    data_param = Parameter("data", Parameter.KEYWORD_ONLY, default=None)
    if varkwargs_name:
        params.insert(-1, data_param)
    else:
        params.append(data_param)
    new_sig = sig.replace(parameters=params)
    arg_names = arg_names[1:]  # remove the first "ax" / self arg

    assert {*arg_names}.issuperset(replace_names or []) or varkwargs_name, (
        "Matplotlib internal error: invalid replace_names ({!r}) for {!r}"
        .format(replace_names, func.__name__))
    assert label_namer is None or label_namer in arg_names, (
        "Matplotlib internal error: invalid label_namer ({!r}) for {!r}"
        .format(label_namer, func.__name__))

    @functools.wraps(func)
    def inner(ax, *args, data=None, **kwargs):
        if data is None:
            return func(ax, *map(sanitize_sequence, args), **kwargs)

        bound = new_sig.bind(ax, *args, **kwargs)
        needs_label = (label_namer
                       and "label" not in bound.arguments
                       and "label" not in bound.kwargs)
        auto_label = (bound.arguments.get(label_namer)
                      or bound.kwargs.get(label_namer))

        for k, v in bound.arguments.items():
            if k == varkwargs_name:
                for k1, v1 in v.items():
                    if replace_names is None or k1 in replace_names:
                        v[k1] = _replacer(data, v1)
            elif k == varargs_name:
                if replace_names is None:
                    bound.arguments[k] = tuple(_replacer(data, v1) for v1 in v)
            else:
                if replace_names is None or k in replace_names:
                    bound.arguments[k] = _replacer(data, v)

        bound.apply_defaults()
        del bound.arguments["data"]

        if needs_label:
            all_kwargs = {**bound.arguments, **bound.kwargs}
            # label_namer will be in all_kwargs as we asserted above that
            # `label_namer is None or label_namer in arg_names`.
            label = _label_from_arg(all_kwargs[label_namer], auto_label)
            if "label" in arg_names:
                bound.arguments["label"] = label
                try:
                    bound.arguments.move_to_end(varkwargs_name)
                except KeyError:
                    pass
            else:
                bound.arguments.setdefault(varkwargs_name, {})["label"] = label

        return func(*bound.args, **bound.kwargs)

    inner.__doc__ = _add_data_doc(inner.__doc__, replace_names)
    inner.__signature__ = new_sig
    return inner
Пример #30
0
 def add_arg(name: str, kind: Any, default: Optional[ast.expr]) -> None:
     default_val = Parameter.empty if default is None else _ValueFormatter(default)
     parameters.append(Parameter(name, kind, default=default_val))