Ejemplo n.º 1
0
def test_reinvoke_namespace_merge():
    class ReinvokableWithExtra(Namespace):
        _name = None
        extra = Refinable()

        @reinvokable
        def __init__(self, **kwargs):
            super().__init__(**kwargs)

    assert reinvoke(
        ReinvokableWithExtra(
            extra__foo=17
        ),
        Namespace(
            extra__bar=42
        )
    ).extra == dict(bar=42, foo=17)

    # Try again with pre-Namespaced kwargs
    assert reinvoke(
        ReinvokableWithExtra(
            **Namespace(extra__foo=17)
        ),
        Namespace(
            extra__bar=42
        )
    ).extra == dict(bar=42, foo=17)
Ejemplo n.º 2
0
    def __init__(self,
                 *bases,
                 base_template=None,
                 content_block=None,
                 assets=None,
                 **kwargs):
        self.name = None

        self.base_template = base_template
        if not self.base_template:
            for base in reversed(bases):
                if base.base_template:
                    self.base_template = base.base_template
                    break

        self.content_block = content_block
        if not self.content_block:
            for base in reversed(bases):
                if base.content_block:
                    self.content_block = base.content_block
                    break

        self.assets = {
            k: v
            for k, v in Namespace(*(base.assets
                                    for base in bases), assets).items()
            if v is not None
        }
        self.config = Namespace(*[x.config for x in bases],
                                recursive_namespace(kwargs))
Ejemplo n.º 3
0
    def __init__(self, *bases, base_template=None, content_block=None, assets=None, root=None, internal=False, **kwargs):
        self.name = None
        self.internal = internal

        self.base_template = base_template
        if not self.base_template:
            for base in reversed(bases):
                if base.base_template:
                    self.base_template = base.base_template
                    break

        self.content_block = content_block
        if not self.content_block:
            for base in reversed(bases):
                if base.content_block:
                    self.content_block = base.content_block
                    break

        if assets:
            from iommi.debug import iommi_debug_on
            if iommi_debug_on():
                print("Warning: The preferred way to add top level assets config to a Style is via the root argument. "
                      "I.e. assets__* becomes root__assets__*")
            setdefaults_path(root, assets=assets)

        self.root = {k: v for k, v in items(Namespace(*(base.root for base in bases), root)) if v is not None}
        self.config = Namespace(*[x.config for x in bases], recursive_namespace(kwargs))
Ejemplo n.º 4
0
def test_namespace_setitem_function_dict():
    def f():
        pass

    x = Namespace(f=f)
    x.setitem_path('f', dict(x=17))
    assert x == dict(f=dict(call_target=f, x=17))
Ejemplo n.º 5
0
def test_namespace_setitem_function_non_dict():
    def f():
        pass

    x = Namespace(f=f)
    x.setitem_path('f', 17)
    assert x == dict(f=17)
Ejemplo n.º 6
0
def test_setdefaults_callable_backward_not_namespace():
    actual = setdefaults_path(
        Namespace(foo__x=17),
        foo=EMPTY,
    )
    expected = Namespace(foo__x=17)
    assert actual == expected
Ejemplo n.º 7
0
def test_namespace_setitem_function_backward():
    def f():
        pass

    x = Namespace(f__x=17)
    x.setitem_path('f', f)
    assert x == dict(f=dict(call_target=f, x=17))
Ejemplo n.º 8
0
def test_dispatch_with_target():
    @dispatch
    def quux_(title):
        # something...
        return title

    @dispatch(
        b='X',
        quux=Namespace(call_target=quux_),
    )
    def bar_(a, b, quux):
        return a + b + quux()

    def baz_(a, b, c):
        # something...
        return a + b + c

    @dispatch(bar=Namespace(call_target=bar_),
              bar__a='5',
              bar__quux__title='hi!',
              baz=Namespace(call_target=baz_))
    def foo(a, b, c, bar, baz):
        x = bar()
        y = baz()
        # do something with the inputs a, b, c...
        return a + b + c + x + y

    assert foo('1',
               '2',
               '3',
               bar__quux__title='7',
               baz__a='A',
               baz__b='B',
               baz__c='C') == '1235X7ABC'
Ejemplo n.º 9
0
def test_namespace_shortcut_overwrite_backward():
    assert Namespace(
        Namespace(x=Namespace(y__z=1, y__zz=2)),
        Namespace(x=Shortcut(a__b=3)),
    ) == Namespace(
        x__a__b=3,
        x__y__z=1,
        x__y__zz=2,
    )
