Giter VIP home page Giter VIP logo

properties's Introduction

properties

Latest PyPI version MIT license ReadTheDocs Travis tests Code coverage

Overview Video

Python Properties

An overview of Properties, November 2016.

Why

Properties provides structure to aid development in an interactive programming environment while allowing for an easy transition to production code. It emphasizes usability and reproducibility for developers and users at every stage of the code life cycle.

Scope

The properties package enables the creation of strongly typed objects in a consistent, declarative way. This allows validation of developer expectations and hooks into notifications and other libraries. It provides documentation with no extra work, and serialization for portability and reproducibility.

Goals

  • Keep a clean namespace for easy interactive programming
  • Prioritize documentation
  • Provide built-in serialization/deserialization
  • Connect to other libraries for GUIs and visualizations

Documentation

API Documentation is available at ReadTheDocs.

Alternatives

  • attrs - "Python Classes Without Boilerplate" - This is a popular, actively developed library that aims to simplify class creation, especially around object protocols (i.e. dunder methods), with concise, declarative code.

    Similarities to Properties include type-checking, defaults, validation, and coercion. There are a number of differences:

    1. attrs acts somewhat like a namedtuple, whereas properties acts more like a dict or mutable object.
      • as a result, attrs is able to tackle hashing, comparison methods, string representation, etc.
      • attrs does not suffer runtime performance penalties as much as properties
      • on the other hand, properties focuses on interactivity, with notifications, serialization/deserialization, and mutable, possibly invalid states.
    2. properties has many built-in types with existing, complex validation already in place. This includes recursive validation of container and instance properties. attrs only allows attribute type to be specified.
    3. properties is more prescriptive and detailed around auto-generated class documentation, for better or worse.
  • traitlets (Jupyter project) and traits (Enthought) - These libraries are driven by GUI development (much of the Jupyter environment is built on traitlets; traits has automatic GUI generation) which leads to many similar features as properties such as strong typing, validation, and notifications. Also, some Properties features and aspects of the API take heavy inspiration from traitlets.

    However, There are a few key areas where properties differs:

    1. properties has a clean namespace - this (in addition to ? docstrings) allows for very easy discovery in an interactive programming environment.
    2. properties prioritizes documentation - this is not explicitly implemented yet in traits or traitlets, but works out-of-the-box in properties.
    3. properties prioritizes serialization - this is present in traits with pickling (but difficult to customize) and in traitlets with configuration files (which require extra work beyond standard class definition); in properties, serialization works out of the box but is also highly customizable.
    4. properties allows invalid object states - the GUI focus of traits/traitlets means an invalid object state at any time is never ok; without that constraint, properties allows interactive object building and experimentation. Validation then occurs when the user is ready and calls validate

    Significant advantages of traitlets and traits over properties are GUI interaction and larger suites of existing property types. Besides numerous types built-in to these libraries, some other examples are trait types that support unit conversion and NumPy/SciPy trait types (note: properties has a NumPy array property type).

    Note

    properties provides a link object which inter-operates with traitlets and follows the same API as traitlets links

  • param - This library also provides type-checking, validation, and notification. It has a few unique features and parameter types (possibly of note is the ability to provide dynamic values for parameters at any time, not just as the default). This was first introduced before built-in Python properties, and current development is very slow.

  • built-in Python dataclass decorator - provides "mutable named tuples with defaults" - this provides similar functionality to the attrs by adding several object protocol dunder methods to a class. Data Classes are clean, lightweight and included with Python 3.7. However, they don't provide as much builtin functionality or customization as the above libraries.

  • built-in Python property - properties/traits-like behavior can be mostly recreated using @property. This requires significantly more work by the programmer, and results in not-declarative, difficult-to-read code.

  • mypy, PEP 484, and PEP 526 - This provides static typing for Python without coersion, notifications, etc. It has a very different scope and implementation than traits-like libraries.

Connections

  • casingSimulations - Research repository for electromagnetic simulations in the presence of well casing
  • OMF - Open Mining Format API and file serialization
  • SimPEG - Simulation and Parameter Estimation in Geophysics
  • Steno3D - Python client for building and uploading 3D models

Installation

To install the repository, ensure that you have pip installed and run:

pip install properties

For the development version:

git clone https://github.com/seequent/properties.git
cd properties
pip install -e .

Examples

Lets start by making a class to organize your coffee habits.

import properties
class CoffeeProfile(properties.HasProperties):
    name = properties.String('What should I call you?')
    count = properties.Integer(
        'How many coffees have you had today?',
        default=0
    )
    had_enough_coffee = properties.Bool(
        'Have you had enough coffee today?',
        default=False
    )
    caffeine_choice = properties.StringChoice(
        'How do you take your caffeine?' ,
        choices=['coffee', 'tea', 'latte', 'cappuccino', 'something fancy'],
        required=False
    )

