def test_injection(): """ Tests that the function can be injected as first argument when inject_as_first_arg=True """ def generic_handler(f, *args, **kwargs): print("This is generic handler called by %s" % f.__name__) # here you could use f.__name__ in a if statement to determine what to do if f.__name__ == "func1": print("called from func1 !") return args, kwargs # generate 2 functions func1 = create_function("func1(a, b)", generic_handler, inject_as_first_arg=True) func2 = create_function("func2(a, d)", generic_handler, inject_as_first_arg=True) func1(1, 2) func2(1, 2)
def test_ex_nihilo(type, decorator): """ First example from the documentation: tests that we can generate a function from a string """ # (1) define the signature. if type == 'str': func_sig = "foo(b, a=0)" func_name = None else: parameters = [Parameter('b', kind=Parameter.POSITIONAL_OR_KEYWORD), Parameter('a', kind=Parameter.POSITIONAL_OR_KEYWORD, default=0), ] func_sig = Signature(parameters) func_name = 'foo' # (2) define the function implementation def func_impl(*args, **kwargs): """This docstring will be used in the generated function by default""" print("func_impl called !") return args, kwargs # (3) create the dynamic function if decorator: gen_func = with_signature(func_sig, func_name=func_name)(func_impl) else: gen_func = create_function(func_sig, func_impl, func_name=func_name) # first check the source code ref_src = "def foo(b, a=0):\n return _func_impl_(b=b, a=a)\n" print("Generated Source :\n" + gen_func.__source__) assert gen_func.__source__ == ref_src # then the behaviour args, kwargs = gen_func(2) assert args == () assert kwargs == {'a': 0, 'b': 2}
def test_create_facades(capsys): """ Simple test to create multiple functions with the same body This corresponds to the answer at https://stackoverflow.com/questions/13184281/python-dynamic-function-creation-with-custom-names/55105893#55105893 :return: """ # generic core implementation def generic_impl(f, *args, **kwargs): print("This is generic impl called by %s" % f.__name__) # here you could use f.__name__ in a if statement to determine what to do if f.__name__ == "func1": print("called from func1 !") return args, kwargs my_module = getmodule(generic_impl) # generate 3 facade functions with various signatures for f_name, f_params in [("func1", "b, *, a"), ("func2", "*args, **kwargs"), ("func3", "c, *, a, d=None")]: if f_name in {"func1", "func3"} and sys.version_info < (3, 0): # Python 2 does not support function annotations; Python 3.0-3.4 do not support variable annotations. pass else: # the signature to generate f_sig = "%s(%s)" % (f_name, f_params) # create the function dynamically f = create_function(f_sig, generic_impl, inject_as_first_arg=True) # assign the symbol somewhere (local context, module...) setattr(my_module, f_name, f) # grab each function and use it if sys.version_info >= (3, 0): func1 = getattr(my_module, 'func1') assert func1(25, a=12) == ((), dict(b=25, a=12)) func2 = getattr(my_module, 'func2') assert func2(25, a=12) == ((25, ), dict(a=12)) if sys.version_info >= (3, 0): func3 = getattr(my_module, 'func3') assert func3(25, a=12) == ((), dict(c=25, a=12, d=None)) captured = capsys.readouterr() with capsys.disabled(): print(captured.out) if sys.version_info >= (3, 0): assert captured.out == """This is generic impl called by func1 called from func1 ! This is generic impl called by func2 This is generic impl called by func3 """ else: assert captured.out == """This is generic impl called by func2
def generate_function(func_sig, dummy_call): def func_impl(*args, **kwargs): """This docstring will be used in the generated function by default""" print("func_impl called !") dummy_call(*args, **kwargs) return args, kwargs return create_function(func_sig, func_impl)
def test_arguments(case_data): """ Tests that the `PARAM_DEF` regexp works correctly """ def generic_handler(*args, **kwargs): return args, kwargs params_str, inputs, (eargs, ekwargs) = case_data.get() f = create_function("foo(%s)" % params_str, generic_handler) args, kwargs = eval("f(%s)" % inputs, globals(), locals()) assert args == eargs assert kwargs == ekwargs
def test_positional_only(): """Tests that as of today one cannot create positional-only functions""" params = [Parameter('a', kind=Parameter.POSITIONAL_ONLY), Parameter('args', kind=Parameter.VAR_POSITIONAL), Parameter('kwargs', kind=Parameter.VAR_KEYWORD)] func_signature = Signature(parameters=params) with pytest.raises(SyntaxError): dynamic_fun = create_function(func_signature, my_handler, func_name="foo") print(dynamic_fun.__source__) assert dynamic_fun(0, 1) == ((1,), {'a': 0})
def test_generator(): """ Tests that we can use a generator as function_handler in `create_function`""" # define the handler that should be called def my_generator_handler(b, a=0): for i in range(a, b): yield i * i # create the dynamic function dynamic_fun = create_function("foo(a, b)", my_generator_handler) assert isgeneratorfunction(dynamic_fun) assert list(dynamic_fun(1, 4)) == [1, 4, 9]
def test_ex_nihilo_kw_only(): """Same than ex nihilo but keyword only""" def func_impl(*args, **kwargs): """This docstring will be used in the generated function by default""" print("func_impl called !") return args, kwargs func_sig = "foo(b, *, a=0, **kwargs)" gen_func = create_function(func_sig, func_impl) ref_src = "def foo(b, *, a=0, **kwargs):\n return _func_impl_(b=b, a=a, **kwargs)\n" print(gen_func.__source__) assert gen_func.__source__ == ref_src
def test_type_comments(): """Tests that """ func_signature = """ foo(b, # type: int a = 0, # type: float ): # type: (...) -> str """ def dummy_handler(*args, **kwargs): return "hello" dynamic_fun = create_function(func_signature, dummy_handler) assert dynamic_fun.__annotations__ == {'a': float, 'b': int, 'return': str}
def create_initiation_function(cls, gen_init): # (1) check which signature we want to create params = [Parameter('self', kind=Parameter.POSITIONAL_OR_KEYWORD)] for mandatory_arg_name in cls.__init_args__: params.append( Parameter(mandatory_arg_name, kind=Parameter.POSITIONAL_OR_KEYWORD)) for default_arg_name, default_arg_val in cls.__opt_init_args__.items(): params.append( Parameter(default_arg_name, kind=Parameter.POSITIONAL_OR_KEYWORD, default=default_arg_val)) sig = Signature(params) # (2) create the init function dynamically return create_function(sig, generic_init)
def test_native_coroutine(): """ Tests that we can use a native async coroutine as function_handler in `create_function`""" # define the handler that should be called from makefun.tests._test_py35 import make_native_coroutine_handler my_native_coroutine_handler = make_native_coroutine_handler() # create the dynamic function dynamic_fun = create_function("foo(sleep_time=2)", my_native_coroutine_handler) # check that this is a coroutine for inspect and for asyncio assert iscoroutinefunction(dynamic_fun) from asyncio import iscoroutinefunction as is_native_co assert is_native_co(dynamic_fun) # verify that the new function is a native coroutine and behaves correctly from asyncio import get_event_loop out = get_event_loop().run_until_complete(dynamic_fun(0.1)) assert out == 0.1
def mark(self) -> Callable[..., Any]: """generate mark method""" assert self.fixture_function fixture_sig: Signature = signature(self.fixture_function) def _mark(**kwargs) -> TestFuncT: _validate_input(mark_sig, **kwargs) return getattr(pytest.mark, self.name)(**kwargs) mark_sig = signature(_mark) mark_parameters = tuple(p for p in fixture_sig.parameters.values() if p.default is not _empty) mark_sig = mark_sig.replace(parameters=mark_parameters) mark = create_function( mark_sig, _mark, func_name=f"{self.name}_mark", module_name=self.fixture_function.__module__, ) return mark
def fixture(cls, *args: Any, **kwargs: Any) -> Union[_FixtureFunctionT, _FixtureFunctionB]: # todo, generics? if len(args) == 1 and not kwargs and callable(args[0]): # if the function is called as a decorator return cls(args[0]).pytest_fixture() elif kwargs.pop("autoparam", False): # if the function is called as a autoparam calling_frame = inspect.stack()[1] assert calling_frame, "no frames?" calling_module = inspect.getmodule(calling_frame[0]) assert calling_module, "no module?" try: # this is the most hack i have ever hacked, this is terrible but should work for what we need # example: # calling_frame.code_context == ['c = fixture(test_1=1, test_2=2, autoparam=True)\n'] calling_context = calling_frame.code_context if calling_context: fixture_name = calling_frame.code_context[0].split("=") else: fixture_name = "gen-fixture" except Exception: fixture_name = "gen-fixture" @_make_class_agnostic def _func(request: SubRequest) -> Any: return request.param # todo we need to skip the current class func = create_function( signature(_func), _func, func_name=fixture_name, module_name=calling_module.__name__, ) return cls(func).pytest_fixture(*args, **kwargs) else: return cls(*args, **kwargs).wrapper
def build_handler(self, route_parameters): """ This special method is used when using the Flask style routes, it using meta-programming to create a controller for the method then links the action, them makes them callable ,the send control back to the normal execution path :param route_parameters: :return: """ Controller = route_parameters["controller"] action = route_parameters["action"] if Controller == "SegoBaseController": handler_id = self.configuration_manager.get("dynamic_routes", action) handler = ctypes.cast(handler_id, ctypes.py_object).value else: _class = self.dynamic_import(Controller) handler = getattr(_class(), action) middleware_manager = self.middleware_manager def handler_implementation(*args, **kwargs): """ Handler wrapper """ if route_parameters is not None: middleware_manager.process_middleware(stage=Middleware.PREPROCESS, \ route=route_parameters, \ request=request, \ response=None) response = handler(**kwargs) if route_parameters is not None: middleware_manager.process_middleware(stage=Middleware.POSTPROCESS, \ route=route_parameters, \ request=request, \ response=response) return response method_signature = str(handler.__name__) + str(signature(handler)) wrapped_handler = create_function(method_signature, handler_implementation, func_name=str(handler.__name__)) return wrapped_handler
def test_generator_based_coroutine(): """ Tests that we can use a generator coroutine as function_handler in `create_function`""" # define the handler that should be called def my_gencoroutine_handler(first_msg): second_msg = (yield first_msg) yield second_msg # create the dynamic function dynamic_fun = create_function("foo(first_msg='hello')", my_gencoroutine_handler) # a legacy (generator-based) coroutine is not an asyncio coroutine.. assert not iscoroutinefunction(dynamic_fun) assert isgeneratorfunction(dynamic_fun) cor = dynamic_fun('hi') first_result = next(cor) assert first_result == 'hi' second_result = cor.send('chaps') assert second_result == 'chaps' with pytest.raises(StopIteration): cor.send('ola')
def test_basic(params_type_hints, with_self_ref): """ Tests that we can create a simple dynamic function from a signature string, redirected to a generic handler. """ if params_type_hints == 1: from typing import Any func_signature = "foo(b, # type: int\n" \ " a = 0, # type: float\n" \ " ):\n # type: (...) -> Any" elif params_type_hints == 2: from typing import Any func_signature = "foo(b: int, a: float=0) -> Any" else: func_signature = "foo(b, a=0)" # this handler will grab the inputs and return them if with_self_ref: def identity_handler(facade, *args, **kwargs): """test doc""" return facade, args, kwargs else: def identity_handler(*args, **kwargs): """test doc""" return args, kwargs # create the dynamic function dynamic_fun = create_function(func_signature, identity_handler, inject_as_first_arg=with_self_ref) # a few asserts on the signature assert dynamic_fun.__name__ == 'foo' assert dynamic_fun.__doc__ == 'test doc' assert dynamic_fun.__module__ == test_basic.__module__ if params_type_hints == 1: # unfortunately assert dynamic_fun.__annotations__ == {} elif params_type_hints == 2: assert dynamic_fun.__annotations__ == { 'a': float, 'b': int, 'return': Any } else: assert dynamic_fun.__annotations__ == {} assert dynamic_fun.__defaults__ == (0, ) assert dynamic_fun.__kwdefaults__ is None if params_type_hints != 1: func_signature = func_signature + ":" if with_self_ref: src = "def " + func_signature + '\n return _func_impl_(foo, b=b, a=a)\n' else: src = "def " + func_signature + '\n return _func_impl_(b=b, a=a)\n' dct = {'__source__': src, '__func_impl__': identity_handler} if not params_type_hints_allowed: dct['__annotations__'] = dict() dct['__kwdefaults__'] = None assert vars(dynamic_fun) == dct # try to call it ! if with_self_ref: f, args, kwargs = dynamic_fun(2) assert f is dynamic_fun else: args, kwargs = dynamic_fun(2) assert args == () assert kwargs == {'a': 0, 'b': 2}
def patch(self): ex = create_function(self.ex_function, execute, doc=self.doc) self.execute = types.MethodType(ex, self) print('%s.%s' % (os.path.splitext(os.path.basename( self.input_path))[0], self.ex_function)) print(self.execute.__doc__)
def call(self) -> Callable[..., Any]: """generate fixture callable""" assert self.fixture_function fixture_sig: Signature = signature(self.fixture_function) fixture_self_index = _param_index(fixture_sig, "self") fixture_request_index = _param_index(fixture_sig, "request") call_sig: Signature call_self_index: Optional[int] call_request_index: Optional[int] fixture_function = self.fixture_function def _the_thing(*args: Any, **kwargs: Any) -> Any: # raise error if we have a signature mismatch _validate_input(call_sig, *args, **kwargs) # coalese args and kwargs for parameter indexing args_n_kwargs = [*args, *kwargs.values()] # pluck the request out assert call_request_index is not None request = args_n_kwargs[call_request_index] mark_kwargs = getattr(request.node.get_closest_marker(self.name), "kwargs", {}) if call_self_index is not None: kwargs = { "self": args[call_self_index], **kwargs, **mark_kwargs } else: kwargs = {**kwargs, **mark_kwargs} # print(signature(fixture_function), args, kwargs, mark_kwargs) return fixture_function(**kwargs) # setup call func if fixture_self_index is None: if fixture_request_index is None: # no self, needs request def _call(request: SubRequest, *args: Any, **kwargs: Any) -> Any: # print("no self, needs request", args, kwargs) return _the_thing(request, *args, **kwargs) else: # no self, has request def _call(*args: Any, **kwargs: Any) -> Any: # print("no self, has request", args, kwargs) return _the_thing(*args, **kwargs) else: # has self if fixture_request_index is None: # has self, needs request def _call(self, request: SubRequest, *args: Any, **kwargs: Any) -> Any: # print("has self, needs request", args, kwargs) return _the_thing(self, request, *args, **kwargs) else: # has self, has request def _call(self, *args: Any, **kwargs: Any) -> Any: # print("has self, has request", args, kwargs) return _the_thing(self, *args, **kwargs) # extract as variable for _validate_input closures above call = wraps(self.fixture_function)(_call) call_sig = _insert_request_param(signature(call)) call_self_index = _param_index(call_sig, "self") call_request_index = _param_index(call_sig, "request") call = create_function(call_sig, _call, func_name=self.name) call.mark = self.mark() return call