Skip to content

Commit

Permalink
fix #206, performance issue - disable assertions by default
Browse files Browse the repository at this point in the history
  • Loading branch information
c0fec0de committed Oct 10, 2023
1 parent 510f543 commit d6198b9
Show file tree
Hide file tree
Showing 12 changed files with 62 additions and 19 deletions.
6 changes: 6 additions & 0 deletions anytree/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Central Configuration."""

import os

# Global Option which enables all internal assertions.
ASSERTIONS = bool(int(os.environ.get("ANYTREE_ASSERTIONS", 0)))
7 changes: 5 additions & 2 deletions anytree/importer/dictimporter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from anytree import AnyNode

from ..config import ASSERTIONS


class DictImporter:
"""
Expand Down Expand Up @@ -38,8 +40,9 @@ def import_(self, data):
return self.__import(data)

def __import(self, data, parent=None):
assert isinstance(data, dict)
assert "parent" not in data
if ASSERTIONS: # pragma: no branch
assert isinstance(data, dict)
assert "parent" not in data
attrs = dict(data)
children = attrs.pop("children", [])
node = self.nodecls(parent=parent, **attrs)
Expand Down
4 changes: 3 additions & 1 deletion anytree/iterators/zigzaggroupiter.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from ..config import ASSERTIONS
from .abstractiter import AbstractIter
from .levelordergroupiter import LevelOrderGroupIter

Expand Down Expand Up @@ -46,7 +47,8 @@ class ZigZagGroupIter(AbstractIter):
@staticmethod
def _iter(children, filter_, stop, maxlevel):
if children:
assert len(children) == 1
if ASSERTIONS: # pragma: no branch
assert len(children) == 1
_iter = LevelOrderGroupIter(children[0], filter_, stop, maxlevel)
while True:
try:
Expand Down
13 changes: 9 additions & 4 deletions anytree/node/nodemixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from anytree.iterators import PreOrderIter

from ..config import ASSERTIONS
from .exceptions import LoopError, TreeError


Expand Down Expand Up @@ -146,7 +147,8 @@ def __detach(self, parent):
if parent is not None:
self._pre_detach(parent)
parentchildren = parent.__children_or_empty
assert any(child is self for child in parentchildren), "Tree is corrupt." # pragma: no cover
if ASSERTIONS: # pragma: no branch
assert any(child is self for child in parentchildren), "Tree is corrupt." # pragma: no cover
# ATOMIC START
parent.__children = [child for child in parentchildren if child is not self]
self.__parent = None
Expand All @@ -158,7 +160,8 @@ def __attach(self, parent):
if parent is not None:
self._pre_attach(parent)
parentchildren = parent.__children_or_empty
assert not any(child is self for child in parentchildren), "Tree is corrupt." # pragma: no cover
if ASSERTIONS: # pragma: no branch
assert not any(child is self for child in parentchildren), "Tree is corrupt." # pragma: no cover
# ATOMIC START
parentchildren.append(self)
self.__parent = parent
Expand Down Expand Up @@ -249,7 +252,8 @@ def children(self, children):
for child in children:
child.parent = self
self._post_attach_children(children)
assert len(self.children) == len(children)
if ASSERTIONS: # pragma: no branch
assert len(self.children) == len(children)
except Exception:
self.children = old_children
raise
Expand All @@ -261,7 +265,8 @@ def children(self):
self._pre_detach_children(children)
for child in self.children:
child.parent = None
assert len(self.children) == 0
if ASSERTIONS: # pragma: no branch
assert len(self.children) == 0
self._post_detach_children(children)

def _pre_detach_children(self, children):
Expand Down
13 changes: 8 additions & 5 deletions anytree/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

import six

from .config import ASSERTIONS

Row = collections.namedtuple("Row", ("pre", "fill", "node"))


