Skip to main content

Building data structures as node trees

Project description

node

This package is the successor of zodict.

Overview

Data structures could be described as trees. Some are by nature treeish, like XML documents, LDAP directories or filesystem directory trees, while others can be treated that way.

Furthermore, python has elegant ways for customizing all sorts of datamodel related APIs. The dictionary container type fits almost completely the purpose of representing a node of a tree. The same API is also described in zope.interface.common.mapping.IFullMapping. Additionaly a node must provide hierarchy information. In this case the contract of zope.location.interfaces.ILocation is used.

Having data structures as such trees has some advantages:

  • Unified data access API to different data models and/or sources

  • Trees are traversable in both directions

  • Once in memory, node trees are fast to deal with

  • Software working on node trees may not need to know about internal data structures, as long as the node tree implementation provides the correct interface contracts

Usage

node ships with some “ready-to-import-and-use” nodes.

An unordered node. This can be used as base for trees where order of items doesn’t matter:

>>> from node.base import BaseNode
>>> root = BaseNode(name='root')
>>> root['child'] = BaseNode()
>>> root.printtree()
<class 'node.base.BaseNode'>: root
  <class 'node.base.BaseNode'>: child

An ordered node. The order of items is preserved:

>>> from node.base import OrderedNode
>>> root = OrderedNode(name='orderedroot')
>>> root['foo'] = OrderedNode()
>>> root['bar'] = OrderedNode()
>>> root.printtree()
<class 'node.base.OrderedNode'>: orderedroot
  <class 'node.base.OrderedNode'>: foo
  <class 'node.base.OrderedNode'>: bar

>>> root.items()
[('foo', <OrderedNode object 'foo' at ...>),
('bar', <OrderedNode object 'bar' at ...>)]

A full API description of the node interface can be found at node.interfaces.INode.

A more fine granular control of node functionality

node utilizes the plumber package.

Thus, different behaviors of nodes are provided by plumbing behaviors. Read the documentation of plumber for details about the plumbing system:

>>> from plumber import plumbing
>>> from node.behaviors import (
...     Nodespaces,
...     Attributes,
...     Lifecycle,
...     NodeChildValidate,
...     Adopt,
...     DefaultInit,
...     Nodify,
...     OdictStorage,
... )

>>> @plumbing(
...     Nodespaces,
...     Attributes,
...     Lifecycle,
...     NodeChildValidate,
...     Adopt,
...     DefaultInit,
...     Nodify,
...     OdictStorage)
... class CustomNode(object):
...     pass

>>> dir(CustomNode)
['__class__', '__contains__', '__delattr__', '__delitem__',
'__dict__', '__doc__', '__format__', '__getattribute__',
'__getitem__', '__hash__', '__implemented__', '__init__',
'__iter__', '__len__', '__module__', '__name__',
'__new__', '__nonzero__', '__parent__', '__plumbing__',
'__plumbing_stacks__', '__provides__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__setitem__',
'__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'_nodespaces', '_notify_suppress', 'acquire', 'allow_non_node_childs',
'attribute_access_for_attrs', 'attributes', 'attributes_factory',
'attrs', 'clear', 'copy', 'deepcopy', 'detach', 'events', 'filtereditems',
'filtereditervalues', 'filteredvalues', 'get', 'has_key', 'items',
'iteritems', 'iterkeys', 'itervalues', 'keys', 'name', 'noderepr',
'nodespaces', 'parent', 'path', 'pop', 'popitem', 'printtree',
'root', 'setdefault', 'storage', 'update', 'values']

As the dir call shows, the CustomNode class was plumbed using given behaviors, so defining a complete INode implementation with some additional behaviours and is now easily done:

>>> node = CustomNode()
>>> node['child'] = CustomNode()
>>> node.printtree()
<class 'CustomNode'>: None
  <class 'CustomNode'>: child

>>> from node.interfaces import INode
>>> INode.providedBy(node)
True

Behaviors

The node package provides several plumbing behaviors:

node.behaviors.DefaultInit

Plumbing part providing default __init__ function on node. See node.interfaces.IDefaultInit.

node.behaviors.Nodify

Plumbing part to Fill in gaps for full INode API. See node.interfaces.INodify.

node.behaviors.Adopt

Plumbing part that provides adoption of children. See node.interfaces.IAdopt.

node.behaviors.NodeChildValidate

Plumbing part for child node validation. See node.interfaces.INodeChildValidate.

node.behaviors.UnicodeAware

Plumbing part to ensure unicode for keys and string values. See node.interfaces.IUnicodeAware.

node.behaviors.Alias

