Skip to content

Commit

Permalink
Merge branch '3.x.x' of github.com:c0fec0de/anytree
Browse files Browse the repository at this point in the history
  • Loading branch information
c0fec0de committed Oct 10, 2023
2 parents 64650e6 + 0dba4d4 commit f905602
Show file tree
Hide file tree
Showing 16 changed files with 166 additions and 25 deletions.
22 changes: 16 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
name: Python
name: test

on: [push]
on: [push, pull_request, release]

jobs:
test:
build:

strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
Expand All @@ -19,8 +20,6 @@ jobs:
python -m pip install --upgrade pip
sudo apt-get install -y graphviz
pip install tox "poetry>=1.4" coveralls
git --version
git submodule --help
- name: TOX
run: tox
- name: Upload coverage data to coveralls.io
Expand All @@ -32,7 +31,7 @@ jobs:

coveralls:
name: Indicate completion to coveralls.io
needs: test
needs: build
runs-on: ubuntu-latest
container: python:3-slim
steps:
Expand All @@ -42,3 +41,14 @@ jobs:
coveralls --service=github --finish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

publish:
if: github.event_name == 'push' && github.ref_type == 'tag'
needs: coveralls
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build and publish to pypi
uses: JRubics/[email protected]
with:
pypi_token: ${{ secrets.PYPI_TOKEN }}
18 changes: 18 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.9"

commands:
- pip install --upgrade --no-cache-dir pip
- pip install --no-cache-dir "poetry>=1.4" "crashtest==0.4.1"
- poetry install --with=doc --without=test
- poetry run make html -C docs
- cp -r docs/build _readthedocs
63 changes: 63 additions & 0 deletions CONTRIBUTE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Contribute

## Branches

* `2.x.x` main line for 2.x.x
* `3.x.x` actual main line
* documentation links refer to `3.x.x`
* `main`
* documentation links refer to `latest`

## Testing

### Create Environment

Run these commands just the first time:

```bash
# Ensure python3 is installed
python3 -m venv .venv
source .venv/bin/activate
pip install tox "poetry>=1.4" "crashtest==0.4.1"
```

### Enter Environment

Run this command once you open a new shell:

```bash
source .venv/bin/activate
```

### Test Your Changes

```bash
# test
tox
```

### Release

```bash
git pull

prev_version=$(poetry version -s)

# Version Bump
poetry version minor
# OR
poetry version patch

# Commit, Tag and Push
version=$(poetry version -s)

sed "s/$prev_version/$version/g" -i README.rst
sed "s/$prev_version/$version/g" -i docs/index.rst

git commit -m"version bump to ${version}" pyproject.toml README.rst docs/index.rst
git tag "${version}" -m "Release ${version}"
git push
git push --tags

# Publishing is handled by CI
```
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
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from anytree.iterators import PreOrderIter

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


Expand Down Expand Up @@ -143,7 +144,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 @@ -155,7 +157,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 @@ -246,7 +249,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 @@ -258,7 +262,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

7 changes: 7 additions & 0 deletions docs/tricks/readonly.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ a
│ └── a1a
└── a2

.. note::

It is important to use the ``_pre_*`` and **not** the ``_post_*`` methods.
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 `_post_detach(parent)` and `_post_attach(parent)` does **not rollback** the tree structure modification.


Temporary
---------
Expand Down
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 f905602

Please sign in to comment.