def test_s_arg_wrapper(): """Tests the arg.s wrapper utility Simulates the same test scenario as test_validators_with_defaults() """ def fails(arg1): """A validator that always passes""" if arg1 == 'UPPER': raise ValueError def my_func(arg1): return arg1 my_func_runner = arg.s( arg.defaults(arg1=arg.val('arg1').upper()), arg.validators(fails) ) with pytest.raises(ValueError): my_func_runner(my_func)(arg1='upper') assert my_func_runner(my_func)(arg1='lower') == 'LOWER' assert isinstance(arg.s()(my_func), arg.Args) assert arg.s()(my_func)('hello') == 'hello'
def run(self): request_args = self.request.data form = self.form_class(request_args) form.full_clean() self.args = { **self.get_default_args(), **request_args, **form.cleaned_data, } def _validate_form(): if not form.is_valid(): raise exceptions.ValidationError(form.errors) wrapper = arg.s(self.get_wrapper(), arg.validators(_validate_form)) self.result = wrapper(self.action.func)(**self.args) object_to_serialize = self.result # Object actions may be parametrized and return a list by default. # Return only one object if this is the case if (isinstance(object_to_serialize, list) and len(object_to_serialize) == 1): object_to_serialize = object_to_serialize[0] if self.refetch_for_serialization: object_to_serialize = self.get_object() serializer = self.viewset.get_serializer( object_to_serialize, context={'request': self.request}) return Response(serializer.data)
def wrapper(cls): arg_decs = [] if cls.select_for_update is not None: # pragma: no branch arg_decs = [arg.contexts(transaction.atomic)] arg_decs += [ arg.defaults( **{ cls.objects_arg: arg.first( 'objects', daf.contrib.single_list('object'), cls.objects_arg, ) }), arg.defaults( **{ cls.objects_arg: djarg.qset( cls.objects_arg, qset=cls.queryset, select_for_update=cls.select_for_update, ) }), super().wrapper, ] return arg.s(*arg_decs)
def run_func(self): """ Run the primary function. This should be called from the "done()" method that is overridden by users. """ self.func_args = { **self.get_default_args(), **self.get_cleaned_data(*self.get_form_list()), } self.func_results = arg.s()(self.func)(**self.func_args) return self.func_results
class GrantStaffObjectsView(djarg.views.SuccessMessageMixin, djarg.views.ObjectsFormView): model = User func = arg.s( arg.contexts(error_collector=raise_trapped_errors), arg.parametrize(user=arg.val('objects')), arg.contexts(trap_errors), )(grant_staff_access) template_name = 'tests/grant_staff_access.html' form_class = GrantAccessObjectForm success_url = '.' success_message = '{granter} successfully granted staff access to users.' def get_default_args(self): return {**super().get_default_args(), **{'granter': self.request.user}}
def wrapper(cls): return arg.s( arg.contexts(trapped_errors=daf.contrib.raise_trapped_errors), arg.defaults( objects=arg.first( 'objects', daf.contrib.single_list('object'), daf.contrib.single_list(cls.object_arg), ) ), arg.defaults(objects=djarg.qset('objects', qset=cls.queryset)), arg.parametrize(**{cls.object_arg: arg.val('objects')}), arg.contexts(daf.contrib.trap_errors), super().wrapper, )
def check_class_definition(cls): """ Verifies all properties have been filled out properly for the action class. Called by the metaclass only on concrete actions """ super().check_class_definition() if not cls.objects_arg: cls.definition_error('Must provide "objects_arg" attribute.') func_parameters = inspect.signature(arg.s()(cls.func).func).parameters if cls.objects_arg not in func_parameters: cls.definition_error( f'objects_arg "{cls.objects_arg}" not an argument to callable.' f' Possible parameters={func_parameters}')
def wrapper(cls): return arg.s( arg.defaults( **{ cls.objects_arg: arg.first( 'objects', daf.contrib.single_list('object'), cls.objects_arg, ) } ), arg.defaults( **{ cls.objects_arg: djarg.qset( cls.objects_arg, qset=cls.queryset ) } ), super().wrapper, )
def wrapper(cls): arg_decs = [] if cls.select_for_update is not None: # pragma: no branch arg_decs = [arg.contexts(transaction.atomic)] arg_decs += [ arg.contexts(trapped_errors=daf.contrib.raise_trapped_errors), arg.defaults(objects=arg.first( 'objects', daf.contrib.single_list('object'), daf.contrib.single_list(cls.object_arg), )), arg.defaults(objects=djarg.qset( 'objects', qset=cls.queryset, select_for_update=cls.select_for_update, )), arg.parametrize(**{cls.object_arg: arg.val('objects')}), arg.contexts(daf.contrib.trap_errors), super().wrapper, ] return arg.s(*arg_decs)
def name(cls): """The identifying name of the action""" return arg.s()(cls.func).func.__name__
class Interface(metaclass=InterfaceMeta): """ Provides base properties for any action interface. """ ### # Core properties of the interface. ### # The primary action action = None # Instrumentation of how the action is run wrapper = arg.s() @classmethod def get_wrapper(cls): # A utility so that instance methods can safely access # the class wrapper variable. self.wrapper() will use # "self" as an argument when calling return cls.wrapper @utils.classproperty def func(cls): return cls.get_wrapper()(cls.action.func) # The namespace to which the interface belongs. Allows for querying # interfaces for a particular type of application such as the admin namespace = '' # The type of interface type = 'interface' @utils.classproperty def uri(cls): """The URI used when registering the interface to an action""" return f'{cls.namespace}.{cls.type}' if cls.namespace else cls.type @classmethod def as_interface(cls, **kwargs): cls.check_interface_definition() interface = cls.build_interface(**kwargs) cls.check_built_interface(interface) return interface @classmethod def build_interface(cls, **kwargs): raise NotImplementedError ### # Mirrored static action properties. # # Static action properties are automatically mirrored as # static properties on interfaces. ### @utils.classproperty def app_label(cls): return cls.action.app_label @utils.classproperty def model(cls): return cls.action.model @utils.classproperty def url_name(cls): return f'{cls.action.url_name}_{cls.type}' @utils.classproperty def url_path(cls): return cls.action.url_path @utils.classproperty def permission_uri(cls): return cls.action.permission_uri @utils.classproperty def display_name(cls): return cls.action.display_name @utils.classproperty def queryset(cls): return cls.action.queryset ### # Mirrored dynamic action properties. # # The get_ methods for dynamic action properties are automatically # mirrored on interfaces. ### def get_success_message(self, args, results): return self.action.get_success_message(args, results) ### # Runtime properties that change behavior. ### # True if the action permission should be required permission_required = False ### # Interface class checkers. # # When interfaces are created, class definitions are checked to ensure # they are are set up correctly. ### @classmethod def definition_error(cls, msg): raise AttributeError(f'{cls.__name__} - {msg}') @classmethod def check_interface_definition(cls): pass @classmethod def check_built_interface(cls, interface): pass
def run_func(self, form): self.func_args = {**self.get_default_args(), **form.cleaned_data} self.func_results = arg.s()(self.func)(**self.func_args) return self.func_results