Plumbing part that provides aliasing of child keys. See node.interfaces.IAlias.

node.behaviors.AsAttrAccess

Plumbing part to get node as IAttributeAccess implementation. See node.interfaces.IAsAttrAccess.

node.behaviors.ChildFactory

Plumbing part providing child factories which are invoked at __getitem__ if object by key is not present at plumbing endpoint yet. See node.interfaces.IChildFactory.

node.behaviors.FixedChildren

Plumbing part that initializes a fixed dictionary as children. See node.interfaces.IFixedChildren.

node.behaviors.GetattrChildren

Plumbing part for child access via __getattr__, given the attribute name is unused. See node.interfaces.IGetattrChildren.

node.behaviors.Nodespaces

Plumbing part for providing nodespaces on node. See node.interfaces.INodespaces.

node.behaviors.Attributes

Plumbing part to provide attributes on node. Requires node.behaviors.Nodespaces part. See node.interfaces.IAttributes.

node.behaviors.Lifecycle

Plumbing part taking care of lifecycle events. See node.interfaces.ILifecycle.

node.behaviors.AttributesLifecycle

Plumbing part for handling ifecycle events at attributes manipulation. See node.interfaces.IAttributesLifecycle.

node.behaviors.Invalidate

Plumbing part for node invalidation. See node.interfaces.Invalidate.

node.behaviors.VolatileStorageInvalidate

Plumbing part for invalidating nodes using a volatile storage. See node.interfaces.Invalidate.

node.behaviors.Cache

Plumbing part for caching. See node.interfaces.ICache.

node.behaviors.Order

Plumbing part for ordering support. See node.interfaces.IOrder.

node.behaviors.UUIDAware

Plumbing part providing a uuid on nodes. See node.interfaces.IUUIDAware.

node.behaviors.Reference

Plumbing part holding an index of all nodes contained in the tree. See node.interfaces.IReference.

node.behaviors.Storage

Provide abstract storage access. See node.interfaces.IStorage.

node.behaviors.DictStorage

Provide dictionary storage. See node.interfaces.IStorage.

node.behaviors.OdictStorage

Provide ordered dictionary storage. See node.interfaces.IStorage.

node.behaviors.Fallback

Provide a way to fall back to values by subpath stored on another node. See node.interfaces.IFallback.

JSON Serialization

Nodes can be serialized to and deserialized from JSON:

>>> from node.serializer import serialize
>>> json_dump = serialize(BaseNode(name='node'))

>>> from node.serializer import deserialize
>>> deserialize(json_dump)
<BaseNode object 'node' at ...>

For details on serialization API please read file in docs/archive/serializer.rst.

Migration

A node which behaves like zodict.Node is contained in node.base.Node. This node is supposed to be used for migration from zodict.

It’s also useful to take a look at the behaviors the original node is build from.

Probably an implementation does not need all the behaviors at once. In this case define the node plumbing directly on a node class instead of inheriting from node.base.Node.

TestCoverage

https://travis-ci.org/bluedynamics/node.svg?branch=master

Summary of the test coverage report:

Name                                                    Stmts   Miss  Cover
---------------------------------------------------------------------------
src/node/base.py                                           23      0   100%
src/node/behaviors/__init__.py                             36      0   100%
src/node/behaviors/alias.py                               103      0   100%
src/node/behaviors/attributes.py                           37      0   100%
src/node/behaviors/cache.py                                69      0   100%
src/node/behaviors/common.py                              130      0   100%
src/node/behaviors/fallback.py                             41      0   100%
src/node/behaviors/lifecycle.py                            48      0   100%
src/node/behaviors/mapping.py                             117      0   100%
src/node/behaviors/nodespace.py                            33      0   100%
src/node/behaviors/nodify.py                               87      0   100%
src/node/behaviors/order.py                               109      0   100%
src/node/behaviors/reference.py                            83      0   100%
src/node/behaviors/storage.py                              31      0   100%
src/node/compat.py                                         10      0   100%
src/node/events.py                                         75      0   100%
src/node/interfaces.py                                     96      0   100%
src/node/locking.py                                        23      0   100%
src/node/serializer.py                                    134      0   100%
src/node/testing/__init__.py                                1      0   100%
src/node/testing/base.py                                   66      0   100%
src/node/testing/env.py                                    18      0   100%
src/node/testing/fullmapping.py                           177      0   100%
src/node/tests/__init__.py                                 88      0   100%
src/node/tests/test_alias.py                              113      0   100%
src/node/tests/test_attributes.py                          38      0   100%
src/node/tests/test_base.py                               245      0   100%
src/node/tests/test_cache.py                               98      0   100%
src/node/tests/test_common.py                             154      0   100%
src/node/tests/test_events.py                              75      0   100%
src/node/tests/test_fallback.py                            46      0   100%
src/node/tests/test_lifecycle.py                          105      0   100%
src/node/tests/test_locking.py                             43      0   100%
src/node/tests/test_mapping.py                             22      0   100%
src/node/tests/test_nodespace.py                           44      0   100%
src/node/tests/test_nodify.py                              45      0   100%
src/node/tests/test_order.py                              134      0   100%
src/node/tests/test_reference.py                           74      0   100%
src/node/tests/test_serializer.py                         222      0   100%
src/node/tests/test_storage.py                             41      0   100%
src/node/tests/test_testing.py                            688      0   100%
src/node/tests/test_tests.py                               50      0   100%
src/node/tests/test_utils.py                              127      0   100%
src/node/utils.py                                         142      0   100%
---------------------------------------------------------------------------
TOTAL                                                    4141      0   100%