Expand All @@ -34,11 +36,12 @@ def __init__(self, vertical, cont, end):
self.vertical = vertical
self.cont = cont
self.end = end
assert len(cont) == len(vertical) == len(end), "'%s', '%s' and '%s' need to have equal length" % (
vertical,
cont,
end,
)
if ASSERTIONS: # pragma: no branch
assert len(cont) == len(vertical) == len(end), "'%s', '%s' and '%s' need to have equal length" % (
vertical,
cont,
end,
)

@property
def empty(self):
Expand Down
5 changes: 4 additions & 1 deletion anytree/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import re

from .config import ASSERTIONS

_MAXCACHE = 20


Expand Down Expand Up @@ -203,7 +205,8 @@ def __start(self, node, path, cmp_):
return node, parts

def __glob(self, node, parts):
assert node is not None
if ASSERTIONS: # pragma: no branch
assert node is not None
nodes = []
if parts:
name = parts[0]
Expand Down
5 changes: 4 additions & 1 deletion anytree/walker.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-

from .config import ASSERTIONS


class Walker:
"""Walk from one node to another."""
Expand Down Expand Up @@ -70,7 +72,8 @@ def walk(start, end):
raise WalkError(msg)
# common
common = Walker.__calc_common(startpath, endpath)
assert common[0] is start.root
if ASSERTIONS: # pragma: no branch
assert common[0] is start.root
len_common = len(common)
# upwards
if start is common[-1]:
Expand Down
1 change: 1 addition & 0 deletions docs/api/anytree.node.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Node Classes
.. automodule:: anytree.node.node

.. automodule:: anytree.node.nodemixin
:private-members:

.. automodule:: anytree.node.symlinknode

Expand Down
8 changes: 4 additions & 4 deletions docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ Detach/Attach Protocol
----------------------

A node class implementation might implement the notification slots
:any:`_pre_detach(parent)`, :any:`_post_detach(parent)`,
:any:`_pre_attach(parent)`, :any:`_post_attach(parent)`.
``_pre_detach(parent)``, ``_post_detach(parent)``,
``_pre_attach(parent)``, ``_post_attach(parent)``.

These methods are *protected* methods,
intended to be overwritten by child classes of :any:`NodeMixin`/:any:`Node`.
Expand Down Expand Up @@ -163,9 +163,9 @@ _post_detach NotifiedNode('/b')


.. important::
An exception raised by :any:`_pre_detach(parent)` and :any:`_pre_attach(parent)` will **prevent** the tree structure to be updated.
An exception raised by ``_pre_detach(parent)`` and ``_pre_attach(parent)`` will **prevent** the tree structure to be updated.
The node keeps the old state.
An exception raised by :any:`_post_detach(parent)` and :any:`_post_attach(parent)` does **not rollback** the tree structure modification.
An exception raised by ``_post_detach(parent)`` and ``_post_attach(parent)`` does **not rollback** the tree structure modification.


Custom Separator
Expand Down
1 change: 1 addition & 0 deletions docs/tricks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Tricks
tricks/yaml
tricks/multidim
tricks/weightededges
tricks/consistencychecks
13 changes: 13 additions & 0 deletions docs/tricks/consistencychecks.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Consistency Checks vs. Speed
============================

Anytree can run some *costly* internal consistency checks.
With version 2.9.2 these got disabled by default.
In case of any concerns about the internal data consistency or just for safety, either

* set the environment variable ``ANYTREE_ASSERTIONS=1``, or
* add the following lines to your code:

>>> import anytree
>>> anytree.config.ASSERTIONS = True

5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ line_length = 120
exclude_lines = [
'return NotImplemented',
'raise NotImplementedError()',
'pragma: no cover'
'pragma: no cover',
]


Expand Down Expand Up @@ -102,6 +102,9 @@ basepython = python3
[testenv]
allowlist_externals = *
setenv =
ANYTREE_ASSERTIONS=1
commands =
poetry install --with=test --with=doc
poetry run black .
Expand Down

0 comments on commit d6198b9

Please sign in to comment.