Package relmgr

Lightweight Object Database Class - API.

Expand source code
# __init__.py

"""
Lightweight Object Database Class - API.
"""

from relmgr.relationship_manager import RelationshipManager

# Version of relationship-manager package
__version__ = "2.0.2"

__all__ = ["RelationshipManager"]  # support "from relmgr import *"" syntax

Sub-modules

relmgr.relationship_manager

Relationship Manager …

Classes

class RelationshipManager (caching: bool = True)

This is the Relationship Manager class to instantiate and use in your projects.

Constructor. Set the option caching if you want faster performance using Python lru_cache technology - defaults to True.

Expand source code
class RelationshipManager():
    """This is the Relationship Manager class to instantiate and use in your projects."""

    def __init__(self, caching: bool = True) -> None:
        """Constructor.  Set the option `caching` if you want
        faster performance using Python `lru_cache` technology 
        - defaults to True.
        """
        if caching:
            self.rm = _RelationshipManagerCaching()
        else:
            self.rm = _EnforcingRelationshipManager()

        self.objects = _Namespace()
        """Optional place for storing objects involved in relationships, so the objects are saved.
        Assign to this `.objects` namespace directly to record your objects
        for persistence puposes.
        """

    def _get_relationships(self) -> List[Tuple[object, object, Union[int, str]]]:
        """Getter"""
        return self.rm._get_relationships()

    def _set_relationships(self, listofrelationshiptuples: List[Tuple[object, object, Union[int, str]]]) -> None:
        self.rm._set_relationships(listofrelationshiptuples)
        """Setter"""

    relationships = property(_get_relationships, _set_relationships)
    """Property to get flat list of relationships tuples"""

    def add_rel(self, source, target, rel_id=1) -> None:
        """Add relationships between `source` and `target` under the optional
        relationship id `rel_id`. The `source` and `target` are typically Python
        objects but can be strings. The `rel_id` is a string or integer and
        defaults to 1. Note that `rel_id` need not be specified unless you want
        to model multiple different relationships between the same objects, thus
        keeping relationships in different 'namespaces' as it were.
        """
        self.rm.add_rel(source, target, rel_id)

    def remove_rel(self, source, target, rel_id=1) -> None:
        """Remove all relationships between `source` and `target` of type `rel_id`.
        If you specify `None` for any parameter a wildcard match removal will occur.
        For example:

        Syntax    | Meaning
        --------|------
        `remove_rel('a', 'b')`     | remove all relationships between 'a' and 'b'
        `remove_rel('a', 'b', None)`     | remove all relationships between 'a' and 'b'
        `remove_rel('a', 'b', 'r1')`     | remove the 'r1' relationship between 'a' and 'b'
        `remove_rel('a', None)`     | remove all pointers (relationships) from 'a'
        `remove_rel(None, 'b')`     | remove any pointers (relationships) to 'b'
        """
        self.rm.remove_rel(source, target, rel_id)

    def find_targets(self, source, rel_id=1) -> List:
        """Find all objects pointed to by me - all the things 'source' is pointing at."""
        return self.rm._find_objects(source, None, rel_id)

    def find_target(self, source, rel_id=1) -> object:
        """Find first object pointed to by me - first target"""
        return self.rm._find_object(source, None, rel_id)

    def find_sources(self, target, rel_id=1) -> List:
        """Find all objects pointing to me. A 'back pointer' query."""
        return self.rm._find_objects(None, target, rel_id)

    def find_source(self, target, rel_id=1) -> object:
        """Find first object pointing to me - first source. A 'back pointer' query."""
        return self.rm._find_object(None, target, rel_id)

    def is_rel(self, source, target, rel_id=1) -> bool:
        """Returns T/F if relationship exists."""
        return self.rm._find_objects(source, target, rel_id)

    def find_rels(self, source, target) -> List:
        """Returns a list of the relationships between source and target.
        Returns a list of relationship ids.
        """
        return self.rm._find_objects(source, target, None)

    def enforce(self, rel_id, cardinality, directionality="directional"):
        """Enforce a relationship by auto creating reciprocal relationships (in the case of 
        'bidirectional' relationships), and by overwriting existing relationships in the case
        of 'onetoone' cardinality.

        `rel_id`: the name of the relationship - either an integer or string.

        `cardinality`:

        - `"onetoone"` - extinguish both old 'source' and 'target' before adding a new relationship
        - `"onetomany"` - extinguish old 'source' before adding a new relationship
        - `"manytomany"` (not implemented)

        `directionality`:
        
        - `"directional"` - the default, no special enforcement
        - `"bidirectional"` - when calling `RelationshipManager.add_rel(source, target)`
        causes not only the primary relationship to be created between 'source' and 'target',
        but also auto creates an additional relationship in the reverse direction between 'target' and 'source'.
        Also ensures both relationships are removed when calling `RelationshipManager.remove_rel`.
        """
        self.rm.enforce(rel_id, cardinality, directionality)

    def dumps(self) -> bytes:
        """Dump relationship tuples and objects to pickled bytes.
        The `objects` attribute and all objects stored therein
        (within the instance of `RelationshipManager.objects`) also get persisted.
        """
        return pickle.dumps(_PersistenceWrapper(
            objects=self.objects, relationships=self.relationships))

    @staticmethod
    def loads(asbytes: bytes):  # -> RelationshipManager:
        """Load relationship tuples and objects from pickled bytes. 
        Returns a `RelationshipManager` instance.
        """
        data: _PersistenceWrapper = pickle.loads(asbytes)
        rm = RelationshipManager()
        rm.objects = data.objects
        rm.relationships = data.relationships
        return rm

    def clear(self) -> None:
        """Clear all relationships, does not affect .objects - if you want to clear that too then
        assign a new empty object to it.  E.g. rm.objects = Namespace()
        """
        self.rm.clear()
        self.objects = _Namespace()

    # Util

    def debug_print_rels(self):
        """Just a diagnostic method to print the relationships in the rm.
        See also the `RelationshipManager.relationships` property."""
        print()
        pprint.pprint(self.relationships)