The CoffeeProfile class has 4 properties, all of which are documented! These can be set on class instantiation:

profile = CoffeeProfile(name='Bob')
print(profile.name)

Out [1]: Bob

Since a default value was provided for had_enough_coffee, the response is (naturally)

print(profile.had_enough_coffee)

Out [2]: False

We can set Bob's caffeine_choice to one of the available choices; he likes coffee

profile.caffeine_choice = 'coffee'

Also, Bob is half way through his fourth cup of coffee today:

profile.count = 3.5

Out [3]: ValueError: The 'count' property of a CoffeeProfile instance must
         be an integer.

Ok, Bob, chug that coffee:

profile.count = 4

Now that Bob's CoffeeProfile is established, properties can check that it is valid:

profile.validate()

Out [4]: True

Property Classes are auto-documented in Sphinx-style reStructuredText! When you ask for the doc string of CoffeeProfile, you get

**Required Properties:**

* **count** (:class:`Integer <properties.basic.Integer>`): How many coffees have you had today?, an integer, Default: 0
* **had_enough_coffee** (:class:`Bool <properties.basic.Bool>`): Have you had enough coffee today?, a boolean, Default: False
* **name** (:class:`String <properties.basic.String>`): What should I call you?, a unicode string

**Optional Properties:**

* **caffeine_choice** (:class:`StringChoice <properties.basic.StringChoice>`): How do you take your caffeine?, any of "coffee", "tea", "latte", "cappuccino", "something fancy"

properties's People

Contributors

bsmithyman avatar fwkoch avatar lasley avatar lheagy avatar rowanc1 avatar varyag00 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

properties's Issues

Upper and lower bouds

Scalar properties such as ints, floats (and potentially complex values, either the real and imaginary parts or amplitude) should be able to have upper and lower bounds

@properties.defaults wrapper needs an overhaul

Right now, this wrapper is used to add_defaults to superclass _defaults - that should probably be more explicit. - These methods should happen on startup and not use self.

