Science Score: 26.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (14.8%) to scientific vocabulary
Last synced: 10 months ago · JSON representation

Repository

Basic Info
  • Host: GitHub
  • Owner: conestack
  • License: other
  • Language: Python
  • Default Branch: master
  • Homepage:
  • Size: 837 KB
Statistics
  • Stars: 24
  • Watchers: 8
  • Forks: 23
  • Open Issues: 0
  • Releases: 0
Created over 15 years ago · Last pushed over 1 year ago
Metadata Files
Readme Changelog License

README.rst

Node
====

.. image:: https://img.shields.io/pypi/v/node.svg
    :target: https://pypi.python.org/pypi/node
    :alt: Latest PyPI version

.. image:: https://img.shields.io/pypi/dm/node.svg
    :target: https://pypi.python.org/pypi/node
    :alt: Number of PyPI downloads

.. image:: https://github.com/conestack/node/actions/workflows/test.yml/badge.svg
    :target: https://github.com/conestack/node/actions/workflows/test.yml
    :alt: Test node


Overview
--------

Node is a library to create nested data models and structures.

These data models are described as trees of nodes, optionally with attributes
and schema definitions.

They utilize:

- Python's
  `mapping and sequence API's `_
  for accessing node members.
- The contract of
  `zope.location.interfaces.ILocation `_
  for hierarchy information.

One purpose of this package is to provide a unified API to different backend
storages. Specific storage related implementations are for example:

- `node.ext.fs `_
- `node.ext.ldap `_
- `node.ext.yaml `_
- `node.ext.zodb `_

Another usecase is providing interfaces for specific application domains.

E.g. for user and group management,
`node.ext.ugm `_ defines the interfaces.
Additional it implements a file based default implementation. Then there are
specific implementations of those interfaces in
`node.ext.ldap `_ and
`cone.sql `_, to access users and groups in
LDAP and SQL databases.

This package is also used to build in-memory models of all sorts.

E.g.  `yafowil `_ is a HTML form processing
and rendering library. It uses node trees for declarative description of the
form model.

Another one to mention is `cone.app `_,
a `Pyramid `_ based development environment
for web applications, which uses node trees to describe the application model.


Basic Usage
-----------

There are two basic node types. Mapping nodes and sequence nodes. This
package provides some basic nodes to start from.


Mapping Nodes
~~~~~~~~~~~~~

Mapping nodes implement ``node.interfaces.IMappingNode``. A mapping in python
is a container object that supports arbitrary key lookups and implements the
methods specified in the ``MutableMapping`` of pythons abstract base classes
respective ``zope.interface.common.mapping.IFullMapping``.

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

.. code-block:: python

    from node.base import BaseNode

    root = BaseNode(name='root')
    root['child'] = BaseNode()

An ordered node. The order of items is preserved:

.. code-block:: python

    from node.base import OrderedNode

    root = OrderedNode(name='orderedroot')
    root['foo'] = OrderedNode()
    root['bar'] = OrderedNode()

With ``printtree`` we can do a quick inspection of our node tree:

.. code-block:: pycon

    >>> root.printtree()
    : orderedroot
      : foo
      : bar


Sequence Nodes
~~~~~~~~~~~~~~

Sequence nodes implement ``node.interfaces.ISequenceNode``. In the context
of this library, a sequence is an implementation of the methods
specified in the ``MutableSequence`` of pythons abstract base classes respective
``zope.interface.common.collections.IMutableSequence``.

Using a list node:

.. code-block:: python

    from node.base import BaseNode
    from node.base import ListNode

    root = ListNode(name='listroot')
    root.insert(0, BaseNode())
    root.insert(1, BaseNode())

Check tree structure with ``printtree``:

.. code-block:: pycon

    >>> root.printtree()
    : listnode
      : 0
      : 1

.. note::

    Sequence nodes are introduced as of node 1.0 and are not as feature rich
    as mapping nodes (yet). If you find inconsistencies or missing features,
    please file an issue or create a pull request at github.


