ripple-down-rules 0.6.19__py3-none-any.whl → 0.6.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- __version__ = "0.6.19"
1
+ __version__ = "0.6.20"
2
2
 
3
3
  import logging
4
4
  logger = logging.Logger("rdr")
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import builtins
4
+ import codecs
4
5
  import copyreg
5
6
  import importlib
6
7
  import json
@@ -16,9 +17,12 @@ from dataclasses import is_dataclass, fields
16
17
  from enum import Enum
17
18
  from os.path import dirname
18
19
  from pathlib import Path
20
+ from subprocess import check_call
21
+ from tempfile import NamedTemporaryFile
19
22
  from textwrap import dedent
20
23
  from types import NoneType
21
24
 
25
+ import six
22
26
  from sqlalchemy.exc import NoInspectionAvailable
23
27
 
24
28
  try:
@@ -1629,12 +1633,204 @@ def edge_attr_setter(parent, child):
1629
1633
  return ""
1630
1634
 
1631
1635
 
1632
- class FilteredDotExporter(DotExporter):
1633
- def __init__(self, root, include_nodes=None, **kwargs):
1636
+ _RE_ESC = re.compile(r'["\\]')
1637
+ class FilteredDotExporter(object):
1638
+
1639
+ def __init__(self, node, include_nodes=None, graph="digraph", name="tree", options=None,
1640
+ indent=4, nodenamefunc=None, nodeattrfunc=None,
1641
+ edgeattrfunc=None, edgetypefunc=None, maxlevel=None):
1642
+ """
1643
+ Dot Language Exporter.
1644
+
1645
+ Args:
1646
+ node (Node): start node.
1647
+
1648
+ Keyword Args:
1649
+ graph: DOT graph type.
1650
+
1651
+ name: DOT graph name.
1652
+
1653
+ options: list of options added to the graph.
1654
+
1655
+ indent (int): number of spaces for indent.
1656
+
1657
+ nodenamefunc: Function to extract node name from `node` object.
1658
+ The function shall accept one `node` object as
1659
+ argument and return the name of it.
1660
+
1661
+ nodeattrfunc: Function to decorate a node with attributes.
1662
+ The function shall accept one `node` object as
1663
+ argument and return the attributes.
1664
+
1665
+ edgeattrfunc: Function to decorate a edge with attributes.
1666
+ The function shall accept two `node` objects as
1667
+ argument. The first the node and the second the child
1668
+ and return the attributes.
1669
+
1670
+ edgetypefunc: Function to which gives the edge type.
1671
+ The function shall accept two `node` objects as
1672
+ argument. The first the node and the second the child
1673
+ and return the edge (i.e. '->').
1674
+
1675
+ maxlevel (int): Limit export to this number of levels.
1676
+
1677
+ >>> from anytree import Node
1678
+ >>> root = Node("root")
1679
+ >>> s0 = Node("sub0", parent=root, edge=2)
1680
+ >>> s0b = Node("sub0B", parent=s0, foo=4, edge=109)
1681
+ >>> s0a = Node("sub0A", parent=s0, edge="")
1682
+ >>> s1 = Node("sub1", parent=root, edge="")
1683
+ >>> s1a = Node("sub1A", parent=s1, edge=7)
1684
+ >>> s1b = Node("sub1B", parent=s1, edge=8)
1685
+ >>> s1c = Node("sub1C", parent=s1, edge=22)
1686
+ >>> s1ca = Node("sub1Ca", parent=s1c, edge=42)
1687
+
1688
+ .. note:: If the node names are not unqiue, see :any:`UniqueDotExporter`.
1689
+
1690
+ A directed graph:
1691
+
1692
+ >>> from anytree.exporter import DotExporter
1693
+ >>> for line in DotExporter(root):
1694
+ ... print(line)
1695
+ digraph tree {
1696
+ "root";
1697
+ "sub0";
1698
+ "sub0B";
1699
+ "sub0A";
1700
+ "sub1";
1701
+ "sub1A";
1702
+ "sub1B";
1703
+ "sub1C";
1704
+ "sub1Ca";
1705
+ "root" -> "sub0";
1706
+ "root" -> "sub1";
1707
+ "sub0" -> "sub0B";
1708
+ "sub0" -> "sub0A";
1709
+ "sub1" -> "sub1A";
1710
+ "sub1" -> "sub1B";
1711
+ "sub1" -> "sub1C";
1712
+ "sub1C" -> "sub1Ca";
1713
+ }
1714
+
1715
+ The resulting graph:
1716
+
1717
+ .. image:: ../static/dotexporter0.png
1718
+
1719
+ An undirected graph:
1720
+
1721
+ >>> def nodenamefunc(node):
1722
+ ... return '%s:%s' % (node.name, node.depth)
1723
+ >>> def edgeattrfunc(node, child):
1724
+ ... return 'label="%s:%s"' % (node.name, child.name)
1725
+ >>> def edgetypefunc(node, child):
1726
+ ... return '--'
1727
+ >>> from anytree.exporter import DotExporter
1728
+ >>> for line in DotExporter(root, graph="graph",
1729
+ ... nodenamefunc=nodenamefunc,
1730
+ ... nodeattrfunc=lambda node: "shape=box",
1731
+ ... edgeattrfunc=edgeattrfunc,
1732
+ ... edgetypefunc=edgetypefunc):
1733
+ ... print(line)
1734
+ graph tree {
1735
+ "root:0" [shape=box];
1736
+ "sub0:1" [shape=box];
1737
+ "sub0B:2" [shape=box];
1738
+ "sub0A:2" [shape=box];
1739
+ "sub1:1" [shape=box];
1740
+ "sub1A:2" [shape=box];
1741
+ "sub1B:2" [shape=box];
1742
+ "sub1C:2" [shape=box];
1743
+ "sub1Ca:3" [shape=box];
1744
+ "root:0" -- "sub0:1" [label="root:sub0"];
1745
+ "root:0" -- "sub1:1" [label="root:sub1"];
1746
+ "sub0:1" -- "sub0B:2" [label="sub0:sub0B"];
1747
+ "sub0:1" -- "sub0A:2" [label="sub0:sub0A"];
1748
+ "sub1:1" -- "sub1A:2" [label="sub1:sub1A"];
1749
+ "sub1:1" -- "sub1B:2" [label="sub1:sub1B"];
1750
+ "sub1:1" -- "sub1C:2" [label="sub1:sub1C"];
1751
+ "sub1C:2" -- "sub1Ca:3" [label="sub1C:sub1Ca"];
1752
+ }
1753
+
1754
+ The resulting graph:
1755
+
1756
+ .. image:: ../static/dotexporter1.png
1757
+
1758
+ To export custom node implementations or :any:`AnyNode`, please provide a proper `nodenamefunc`:
1759
+
1760
+ >>> from anytree import AnyNode
1761
+ >>> root = AnyNode(id="root")
1762
+ >>> s0 = AnyNode(id="sub0", parent=root)
1763
+ >>> s0b = AnyNode(id="s0b", parent=s0)
1764
+ >>> s0a = AnyNode(id="s0a", parent=s0)
1765
+
1766
+ >>> from anytree.exporter import DotExporter
1767
+ >>> for line in DotExporter(root, nodenamefunc=lambda n: n.id):
1768
+ ... print(line)
1769
+ digraph tree {
1770
+ "root";
1771
+ "sub0";
1772
+ "s0b";
1773
+ "s0a";
1774
+ "root" -> "sub0";
1775
+ "sub0" -> "s0b";
1776
+ "sub0" -> "s0a";
1777
+ }
1778
+ """
1779
+ self.node = node
1780
+ self.graph = graph
1781
+ self.name = name
1782
+ self.options = options
1783
+ self.indent = indent
1784
+ self.nodenamefunc = nodenamefunc
1785
+ self.nodeattrfunc = nodeattrfunc
1786
+ self.edgeattrfunc = edgeattrfunc
1787
+ self.edgetypefunc = edgetypefunc
1788
+ self.maxlevel = maxlevel
1634
1789
  self.include_nodes = include_nodes
