c0fec0de / anytree

Python tree data library
Apache License 2.0
955 stars 133 forks source link

2.12.1: pytest fails because missing files #258

Open kloczek opened 8 months ago

kloczek commented 8 months ago

I'm packaging your module as an rpm package so I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.

Looks like pytest fails because some files are missing in git repo.

Here is pytest output: ```console + PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-anytree-2.12.1-2.fc36.x86_64/usr/lib64/python3.9/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-anytree-2.12.1-2.fc36.x86_64/usr/lib/python3.9/site-packages + /usr/bin/pytest -ra -m 'not network' ==================================================================================== test session starts ==================================================================================== platform linux -- Python 3.9.18, pytest-8.1.1, pluggy-1.4.0 rootdir: /home/tkloczko/rpmbuild/BUILD/anytree-2.12.1 configfile: pyproject.toml collected 163 items tests/test_cachedsearch.py .... [ 2%] tests/test_dictexporter.py .... [ 4%] tests/test_dictimporter.py .. [ 6%] tests/test_dotexport.py FFF [ 7%] tests/test_dotexporter.py ...... [ 11%] tests/test_examples.py . [ 12%] tests/test_iterators.py ...... [ 15%] tests/test_jsonexporter.py . [ 16%] tests/test_jsonimporter.py . [ 17%] tests/test_lightnode.py ..................... [ 30%] tests/test_mermaidexporter.py ..... [ 33%] tests/test_node.py ................................ [ 52%] tests/test_node_attach_detach.py ... [ 54%] tests/test_node_integrity.py . [ 55%] tests/test_node_sep.py ... [ 57%] tests/test_node_symlink.py . [ 57%] tests/test_pickle.py . [ 58%] tests/test_render.py .......... [ 64%] tests/test_resolver.py .......... [ 70%] tests/test_search.py ..... [ 73%] tests/test_special_methods_access.py ................................. [ 93%] tests/test_uniquedotexporter.py ...... [ 97%] tests/test_util.py ... [ 99%] tests/test_walker.py . [100%] ========================================================================================= FAILURES ========================================================================================== ________________________________________________________________________________________ test_tree1 _________________________________________________________________________________________ @with_setup(setup, teardown) def test_tree1(): """Tree1.""" root = Node("root") s0 = Node("sub0", parent=root) Node("sub0B", parent=s0) Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) Node("sub1A", parent=s1) Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) Node(99, parent=s1c) > RenderTreeGraph(root).to_dotfile(join(GENPATH, "tree1.dot")) tests/test_dotexport.py:39: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ anytree/exporter/dotexporter.py:284: in to_dotfile with codecs.open(filename, "w", "utf-8") as file: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ filename = '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree1.dot', mode = 'wb', encoding = 'utf-8', errors = 'strict', buffering = -1 def open(filename, mode='r', encoding=None, errors='strict', buffering=-1): """ Open an encoded file using the given mode and return a wrapped version providing transparent encoding/decoding. Note: The wrapped version will only accept the object format defined by the codecs, i.e. Unicode objects for most builtin codecs. Output is also codec dependent and will usually be Unicode as well. Underlying encoded files are always opened in binary mode. The default file mode is 'r', meaning to open the file in read mode. encoding specifies the encoding which is to be used for the file. errors may be given to define the error handling. It defaults to 'strict' which causes ValueErrors to be raised in case an encoding error occurs. buffering has the same meaning as for the builtin open() API. It defaults to -1 which means that the default buffer size will be used. The returned wrapped file object provides an extra attribute .encoding which allows querying the used encoding. This attribute is only available if an encoding was specified as parameter. """ if encoding is not None and \ 'b' not in mode: # Force opening of the file in binary mode mode = mode + 'b' > file = builtins.open(filename, mode, buffering) E FileNotFoundError: [Errno 2] No such file or directory: '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree1.dot' /usr/lib64/python3.9/codecs.py:905: FileNotFoundError ________________________________________________________________________________________ test_tree2 _________________________________________________________________________________________ @with_setup(setup, teardown) def test_tree2(): """Tree2.""" root = Node("root") s0 = Node("sub0", parent=root, edge=2) Node("sub0B", parent=s0, foo=4, edge=109) Node("sub0A", parent=s0, edge="") s1 = Node("sub1", parent=root, edge="") Node("sub1A", parent=s1, edge=7) Node("sub1B", parent=s1, edge=8) s1c = Node("sub1C", parent=s1, edge=22) Node("sub1Ca", parent=s1c, edge=42) def nodenamefunc(node): return "{}:{}".format(node.name, node.depth) def edgeattrfunc(node, child): return 'label="{}:{}"'.format(node.name, child.name) r = RenderTreeGraph( root, options=["rankdir=LR;"], nodenamefunc=nodenamefunc, nodeattrfunc=lambda node: "shape=box", edgeattrfunc=edgeattrfunc, ) > r.to_dotfile(join(GENPATH, "tree2.dot")) tests/test_dotexport.py:70: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ anytree/exporter/dotexporter.py:284: in to_dotfile with codecs.open(filename, "w", "utf-8") as file: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ filename = '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree2.dot', mode = 'wb', encoding = 'utf-8', errors = 'strict', buffering = -1 def open(filename, mode='r', encoding=None, errors='strict', buffering=-1): """ Open an encoded file using the given mode and return a wrapped version providing transparent encoding/decoding. Note: The wrapped version will only accept the object format defined by the codecs, i.e. Unicode objects for most builtin codecs. Output is also codec dependent and will usually be Unicode as well. Underlying encoded files are always opened in binary mode. The default file mode is 'r', meaning to open the file in read mode. encoding specifies the encoding which is to be used for the file. errors may be given to define the error handling. It defaults to 'strict' which causes ValueErrors to be raised in case an encoding error occurs. buffering has the same meaning as for the builtin open() API. It defaults to -1 which means that the default buffer size will be used. The returned wrapped file object provides an extra attribute .encoding which allows querying the used encoding. This attribute is only available if an encoding was specified as parameter. """ if encoding is not None and \ 'b' not in mode: # Force opening of the file in binary mode mode = mode + 'b' > file = builtins.open(filename, mode, buffering) E FileNotFoundError: [Errno 2] No such file or directory: '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree2.dot' /usr/lib64/python3.9/codecs.py:905: FileNotFoundError _______________________________________________________________________________________ test_tree_png _______________________________________________________________________________________ @with_setup(setup, teardown) def test_tree_png(): """Tree to png.""" root = Node("root") s0 = Node("sub0", parent=root) Node("sub0B", parent=s0) Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) Node("sub1A", parent=s1) Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) Node("sub1Ca", parent=s1c) > RenderTreeGraph(root).to_picture(join(GENPATH, "tree1.png")) tests/test_dotexport.py:87: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ anytree/exporter/dotexporter.py:303: in to_picture check_call(cmd) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ popenargs = (['dot', '/tmp/tmpmr32tg5n', '-T', 'png', '-o', '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree1.png'],), kwargs = {}, retcode = 1 cmd = ['dot', '/tmp/tmpmr32tg5n', '-T', 'png', '-o', '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree1.png'] def check_call(*popenargs, **kwargs): """Run command with arguments. Wait for command to complete. If the exit code was zero then return, otherwise raise CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute. The arguments are the same as for the call function. Example: check_call(["ls", "-l"]) """ retcode = call(*popenargs, **kwargs) if retcode: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] > raise CalledProcessError(retcode, cmd) E subprocess.CalledProcessError: Command '['dot', '/tmp/tmpmr32tg5n', '-T', 'png', '-o', '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree1.png']' returned non-zero exit status 1. /usr/lib64/python3.9/subprocess.py:373: CalledProcessError ----------------------------------------------------------------------------------- Captured stderr call ------------------------------------------------------------------------------------ Error: Could not open "/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree1.png" for writing : No such file or directory ===================================================================================== warnings summary ====================================================================================== tests/test_dotexport.py::test_tree1 tests/test_dotexport.py::test_tree2 tests/test_dotexport.py::test_tree_png /home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/anytree/dotexport.py:9: DeprecationWarning: anytree.RenderTreeGraph has moved. Use anytree.exporter.DotExporter instead warnings.warn( tests/test_node.py::test_ancestors /home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/anytree/node/nodemixin.py:359: DeprecationWarning: .anchestors was a typo and will be removed in version 3.0.0 warnings.warn(".anchestors was a typo and will be removed in version 3.0.0", DeprecationWarning) -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html ================================================================================== short test summary info ================================================================================== FAILED tests/test_dotexport.py::test_tree1 - FileNotFoundError: [Errno 2] No such file or directory: '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree1.dot' FAILED tests/test_dotexport.py::test_tree2 - FileNotFoundError: [Errno 2] No such file or directory: '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree2.dot' FAILED tests/test_dotexport.py::test_tree_png - subprocess.CalledProcessError: Command '['dot', '/tmp/tmpmr32tg5n', '-T', 'png', '-o', '/home/tkloczko/rpmbuild/BUILD/anytree-2.12.1/tests/dotexport/tree1.png']' returned non-zero exit... ========================================================================= 3 failed, 160 passed, 4 warnings in 0.99s ========================================================================= ```
List of installed modules in build env: ```console Package Version ----------------------------- ----------- alabaster 0.7.16 Babel 2.14.0 build 1.1.1 charset-normalizer 3.3.2 distro 1.9.0 docutils 0.20.1 exceptiongroup 1.1.3 idna 3.6 imagesize 1.4.1 importlib_metadata 7.0.1 iniconfig 2.0.0 installer 0.7.0 Jinja2 3.1.3 MarkupSafe 2.1.3 packaging 24.0 pluggy 1.4.0 poetry-core 1.9.0 Pygments 2.17.2 pyproject_hooks 1.0.0 pytest 8.1.1 python-dateutil 2.9.0.post0 requests 2.31.0 snowballstemmer 2.2.0 Sphinx 7.2.6 sphinxcontrib-applehelp 1.0.8 sphinxcontrib-devhelp 1.0.5 sphinxcontrib-htmlhelp 2.0.5 sphinxcontrib-jsmath 1.0.1 sphinxcontrib-qthelp 1.0.7 sphinxcontrib-serializinghtml 1.1.10 tokenize_rt 5.2.0 tomli 2.0.1 urllib3 1.26.18 wheel 0.43.0 zipp 3.17.0 ```

Please let me know if you need more details or want me to perform some diagnostics.

ncopa commented 7 months ago

I think this happens due to a race condition. The GENPATH directory is created an destroyed for each test, and I suspect that recent pytest tries to run the tests in parallel, meaning one test deletes the GENPATH while second tests tries to write to it.

Its better to use the tmpdir fixture.