Behaviors
~~~~~~~~~

``node`` utilizes the `plumber `_ package.

The different functionalities of nodes are provided as plumbing behaviors:

.. code-block:: python

    from node.behaviors import DefaultInit
    from node.behaviors import MappingNode
    from node.behaviors import OdictStorage
    from plumber import plumbing

    @plumbing(
        DefaultInit,
        MappingNode,
        OdictStorage)
    class CustomNode:
        pass

When inspecting the ``CustomNode`` class, we can see it was plumbed using given
behaviors, now representing a complete node implementation:

.. code-block:: pycon

    >>> dir(CustomNode)
    ['__bool__', '__class__', '__contains__', '__delattr__', '__delitem__',
    '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
    '__getattribute__', '__getitem__', '__gt__', '__hash__', '__implemented__',
    '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__',
    '__module__', '__name__', '__ne__', '__new__', '__nonzero__', '__parent__',
    '__plumbing__', '__plumbing_stacks__', '__provides__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__', 'acquire', 'clear', 'copy',
    'deepcopy', 'detach', 'filtereditems', 'filtereditervalues', 'filteredvalues',
    'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys',
    'name', 'noderepr', 'parent', 'path', 'pop', 'popitem', 'printtree', 'root',
    'setdefault', 'storage', 'treerepr', 'update', 'values']

Please read the documentation of ``plumber`` for detailed information about the
plumbing system.


Attributes
~~~~~~~~~~

While it is not strictly necessary, it's a good idea to separate the
hierarchical structure of a model from the node related attributes to avoid
naming conflicts. Attributes are provided via ``node.behaviors.Attributes``
plumbing behavior:

.. code-block:: python

    from node.behaviors import Attributes
    from node.behaviors import DefaultInit
    from node.behaviors import DictStorage
    from node.behaviors import MappingNode
    from plumber import plumbing

    @plumbing(
        Attributes,
        DefaultInit,
        MappingNode,
        DictStorage)
    class NodeWithAttributes:
        pass

The node now provides an ``attrs`` attribute. Node attributes are itself just
a node:

.. code-block:: pycon

    >>> node = NodeWithAttributes()
    >>> attrs = node.attrs
    >>> attrs
    

    >>> attrs['foo'] = 'foo'

If it's desired to access attribute members via python attribute access,
``attribute_access_for_attrs`` must be set on node:

.. code-block:: pycon

    >>> node.attribute_access_for_attrs = True
    >>> attrs = node.attrs
    >>> attrs.foo = 'bar'
    >>> attrs.foo
    'bar'

A custom attributes implementation can be set by defining
``attributes_factory`` on the node:

.. code-block:: python

    from node.behaviors import NodeAttributes

    class CustomAttributes(NodeAttributes):
        pass

    class CustomAttributesNode(NodeWithAttributes):
        attributes_factory = CustomAttributes

This factory is then used to instantiate the attributes:

.. code-block:: pycon

    >>> node = CustomAttributesNode()
    >>> node.attrs
    


Schema
~~~~~~

To describe the data types of node members, this package provides a mechanism
for defining schemata.

This can happen in different ways. One is to define the schema for node members
directly. This is useful for nodes representing a leaf in the hierarchy or for
node attribute nodes:

.. code-block:: python

    from node import schema
    from node.base import BaseNode
    from node.behaviors import DefaultInit
    from node.behaviors import DictStorage
    from node.behaviors import MappingNode
    from node.behaviors import Schema
    from plumber import plumbing

    @plumbing(
        DefaultInit,
        MappingNode,
        DictStorage,
        Schema)
    class SchemaNode:
        schema = {
            'int': schema.Int(),
            'float': schema.Float(default=1.),
            'str': schema.Str(),
            'bool': schema.Bool(default=False),
            'node': schema.Node(BaseNode)
        }

