Source code for normalize.record

#
# This file is a part of the normalize python library
#
# normalize is free software: you can redistribute it and/or modify
# it under the terms of the MIT License.
#
# normalize is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# MIT License for more details.
#
# You should have received a copy of the MIT license along with
# normalize.  If not, refer to the upstream repository at
# http://github.com/hearsaycorp/normalize
#

from __future__ import absolute_import

import normalize.exc as exc
from normalize.identity import record_id
from normalize.record.meta import RecordMeta


class _Unset(object):
    pass


[docs]class Record(object): """Base class for normalize instances and collections. """ __metaclass__ = RecordMeta
[docs] def __init__(self, init_dict=None, **kwargs): """Instantiates a new ``Record`` type. You may specify a ``dict`` to initialize from, *or* use keyword argument form. The default interpretation of the first positional argument is to treat it as if its contents were passed in as keyword arguments. Subclass API: subclasses are permitted to interpret positional arguments in non-standard ways, but in general it is expected that if keyword arguments are passed, then they are already of the right type (or, of a type that the ``coerce`` functions associated with the properties can accept). The only exception to this is if ``init_dict`` is an ``OhPickle`` instance, you should probably just return (see :py:class:`OhPickle`) """ if isinstance(init_dict, OhPickle): return if init_dict and kwargs: raise exc.AmbiguousConstruction() if not init_dict: init_dict = kwargs for prop, val in init_dict.iteritems(): meta_prop = type(self).properties.get(prop, None) if meta_prop is None: raise exc.PropertyNotKnown( propname=prop, typename=type(self).__name__, ) meta_prop.init_prop(self, val) missing = type(self).eager_properties - set(init_dict.keys()) for propname in missing: meta_prop = type(self).properties[propname] meta_prop.init_prop(self)
[docs] def __getnewargs__(self): """Stub method which arranges for an ``OhPickle`` instance to be passed to the constructor above when pickling out. """ return (OhPickle(),)
[docs] def __getstate__(self): """Implement saving, for the pickle out API. Returns the instance dict""" return self.__dict__
[docs] def __setstate__(self, instance_dict): """Implement loading, for the pickle in API. Sets the instance dict directly.""" self.__dict__.update(instance_dict)
[docs] def __str__(self): """Marshalling to string form. This is what you see if you cast the object to a string or use the ``%s`` format code, and is supposed to be an "informal" representation when you don't want a full object dump like ``repr()`` would provide. If you defined a ``primary_key`` for the object, then the values of the attributes you specified will be included in the string representation, eg ``<Task 17>``. Otherwise, the *implicit* primary key (eg, a tuple of all of the defined attributes with their values) is included, up to the first 30 characters or so. """ pk = self.__pk__ key = repr(pk[0] if len(pk) == 1 else pk) return "<%s %s>" % ( type(self).__name__, key[:30] + "..." if len(key) > 32 else key )
[docs] def __repr__(self): """Marshalling to Python source. This is what you will see when printing objects using the ``%r`` format code. This function is recursive and should generally satisfy the requirement that it is valid Python source, assuming all class names are in scope and all values implement ``__repr__`` as suggested in the python documentation. """ typename = type(self).__name__ values = list() for propname in sorted(type(self).properties): if propname not in self.__dict__: continue else: values.append("%s=%r" % (propname, self.__dict__[propname])) return "%s(%s)" % (typename, ", ".join(values))
[docs] def __eq__(self, other): """Compare two Record classes; recursively compares all attributes for equality (except those marked 'extraneous'). See also :py:meth:`diff` for a version where the comparison can be fine-tuned.""" if type(self) != type(other): return False for propname, prop in type(self).properties.iteritems(): if not prop.extraneous: if getattr(self, propname, _Unset) != getattr( other, propname, _Unset ): return False return True
[docs] def __ne__(self, other): """implemented for compatibility""" return not self.__eq__(other)
@property
[docs] def __pk__(self): """This property returns the "primary key" for this object. This is similar to what is used when comparing Collections via :py:mod:`normalize.diff`, and is used for stringification and for the ``id()`` built-in. """ return record_id(self, type(self))
[docs] def __hash__(self): """Implements ``id()`` for Record types. """ return self.__pk__.__hash__()
def diff_iter(self, other, **kwargs): """Generator method which returns the differences from the invocant to the argument. args: ``other=``\ *Record*\ \|\ *Anything* The thing to compare against; the types must match, unless ``duck_type=True`` is passed. *diff_option*\ =\ *value* Unknown keyword arguments are eventually passed to a :ref:`DiffOptions` constructor. """ from normalize.diff import diff_iter return diff_iter(self, other, **kwargs) def diff(self, other, **kwargs): """Compare an object with another and return a :py:class:`DiffInfo` object. Accepts the same arguments as :py:meth:`normalize.record.Record.diff_iter` """ from normalize.diff import diff return diff(self, other, **kwargs)
class OhPickle(object): """Sentinel type for Un-Pickling. ``pickle`` does not allow a ``__getinitargs__``/``__getnewargs__`` to return keyword constructor arguments, so this value is passed to ``__init__`` when unpickling. It indicates to not perform any immediate post-construction checks and instead just return and let ``__getstate``__ set this object up. """ def __str__(self): return "<OhPickle>"