Python Versions

  • Python 2.7, 3.3+, pypy

  • May work with other versions (untested)

Contributors

  • Robert Niederreiter

  • Florian Friesdorf

  • Jens Klein

Changes

0.9.21 (2017-06-15)

  • Introduce node.events.EventDispatcher and node.events.EventAttribute. [rnix, 2017-06-15]

  • Use setattr in instance_property decorator instead of object.__setattr__ in order to avoid errors with custom low level __setattr__ implementations. [rnix, 2017-06-14]

0.9.20 (2017-06-07)

  • Type cast sort key to node.compat.UNICODE_TYPE in node.behaviors.Nodify.treerepr to avoid unicode decode errors. [rnix, 2017-06-07]

0.9.19 (2017-06-07)

  • Python 3 and pypy compatibility. [rnix, 2017-06-02]

  • Drop support for Python < 2.7. [rnix, 2017-06-02]

  • Add __bool__ to node.behaviors.Nodify. [rnix, 2017-06-02]

  • Add __bool__ to node.utils.UNSET. [rnix, 2017-06-02]

  • Add treerepr in node.behaviors.nodify.Nodify and move code from printtree to it. Returs tree representation as string instead of printing it. printtree uses treerepr now. As enhancement treerepr sorts children of node if it does not implement IOrdered in order to ensure consistend output which can be used to write tests against. [rnix, 2017-06-02]

  • Use object.__getattribute__ explicitely in node.utils.instance_property to check whether property value already has been computed in order to avoid problems when oberwriting __getattr__ on classes using instance_property decorator. [rnix, 2017-06-02]

0.9.18.1 (2017-02-23)

  • Fix permissions. [rnix, 2017-02-23]

0.9.18 (2017-02-14)

  • Add node.utils.node_by_path. [rnix, 2017-02-07]

  • Do not depend on unittest2 since its is not used. [jensens, 2017-01-17]

  • Add node.behaviors.Fallback behavior. [jensens, 2017-01-17]

0.9.17 (2017-01-17)

  • Add basic JSON serializer and deserializer. [rnix, 2016-12-03]

0.9.16 (2015-10-08)

  • Only encode name in node.behaviors.nodify.Nodify.__repr__ and node.behaviors.nodify.Nodify.noderepr if name is unicode instance. [rnix, 2015-10-03]

  • Improve node.behaviors.nodify.Nodify.printtree. None node children are printed with key. [rnix, 2015-10-03]

0.9.15 (2014-12-17)

  • Fix dependency declaration to odict in order to make setuptools 8.x+ happy; using >= instead of > now. [jensens, 2014-12-17]

0.9.14

  • use plumbing decorator instead of plumber metaclass. [rnix, 2014-07-31]

0.9.13

  • Introduce node.behaviors.cache.VolatileStorageInvalidate. [rnix, 2014-01-15]

0.9.12

  • Add zope.component to install dependencies. [rnix, 2013-12-09]

0.9.11

  • Use node.utils.UNSET instance in node.behaviors.mapping.ExtendedWriteMapping.pop. [rnix, 2013-02-10]

  • Improve node.utils.Unset. Add Unset instance at node.utils.UNSET. [rnix, 2013-02-10]

0.9.10

  • Fix node.utils.StrCodec.encode to return value as is if str and decoding failed. [rnix, 2012-11-07]

0.9.9

  • Python 2.7 compatibility. [rnix, 2012-10-15]

  • Remove zope.component.event B/C. [rnix, 2012-10-15]

  • Remove zope.location B/C. [rnix, 2012-10-15]

  • Remove zope.lifecycleevent B/C. [rnix, 2012-10-15]

  • Pep8. [rnix, 2012-10-15]

