Example #1
0
    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}))
Example #2
0
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'
Example #3
0
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
Example #4
0
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'
Example #5
0
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'
Example #6
0
 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()
Example #7
0
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
Example #8
0
    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
Example #9
0
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
Example #10
0
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'
    )