Children defined in the schema provide a default value. If not explicitely
defined, the default value is always ``node.utils.UNSET``:

.. code-block:: pycon

    >>> node = SchemaNode()
    >>> node['int']
    

    >>> node['float']
    1.0

    >>> node['bool']
    False

Children defined in the schema are validated against the defined type when
setting it's value:

.. code-block:: pycon

    >>> node = SchemaNode()
    >>> node['int'] = 'A'
    Traceback (most recent call last):
      ...
    ValueError: A is no  type

For accessing members defined in the schema as node attributes,
``SchemaAsAttributes`` plumbing behavior can be used:

.. code-block:: python

    from node.behaviors import SchemaAsAttributes

    @plumbing(SchemaAsAttributes)
    class SchemaAsAttributesNode(BaseNode):
        schema = {
            'int': schema.Int(default=1),
        }

Node ``attrs`` now provides access to the schema members:

.. code-block:: pycon

    >>> node = SchemaAsAttributesNode()
    >>> node.attrs['int']
    1

Schema members can also be defined as class attributes. This is syntactically
the most elegant way, but comes with the tradeoff of possible naming conflicts:

.. code-block:: python

    from node.behaviors import SchemaProperties

    @plumbing(
        DefaultInit,
        MappingNode,
        DictStorage,
        SchemaProperties)
    class SchemaPropertiesNode:
        text = schema.Str(default='Text')

Here we access ``text`` as class attribute:

.. code-block:: pycon

    >>> node = SchemaPropertiesNode()
    >>> node.text
    'Text'

    >>> node.text = 1
    Traceback (most recent call last):
      ...
    ValueError: 1 is no  type


Plumbing Behaviors
------------------

General Behaviors
~~~~~~~~~~~~~~~~~

**node.behaviors.DefaultInit**
    Plumbing behavior providing default ``__init__`` function on node. This
    behavior is going to be deprecated in future versions. Use
    ``node.behaviors.NodeInit`` instead.
    See ``node.interfaces.IDefaultInit``.

**node.behaviors.NodeInit**
    Plumbing behavior for transparent setting of ``__name__`` and ``__parent__``
    at object initialization time.
    See ``node.interfaces.INodeInit``.

**node.behaviors.Node**
    Fill in gaps for full INode API. See ``node.interfaces.INode``.

**node.behaviors.ContentishNode**
    A node which can contain children. See
    ``node.interfaces.IContentishNode``. Concrete implementations are
    ``node.behaviors.MappingNode`` and ``node.behaviors.SequenceNode``.

**node.behaviors.Attributes**
    Provide attributes on node. See ``node.interfaces.IAttributes``. If
    ``node.behaviors.Nodespaces`` is applied on node, the attributes instance
    gets stored internally in ``__attrs__`` nodespace, otherwise its set on
    ``__attrs__`` attribute.

**node.behaviors.Events**
    Provide an event registration and dispatching mechanism.
    See ``node.interfaces.IEvents``.

**node.behaviors.BoundContext**
    Mechanism for scoping objects to interfaces and classes.
    See ``node.interfaces.IBoundContext``.

**node.behaviors.NodeReference**
    Plumbing behavior holding an index of nodes contained in the tree.
    See ``node.interfaces.INodeReference``.

**node.behaviors.WildcardFactory**
    Plumbing behavior providing factories by wildcard patterns.
    See ``node.interfaces.IWildcardFactory``.


Mapping Behaviors
~~~~~~~~~~~~~~~~~

**node.behaviors.MappingNode**
    Turn an object into a mapping node. Extends ``node.behaviors.Node``.
    See ``node.interfaces.IMappingNode``.

**node.behaviors.MappingAdopt**
    Plumbing behavior that provides ``__name__`` and ``__parent__`` attribute
    adoption on child nodes of mapping.
    See ``node.interfaces.IMappingAdopt``.

