File: _observer_graph.py

package info (click to toggle)
python-traits 6.4.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 8,648 kB
  • sloc: python: 34,801; ansic: 4,266; makefile: 102
file content (94 lines) | stat: -rw-r--r-- 3,708 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# (C) Copyright 2005-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!


class ObserverGraph:
    """ An ``ObserverGraph`` is an object for describing what traits are being
    observed on an instance of ``HasTraits``.

    The most basic unit in a graph is a node, which is a context specific
    observer. For example, a node can be an observer specialized in
    changes on a named trait, an observer specialized in
    changes on a number of traits matching a certain criteria, an observer
    specialized in mutations on a list, etc.

    The most basic example is an ``ObserverGraph`` that contains only one node,
    e.g. for observing changes on a named trait.

    An ``ObserverGraph`` can have branches, e.g. to observe more than one trait
    on a nested object. The attribute ``children`` represents these branches.
    Each item in ``children`` is another ``ObserverGraph``.

    In order to (1) avoid hooking up a user callback with the same observer
    twice, and (2) remove an observer when they are not needed, once an
    ``ObserverGraph`` object is constructed (e.g. after mutating ``children``
    for constructing branches) and is ready to be used against an instance
    of ``HasTraits``, it should not be mutated again.

    For the same reason, ``ObserverGraph`` implements ``__hash__`` and
    ``__eq__`` and requires its nodes to also support these methods.

    An ``ObserverGraph`` does not keep states regarding the HasTraits instances
    and the user callbacks it was used with. An ``ObserverGraph`` can be
    reused multiple times on different ``HasTraits`` instance and with
    different user callback.

    This object is considered a low-level object for the observer mechanism.
    It is not intended to be instantiated by users directly. Users will be
    given higher-level wrappers for creating ``ObserverGraph`` objects.

    Parameters
    ----------
    node : any
        A context specific observer.
        It must be a hashable object. In practice, this will be
        an instance that implements ``IObserver``.
    children : iterable of ObserverGraph, optional
        Branches on this graph. All children must be unique.

    Raises
    ------
    ValueError
        If not all children are unique.
    """

    __slots__ = ("node", "children")

    def __init__(self, *, node, children=None):

        if children is not None and len(set(children)) != len(children):
            raise ValueError("Not all children are unique.")

        self.node = node
        self.children = list(children) if children is not None else []

    def __hash__(self):
        """ Return the hash of this ObserverGraph."""
        return hash(
            (type(self).__name__, self.node, frozenset(self.children))
        )

    def __eq__(self, other):
        """ Return true if another object is an ObserverGraph with the
        same content. The order of children is not taken into account
        in the comparison.
        """
        return (
            type(self) is type(other)
            and self.node == other.node
            and set(self.children) == set(other.children)
        )

    def __repr__(self):
        formatted_args = [f"node={self.node!r}"]
        if self.children:
            formatted_args.append(f"children={self.children!r}")

        return f"{self.__class__.__name__}({', '.join(formatted_args)})"