Exemplo n.º 1
0
def test_frozen():
    """
    FrozenDict is not modifyable
    """
    f = FrozenDict(a=1)
    with pytest.raises(ValueError):
        f['b'] = 5
Exemplo n.º 2
0
Arquivo: core.py Projeto: betatim/vdom
    def __init__(
        self,
        tag_name,
        attributes=None,
        style=None,
        children=None,
        key=None,
        event_handlers=None,
        schema=None,
    ):
        if isinstance(tag_name, dict) or isinstance(tag_name, list):
            # Backwards compatible interface
            warnings.warn('Passing dict to VDOM constructor is deprecated')
            value = tag_name
            vdom_obj = VDOM.from_dict(value)
            tag_name = vdom_obj.tag_name
            style = vdom_obj.style
            event_handlers = vdom_obj.event_handlers
            attributes = vdom_obj.attributes
            children = vdom_obj.children
            key = vdom_obj.key
        self.tag_name = tag_name
        # Sort attributes so our outputs are predictable
        self.attributes = FrozenDict(sorted(
            attributes.items())) if attributes else FrozenDict()
        self.children = tuple(children) if children else tuple()
        self.key = key
        # Sort attributes so our outputs are predictable
        self.style = FrozenDict(sorted(
            style.items())) if style else FrozenDict()
        self.event_handlers = (FrozenDict(sorted(event_handlers.items()))
                               if event_handlers else FrozenDict())

        # Validate that all children are VDOMs or strings
        if not all(
                isinstance(c, (VDOM, string_types[:])) for c in self.children):
            raise ValueError(
                'Children must be a list of VDOM objects or strings')

        # All style keys & values must be strings
        if not all(
                isinstance(k, string_types) and isinstance(v, string_types)
                for k, v in self.style.items()):
            raise ValueError('Style must be a dict with string keys & values')

        # mark completion of object creation. Object is immutable from now.
        self._frozen = True

        if schema is not None:
            self.validate(schema)
Exemplo n.º 3
0
    def __init__(self, tag_name, attributes=None, children=None, key=None, schema=None):
        if isinstance(tag_name, dict) or isinstance(tag_name, list):
            # Backwards compatible interface
            warnings.warn('Passing dict to VDOM constructor is deprecated')
            value = tag_name
            vdom_obj = VDOM.from_dict(value)
            tag_name = vdom_obj.tag_name
            attributes = vdom_obj.attributes
            children = vdom_obj.children
            key = vdom_obj.key
        self.tag_name = tag_name
        self.attributes = FrozenDict(attributes) if attributes else FrozenDict()
        self.children = tuple(children) if children else tuple()
        self.key = key

        # Validate that all children are VDOMs or strings
        if not all([isinstance(c, (VDOM, string_types[:])) for c in self.children]):
            raise ValueError('Children must be a list of VDOM objects or strings')

        # mark completion of object creation. Object is immutable from now.
        self._frozen = True

        if schema is not None:
            self.validate(schema)
