amethyst.core.obj

Object(*args, **kwargs) Amethyst Base Object
Attr([convert, verify, isa, default, …]) Base class for Amethyst Object Attributes
amethyst_deflate(obj[, deflator]) Deflate a structure of amethyst-encodable objects into a “dumb” structure of plain dicts, lists, numbers, and strings.
amethyst_inflate(obj[, inflator, start]) Inflate an annotated “dumb” structure to a structure of objects, the opposite of amethyst_deflate().
register_amethyst_type(cls, encode, decode) Adds a type to the global list (global_amethyst_encoders) for object encoding and decoding.

SYNOPSIS

from amethyst.core import Object, Attr

class MyObject(Object):
    # Attr() defines properties and will include in serialization.
    foo = Attr(int)
    bar = Attr(str).strip()

    # foo and bar will be automatically extracted from kwargs.
    # .other will not be serialized by .toJSON()
    def __init__(self, other=None, **kwargs):
        super().__init__(**kwargs)
        self.other = other


# ...
myobj = MyObject(foo=23, other="Hello")
myobj.toJSON()   # { "__my.module.MyObject__": { "foo": 23 } }

myobj = MyObject()
myobj.fromJSON('{ "foo": 23, "bar": " plugh  " }')
print(myobj.bar)      # "plugh"  (no spaces)

DESCRIPTION

Object implements the dictionary interface and stores everything in self.dict.

Subclasses can define Attr which will have properties defined as shortcuts to read and write keys in the dictionary.

By storing all attributes in a dict, we can be trivially serialized. toJSON() and fromJSON() methods exist to help with this, and should be used for all JSON serialization since they will correctly handle set() and other values (see the Object.JSONEncoder() and Object.JSONObjectHook() methods). Additionally, the JSON methods will perform automatic validation based on type information passed to the Attr objects and will ensure that it is loading data for the correct class and that no unexpected keys are present.

exception amethyst.core.obj.AmethystException
exception amethyst.core.obj.ImmutableObjectException
exception amethyst.core.obj.DuplicateAttributeException
class amethyst.core.obj.Attr(convert=None, verify=None, isa=None, default=None, builder=None, fget=None, fset=None, fdel=None, doc=None, OVERRIDE=False)

Base class for Amethyst Object Attributes

Attribute descriptions primarily consist of a function which takes in a value and either returns a (possibly modified) value or else raises a ValueError. Python’s standard object constructors generally work well, though beware that str will generally accept anything.

foo = Attr(int)                   # Coerce to int (strict parsing)
foo = Attr(float).int()           # Parse via float, but then integerize
foo = 0 < Attr(int)               # Positive integer
foo = (0 <= Attr(int)) <= 200     # Alas, parens are necessary!

# Stringify, then strip whitespace
foo = Attr(str).strip()

# Accept bytes or str, decoding if possible (only decodes
# bytes since decode not a method of str)
foo = Attr(isa=(bytes, str)).decode("UTF-8")

# Coerce to a list via .split()
foo = Attr(isa=(list, str)).split()

Anything based off of Amethyst’s Object class generally will work as well:

class MyClass(amethyst.core.Object):
    ...

class MyClass2(amethyst.core.Object):
    foo = Attr(MyClass)
Variables:name – Attribute name when assigned to an Object (auto-set by metaclass).
__init__(convert=None, verify=None, isa=None, default=None, builder=None, fget=None, fset=None, fdel=None, doc=None, OVERRIDE=False)
Parameters:
  • convert – Attribute converter. Must be a callable or else a text string of a class or function name. Classes and functions may be imported from other packages by prefixing prepending the package name and a dot. For instance, numpy.array (see amethyst.core.util.get_class() for string processing details). Callable should accept a single argument, the value, and should return a canonicalized value. Invalid values should raise a ValueError(). If converter is None, values will be accepted unmodified.
  • isa – Called after conversion but before verification, ensures that the value is one of the passed types. Is a shortcut for verify=lambda val: isinstance(val, isa)
  • verify – Attribute verifier. Called after conversion, this callable should return a truthy result if the value is acceptable.
  • default

    Default value applied at object creation time. If default is a callable, it will be called to produce the default (e.g., list).

    Note

    The default value (or result of callable) is assumed valid and will not pass through conversion or verification.

  • builder – Callable which will lazily build a default value when the attribute is first used.
  • fget
  • fset
  • fdel – If any of fget, fset, or fdel are defined, they will be used to construct the object property. If all three are none (the default), then the functions which get/set/del the appropriate key in the object dictionary will be defined.
  • doc – Documentation to be attached to the property.
  • OVERRIDE – When true, allow attribute to replace an existing attribute (from a parent class).
