Major Features
- Serialization:
- **Note**: `as_json` has been renamed `to_json` for better parallels between `from_json` and `to_json`
- Currently serialization/deserialization uses JSON. This can be overridden by registering custom `serializer`s and `deserializer`s to `Property` instances, and possibly overriding `serialize` in a `HasProperties` subclass. All the serialization methods pass around any `kwargs` they receive. This allows more flexibility in custom serializers (for example, an open file can be passed around).
- In `HasProperties`:
- `serialize` creates a JSON dictionary of all the properties using the property serialize functions. This dictionary may have a `__class__` key that stores the `HasProperties` class for smart deserialization.
- `deserialize` takes a dictionary and creates a new instance of the`HasProperties` class using valid property keywords in the dictionary. If `deserialize` is used on `trusted` JSON, it will use the `_REGISTRY` to find the correct `__class__` to construct. If the JSON is not `trusted`, `deserialize` constructs an instance of the class it is called on.
- In `Property` classes and subclasses:
- `to_json` is a static method that takes a valid property value and converts it to JSON-serializable version. If the value cannot be converted statically (ie a `Property` instance is required), this errors.
- `from_json` is a static method that takes a JSON-serializable value and converts it to the a valid property value. Again, if a `Property` instance is required, this errors.
- `serialize` takes a property value and serializes it using either the registered `serializer`, `to_json`, or `Property`-subclass-specific serialization logic.
- `deserialize` takes a JSON value and deserializes it to a valid property value using either the registered `deserializer`, `from_json`, or `Property`-subclass-specific deserialization logic.
- Defaults:
- `defaults` wrapper has been removed. To implement dynamic defaults, specify a callable in the `_defaults` dictionary - this can no longer depend on `self`.
- `_defaults` dictionary is now inherited. Any defaults not specified in a class will be inherited from parent classes.
- This differs from old behaviour where `_defaults` overwrote parent class values for `_defaults`
- default values are set before `__init__` or with new function `_reset`. Deleting a property sets it to undefined.
- This differs from old behavior where defaults were set on first `get` and deleting a property set it to default.
- On init, defaults do not fire change notifications, kwargs do.
- Additional validation of handlers and defaults in the metaclass.
- Metadata tagging:
- Properties can be tagged with additional arbitrary metadata. This is stored on the property in `meta`
- e.g. `properties.String('password').tag(encrypt=True)`
- Dependencies have been reduced for the base `properties` functionality. As a result, installation from PyPI has changed.
- `pip install properties` only installs `properties` and the basic requirement `six`
- `pip install propreties[math]` gets `numpy` and `vectormath` for the math properties (`Array` is now a math property)
- `pip install properties[image]` gets image dependencies (`ImagePNG is now native to`properties`;`properties_image` is no longer needed)
- `pip install properties[full]` gets everything
- Beta release support
Minor Changes
- Overriding `__init__` has been simplified by moving some instance setup to `PropertyMetaclass` `__call__`
- By default `__init__` now only sets `kwargs` (and fires change notifications)
- `Float`, `Integer`, and `Complex` validation has been changed to be more leniant. In `try`/`except`, values are coerced then equality is checked. If both of those pass with no exceptions, the value is valid.
- Notably, this allows `numpy.float16` and other similar numpy classes that are not subclasses of `float`/`int`/`complex` to pass validation.
- This does not apply for `Bool` - values here must be type `bool` to pass (eg. `numpy.bool8` does not pass.
- New `task` module for defining for implementing callable `HasProperties` `Task`s
- `Array` properties may now be of `dtype` `bool` in addition to `int` or `float`
- Exception types:
- **Note**: changing exception types may introduce backwards-incompatibility in error handling
- No more `AssertionErrors` or `KeyErrors` are raised. Now properties raises three types of errors:
- `AttributeErrors` on assignment to nonexistent properties/attributes
- `TypeErrors` when `Property` attributes (e.g. `required`) are set to invalid values
- `ValueErrors` when property values are invalid (i.e. validation on `set` fails)
- Terms used to construct properties are now stored on the property for recreating a (possibly modified) version of the property. The `terms` attribute is an instance of a `PropertyTerms` `namedtuple`, which contains fields: `name`, `cls`, `args`, `kwargs` and `meta`.
- `StringChoice` properties now allow `set`s as input. There is also additional validation to ensure no duplicates are present.
- `filter_props` now has an option to exclude immutable properties
- Minor improvements to doc strings and sphinx docs
- Properties are now alphabetized in the docs (or the order can be customized by setting `_doc_order` to a list of the property names in the desired order).
- Property attributes `help` and `helpdoc` were renamed `doc` to remove the name conflict with builtin `help` this change is **not backwards compatible** but it is unlikely these attributes are referenced externally anywhere.
- Expanded testing
- Video added to README
Bug Fixes
- `String` properties now can handle unicode because casting to `str` was removed.
- There is now a `unicode` attribute. If `True` (default) all strings are coerced to unicode; if `False` the input string type is maintained.
- Property `validator`s (observers that fire on set, before the change) may now coerce the value by modifying the `change` dictionary. Previously they were allowed to have a coerced return value, but this value was never used.
- Array `dtype` validation previously allowed bad types to pass as long as 1+ good type was present. Now only good types (`int`, `float`, and `bool`) are allowed.