Static methods

def loads(asbytes: bytes)

Load relationship tuples and objects from pickled bytes. Returns a RelationshipManager instance.

Expand source code
@staticmethod
def loads(asbytes: bytes):  # -> RelationshipManager:
    """Load relationship tuples and objects from pickled bytes. 
    Returns a `RelationshipManager` instance.
    """
    data: _PersistenceWrapper = pickle.loads(asbytes)
    rm = RelationshipManager()
    rm.objects = data.objects
    rm.relationships = data.relationships
    return rm

Instance variables

var objects

Optional place for storing objects involved in relationships, so the objects are saved. Assign to this .objects namespace directly to record your objects for persistence puposes.

var relationships : List[Tuple[object, object, Union[int, str]]]

Property to get flat list of relationships tuples

Expand source code
def _get_relationships(self) -> List[Tuple[object, object, Union[int, str]]]:
    """Getter"""
    return self.rm._get_relationships()

Methods

def add_rel(self, source, target, rel_id=1) ‑> NoneType

Add relationships between source and target under the optional relationship id rel_id. The source and target are typically Python objects but can be strings. The rel_id is a string or integer and defaults to 1. Note that rel_id need not be specified unless you want to model multiple different relationships between the same objects, thus keeping relationships in different 'namespaces' as it were.

Expand source code
def add_rel(self, source, target, rel_id=1) -> None:
    """Add relationships between `source` and `target` under the optional
    relationship id `rel_id`. The `source` and `target` are typically Python
    objects but can be strings. The `rel_id` is a string or integer and
    defaults to 1. Note that `rel_id` need not be specified unless you want
    to model multiple different relationships between the same objects, thus
    keeping relationships in different 'namespaces' as it were.
    """
    self.rm.add_rel(source, target, rel_id)
def clear(self) ‑> NoneType

Clear all relationships, does not affect .objects - if you want to clear that too then assign a new empty object to it. E.g. rm.objects = Namespace()

Expand source code
def clear(self) -> None:
    """Clear all relationships, does not affect .objects - if you want to clear that too then
    assign a new empty object to it.  E.g. rm.objects = Namespace()
    """
    self.rm.clear()
    self.objects = _Namespace()
def debug_print_rels(self)

Just a diagnostic method to print the relationships in the rm. See also the RelationshipManager.relationships property.

Expand source code
def debug_print_rels(self):
    """Just a diagnostic method to print the relationships in the rm.
    See also the `RelationshipManager.relationships` property."""
    print()
    pprint.pprint(self.relationships)
def dumps(self) ‑> bytes

Dump relationship tuples and objects to pickled bytes. The objects attribute and all objects stored therein (within the instance of RelationshipManager.objects) also get persisted.

Expand source code
def dumps(self) -> bytes:
    """Dump relationship tuples and objects to pickled bytes.
    The `objects` attribute and all objects stored therein
    (within the instance of `RelationshipManager.objects`) also get persisted.
    """
    return pickle.dumps(_PersistenceWrapper(
        objects=self.objects, relationships=self.relationships))
def enforce(self, rel_id, cardinality, directionality='directional')

Enforce a relationship by auto creating reciprocal relationships (in the case of 'bidirectional' relationships), and by overwriting existing relationships in the case of 'onetoone' cardinality.

rel_id: the name of the relationship - either an integer or string.