build_property(name)
get_default()
copy_meta(*others)

Copy metadata from another Attr object. This method is used when defining derived attributes (e.g., strip()) to copy documentation and the OVERRIDE flag. Returns the object itself for chaining.

__call__(value, key=None)
__and__(other)
__rand__(other)
__or__(other)
__ror__(other)
__eq__(other)

Tests via amethyst.core.util.smartmatch()

Warning

Hash lookups must be idempotent (looking up the result of a previous lookup had better return the same thing) since we offer no guarantees that validation may not happen more than once.

GOOD:: { "a": "A", "b": "B",  "A": "A", "B": "B" }

BAD:: { "a": "A", "b": "B" }  # will fail on repeated validation since "A" and "B" are not keys

__ne__(other)

Ensure no smartmatch

__lt__(other)
__le__(other)
__ge__(other)
__gt__(other)
__mod__(other)
__pos__()
__abs__()
float()
int()
complex()
strip(chars=None)

Return a new attribute which strips whitespace if applicable (duck typing).

rstrip(chars=None)

Return a new attribute which strips whitespace from the right side if applicable (duck typing).

lstrip(chars=None)

Return a new attribute which strips whitespace from the left side if applicable (duck typing).

encode(encoding='UTF-8', errors='strict')

Return a new attribute which encodes value if applicable (duck typing). Defaults to UTF-8 encoding.

decode(encoding='UTF-8', errors='strict')

Return a new attribute which decodes value if applicable (duck typing). Defaults to UTF-8 encoding.

lower()

Return a new attribute which lower-cases value if applicable (duck typing).

upper()

Return a new attribute which upper-cases value if applicable (duck typing).

title()

Return a new attribute which title-cases value if applicable (duck typing).

capitalize()

Return a new attribute which capitalizes value if applicable (duck typing).

casefold()

Return a new attribute which casefolds value if applicable (duck typing).

split(sep=None, maxsplit=-1)

Return a new attribute which splits its value if applicable (duck typing).

amethyst.core.obj.register_amethyst_type(cls, encode, decode, name=None, overwrite=False, wrap_encode=True)

Adds a type to the global list (global_amethyst_encoders) for object encoding and decoding. Subclasses of Object are automatically registered, so you should only need to register external objects that you use.

Parameters:
  • cls (class) – Class to register
  • encode (callable) – Callable which will transform the object into a “dumb” structure of primitive objects (dict, list, str, int, float).
  • decode (callable) – Callable which will transform the “dumb” structure of primitive objects back into the object.
  • name (str) – Globally unique string identifying the class. This name should be something which won’t appear as a dictionary key in normal data. Defaults to “__MODULENAME.CLASSNAME__”
  • overwrite (bool) – By default, this function will raise an error if a class or name is aready registered. Pass True to override any existing registrations.
  • wrap_encode (bool) –

    By default, the encoded object will be wrapped in a single-key dict: { name: ENCODED_OBJECT } so that the decoder can be called when inflating a structure containing the object. Pass True in this parameter in order to avoid wrapping the encoded structure.


    You might want to set this parameter if your object is naturally expressed as a basic object and you are certain that all uses after inflation will automatically coerce the value to your desired object when needed. For instance, a URL object where all functions and methods which accept the URL object also accept a plain string. You could pass an encoder which deflates to plain strings and set wrap_encode=False and then URLs would appear as plain strings in your exported structures, which may be easier to work with in external applications.


    This option also offers an escape hatch for hypothetical cases where you may need to wrap your encoded object in your encoder itself.

amethyst.core.obj.amethyst_deflate(obj, deflator=None)

Deflate a structure of amethyst-encodable objects into a “dumb” structure of plain dicts, lists, numbers, and strings. The deflated structure should be easily serializable by most any reasonable serialization library (yaml, lxml, …)

Makes use of global_amethyst_encoders by default. Pass an amethyst Object as second argument to make use of any Object-local encoders.

Note: If your target is JSON, the amethyst object’s Object.toJSON() method is probably better.

amethyst.core.obj.amethyst_inflate(obj, inflator=None, start=0)

Inflate an annotated “dumb” structure to a structure of objects, the opposite of amethyst_deflate().