Ejemplo n.º 10
0
def test_flatten_identity_on_namespace_should_not_trigger_loop_detection():
    foo = Namespace(show=True)
    assert flatten(Namespace(
        party1_labels=foo,
        party2_labels=foo,
    )) == dict(
        party1_labels__show=True,
        party2_labels__show=True,
    )
Ejemplo n.º 11
0
def test_flatten_broken():
    assert flatten(
        Namespace(
            party1_labels=Namespace(show=True),
            party2_labels=Namespace(show=True),
        )) == dict(
            party1_labels__show=True,
            party2_labels__show=True,
        )
Ejemplo n.º 12
0
def create_members_from_model(default_factory,
                              model,
                              member_params_by_member_name,
                              include: List[str] = None,
                              exclude: List[str] = None,
                              extra: Dict[str, Any] = None):
    if extra is None:
        extra = {}

    # TODO: assert that extra does not collide with the include/exclude/etc fields

    def should_include(name):
        if exclude is not None and name in exclude:
            return False
        if include is not None:
            return name in include
        return True

    members = []

    # Validate include/exclude parameters
    field_names = {x.name for x in get_fields(model)}
    if include:
        not_existing = {
            x
            for x in include if x.partition('__')[0] not in field_names
        }
        assert not not_existing, 'You can only include fields that exist on the model: %s specified but does not exist' % ', '.join(
            sorted(not_existing))
    if exclude:
        not_existing = {x for x in exclude if x not in field_names}
        assert not not_existing, 'You can only exclude fields that exist on the model: %s specified but does not exist' % ', '.join(
            sorted(not_existing))

    extra_includes = [x for x in include if '__' in x] if include else []

    for field in get_fields(model):
        if should_include(field.name):
            foo = member_params_by_member_name.pop(field.name, {})
            if isinstance(foo, dict):
                subkeys = Namespace(**foo)
                subkeys.setdefault('call_target', default_factory)
                foo = subkeys(name=field.name, model=model, model_field=field)
            if foo is None:
                continue
            if isinstance(foo, list):
                members.extend(foo)
            else:
                assert foo.name, "Fields must have a name attribute"
                assert foo.name == field.name, f"Field {foo.name} has a name that doesn't match the model field it belongs to: {field.name}"
                members.append(foo)
    assert_kwargs_empty(member_params_by_member_name)
    all_members = members + [
        default_factory(model=model, field_name=x) for x in extra_includes
    ]
    return Struct({x.name: x for x in all_members}, **extra)
Ejemplo n.º 13
0
def test_deprecation_of_string_promotion2():
    foo = Namespace(foo__bar=True)
    with pytest.deprecated_call() as d:
        foo = Namespace(foo, foo='foo')

    assert str(
        d.list[0].message
    ) == 'Deprecated promotion of written string value "foo" to dict(foo=True)'

    assert foo == Namespace(foo__foo=True, foo__bar=True)
Ejemplo n.º 14
0
def test_setdefaults_path_empty_marker_no_side_effect():
    assert setdefaults_path(
        Namespace(a__b=1, a__c=2),
        a=Namespace(d=3),
        a__e=4,
    ) == Namespace(
        a__b=1,
        a__c=2,
        a__d=3,
        a__e=4,
    )
Ejemplo n.º 15
0
def collect_members(container, *, name: str, items_dict: Dict = None, items: Dict[str, Any] = None, cls: Type, unknown_types_fall_through=False):
    forbidden_names = FORBIDDEN_NAMES & (set(keys(items_dict or {})) | set(keys(items or {})))
    if forbidden_names:
        raise ForbiddenNamesException(f'The names {", ".join(sorted(forbidden_names))} are reserved by iommi, please pick other names')

    assert name != 'items'
    unbound_items = Struct()
    _unapplied_config = {}

    if items_dict is not None:
        for key, x in items_of(items_dict):
            x._name = key
            unbound_items[key] = x

    if items is not None:
        for key, item in items_of(items):
            if isinstance(item, Traversable):
                # noinspection PyProtectedMember
                assert not item._is_bound
                item._name = key
                unbound_items[key] = item
            elif isinstance(item, dict):
                if key in unbound_items:
                    _unapplied_config[key] = item
                else:
                    item = setdefaults_path(
                        Namespace(),
                        item,
                        call_target__cls=cls,
                        _name=key,
                    )
                    unbound_items[key] = item()
            else:
                assert unknown_types_fall_through or item is None, f'I got {type(item)} when creating a {cls.__name__}.{key}, but I was expecting Traversable or dict'
                unbound_items[key] = item

    for k, v in items_of(Namespace(_unapplied_config)):
        unbound_items[k] = unbound_items[k].reinvoke(v)
        # noinspection PyProtectedMember
        assert unbound_items[k]._name is not None

    to_delete = {
        k
        for k, v in items_of(unbound_items)
        if v is None
    }

    for k in to_delete:
        del unbound_items[k]

    sort_after(unbound_items)

    set_declared_member(container, name, unbound_items)
    setattr(container, name, NotBoundYet(container, name))