1635
- node_name_func = get_unique_node_names_func(root)
1790
+ node_name_func = get_unique_node_names_func(node)
1636
1791
  self.include_node_names = [node_name_func(n) for n in self.include_nodes] if include_nodes else None
1637
- super().__init__(root, **kwargs)
1792
+
1793
+ def __iter__(self):
1794
+ # prepare
1795
+ indent = " " * self.indent
1796
+ nodenamefunc = self.nodenamefunc or self._default_nodenamefunc
1797
+ nodeattrfunc = self.nodeattrfunc or self._default_nodeattrfunc
1798
+ edgeattrfunc = self.edgeattrfunc or self._default_edgeattrfunc
1799
+ edgetypefunc = self.edgetypefunc or self._default_edgetypefunc
1800
+ return self.__iter(indent, nodenamefunc, nodeattrfunc, edgeattrfunc,
1801
+ edgetypefunc)
1802
+
1803
+ @staticmethod
1804
+ def _default_nodenamefunc(node):
1805
+ return node.name
1806
+
1807
+ @staticmethod
1808
+ def _default_nodeattrfunc(node):
1809
+ return None
1810
+
1811
+ @staticmethod
1812
+ def _default_edgeattrfunc(node, child):
1813
+ return None
1814
+
1815
+ @staticmethod
1816
+ def _default_edgetypefunc(node, child):
1817
+ return "->"
1818
+
1819
+ def __iter(self, indent, nodenamefunc, nodeattrfunc, edgeattrfunc, edgetypefunc):
1820
+ yield "{self.graph} {self.name} {{".format(self=self)
1821
+ for option in self.__iter_options(indent):
1822
+ yield option
1823
+ for node in self.__iter_nodes(indent, nodenamefunc, nodeattrfunc):
1824
+ yield node
1825
+ for edge in self.__iter_edges(indent, nodenamefunc, edgeattrfunc, edgetypefunc):
1826
+ yield edge
1827
+ yield "}"
1828
+
1829
+ def __iter_options(self, indent):
1830
+ options = self.options
1831
+ if options:
1832
+ for option in options:
1833
+ yield "%s%s" % (indent, option)
1638
1834
 
