def __call__(self, *args, tag=None, **kwargs): # If args/kwargs form a complete binding with no AbstractParameters # then return a simple call to the underlying function. try: funcsig = Signature.from_callable(self.func) binding = funcsig.bind(*args, **kwargs) kwargs = binding.arguments is_abstract_parameter = [ isinstance(v, AbstractParameter) for v in kwargs.values() ] if not any(is_abstract_parameter): return self.func(**kwargs) except TypeError: pass # If no arguments are supplied then return the derived Parameter. if not any([args, kwargs, tag]): return self.derive() # Otherwise return a new Derivative with an updated binding. new_kwargs = self.kwargs.copy() funcsig = Signature.from_callable(self.func) binding = funcsig.bind_partial(*args, **kwargs) new_kwargs.update(binding.arguments) tag = self.tag if tag is None else tag return Derivative(self.func, self.method, tag, **new_kwargs)
def composite_outer_arguments(outer,inner,*args,**kwargs): if callable(outer) and callable(inner): def comp(*iargs,**ikwargs): return outer(inner(*iargs,**ikwargs),*args,**ikwargs) comp.__signature__ = Signature.from_callable(inner).replace(return_annotation=Signature.from_callable(outer).return_annotation) return comp else:
def test_register_signature(): signature = Signature.from_callable(registry.register) with not_raises(TypeError): signature.bind(object(), object()) signature.bind(object(), wrapper=object()) signature.bind(expression=object(), wrapper=object()) # register can behave as a decorator too returned_decorator = registry.register(object) signature = Signature.from_callable(returned_decorator) with not_raises(TypeError): signature.bind(object())
def composite(outer,inner,*args): comp = None if callable(outer) and callable(inner): def temp(*args, **kwargs): return outer(inner(*args,**kwargs)) comp = temp else: raise TypeError("the outer and inner functions you provide have to be callable") comp.__signature__ = Signature.from_callable(inner).replace(return_annotation=Signature.from_callable(outer).return_annotation) if len(args)!=0: if all(callable, args): return composite(comp,args[0],*args) else: raise TypeError("all functions provided need to be callable in order to be composited") return comp
def test___prepare_call_args_from_dict__exceptions(self): sample = SampleRpcObject(10) signature = Signature.from_callable( sample.pos_or_kw__var_pos__kw_only__kwargs) processor = RpcRequestProcessor(sample) with pytest.raises(TypeError, message='`get_patterns` must be a list'): processor._prepare_call_args_from_dict(signature, { 'key': 'lol', 'get_patterns': 1 }) with pytest.raises(TypeError, message='You must specify `key` argument'): processor._prepare_call_args_from_dict(signature, {}) with pytest.raises( TypeError, message= 'Keyword arguments passed in the variable `kwargs` must be a dict' ): processor._prepare_call_args_from_dict(signature, { 'key': 1, 'kwargs': 'qwe' })
def test___prepare_call_args_from_dict(self): sample = SampleRpcObject(10) signature = Signature.from_callable( sample.pos_or_kw__var_pos__kw_only__kwargs) processor = RpcRequestProcessor(sample) params = { 'key': 'lol', 'get_patterns': [1, 2, 3], 'additional_kw': 2, 'by': 'qwe', # positional only 'kwargs': { 'trash': 1 } } res = processor._prepare_call_args_from_dict(signature, params) assert res == (('lol', 1, 2, 3), { 'by': 'qwe', 'trash': 1, 'additional_kw': 2 }) # can deal with empty *get_patterns params = {'key': 'lol'} res = processor._prepare_call_args_from_dict(signature, params) assert res == (('lol', ), {'by': None})
def submit(self, func, *args, **kwargs): self.ran_dummy_executor = True sig = Signature.from_callable(func) assert sig.bind(*args, **kwargs).arguments["ref_input"] == refinp dummy_future = concurrent.futures.Future() dummy_future.set_result(None) return dummy_future
def connect(self, slot: Callable) -> None: """Connects the signal to callable that will receive the signal when emitted. Arguments: slot: Callable with signature that match the signature defined for signal. Raises: ValueError: When callable signature does not match the signature of signal. """ if not callable(slot): raise ValueError(f"Connection to non-callable '{slot.__class__.__name__}' object failed") # Verify signatures sig = Signature.from_callable(slot).replace(return_annotation=Signature.empty) if str(sig) != str(self._sig): # Check if the difference is only in keyword arguments with defaults. if not self._kw_test(sig): raise ValueError("Callable signature does not match the signal signature") if isinstance(slot, partial) or slot.__name__ == '<lambda>': # If it's a partial, a Signal or a lambda. if slot not in self._slots: self._slots.append(slot) elif ismethod(slot): # Check if it's an instance method and store it with the instance as the key self._islots[slot.__self__] = slot.__func__ else: # If it's just a function then just store it as a weakref. newSlotRef = ref(slot) if newSlotRef not in self._slots: self._slots.append(newSlotRef)
def test_invalid_signatures(method) -> None: with pytest.raises(TypeError) as e: check(method.__name__)(method) sig = Signature.from_callable(method) message = f'invalid check handler "{method.__name__}": incompatible return type annotation in signature {repr(sig)}, expected to match <Signature () -> Union[bool, str, Tuple[bool, str], NoneType]>' assert str(e.value) == message
def register_factory( self, factory: Callable, return_type: Optional[Type], life_style: ServiceLifeStyle, ) -> None: if not callable(factory): raise InvalidFactory(return_type) sign = Signature.from_callable(factory) if return_type is None: if sign.return_annotation is _empty: raise MissingTypeException() return_type = sign.return_annotation if isinstance(return_type, str): # pragma: no cover # Python 3.10 annotations = _get_factory_annotations_or_throw(factory) return_type = annotations["return"] self._bind( return_type, FactoryResolver(return_type, self._check_factory(factory, sign, return_type), life_style), )
def get_executor(self, method: Callable) -> Callable: sig = Signature.from_callable(method) params = { key: Dependency(key, value.annotation) for key, value in sig.parameters.items() } if sys.version_info >= (3, 10): # pragma: no cover # Python 3.10 annotations = _get_factory_annotations_or_throw(method) for key, value in params.items(): if key in annotations: value.annotation = annotations[key] fns = [] for key, value in params.items(): fns.append(self._get_getter(key, value)) if iscoroutinefunction(method): async def async_executor(scoped: Optional[Dict[Type, Any]] = None): with GetServiceContext(self, scoped) as context: return await method(*[fn(context) for fn in fns]) return async_executor def executor(scoped: Optional[Dict[Type, Any]] = None): with GetServiceContext(self, scoped) as context: return method(*[fn(context) for fn in fns]) return executor
def create_parser(self, doc=""): """ :param doc: :return: """ # I have a feeling this will ve changed but for the time being it's kinda cool sig = Signature.from_callable(self.execute) parameter_types = self.execute.__annotations__ if len(sig.parameters) != len(parameter_types): raise ValueError( "All parameters definied in 'execute' must have types specified. See https://docs.python.org/3/library/typing.html for more" ) parser = argparse.ArgumentParser(description=doc) for parameter in sig.parameters.values(): parameter_type = parameter_types[parameter.name] if parameter.default != Parameter.empty and parameter_type is bool: # if the default is specified as false then we want to store true, otherwise the opposite arg_options = { "action": "store_true" if not parameter.default else "store_false" } elif parameter.default != Parameter.empty: arg_options = { "default": parameter.default, "required": False, "type": parameter_type } else: arg_options = {"required": True} parser.add_argument( '--{}'.format(parameter.name.replace("_", "-")), **arg_options) return parser
def easy_init(cls): if hasattr(cls, '__init__') and type( cls.__init__).__name__ == 'function' and callable(cls.__init__): sig = Signature.from_callable(cls.__init__) sig = sig.replace(parameters=list( map(lambda val: val[1], filter(lambda val: val[0] != 'self', sig.parameters.items())))) init_func = cls.__init__ elif hasattr(cls, '_fields') and type(cls._fields) is list: sig = Signature([ Parameter(name, Parameter.POSITIONAL_OR_KEYWORD) for name in cls._fields ]) init_func = lambda self, /, *args, **kwargs: None #print(sig) def new_init(self, *args, **kwargs): #print(self,file=sys.stderr) #print(*args,'\n',**kwargs) print(sig) bound = sig.bind(*args, **kwargs) for name, val in bound.arguments.items(): print(name) setattr(self, name, val) return init_func(self, *args, **kwargs) new_init.__signature__ = sig setattr(cls, '__init__', new_init) return cls
def normalize_handler( route: Route, services: Services) -> Callable[[Request], Awaitable[Response]]: method = route.handler sig = Signature.from_callable(method) params = sig.parameters params_len = len(params) if any( str(param).startswith("*") or param.kind.value == _ParameterKind.KEYWORD_ONLY for param in params.values()): raise UnsupportedSignatureError(method) if inspect.iscoroutinefunction(method): normalized = get_async_wrapper(services, route, method, params, params_len) else: normalized = get_sync_wrapper(services, route, method, params, params_len) if normalized is not method: copy_special_attributes(method, normalized) _copy_name_and_docstring(method, normalized) return normalized
def test_update_self_ignore_existing_anno(self): """Don't annotate first arg of instance methods if asked to ignore""" sig = Signature.from_callable(UpdateSignatureHelper.an_instance_method) sig = update_signature_args(sig, {'self': UpdateSignatureHelper}, has_self=True, existing_annotation_strategy=ExistingAnnotationStrategy.IGNORE) expected = Signature(parameters=[Parameter('self', Parameter.POSITIONAL_OR_KEYWORD)]) assert sig == expected
def test_update_yield_none_and_return(self): sig = Signature.from_callable(UpdateSignatureHelper.a_class_method) sig = update_signature_return(sig, return_type=str, yield_type=NoneType) assert sig == Signature(return_annotation=Generator[NoneType, NoneType, str])
def test_update_self(self): """Don't annotate first arg of instance methods""" sig = Signature.from_callable(UpdateSignatureHelper.an_instance_method) sig = update_signature_args(sig, {'self': UpdateSignatureHelper}, True) expected = Signature( parameters=[Parameter('self', Parameter.POSITIONAL_OR_KEYWORD)]) assert sig == expected
def proxy_func(): n = name f = lambda *args, _async=False, **kwargs: self._pp_issue_tn( 'call', n, *args, async=_async, **kwargs) f.__signature__ = Signature.from_callable(func) f.__doc__ = func.__doc__ return f
def test_nonetype_annotation(self): """NoneType should always be rendered as None""" sig = Signature.from_callable(UpdateSignatureHelper.has_annos) sig = update_signature_args(sig, {'a': Dict[str, NoneType]}, has_self=False, existing_annotation_strategy=ExistingAnnotationStrategy.IGNORE) stub = FunctionStub('test', sig, FunctionKind.MODULE) expected = 'def test(a: Dict[str, None], b) -> int: ...' assert stub.render() == expected
def __init__(self, func, method, tag, *args, **kwargs): self.func = func self.method = method self.tag = tag funcsig = Signature.from_callable(func) binding = funcsig.bind_partial(*args, **kwargs) self.kwargs = binding.arguments
def __init__(self, project, analysis_cls): self._project = project self._analysis_cls = analysis_cls self.__doc__ = '' self.__doc__ += analysis_cls.__doc__ or '' self.__doc__ += analysis_cls.__init__.__doc__ or '' self.__call__.__func__.__signature__ = Signature.from_callable( analysis_cls.__init__)
def test_update_yield(self): sig = Signature.from_callable(UpdateSignatureHelper.a_class_method) sig = update_signature_return(sig, yield_type=int) assert sig == Signature(return_annotation=Iterator[int]) sig = update_signature_return(sig, return_type=NoneType, yield_type=int) assert sig == Signature(return_annotation=Iterator[int])
def decorator(wrapper_func: WrapperAcceptingBoundArgs): original_signature = Signature.from_callable(original_func) @wraps(original_func) def from_args_to_bound_args_obj(*args, **kwargs): arguments = original_signature.bind(*args, **kwargs) return wrapper_func(arguments) return from_args_to_bound_args_obj
def test_update_class(self): """Don't annotate the first arg of classmethods""" sig = Signature.from_callable( UpdateSignatureHelper.a_class_method.__func__) sig = update_signature_args(sig, {'cls': Type[UpdateSignatureHelper]}, True) expected = Signature( parameters=[Parameter('cls', Parameter.POSITIONAL_OR_KEYWORD)]) assert sig == expected
def test_update_arg_ignore_existing_anno(self): """Update stubs only bases on traces.""" sig = Signature.from_callable(UpdateSignatureHelper.has_annos) sig = update_signature_args(sig, {'a': str, 'b': bool}, has_self=False, ignore_existing_annotations=True) params = [ Parameter('a', Parameter.POSITIONAL_OR_KEYWORD, annotation=str), Parameter('b', Parameter.POSITIONAL_OR_KEYWORD, annotation=bool), ] assert sig == Signature(parameters=params, return_annotation=int)
def test_update_arg(self): """Update arg annotations from types""" sig = Signature.from_callable(UpdateSignatureHelper.has_annos) sig = update_signature_args(sig, {'b': int}, False) params = [ Parameter('a', Parameter.POSITIONAL_OR_KEYWORD, annotation=int), Parameter('b', Parameter.POSITIONAL_OR_KEYWORD, annotation=int), ] assert sig == Signature(parameters=params, return_annotation=int)
def __init__(self, concrete_type, services, lifestyle): assert isclass(concrete_type) assert not isabstract(concrete_type) self.concrete_type = concrete_type self.services = services sig = Signature.from_callable(concrete_type.__init__) self.params = sig.parameters self.lifestyle = lifestyle
def normalize_handler(services, method): sig = Signature.from_callable(method) params = sig.parameters params_len = len(params) if inspect.iscoroutinefunction(method): return get_async_wrapper(services, method, params, params_len) return get_sync_wrapper(services, method, params, params_len)
def has_method(obj, method_name, signature=None): for member in inspect.getmembers(obj, predicate=inspect.ismethod): member_method_name = member[0] member_method = member[1] if member_method_name == method_name: if signature is not None: return Signature.from_callable(member_method) == signature return True return False
def test_update_arg_ignore_existing_anno_None(self): """Update arg annotations from types""" sig = Signature.from_callable(UpdateSignatureHelper.has_annos) sig = update_signature_args(sig, {'a': None, 'b': int}, has_self=False, ignore_existing_annotations=True) params = [ Parameter('a', Parameter.POSITIONAL_OR_KEYWORD, annotation=inspect.Parameter.empty), Parameter('b', Parameter.POSITIONAL_OR_KEYWORD, annotation=int), ] assert sig == Signature(parameters=params, return_annotation=int)
def required_args(attrs): """Extract the required arguments from a class's attrs. Arguments: attrs (:py:class:`dict`) :The attributes of a class. Returns: :py:class:`set`: The required arguments. """ init_args = attr_args = set() if '__init__' in attrs: sig = Signature.from_callable(attrs['__init__']) init_args = set( name for name, param in sig.parameters.items() if param.kind == Parameter.KEYWORD_ONLY and param.default is Signature.empty ) if 'REQUIRED' in attrs: attr_args = attrs['REQUIRED'] return set.union(attr_args, init_args)