class FakeThing(JsonRecord):
     att_a = JsonProperty()
     att_b = JsonProperty()
     att_c = JsonListProperty(of=FakeItem)
 class FakeItem(JsonRecord):
     service_id = JsonProperty(json_name="id")
     primary_key = [service_id]
     name = JsonProperty()
Exemple #3
0
class JsonRecord(Record):
    """Version of a Record which deals primarily in JSON form.

    This means:

    1. The first argument to the constructor is assumed to be a JSON data
       dictionary, and passed through the class' ``json_to_initkwargs``
       method before being used to set actual properties

    2. Unknown keys are permitted, and saved in the "unknown_json_keys"
       property, which is merged back on output (ie, calling ``.json_data()``
       or ``to_json()``)
    """
    unknown_json_keys = JsonProperty(json_name=None, extraneous=True)

    def __init__(self, json_data=None, **kwargs):
        """Build a new JsonRecord sub-class.

        args:
            ``json_data=``\ *DICT|other*
                JSON data (string or already ``json.loads``'d).  If not
                a JSON dictionary with keys corresponding to the
                ``json_name`` or the properties within, then
                ``json_to_initkwargs`` should be overridden to handle
                the unpacking differently

            ``**kwargs``
                ``JsonRecord`` instances may also be constructed by
                passing in attribute initializers in keyword form.  The
                keys here should be the names of the attributes and the
                python values, not the JSON names or form.
        """
        if isinstance(json_data, OhPickle):
            return
        if isinstance(json_data, basestring):
            json_data = json.loads(json_data)
        if json_data is not None:
            kwargs = type(self).json_to_initkwargs(json_data, kwargs)
        super(JsonRecord, self).__init__(**kwargs)

    @classmethod
    def json_to_initkwargs(self, json_data, kwargs):
        """Subclassing hook to specialize how JSON data is converted
        to keyword arguments"""
        if isinstance(json_data, basestring):
            json_data = json.loads(json_data)
        return json_to_initkwargs(self, json_data, kwargs)

    @classmethod
    def from_json(self, json_data):
        """This method can be overridden to specialize how the class is loaded
        when marshalling in; however beware that it is not invoked when the
        caller uses the ``from_json()`` function directly."""
        return self(json_data)

    def json_data(self, extraneous=False):
        """Returns the JSON data form of this ``JsonRecord``.  The 'unknown'
        JSON keys will be merged back in, if:

        1. the ``extraneous=True`` argument is passed.

        2. the ``unknown_json_keys`` property on this class is replaced by one
           not marked as ``extraneous``
        """
        jd = to_json(self, extraneous)
        if hasattr(self, "unknown_json_keys"):
            prop = type(self).properties['unknown_json_keys']
            if extraneous or not prop.extraneous:
                for k, v in self.unknown_json_keys.iteritems():
                    if k not in jd:
                        jd[k] = v
        return jd

    def diff_iter(self, other, **kwargs):
        """Generator method which returns the differences from the invocant to
        the argument.  This specializes :py:meth:`Record.diff_iter` by
        returning :py:class:`JsonDiffInfo` objects.
        """
        for diff in super(JsonRecord, self).diff_iter(other, **kwargs):
            # TODO: object copy/upgrade constructor
            newargs = diff.__getstate__()
            yield JsonDiffInfo(**(newargs))

    def diff(self, other, **kwargs):
        """Compare an object with another.  This specializes
        :py:meth:`Record.diff` by returning a :py:class:`JsonDiff` object.
        """
        return JsonDiff(
            base_type_name=type(self).__name__,
            other_type_name=type(other).__name__,
            values=self.diff_iter(other, **kwargs),
        )
Exemple #4
0
class AutoJsonRecord(JsonRecord):
    unknown_json_keys = JsonProperty(
        json_name=None, extraneous=False, default=lambda: {},
    )

    @classmethod
    def auto_upgrade_dict(cls, thing):
        return AutoJsonRecord(thing)

    @classmethod
    def auto_upgrade_list(cls, thing):
        if len(thing) and isinstance(thing[0], dict):
            return list_of(AutoJsonRecord)(thing)
        else:
            return thing

    @classmethod
    def auto_upgrade_any(cls, thing):
        if isinstance(thing, dict):
            return cls.auto_upgrade_dict(thing)
        elif isinstance(thing, list):
            return cls.auto_upgrade_list(thing)
        else:
            return thing

    @classmethod
    def convert_json_key_in(cls, key):
        return re.sub(
            r'([a-z\d])([A-Z])',
            lambda m: "%s_%s" % (m.group(1), m.group(2).lower()),
            key,
        )

    @classmethod
    def convert_json_key_out(cls, key):
        return re.sub(
            r'([a-z\d])_([a-z])',
            lambda m: "%s%s" % (m.group(1), m.group(2).upper()),
            key,
        )

    @classmethod
    def json_to_initkwargs(cls, json_data, kwargs):
        kwargs = super(AutoJsonRecord, cls).json_to_initkwargs(
            json_data, kwargs,
        )
        # upgrade any dictionaries to AutoJsonRecord, and
        # any lists of dictionaries to list_of(AutoJsonRecord)
        if 'unknown_json_keys' in kwargs:
            kwargs['unknown_json_keys'] = {
                cls.convert_json_key_in(k): cls.auto_upgrade_any(v) for
                k, v in list(kwargs['unknown_json_keys'].items())
            }
        return kwargs

    def json_data(self, extraneous=False):
        jd = to_json(self, extraneous)
        if hasattr(self, "unknown_json_keys"):
            prop = type(self).properties['unknown_json_keys']
            if extraneous or not prop.extraneous:
                for k, v in self.unknown_json_keys.items():
                    k = type(self).convert_json_key_out(k)
                    if k not in jd:
                        jd[k] = to_json(v, extraneous)
        return jd

    def __getattr__(self, attr):
        if attr in type(self).properties:
            return type(self).properties[attr].__get__(self)
        else:
            return self.unknown_json_keys[attr]

    def __setattr__(self, attr, value):
        if attr in type(self).properties:
            return type(self).properties[attr].__set__(self)
        else:
            self.unknown_json_keys[attr]

    def __delattr__(self, attr, value):
        if attr in type(self).properties:
            return type(self).properties[attr].__del__(self)
        else:
            del self.unknown_json_keys[attr]
Exemple #5
0
 class EnumsGalore(JsonRecord):
     my_enum = JsonProperty(isa=MyEnum.EnumValue)
     enum_list = JsonListProperty(of=MyEnum.EnumValue)
     enum_map = JsonDictProperty(of=MyEnum.EnumValue)
Exemple #6
0
 class Banana(JsonRecord):
     color = Property(isa=str)
     length = Property(isa=int)
     contents = JsonProperty(isa=Fruit)
     vitamins = JsonProperty(isa=str)