1639
1835
  def __iter_nodes(self, indent, nodenamefunc, nodeattrfunc):
1640
1836
  for node in PreOrderIter(self.node, maxlevel=self.maxlevel):
@@ -1653,12 +1849,68 @@ class FilteredDotExporter(DotExporter):
1653
1849
  continue
1654
1850
  for child in node.children:
1655
1851
  childname = nodenamefunc(child)
1852
+ if self.include_nodes is not None and childname not in self.include_node_names:
1853
+ continue
1656
1854
  edgeattr = edgeattrfunc(node, child)
1657
1855
  edgetype = edgetypefunc(node, child)
1658
1856
  edgeattr = " [%s]" % edgeattr if edgeattr is not None else ""
1659
1857
  yield '%s"%s" %s "%s"%s;' % (indent, DotExporter.esc(nodename), edgetype,
1660
1858
  DotExporter.esc(childname), edgeattr)
1661
1859
 
1860
+ def to_dotfile(self, filename):
1861
+ """
1862
+ Write graph to `filename`.
1863
+
1864
+ >>> from anytree import Node
1865
+ >>> root = Node("root")
1866
+ >>> s0 = Node("sub0", parent=root)
1867
+ >>> s0b = Node("sub0B", parent=s0)
1868
+ >>> s0a = Node("sub0A", parent=s0)
1869
+ >>> s1 = Node("sub1", parent=root)
1870
+ >>> s1a = Node("sub1A", parent=s1)
1871
+ >>> s1b = Node("sub1B", parent=s1)
1872
+ >>> s1c = Node("sub1C", parent=s1)
1873
+ >>> s1ca = Node("sub1Ca", parent=s1c)
1874
+
1875
+ >>> from anytree.exporter import DotExporter
1876
+ >>> DotExporter(root).to_dotfile("tree.dot")
1877
+
1878
+ The generated file should be handed over to the `dot` tool from the
1879
+ http://www.graphviz.org/ package::
1880
+
1881
+ $ dot tree.dot -T png -o tree.png
1882
+ """
1883
+ with codecs.open(filename, "w", "utf-8") as file:
1884
+ for line in self:
1885
+ file.write("%s\n" % line)
1886
+
1887
+ def to_picture(self, filename):
1888
+ """
1889
+ Write graph to a temporary file and invoke `dot`.
1890
+
1891
+ The output file type is automatically detected from the file suffix.
1892
+
1893
+ *`graphviz` needs to be installed, before usage of this method.*
1894
+ """
1895
+ fileformat = os.path.splitext(filename)[1][1:]
1896
+ with NamedTemporaryFile("wb", delete=False) as dotfile:
1897
+ dotfilename = dotfile.name
1898
+ for line in self:
1899
+ dotfile.write(("%s\n" % line).encode("utf-8"))
1900
+ dotfile.flush()
1901
+ cmd = ["dot", dotfilename, "-T", fileformat, "-o", filename]
1902
+ check_call(cmd)
1903
+ try:
1904
+ os.remove(dotfilename)
1905
+ except Exception: # pragma: no cover
1906
+ msg = 'Could not remove temporary file %s' % dotfilename
1907
+ logging.getLogger(__name__).warn(msg)
1908
+
1909
+ @staticmethod
1910
+ def esc(value):
1911
+ """Escape Strings."""
1912
+ return _RE_ESC.sub(lambda m: r"\%s" % m.group(0), six.text_type(value))
1913
+
1662
1914
 