0.9.8

  • Deprecate the use of node.parts. Use node.behaviors instead. [rnix, 2012-07-28]

  • Adopt to plumber 1.2 [rnix, 2012-07-28]

0.9.7

  • Introduce node.interfaces.IOrdered Marker interface. Set this interface on node.parts.storage.OdictStorage. [rnix, 2012-05-21]

  • node.parts.mapping.ClonableMapping now supports deepcopy. [rnix, 2012-05-18]

  • Use zope.interface.implementer instead of zope.interface.implements all over the place. [rnix, 2012-05-18]

  • Remove superfluos interfaces. [rnix, 2012-05-18]

  • Remove Zodict from node.utils. [rnix, 2012-05-18]

  • Remove AliasedNodespace, use Alias part instead. [rnix, 2012-05-18]

  • Move aliaser objects from node.aliasing to node.parts.alias. [rnix, 2012-05-18]

  • Remove composition module. [rnix, 2012-05-18]

  • Remove bbb module. [rnix, 2012-05-18]

0.9.6

  • Do not inherit node.parts.Reference from node.parts.UUIDAware. [rnix, 2012-01-30]

  • Set uuid in node.parts.Reference.__init__ plumb. [rnix, 2012-01-30]

0.9.5

  • add node.parts.nodify.Nodify.acquire function. [rnix, 2011-12-05]

  • add node.parts.ChildFactory plumbing part. [rnix, 2011-12-04]

  • add node.parts.UUIDAware plumbing part. [rnix, 2011-12-02]

  • fix node.parts.Order.swap in order to work with pickled nodes. [rnix, 2011-11-28]

  • use node.name instead of node.__name__ in node.parts.nodify.Nodify.path. [rnix, 2011-11-17]

  • add swap to node.parts.Order. [rnix, 2011-10-05]

  • add insertfirst and insertlast to node.parts.Order. [rnix, 2011-10-02]

0.9.4

  • add node.utils.debug decorator. [rnix, 2011-07-23]

  • remove non storage contract specific properties from node.aliasing.AliasedNodespace [rnix, 2011-07-18]

  • node.aliasing test completion [rnix, 2011-07-18]

  • Add non strict functionality to node.aliasing.DictAliaser for accessing non aliased keys as is as fallback [rnix, 2011-07-18]

  • Consider INode implementing objects in node.utils.StrCodec [rnix, 2011-07-16]

  • Remove duplicate implements in storage parts [rnix, 2011-05-16]

0.9.3

  • Increase test coverage [rnix, 2011-05-09]

  • Add interfaces IFixedChildren and IGetattrChildren for related parts. [rnix, 2011-05-09]

  • Rename Unicode part to UnicodeAware. [rnix, 2011-05-09]

  • Add node.utils.StrCodec. [rnix, 2011-05-09]

  • Inherit INodify interface from INode. [rnix, 2011-05-08]

  • Locking tests. Add time.sleep after thread start. [rnix, 2011-05-08]

  • Cleanup BaseTester, remove sorted_output flag (always sort), also search class bases for detection in wherefrom. [rnix, 2011-05-08]

  • Remove useless try/except in utils.AttributeAccess. [rnix, 2011-05-08]

  • Add instance_property decorator to utils. [rnix, 2011-05-06]

  • Add FixedChildren and GetattrChildren parts. [chaoflow, 2011-04-22]

0.9.2

  • Add __nonzero__ on Nodifiy part always return True. [rnix, 2011-03-15]

0.9.1

  • Provide node.base.Node with same behavior like zodict.Node for migration purposes. [rnix, 2011-02-08]

0.9

  • Make it work [rnix, chaoflow, et al]

License

Copyright (c) 2009-2017, BlueDynamics Alliance, Austria All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  • Neither the name of the BlueDynamics Alliance nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY BlueDynamics Alliance AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BlueDynamics Alliance BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

node-0.9.21.tar.gz (62.8 kB view details)

Uploaded Source

File details

Details for the file node-0.9.21.tar.gz.

File metadata

  • Download URL: node-0.9.21.tar.gz
  • Upload date:
  • Size: 62.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for node-0.9.21.tar.gz
Algorithm Hash digest
SHA256 7dfc403a93428686df22032a202c5437ae17fac99e1abaa41420385f6f61eb9f
MD5 3f412a28855a70500c49a64d06296560
BLAKE2b-256 0541d84c61cf997d2ad49597d405a3a4cb675eb6a0d3b4c488469fd6d704ed23

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page