**node.behaviors.MappingConstraints**
    Plumbing behavior for constraints on mapping nodes.
    See ``node.interfaces.IMappingConstraints``.

**node.behaviors.UnicodeAware**
    Plumbing behavior to ensure unicode for keys and string values.
    See ``node.interfaces.IUnicodeAware``.

**node.behaviors.Alias**
    Plumbing behavior that provides aliasing of child keys.
    See ``node.interfaces.IAlias``.

**node.behaviors.AsAttrAccess**
    Plumbing behavior to get node as IAttributeAccess implementation.
    See ``node.interfaces.IAsAttrAccess``.

**node.behaviors.ChildFactory**
    Plumbing behavior providing child factories.
    See ``node.interfaces.IChildFactory``.

**node.behaviors.FixedChildren**
    Plumbing Behavior that initializes a fixed dictionary as children.
    See ``node.interfaces.IFixedChildren``.

**node.behaviors.Nodespaces**
    Plumbing behavior for providing nodespaces on node.
    See ``node.interfaces.INodespaces``.

**node.behaviors.Lifecycle**
    Plumbing behavior taking care of lifecycle events.
    See ``node.interfaces.ILifecycle``.

**node.behaviors.AttributesLifecycle**
    Plumbing behavior for handling lifecycle events on attribute manipulation.
    See ``node.interfaces.IAttributesLifecycle``.

**node.behaviors.Invalidate**
    Plumbing behavior for node invalidation.
    See ``node.interfaces.Invalidate``.

**node.behaviors.VolatileStorageInvalidate**
    Plumbing behavior for invalidating nodes using a volatile storage.
    See ``node.interfaces.Invalidate``.

**node.behaviors.Cache**
    Plumbing behavior for caching.
    See ``node.interfaces.ICache``.

**node.behaviors.MappingOrder**
    Plumbing behavior for ordering support on mapping nodes.
    See ``node.interfaces.IMappingOrder``.

**node.behaviors.UUIDAware**
    Plumbing behavior providing a uuid on nodes.
    See ``node.interfaces.IUUIDAware``.

**node.behaviors.MappingReference**
    Plumbing behavior to provide ``node.interfaces.INodeReference`` on mapping
    nodes. See ``node.interfaces.IMappingReference``.

**node.behaviors.MappingStorage**
    Provide abstract mapping storage access.
    See ``node.interfaces.IMappingStorage``.

**node.behaviors.DictStorage**
    Provide dictionary storage. Extends ``node.behaviors.MappingStorage``.
    See ``node.interfaces.IMappingStorage``.

**node.behaviors.OdictStorage**
    Provide ordered dictionary storage. Extends
    ``node.behaviors.MappingStorage``. See ``node.interfaces.IMappingStorage``.

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

**node.behaviors.Schema**
    Provide schema validation and value serialization on node values.
    See ``node.interfaces.ISchema``.

**node.behaviors.SchemaAsAttributes**
    Provide schema validation and value serialization on node values via
    dedicated attributes object.
    See ``node.interfaces.ISchemaAsAttributes``.

**node.behaviors.SchemaProperties**
    Provide schema fields as class properties.
    See ``node.interfaces.ISchemaProperties``.

**node.behaviors.MappingFilter**
    Filter mapping children by class or interface.
    See ``node.interfaces.IChildFilter``.


Sequence Behaviors
~~~~~~~~~~~~~~~~~~

**node.behaviors.SequenceNode**
    Turn an object into a sequence node. Extends ``node.behaviors.Node``.
    See ``node.interfaces.IMappingNode``.

**node.behaviors.SequenceAdopt**
    Plumbing behavior that provides ``__name__`` and ``__parent__`` attribute
    adoption on child nodes of sequence.
    See ``node.interfaces.ISequenceAdopt``.

**node.behaviors.SequenceConstraints**
    Plumbing behavior for constraints on sequence nodes.
    See ``node.interfaces.ISequenceConstraints``.