There should also be functions to dynamically calculate defaults of properties (something like @properties.default('prop_name').

Array Validation

Clean up the array validation, rely more on numpy, less for loops!

Clean up property info()

Properties currently have both info() and info_text, where info_text is a class attribute and info() is a dynamic string based on the instance. However there is some muddle about how these are used (ie StringChoice overrides info_text as an @property; some info() methods use info_text, others do not).

Currently, this is only used in the sphinx docs and error messages on property instances, so they could possibly be combined into one method (or @property). At the very least, it should be cleaned up so info_text is a class attribute and info() is an instance method (and perhaps these could have more descriptive names).

Expanding functionality to function variables.

Is it possible to extend the functionality of properties to support function variables?

Something like this

arg1 = properties.Float('Argument description', default=1.0, min=0.).as_argument()

def my_function(arg1):
    """
    Function description
    """
    # Argument description would be combined and
    # arg1 could be validated as with properties
    arg1.validite() 
    result = do_work_with_arg1
    return result

This would be really useful when you have the same variable/argument as an input in multiple functions in a library.

String choices cannot be described

We do not currently have the ability to store descriptions on the StringChoice property.

"choices": [
    "C",
    "N",
    "F"
],
"descriptions": [
    "Cell centred data",
    "Nodal data",
    "Face data"
]

Bool Array

Right now Arrays are restricted to floats and ints. Could this be extended to include bools as well? eg

import numpy as np
np.array(np.ones(4), dtype=bool)

or perhaps a separate BoolArray class?

suggestions for extending the types allowed

I am working on implementing properties in the Regularization class of SimPEG. We have defined a custom class Utils.Zero that acts like a zero by overwriting the ufuncs (https://github.com/simpeg/simpeg/blob/master/SimPEG/Utils/matutils.py#L469). Since it is not inheriting from numpy and doesn't have a dtype, it is not recognized as an allowable type on validation.

from SimPEG import Utils

class Regularization(properties.HasProperties): 
    mref = properties.Array("reference model", default=Utils.Zero())

Do you have a recommendation for how to have Utils.Zero() interpoerate with the properties.Array?

Regexp

Can we add something like regexp to the strings? Similar to how we can bound floats with min/max.

email = properties.String("The users email", regexp=r".+\@.+\..+", strip=" ")

StringChoices should accept a set for choices

We are requiring that the user pass a list or tuple here (or a dict). However, we are using it like a set. At the least, we should accept a set. In some ways, I would prefer that we handle this in a way that does not require direct inheritance from set, list, tuple or dict, but rather uses ABCs.

Add HasProperties to the docs

Right now, different Property classes are well documented, but the only thing the docs say about HasProperties is that properties must be defined inside them (just a footnote).

Test coverage on tasks

Not pressing, since tasks are relatively simple and not widely used, but this would be good to add sometime.

init.py

Could we please remove the import * parts of properties and replace with the the suggestions from PEP328.

http://legacy.python.org/dev/peps/pep-0328/

PEP0328

(import * is not an option ;-)

Instead, it should be possible to use Python's standard grouping mechanism (parentheses) to write the import statement:

from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
    LEFT, DISABLED, NORMAL, RIDGE, END)

Float validation is broken

I've noticed this because we accidentally tried to build a Colour Legend from a list of float tuples. The float tuples did not pass the Colour validation (good) but they passed the Float validation (bad).
So, passing a tuple of floats into a Float passes the validation, but it should not.
The validator checks if the value is of type (float, integer_types) but it does not raise an exception if this fails. The following in_bounds check is skipped if the Float has no min/max values set.

Immutable properties with dynamic values

This would be useful for defining one property with a value totally dependent on other properties. The behaviour would be very similar to defining and @property, but as a Property it would have additional documentation, declarative definition, possibly additional validation.

For example, instead of something like this:

class BuriedSource(properties.HasProperties):
    depth = properties.Float(
        doc='Depth of source'
    )
    @property
    def loc_vector(self):
        return vectormath.Vector3([0., 0., -self.depth])

you could do:

class BuriedSource(properties.HasProperties):
    depth = properties.Float(
        doc='Depth of source'
    )
    loc_vector = properties.Vector3(
        doc='Vector location of source',
        dynamic_value=lambda self: [0., 0., -self.depth]
    )

loc_vector would then be auto-documented and coerced to a Vector3 automatically.

By default, a property with dynamic_value defined would be otherwise immutable - the value is only calculated on get and never set. Possibly there could be another attribute like overwritable where the dynamic value can be permanently overwritten (at least until it is deleted).

Required default to True

It is less likely a property is optional than required, I think. Especially now that required properties can have defaults - being required doesn't necessarily mean the user of the HasProperties class has to worry about setting it.

Tagging of Properties with metadata

Allow setting metadata values of properties that are not predefined attributes.

Something along the lines of how Traitlets does it:

num = Integer(5, help="a number").tag(config=True)

Required and default

Currently the functionality rejects setting default required properties. This should be rethought.

Improve Property error message if instance is None

Property validation is sometimes used without HasProperties class (for example in PropertyMetaclass or possibly as a standalone Property instance). In this case, self.name is '' and the instance is NoneType. Validation still works, but if there is an error, it looks like this:

screen shot 2016-11-25 at 12 46 39 pm

Instead a shorter error message should be displayed - ie the second half of that message only.

Better validation of List properties

Properties are validated on set; List modifications by index do not trigger this validation (nor change notifications). This is somewhat mitigated by calling validate() on the HasProperties class, however that only checks if the elements are valid - it does not coerce them. (For example a List of Colors may have an entry 'red'. That will pass validation but will not be coerced to the expected RGB value.) The only way to ensure list validation and coercion occurs is setting the entire list (even if it's just to itself): my_object.my_list = my_object.my_list

The best way to fix this is probably using a custom List that is able to listen for changes to it's elements. There may be less drastic intermediate steps to mitigate this.

Fix pointer recursion problems

Undefined auto_create=True pointers to other PropertyClasses with auto_create=True pointers have infinite recursion problems on validate.

sphinx docs vs plain text docs

All the nice markup features that make the sphinx docs look good cause the plain text docstrings (those accessed by ? in the notebook) to look messy. As we start linking docs with intersphinx, things get more and more complicated.

Should we somehow separate these into two different docstrings, sphinx and plain text?

Improve doc formatting of properties

The auto-generated docs are starting to look a bit messy using Sphinx built in property/attribute tags. It is strange that "Required" and "Parameters" are on different lines, then the immutable "attributes" are formatted differently (see screenshot below). I think we need some more customized formatting that doesn't use the builtin property/attribute tags.

screen shot 2016-12-09 at 8 58 10 am

Revisiting numpy dependency

@fwkoch, @varyag00, @rowanc1: What are your thoughts on the viability of removing the numpy dependency from properties and putting it in something like properties_array? The numpy dependency is a major deployment hurdle for lightweight web environments.

Thanks!

Validate default but keep original value

For example, setting color property default to 'random' should validate it, but still give 'random' if you get the default.

Need to ensure default is validated whenever it needs to be then.

Validate defaults

this should immediately error: properties.Int('My int', default='one hundred')

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.