def _call(self, **call_args): objects = arg.load(self._objects, **call_args) if isinstance(objects, models.QuerySet): return self._attach_select_for_update(objects) if isinstance(self._qset, arg.Lazy): qset = arg.load(self._qset, **call_args) else: qset = self._qset # No object. Return empty queryset if objects is None: return qset.none() # Ensure we are always working with a list from now on if not isinstance(objects, (list, tuple)): objects = [objects] # Empty list. Return empty queryset if not objects: return qset.none() # Handle list of models or list of pks pk_in = f'{self._pk}__in' if isinstance(objects[0], models.Model): return self._attach_select_for_update( qset.filter( **{pk_in: [getattr(obj, self._pk) for obj in objects]})) else: return self._attach_select_for_update( qset.filter(**{pk_in: objects}))
def test_lazy_evaluation_chaining(): """Verifies that we can chain calls to lazy objects""" assert arg.load(arg.val('value').upper(), value='aa') == 'AA' assert arg.load(arg.val('value').upper().lower(), value='Aa') == 'aa' assert arg.load(arg.val('func_val')(), func_val=lambda: 'ret') == 'ret' class MyClass: def __init__(self, val): self.val = val # Instantiate a class with a dynamic attribute and return an attribute # of that class assert arg.load(arg.init(MyClass, val=arg.val('a')).val, a='hi') == 'hi'
def adapt(form, func, default_args=None): """ Adapt a form to an python-args func, ensuring the form validation behaves seamlessly with function validation. Evaluate any djarg.form.Field classes that are fields of the form. Args: form (django.forms.Form): The Django form being adapted. func (function): A function decorated with ``python-args`` decorators. default_args (dict, default=None): A dictionary of any other default arguments that are used when calling the ``python-args`` function. """ default_args = default_args or {} # Instantiate any lazy fields for label, field in form.fields.items(): if isinstance(field, arg.Lazy): form.fields[label] = arg.load(field, **{ **{ 'form': form }, **default_args }) # Attached additional form field validators for label, field in form.fields.items(): field.validators.append(get_field_validator(func, label)) # Attach additional form clean methods form.clean = get_form_clean(func, form, default_args=default_args) return form
def test_func(): """Tests usage of the arg.func utility for lazily-evaluating functions""" lazy_func = arg.func(lambda: '1') assert arg.load(lazy_func) == '1' def upper_arg(arg): return arg.upper() lazy_func = arg.func(upper_arg) assert arg.load(lazy_func, arg='hi') == 'HI' # Trying to run a lazy func without proper args results in a bind error with pytest.raises(arg.BindError): assert arg.load(lazy_func) == 'default' # Provide a default when arguments cannot be bound lazy_func = arg.func(upper_arg, 'default') assert arg.load(lazy_func) == 'default'
def test_init(): """Tests usage of the arg.init utility for lazily initializing classes""" class LazyClass: def __init__(self, arg, kwarg=None): self.arg = arg self.kwarg = kwarg lazy_class = arg.init(LazyClass, 'arg', kwarg='kwarg') inst = arg.load(lazy_class) assert inst.arg == 'arg' assert inst.kwarg == 'kwarg' # Verify we can use other lazy objects as parameters to class # initialization lazy_class = arg.init( LazyClass, 'arg', kwarg=arg.func(lambda extra: extra) ) inst = arg.load(lazy_class, extra='extra') assert inst.arg == 'arg' assert inst.kwarg == 'extra'
def get_queryset(self): """ Returns the queryset for single-object views. If the queryset as been overridden to be a "lazy" function (e.g. from ``python-args``), the function will be evaluated with ``request`` as a keyword argument and returned. Otherwise, it defaults to Django's generic detail-view ``get_queryset()`` function. """ if isinstance(self.queryset, arg.Lazy): return arg.load(self.queryset, request=self.request) else: return super().get_queryset()
def test_first(): """Tests arg.first utility for lazily loading the first loadable value""" assert arg.load(arg.first(arg.val('a'), arg.val('b')), b=2) == 2 assert arg.load(arg.first(arg.val('a'), arg.val('b')), a=3) == 3 with pytest.raises(arg.BindError): arg.load(arg.first(arg.val('a'), arg.val('b')), c=3) with pytest.raises(TypeError): arg.first(1, 2, 3) assert arg.load(arg.first(lambda a: '1', lambda b: '2'), b='val') == '2' assert ( arg.load(arg.first(arg.val('a'), arg.val('b'), default='nothing'), c=3) == 'nothing' ) assert arg.load(arg.first('a', 'b', 'c', 'd'), c=2, d=3) == 2
def get_form_list(self, until=None): """ Overrides get_form_list() to dynamically evaluate arg.func() conditions. Allows conditional evaluation up to a specific step so that we avoid various infinite recursion issues. """ form_list = collections.OrderedDict() self._condition_cache = getattr(self, '_condition_cache', {}) for step, form_class in self.form_list.items(): if step == until: break if step not in self._condition_cache: condition = self.condition_dict.get(step, True) if callable(condition): if isinstance(condition, arg.func): # Evaluate the cleaned data so far. If None, it means # a previous step didn't validate and we should include # the form as a step until we have enough data to # invalidate it args_so_far = self.get_cleaned_data(*form_list) if args_so_far is not None: condition = arg.load( condition, **{ **self.get_default_args(), **args_so_far }, ) else: condition = True else: condition = condition(self) self._condition_cache[step] = condition if self._condition_cache[step]: form_list[step] = form_class return form_list
def test_val(): """Tests the arg.val utility function for lazily obtaining values""" assert arg.load(arg.val('arg_name'), arg_name=1) == 1 with pytest.raises(arg.BindError): assert arg.load(arg.val('arg_name'), missing_arg=2) assert arg.load(arg.val('arg_name', 1), missing_arg=2) == 1
def test_nested_lazy_calling(): """Verifies that lazy objects can be nested in others""" assert ( arg.load(arg.func(arg.func(lambda value: value).upper()), value='hi') == 'HI' )