1663
1915
  def render_tree(root: Node, use_dot_exporter: bool = False,
1664
1916
  filename: str = "scrdr", only_nodes: List[Node] = None):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.6.19
3
+ Version: 0.6.20
4
4
  Summary: Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning.
5
5
  Author-email: Abdelrhman Bassiouny <abassiou@uni-bremen.de>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1,11 +1,11 @@
1
- ripple_down_rules/__init__.py,sha256=SI5OK8zlaHJ4LnHNQ1hp0L91Xem5KQsF_6uS7NrGzVU,100
1
+ ripple_down_rules/__init__.py,sha256=xpB0ANrfA5A060d9mDqT8Wy9YWsSGJIlfSH6X5JBu4E,100
2
2
  ripple_down_rules/experts.py,sha256=4-dMIVeMzFXCLYl_XBG_P7_Xs4sZih9-vZxCIPri6dA,12958
3
3
  ripple_down_rules/helpers.py,sha256=RUdfiSWMZjGwCxuCy44TcEJf2UNAFlPJusgHzuAs6qI,4583
4
4
  ripple_down_rules/rdr.py,sha256=mtY_kBOjUXRZYz4QFaKN6C2O7liFpbMdnjUnFFnXbdU,57550
5
5
  ripple_down_rules/rdr_decorators.py,sha256=fxoMMAZB1HTfEsuzfIdo3b7PiPB_-AAjD8hy4L0sawQ,11389
6
6
  ripple_down_rules/rules.py,sha256=ULIHRbVfKd2L3zzPnssmaeqInWHifPvwwa16MyzakPc,23548
7
7
  ripple_down_rules/start-code-server.sh,sha256=otClk7VmDgBOX2TS_cjws6K0UwvgAUJhoA0ugkPCLqQ,949
8
- ripple_down_rules/utils.py,sha256=uOa_b3FoDnoTLp84RnMbeSyiTMgREtUOkOImQj8B5HQ,64204
8
+ ripple_down_rules/utils.py,sha256=mhvwBEyGhwAYocBYjAlmFh192pEvYrx-KObnMa1aSnk,73189
9
9
  ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
10
10
  ripple_down_rules/datastructures/callable_expression.py,sha256=ysK-4JmZ4oSUTJC7zpo_o77g4ONxPDEcIpSWggsnx3c,13320
11
11
  ripple_down_rules/datastructures/case.py,sha256=PJ7_-AdxYic6BO5z816piFODj6nU5J6Jt1YzTFH-dds,15510
@@ -17,8 +17,8 @@ ripple_down_rules/user_interface/ipython_custom_shell.py,sha256=yp-F8YRWGhj1PLB3
17
17
  ripple_down_rules/user_interface/object_diagram.py,sha256=FEa2HaYR9QmTE6NsOwBvZ0jqmu3DKyg6mig2VE5ZP4Y,4956
18
18
  ripple_down_rules/user_interface/prompt.py,sha256=JceEUGYsd0lIvd-v2y3D3swoo96_C0lxfp3CxM7Vfts,8900
19
19
  ripple_down_rules/user_interface/template_file_creator.py,sha256=kwBbFLyN6Yx2NTIHPSwOoytWgbJDYhgrUOVFw_jkDQ4,13522
20
- ripple_down_rules-0.6.19.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
- ripple_down_rules-0.6.19.dist-info/METADATA,sha256=rei_pvqow5hMMQOqBDAq-CxM9HTXXrxZ5f-knn-5zms,48294
22
- ripple_down_rules-0.6.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- ripple_down_rules-0.6.19.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
- ripple_down_rules-0.6.19.dist-info/RECORD,,
20
+ ripple_down_rules-0.6.20.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
+ ripple_down_rules-0.6.20.dist-info/METADATA,sha256=XE3l7CrRsjXZLfW5LpHoYTJbIVT9rOHKr9S3qWVqGyA,48294
22
+ ripple_down_rules-0.6.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ ripple_down_rules-0.6.20.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
+ ripple_down_rules-0.6.20.dist-info/RECORD,,