Exemplo n.º 4
0
Arquivo: core.py Projeto: betatim/vdom
class VDOM(object):
    """A basic virtual DOM class which allows you to write literal VDOM spec

    >>> VDOM(tag_name='h1', children='Hey', attributes: {}})

    >>> from vdom.helpers import h1
    >>> h1('Hey')
    """

    # This class should only have these 5 attributes
    __slots__ = [
        'tag_name', 'attributes', 'style', 'children', 'key', 'event_handlers',
        '_frozen'
    ]

    def __init__(
        self,
        tag_name,
        attributes=None,
        style=None,
        children=None,
        key=None,
        event_handlers=None,
        schema=None,
    ):
        if isinstance(tag_name, dict) or isinstance(tag_name, list):
            # Backwards compatible interface
            warnings.warn('Passing dict to VDOM constructor is deprecated')
            value = tag_name
            vdom_obj = VDOM.from_dict(value)
            tag_name = vdom_obj.tag_name
            style = vdom_obj.style
            event_handlers = vdom_obj.event_handlers
            attributes = vdom_obj.attributes
            children = vdom_obj.children
            key = vdom_obj.key
        self.tag_name = tag_name
        # Sort attributes so our outputs are predictable
        self.attributes = FrozenDict(sorted(
            attributes.items())) if attributes else FrozenDict()
        self.children = tuple(children) if children else tuple()
        self.key = key
        # Sort attributes so our outputs are predictable
        self.style = FrozenDict(sorted(
            style.items())) if style else FrozenDict()
        self.event_handlers = (FrozenDict(sorted(event_handlers.items()))
                               if event_handlers else FrozenDict())

        # Validate that all children are VDOMs or strings
        if not all(
                isinstance(c, (VDOM, string_types[:])) for c in self.children):
            raise ValueError(
                'Children must be a list of VDOM objects or strings')

        # All style keys & values must be strings
        if not all(
                isinstance(k, string_types) and isinstance(v, string_types)
                for k, v in self.style.items()):
            raise ValueError('Style must be a dict with string keys & values')

        # mark completion of object creation. Object is immutable from now.
        self._frozen = True

        if schema is not None:
            self.validate(schema)

    def __setattr__(self, attr, value):
        """
        Make instances immutable after creation
        """
        if hasattr(self, '_frozen') and self._frozen:
            raise AttributeError("Cannot change attribute of immutable object")
        super(VDOM, self).__setattr__(attr, value)

    def validate(self, schema):
        """
        Validate VDOM against given JSON Schema

        Raises ValidationError if schema does not match
        """
        try:
            validate(instance=self.to_dict(),
                     schema=schema,
                     cls=Draft4Validator)
        except ValidationError as e:
            raise ValidationError(_validate_err_template.format(
                VDOM_SCHEMA, e))

    def to_dict(self):
        """Converts VDOM object to a dictionary that passes our schema
        """
        attributes = dict(self.attributes.items())
        if self.style:
            attributes.update({"style": dict(self.style.items())})
        vdom_dict = {'tagName': self.tag_name, 'attributes': attributes}
        if self.event_handlers:
            event_handlers = dict(self.event_handlers.items())
            for key, value in event_handlers.items():
                value = create_event_handler(key, value)
                event_handlers[key] = value
            vdom_dict['eventHandlers'] = event_handlers
        if self.key:
            vdom_dict['key'] = self.key
        vdom_dict['children'] = [
            c.to_dict() if isinstance(c, VDOM) else c for c in self.children
        ]
        return vdom_dict

    def to_json(self):
        return json.dumps(self.to_dict())

    def to_html(self):
        return self._repr_html_()

    def json_contents(self):
        warnings.warn(
            'VDOM.json_contents method is deprecated, use to_json instead')
        return self.to_json()

    def _to_inline_css(self, style):
        """
        Return inline CSS from CSS key / values
        """
        return "; ".join([
            '{}: {}'.format(convert_style_key(k), v) for k, v in style.items()
        ])

    def _repr_html_(self):
        """
        Return HTML representation of VDOM object.

        HTML escaping is performed wherever necessary.
        """
        # Use StringIO to avoid a large number of memory allocations with string concat
        with io.StringIO() as out:
            out.write('<{tag}'.format(tag=escape(self.tag_name)))
            if self.style:
                # Important values are in double quotes - cgi.escape only escapes double quotes, not single quotes!
                out.write(' style="{css}"'.format(
                    css=escape(self._to_inline_css(self.style))))

            for k, v in self.attributes.items():
                # Important values are in double quotes - cgi.escape only escapes double quotes, not single quotes!
                if isinstance(v, string_types):
                    out.write(' {key}="{value}"'.format(key=escape(k),
                                                        value=escape(v)))
                if isinstance(v, bool) and v:
                    out.write(' {key}'.format(key=escape(k)))
            out.write('>')

            for c in self.children:
                if isinstance(c, string_types):
                    out.write(escape(safe_unicode(c)))
                else:
                    out.write(c._repr_html_())

            out.write('</{tag}>'.format(tag=escape(self.tag_name)))

            return out.getvalue()

    def _repr_mimebundle_(self, include=None, exclude=None, **kwargs):
        return {
            'application/vdom.v1+json': self.to_dict(),
            'text/plain': self.to_html()
        }

    @classmethod
    def from_dict(cls, value):
        try:
            validate(instance=value, schema=VDOM_SCHEMA, cls=Draft4Validator)
        except ValidationError as e:
            raise ValidationError(_validate_err_template.format(
                VDOM_SCHEMA, e))
        attributes = value.get('attributes', {})
        style = None
        event_handlers = None
        if 'style' in attributes:
            # Make a copy of attributes, since we're gonna remove styles from it
            attributes = attributes.copy()
            style = attributes.pop('style')
        for key, val in attributes.items():
            if callable(val):
                attributes = attributes.copy()
                if event_handlers == None:
                    event_handlers = {key: attributes.pop(key)}
                else:
                    event_handlers[key] = attributes.pop(key)
        return cls(
            tag_name=value['tagName'],
            attributes=attributes,
            style=style,
            event_handlers=event_handlers,
            children=[
                VDOM.from_dict(c) if isinstance(c, dict) else c
                for c in value.get('children', [])
            ],
            key=value.get('key'),
        )