Ejemplo n.º 16
0
def test_setdefatults_path_retain_empty():
    assert setdefaults_path(Namespace(a=Namespace()),
                            a__b=Namespace()) == Namespace(a__b=Namespace(), )

    assert setdefaults_path(
        Namespace(),
        attrs__class=Namespace(),
    ) == Namespace(attrs__class=Namespace(), )
Ejemplo n.º 17
0
def test_setdefatults_path_retain_empty():
    actual = setdefaults_path(Namespace(a=Namespace()), a__b=Namespace())
    expected = Namespace(a__b=Namespace())
    assert expected == actual

    actual = setdefaults_path(Namespace(), attrs__class=Namespace())
    expected = Namespace(attrs__class=Namespace())
    assert expected == actual
Ejemplo n.º 18
0
    def __init__(self, **kwargs):
        """
        Note that, in addition to the parameters with the defined behavior below, you can pass in any keyword argument you need yourself, including callables that conform to the protocol, and they will be added and evaluated as members.

        All these parameters can be callables, and if they are, will be evaluated with the keyword arguments form and field. The only exceptions are `is_valid` (which gets `form`, `field` and `parsed_data`), `render_value` (which takes `form`, `field` and `value`) and `parse` (which gets `form`, `field`, `string_value`). Example of using a lambda to specify a value:

        .. code:: python

            Field(attrs__id=lambda form, field: 'my_id_%s' % field._name)

        :param after: Set the order of columns, see the `howto <https://docs.iommi.rocks/en/latest/howto.html#how-do-i-change-the-order-of-the-fields>`_ for an example.
        :param is_valid: validation function. Should return a tuple of `(bool, reason_for_failure_if_bool_is_false)` or raise ValidationError. Default: `lambda form, field, parsed_data: (True, '')`
        :param parse: parse function. Default just returns the string input unchanged: `lambda form, field, string_value: string_value`
        :param initial: initial value of the field
        :param attr: the attribute path to apply or get the data from. For example using `foo__bar__baz` will result in `your_instance.foo.bar.baz` will be set by the `apply()` function. Defaults to same as name
        :param attrs: a dict containing any custom html attributes to be sent to the `input__template`.
        :param display_name: the text in the HTML label tag. Default: `capitalize(name).replace('_', ' ')`
        :param template: django template filename for the entire row. Normally you shouldn't need to override on this level. Prefer overriding `input__template`, `label__template` or `error__template` as needed.
        :param template_string: You can inline a template string here if it's more convenient than creating a file. Default: `None`
        :param input__template: django template filename for the template for just the input control.
        :param label__template: django template filename for the template for just the label tab.
        :param errors__template: django template filename for the template for just the errors output. Default: `'iommi/form/errors.html'`
        :param required: if the field is a required field. Default: `True`
        :param help_text: The help text will be grabbed from the django model if specified and available.

        :param editable: Default: `True`
        :param strip_input: runs the input data through standard python .strip() before passing it to the parse function (can NOT be callable). Default: `True`
        :param render_value: render the parsed and validated value into a string. Default just converts to unicode: `lambda form, field, value: unicode(value)`
        :param is_list: interpret request data as a list (can NOT be a callable). Default: `False``
        :param read_from_instance: callback to retrieve value from edited instance. Invoked with parameters field and instance.
        :param write_to_instance: callback to write value to instance. Invoked with parameters field, instance and value.
        """

        model_field = kwargs.get('model_field')
        if model_field and model_field.remote_field:
            kwargs['model'] = model_field.remote_field.model

        super(Field, self).__init__(**kwargs)

        # value/value_data_list is the final step that contains parsed and valid data
        self.value = None

        self._choice_tuples = None

        self.non_editable_input = Namespace({
            **flatten(self.input),
            **self.non_editable_input,
            '_name': 'non_editable_input',
        })()
        self.input = self.input(_name='input')
        self.label = self.label(_name='label')