cardinality:

  • "onetoone" - extinguish both old 'source' and 'target' before adding a new relationship
  • "onetomany" - extinguish old 'source' before adding a new relationship
  • "manytomany" (not implemented)

directionality:

  • "directional" - the default, no special enforcement
  • "bidirectional" - when calling RelationshipManager.add_rel()(source, target) causes not only the primary relationship to be created between 'source' and 'target', but also auto creates an additional relationship in the reverse direction between 'target' and 'source'. Also ensures both relationships are removed when calling RelationshipManager.remove_rel().
Expand source code
def enforce(self, rel_id, cardinality, directionality="directional"):
    """Enforce a relationship by auto creating reciprocal relationships (in the case of 
    'bidirectional' relationships), and by overwriting existing relationships in the case
    of 'onetoone' cardinality.

    `rel_id`: the name of the relationship - either an integer or string.

    `cardinality`:

    - `"onetoone"` - extinguish both old 'source' and 'target' before adding a new relationship
    - `"onetomany"` - extinguish old 'source' before adding a new relationship
    - `"manytomany"` (not implemented)

    `directionality`:
    
    - `"directional"` - the default, no special enforcement
    - `"bidirectional"` - when calling `RelationshipManager.add_rel(source, target)`
    causes not only the primary relationship to be created between 'source' and 'target',
    but also auto creates an additional relationship in the reverse direction between 'target' and 'source'.
    Also ensures both relationships are removed when calling `RelationshipManager.remove_rel`.
    """
    self.rm.enforce(rel_id, cardinality, directionality)
def find_rels(self, source, target) ‑> List

Returns a list of the relationships between source and target. Returns a list of relationship ids.

Expand source code
def find_rels(self, source, target) -> List:
    """Returns a list of the relationships between source and target.
    Returns a list of relationship ids.
    """
    return self.rm._find_objects(source, target, None)
def find_source(self, target, rel_id=1) ‑> object

Find first object pointing to me - first source. A 'back pointer' query.

Expand source code
def find_source(self, target, rel_id=1) -> object:
    """Find first object pointing to me - first source. A 'back pointer' query."""
    return self.rm._find_object(None, target, rel_id)
def find_sources(self, target, rel_id=1) ‑> List

Find all objects pointing to me. A 'back pointer' query.

Expand source code
def find_sources(self, target, rel_id=1) -> List:
    """Find all objects pointing to me. A 'back pointer' query."""
    return self.rm._find_objects(None, target, rel_id)
def find_target(self, source, rel_id=1) ‑> object

Find first object pointed to by me - first target

Expand source code
def find_target(self, source, rel_id=1) -> object:
    """Find first object pointed to by me - first target"""
    return self.rm._find_object(source, None, rel_id)
def find_targets(self, source, rel_id=1) ‑> List

Find all objects pointed to by me - all the things 'source' is pointing at.

Expand source code
def find_targets(self, source, rel_id=1) -> List:
    """Find all objects pointed to by me - all the things 'source' is pointing at."""
    return self.rm._find_objects(source, None, rel_id)
def is_rel(self, source, target, rel_id=1) ‑> bool

Returns T/F if relationship exists.

Expand source code
def is_rel(self, source, target, rel_id=1) -> bool:
    """Returns T/F if relationship exists."""
    return self.rm._find_objects(source, target, rel_id)
def remove_rel(self, source, target, rel_id=1) ‑> NoneType

Remove all relationships between source and target of type rel_id. If you specify None for any parameter a wildcard match removal will occur. For example:

Syntax Meaning
remove_rel('a', 'b') remove all relationships between 'a' and 'b'
remove_rel('a', 'b', None) remove all relationships between 'a' and 'b'
remove_rel('a', 'b', 'r1') remove the 'r1' relationship between 'a' and 'b'
remove_rel('a', None) remove all pointers (relationships) from 'a'
remove_rel(None, 'b') remove any pointers (relationships) to 'b'
Expand source code
def remove_rel(self, source, target, rel_id=1) -> None:
    """Remove all relationships between `source` and `target` of type `rel_id`.
    If you specify `None` for any parameter a wildcard match removal will occur.
    For example:

    Syntax    | Meaning
    --------|------
    `remove_rel('a', 'b')`     | remove all relationships between 'a' and 'b'
    `remove_rel('a', 'b', None)`     | remove all relationships between 'a' and 'b'
    `remove_rel('a', 'b', 'r1')`     | remove the 'r1' relationship between 'a' and 'b'
    `remove_rel('a', None)`     | remove all pointers (relationships) from 'a'
    `remove_rel(None, 'b')`     | remove any pointers (relationships) to 'b'
    """
    self.rm.remove_rel(source, target, rel_id)