This function should be used to assist in inflation of Object instances using alternative serialization tools. Any serializer that can produce plain dicts and lists should work with this function.

This function handles either single-key or flat style annotations. Un-annotated constructions will pass through unchanged.

This function makes use of global_amethyst_encoders by default. Pass an amethyst Object as second argument to make use of any Object-local encoders.

Note

If your source is JSON, the amethyst object’s Object.fromJSON() or class Object.newFromJSON() method is probably better.

class amethyst.core.obj.Object(*args, **kwargs)

Amethyst Base Object

Variables:
  • _attrs – Dictionary mapping attribute names to Attr objects. Should not be modified, but can be read for introspection of an Object.
  • _jsonencoders

    Dictionary mapping class objects to callable encoders which should produce a JSON-serializable object. These functions are called from the JSONEncoder method. Per the json documentation, these functions should return an object which is JSON serializable or else raise a TypeError. These encoders are specific to the class. Use register_amethyst_type() to register a class globally.

    Note

    _jsonencoders is a lower-level tool than register_amethyst_type() and offers direct access to the encoders (behaves like overwrite=True, wrap_encode=False)

  • _jsonhooks

    Dictionary mapping class identifiers (strings of form “__MODULENAME.CLASSNAME__”) to callable decoders which should inflate simple structures to corresponding objects. These functions are called from the JSONObjectHook method when inflating data. These decoders are specific to the class. Use register_amethyst_type() to register a class globally.

    Note

    _jsonhooks is a lower-level tool than register_amethyst_type() and offers direct access to the decoders (behaves like overwrite=True, wrap_encode=False)

amethyst_includeclass = True

When True (the default), serialization will include a key “__class__” containing the class name of the object which can be used during loading to verify that the object is of the correct type.

amethyst_verifyclass = True

When True (the default), loading data from JSON or a dict passed to amethyst_load_data() will check for the “__class__” key described above, and an exception will be thrown if it is not found.

amethyst_import_strategy = 'strict'

When “strict” (the default), then loading data from JSON or a dictionary via amethyst_load_data() requires all keys present in the data structure to correspond with keys in the attribute list. If any additional keys are present, an exception will be raised. When “loose”, additional keys will be ignored and not copied into the object dictionary. When “sloppy”, unknown attributes will be copied unmodified into the object dict.

__init__(*args, **kwargs)

Initializes self.dict with all passed kwargs. Object is mutable by default.

Warning

Passing a single argument that is an instance of the class itself is reserved for internal use only and behavior may change.

amethyst_assert_mutable(msg='May not modify, object is immutable')
amethyst_is_mutable()
amethyst_make_mutable()
amethyst_make_immutable()
__str__()

Return str(self).

__repr__()

Return repr(self).

__len__()
__contains__(key)
__iter__()
__eq__(other)

Object equality. Tests all :py:func:`Attr()`s and only :py:func:`Attr()`s. Other python properties or “garbage” in the underlying dict (which may arise from “sloppy” imports) will be ignored.

__ne__(other)

Return self!=value.

items(**kwargs)

Subclasses: this method may be overridden with an unrelated implementation.

iteritems(**kwargs)

Subclasses: this method may be overridden with an unrelated implementation.

keys(**kwargs)

Subclasses: this method may be overridden with an unrelated implementation.

values(**kwargs)

Subclasses: this method may be overridden with an unrelated implementation.

__getitem__(key)
__setitem__(key, value)
__delitem__(key)
get(key, dflt=None)

Subclasses: this method may be overridden with an unrelated implementation.

set(*args, **kwargs)

Verify then set canonicalized value. Positional args take precedence over kwargs.

obj.set(key, val)
obj.set(foo=val)

Subclasses: this method may be overridden with an unrelated implementation.

setdefault(key, value)

If missing a value, verify then set

Subclasses: this method may be overridden with an unrelated implementation.

direct_set(*args, **kwargs)

Set values BYPASSING VALIDATION but respecting mutability. Positional args take precedence over kwargs.

obj.direct_set(key, val)
obj.direct_set(foo=val)

Subclasses: this method may be overridden with an unrelated implementation.

pop(key, dflt=None)

Subclasses: this method may be overridden with an unrelated implementation.

update(*args, **kwargs)

Subclasses: this method may be overridden with an unrelated implementation.

direct_update(*args, **kwargs)

Update internal dictionary BYPASSING VALIDATION but respecting mutability.

Subclasses: this method may be overridden with an unrelated implementation.