**node.behaviors.SequenceStorage**
    Provide abstract sequence storage access.
    See ``node.interfaces.ISequenceStorage``.

**node.behaviors.ListStorage**
    Provide list storage. See ``node.interfaces.ISequenceStorage``.

**node.behaviors.SequenceReference**
    Plumbing behavior to provide ``node.interfaces.INodeReference`` on sequence
    nodes. See ``node.interfaces.ISequenceReference``.

**node.behaviors.SequenceFilter**
    Filter sequence children by class or interface.
    See ``node.interfaces.IChildFilter``.

**node.behaviors.SequenceOrder**
    Plumbing behavior for ordering support on sequence nodes.
    See ``node.interfaces.ISequenceOrder``.


JSON Serialization
------------------

Nodes can be serialized to and deserialized from JSON:

.. code-block:: pycon

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

    >>> from node.serializer import deserialize
    >>> deserialize(json_dump)
    

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


Python Versions
---------------

- Python 3.8+ (tested)
- May work with other versions (untested)


Contributors
============

- Robert Niederreiter
- Florian Friesdorf
- Jens Klein

Owner

  • Name: Conestack
  • Login: conestack
  • Kind: organization
  • Location: Europe

GitHub Events

Total
  • Push event: 2
  • Fork event: 1
Last Year
  • Push event: 2
  • Fork event: 1

Committers

Last synced: about 1 year ago

All Time
  • Total Commits: 577
  • Total Committers: 6
  • Avg Commits per committer: 96.167
  • Development Distribution Score (DDS): 0.175
Past Year
  • Commits: 9
  • Committers: 1
  • Avg Commits per committer: 9.0
  • Development Distribution Score (DDS): 0.0
Top Committers
Name Email Commits
Robert Niederreiter o****e@s****t 476
Florian Friesdorf f****o@c****t 63
Jens W. Klein jk@k****t 29
Johannes Raggam r****l@a****t 5
Attila Olah a****h@g****m 3
Christoph Scheid c@s****e 1
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 12 months ago

All Time
  • Total issues: 8
  • Total pull requests: 16
  • Average time to close issues: over 1 year
  • Average time to close pull requests: 4 days
  • Total issue authors: 7
  • Total pull request authors: 8
  • Average comments per issue: 3.13
  • Average comments per pull request: 0.44
  • Merged pull requests: 9
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • attilaolah (2)
  • jensens (1)
  • jacobsvante (1)
  • kylefoley76 (1)
  • agitator (1)
  • 1letter (1)
  • domenkozar (1)
Pull Request Authors
  • rnixx (8)
  • ardyanrm9 (2)
  • Muhamad127 (2)
  • hannahaminz (2)
  • VIPERVIJAY (2)
  • attilaolah (2)
  • drzn18 (2)
  • jensens (1)
Top Labels
Issue Labels
bug (2)
Pull Request Labels
feature (1)

Packages

  • Total packages: 1
  • Total downloads:
    • pypi 20,346 last-month
  • Total docker downloads: 225
  • Total dependent packages: 536
  • Total dependent repositories: 211
  • Total versions: 37
  • Total maintainers: 3
pypi.org: node

Building data structures as node trees

  • Versions: 37
  • Dependent Packages: 536
  • Dependent Repositories: 211
  • Downloads: 20,346 Last month
  • Docker Downloads: 225
Rankings
Dependent packages count: 1.0%
Dependent repos count: 1.1%
Downloads: 2.9%
Docker downloads count: 3.6%
Average: 6.4%
Stargazers count: 14.5%
Forks count: 15.3%
Maintainers (3)
Last synced: 10 months ago

Dependencies

setup.py pypi
  • odict >=1.9.0
  • plumber >=1.5
  • setuptools *
  • zope.component *
  • zope.deferredimport *
  • zope.deprecation *
  • zope.lifecycleevent *
.github/workflows/test.yml actions
  • actions/checkout v4 composite
  • actions/setup-python v5 composite