Exemplo n.º 5
0
def test_ordering():
    """
    FrozenDict should preserve ordering
    """
    f = FrozenDict([('a', 1), ('b', 2)])
    assert list(f.items()) == [('a', 1), ('b', 2)]
Exemplo n.º 6
0
class VDOM(object):
    """A basic virtual DOM class which allows you to write literal VDOM spec

    >>> VDOM(tag_name='h1', children='Hey', attributes: {}})

    >>> from vdom.helpers import h1
    >>> h1('Hey')
    """
    # This class should only have these 4 attributes
    __slots__ = ['tag_name', 'attributes', 'children', 'key', '_frozen']

    def __init__(self, tag_name, attributes=None, children=None, key=None, schema=None):
        if isinstance(tag_name, dict) or isinstance(tag_name, list):
            # Backwards compatible interface
            warnings.warn('Passing dict to VDOM constructor is deprecated')
            value = tag_name
            vdom_obj = VDOM.from_dict(value)
            tag_name = vdom_obj.tag_name
            attributes = vdom_obj.attributes
            children = vdom_obj.children
            key = vdom_obj.key
        self.tag_name = tag_name
        self.attributes = FrozenDict(attributes) if attributes else FrozenDict()
        self.children = tuple(children) if children else tuple()
        self.key = key

        # Validate that all children are VDOMs or strings
        if not all([isinstance(c, (VDOM, string_types[:])) for c in self.children]):
            raise ValueError('Children must be a list of VDOM objects or strings')

        # mark completion of object creation. Object is immutable from now.
        self._frozen = True

        if schema is not None:
            self.validate(schema)

    def __setattr__(self, attr, value):
        """
        Make instances immutable after creation
        """
        if hasattr(self, '_frozen') and self._frozen:
            raise AttributeError("Cannot change attribute of immutable object")
        super(VDOM, self).__setattr__(attr, value)

    def validate(self, schema):
        """
        Validate VDOM against given JSON Schema

        Raises ValidationError if schema does not match
        """
        try:
            validate(instance=self.to_dict(), schema=schema, cls=Draft4Validator)
        except ValidationError as e:
            raise ValidationError(_validate_err_template.format(VDOM_SCHEMA, e))

    def to_dict(self):
        vdom_dict = {
            'tagName': self.tag_name,
            'attributes': self.attributes
        }
        if self.key:
            vdom_dict['key'] = self.key
        vdom_dict['children'] = [c.to_dict() if isinstance(c, VDOM) else c for c in self.children]
        return vdom_dict

    def to_json(self):
        return json.dumps(self.to_dict())

    def to_html(self):
        return self._repr_html_()

    def json_contents(self):
        warnings.warn('VDOM.json_contents method is deprecated, use to_json instead')
        return self.to_json()

    def _repr_html_(self):
        """
        Return HTML representation of VDOM object.

        HTML escaping is performed wherever necessary.
        """
        # Use StringIO to avoid a large number of memory allocations with string concat
        with io.StringIO() as out:
            out.write('<{tag}'.format(tag=escape(self.tag_name)))

            for k, v in self.attributes.items():
                # Important values are in double quotes - cgi.escape only escapes double quotes, not single quotes!
                out.write(' {key}="{value}"'.format(key=escape(k), value=escape(v)))
            out.write('>')

            for c in self.children:
                if isinstance(c, string_types):
                    out.write(escape(safe_unicode(c)))
                else:
                    out.write(c._repr_html_())

            out.write('</{tag}>'.format(tag=escape(self.tag_name)))

            return out.getvalue()

    def _repr_mimebundle_(self, include, exclude, **kwargs):
        return {
            'application/vdom.v1+json': self.to_dict(),
            'text/plain': self.to_html()
        }

    @classmethod
    def from_dict(cls, value):
        try:
            validate(instance=value, schema=VDOM_SCHEMA, cls=Draft4Validator)
        except ValidationError as e:
            raise ValidationError(_validate_err_template.format(VDOM_SCHEMA, e))
        return cls(
            tag_name=value['tagName'],
            attributes=value.get('attributes', {}),
            children=[VDOM.from_dict(c) if isinstance(c, dict) else c for c in value.get('children', [])],
            key=value.get('key')
        )