amethyst_validate_update(d, import_strategy=None)

Convert and validate with the intention of updating only some of the object’s .dict values. Returns a new dictionary with canonicalized values, but does not initialize any missing keys with attribute default values (which distinguishes this from amethyst_validate_data()).

This method does not change the object. Pass the resulting dict to the .update() method (or .direct_update() if you decide to accept the changes.

amethyst_validate_data(d, import_strategy=None)

Convert and validate with the intention of replacing all of the object’s .dict values. Returns a new dictionary with canonicalized values, and defaults inserted (which distinguishes this from amethyst_validate_update()).

This method does not change the object. Typical usage would look like either:

myobj.dict = myobj.amethyst_validate_data(data)

or

validated = myobj.amethyst_validate_data(data)
mynewobj = MyClass(**validated)

Subclasses of Object can also use this method to inflate specific attibutes at load time. For instance, to inflate non-Object objects or ensure objects from hand-written config files. Be sure to override amethyst_validate_update() as well if programmatic updates may need special inflation rules.

attr_value_ok(name, value)

Validate a single value independently of any others. Just checks that the attribute validator does not raise an exception.

Subclasses: this method may be overridden with an unrelated implementation.

amethyst_load_data(data, import_strategy=None, verifyclass=None)

Loads a data dictionary with validation. Modifies the passed dict and replaces current self.dict object with the one passed.

Parameters:

This method transparently loads data in either “single-key” or “flat” formats:

{ "__my.module.MyClass__": { ... obj.dict ... } }

{ "__class__": "MyClass", ... obj.dict ... }

Keep in mind that the default base value for amethyst_verifyclass is True, so, by default, at least one of the class identification keys is expected to be present.

load_data(data, import_strategy=None, verifyclass=None)

Alias for amethyst_load_data

Subclasses: this method may be overridden with an unrelated implementation.

JSONEncoder(obj)

Fallback method for JSON encoding.

If the standard JSONEncoder is unable to encode an object, this method will be called. Per the json documentation, it should return an object which is JSON serializable or else raise a TypeError.

This base encoder, looks up an object’s class in a dict and calls the corresponding function to do the translation. The built-in translators map:

set       => { "__set__": [ ... ] }
frozenset => { "__frozenset__": [ ... ] }

Additional translators may be added by creating a class variable jsonencoders which is a dict mapping classes to a function. These translators will merged onto the base translators (silently replacing duplicates) by the metaclass at class (not object) creation.

JSONObjectHook(obj)

Object hook for JSON decoding.

This method is called for every decoded JSON object. If necessary, it should return a new or modified object that should be used instead.

This base encoder, translates single-key dicts into new objects if the single-key is a special value. The built-in translators are:

{ "__set__": [ ... ] }       => set
{ "__frozenset__": [ ... ] } => frozenset

Additional translators may be added by creating a class variable jsonhooks which is a dict mapping the special key to a function. These translators will merged onto the base translators (silently replacing duplicates) by the metaclass at class (not object) creation.

Keep in mind that JSON input comes from untrusted sources, so translators will need to be robust against malformed structures.

toJSON(includeclass=None, style=None, **kwargs)

Paramters are sent directly to json.dumps except:

Parameters:
  • includeclass – When true, include a class indicator using the method requested by the style parameter. When None (the default), defer to the value of the class variable amethyst_includeclass.
  • style

    When including class, what style to use (root-level object only). Options are:

    • ”flat” to produce a JSON string in the form:
      { "__class__": "__my.module.MyClass__", ... obj.dict ... }
      
    • ”single-key” to produce a JSON string in the form:
      { "__my.module.MyClass__": { ... obj.dict ... } }
      

The default style is taken from the class amethyst_classhint_style attribute.

classmethod newFromJSON(source, import_strategy=None, verifyclass=None, **kwargs)
fromJSON(source, import_strategy=None, verifyclass=None, **kwargs)

Paramters are sent directly to json.load or json.loads except:

Parameters:
deflate_data()

Deflate object into a “dumb” structure of plain dicts, lists, numbers, and strings. The deflated structure should be easily serializable by most any reasonable serialization library (yaml, lxml, …)

Subclasses: this method may be overridden with an unrelated implementation.

classmethod inflate_new(obj)

Inflate a “dumb” structure to a structure of objects, the opposite of deflate_data(). Allows inflation from arbitrary serialization tools, as long as they can produce dicts and lists.

Subclasses: this method may be overridden with an unrelated implementation.