Ejemplo n.º 19
0
 class MyPage(Page):
     header = Fragment()
     some_form = Form(fields=Namespace(fisk=Field(), ))
     some_other_form = Form(fields=Namespace(
         fjomp=Field(),
         fisk=Field(),
     ))
     a_table = Table(
         model=TFoo,
         columns=Namespace(
             columns=Column(),
             fusk=Column(attr='b', filter__include=True),
         ),
     )
Ejemplo n.º 20
0
    def crud(cls, request, operation, form, app_name, model_name, pk=None, call_target=None, **kwargs):
        model = django_apps.all_models[app_name][model_name]
        instance = model.objects.get(pk=pk) if pk is not None else None

        if not cls.has_permission(request, operation=operation, model=model, instance=instance):
            raise Http404()

        def on_save(form, instance, **_):
            message = f'{form.model._meta.verbose_name.capitalize()} {instance} was ' + ('created' if form.extra.is_create else 'updated')
            messages.add_message(request, messages.INFO, message, fail_silently=True)

        def on_delete(form, instance, **_):
            message = f'{form.model._meta.verbose_name.capitalize()} {instance} was deleted'
            messages.add_message(request, messages.INFO, message, fail_silently=True)

        form = setdefaults_path(
            Namespace(),
            form,
            call_target__cls=cls.get_meta().form_class,
            auto__instance=instance,
            auto__model=model,
            call_target__attribute=operation,
            extra__on_save=on_save,
            extra__on_delete=on_delete,
        )

        return call_target(
            **{f'parts__{operation}_{app_name}_{model_name}': form},
            **kwargs,
        )
Ejemplo n.º 21
0
    def __html__(self, *, render=None):
        a = setdefaults_path(
            Namespace(),
            self.a,
            children__text=self.display_name,
            attrs__href=self.url,
            _name='a',
        )
        if self._active:
            setdefaults_path(
                a,
                attrs__class={self.active_class: True},
            )

        if self.url is None and a.tag == 'a':
            a.tag = None

        fragment = Fragment(
            children__a=a,
            tag=self.tag,
            template=self.template,
            attrs=self.attrs,
            _name='fragment',
        )
        fragment = fragment.bind(parent=self)
        # need to do this here because otherwise the sub menu will get get double bind
        for name, item in items(self.sub_menu):
            assert name not in fragment.children
            fragment.children[name] = item

        return fragment.__html__()
Ejemplo n.º 22
0
def evaluate_strict_container(c, **kwargs):
    return Namespace(
        {
            k: evaluate_strict(v, **kwargs)
            for k, v in c.items()
        }
    )
Ejemplo n.º 23
0
def collect_members(*, items_dict: Dict = None, items: Dict[str, Any] = None, cls: Type, unapplied_config: Dict) -> Dict[str, Any]:
    unbound_items = {}

    if items_dict is not None:
        for name, x in items_dict.items():
            x.name = name
            unbound_items[name] = x

    if items is not None:
        for name, item in items.items():
            if not isinstance(item, dict):
                item.name = name
                unbound_items[name] = item
            else:
                if name in unbound_items:
                    unapplied_config[name] = item
                else:
                    item = setdefaults_path(
                        Namespace(),
                        item,
                        call_target__cls=cls,
                        name=name,
                    )
                    unbound_items[name] = item()

    return Struct({x.name: x for x in sort_after(list(unbound_items.values()))})
Ejemplo n.º 24
0
    def __init__(self,
                 *,
                 _parts_dict: Dict[str, PartType] = None,
                 parts: dict,
                 **kwargs):
        super(Page, self).__init__(**kwargs)

        self.parts = {
        }  # This is just so that the repr can survive if it gets triggered before parts is set properly

        # First we have to up sample parts that aren't PagePart into Fragment
        def as_fragment_if_needed(k, v):
            if not isinstance(v, PagePart):
                return Fragment(v, name=k)
            else:
                return v

        _parts_dict = {
            k: as_fragment_if_needed(k, v)
            for k, v in _parts_dict.items()
        }
        parts = Namespace(
            {k: as_fragment_if_needed(k, v)
             for k, v in parts.items()})

        self._columns_unapplied_data = {}
        self.declared_parts: Dict[str, PartType] = collect_members(
            items=parts,
            items_dict=_parts_dict,
            cls=self.get_meta().member_class,
            unapplied_config=self._columns_unapplied_data)
Ejemplo n.º 25
0
def test_evaluate_attrs_2():
    actual = evaluate_attrs(
        Struct(attrs=Namespace(
            class__table=True,
            class__foo=lambda foo: True,
            data=1,
            data2=lambda foo: foo,
            style__foo=1,
            style__bar=lambda foo: f'foo{3}',
        ), ),
        foo=3,
    )

    expected = {
        'class': {
            'table': True,
            'foo': True,
        },
        'style': {
            'foo': 1,
            'bar': 'foo3'
        },
        'data': 1,
        'data2': 3,
    }

    assert actual == expected
Ejemplo n.º 26
0
def test_render_attrs_empty_style():
    assert render_attrs_test(
        Namespace(
            style__foo=None,
            style__bar=None,
        )
    ) == ''
Ejemplo n.º 27
0
def test_render_attrs_empty_class():
    assert render_attrs_test(
        Namespace(
            class__foo=False,
            class__bar=False,
        )
    ) == ''
Ejemplo n.º 28
0
    def create_declared_member(model_field_name):
        definition_or_member = member_params_by_member_name.pop(model_field_name, {})
        name = model_field_name.replace('__', '_')
        if isinstance(definition_or_member, dict):
            definition = setdefaults_path(
                Namespace(),
                definition_or_member,
                _name=name,
                # TODO: this should work, but there's a bug in tri.declarative, working around for now
                # call_target__attribute='from_model' if definition_or_member.get('attr', model_field_name) is not None else None,
                call_target__cls=member_class,
            )
            if definition_or_member.get('attr', model_field_name) is not None:
                setdefaults_path(
                    definition,
                    call_target__attribute='from_model',
                )
            if include is not None and name in include:
                setdefaults_path(
                    definition,
                    include=True,
                )

            member = definition(
                model=model,
                model_field_name=definition_or_member.get('attr', model_field_name),
            )
        else:
            member = definition_or_member
        if member is None:
            return
        members[name] = member
Ejemplo n.º 29
0
    def __init__(self,
                 *,
                 _parts_dict: Dict[str, PartType] = None,
                 parts: dict,
                 **kwargs):
        super(Page, self).__init__(**kwargs)

        self.parts = {
        }  # This is just so that the repr can survive if it gets triggered before parts is set properly

        # First we have to up sample parts that aren't Part into Fragment
        def as_fragment_if_needed(k, v):
            if v is None:
                return None
            if not isinstance(v, (dict, Traversable)):
                return Fragment(children__text=v, _name=k)
            else:
                return v

        _parts_dict = {
            k: as_fragment_if_needed(k, v)
            for k, v in items(_parts_dict)
        }
        parts = Namespace(
            {k: as_fragment_if_needed(k, v)
             for k, v in items(parts)})

        collect_members(self,
                        name='parts',
                        items=parts,
                        items_dict=_parts_dict,
                        cls=self.get_meta().member_class)
Ejemplo n.º 30
0
def test_all_filter_shortcuts():
    class MyFancyFilter(Filter):
        class Meta:
            extra__fancy = True

    class MyFancyQuery(Query):
        class Meta:
            member_class = MyFancyFilter

    all_shortcut_names = keys(
        get_members(
            cls=MyFancyFilter,
            member_class=Shortcut,
            is_member=is_shortcut,
        )
    )

    config = {f'filters__filter_of_type_{t}__call_target__attribute': t for t in all_shortcut_names}

    type_specifics = Namespace(
        filters__filter_of_type_choice__choices=[],
        filters__filter_of_type_multi_choice__choices=[],
        filters__filter_of_type_choice_queryset__choices=TFoo.objects.none(),
        filters__filter_of_type_multi_choice_queryset__choices=TFoo.objects.none(),
        filters__filter_of_type_many_to_many__model_field=TBaz.foo.field,
        filters__filter_of_type_foreign_key__model_field=TBar.foo.field,
    )

    query = MyFancyQuery(**config, **type_specifics).bind(request=req('get'))

    for name, filter in items(query.filters):
        assert filter.extra.get('fancy'), name