From c92f46bbde372363c2fb4c1942a1f24ca0620d03 Mon Sep 17 00:00:00 2001 From: Lorenz Neureuter Date: Sat, 7 Feb 2026 23:17:08 -0500 Subject: [PATCH 1/5] Remove path dependency --- cadquery/occ_impl/importers/assembly.py | 8 +++++--- environment.yml | 1 - setup.py | 1 - tests/conftest.py | 16 ++++++++++++++++ tests/test_assembly.py | 16 ++++++++-------- tests/test_examples.py | 12 +++++++++--- tests/test_exporters.py | 2 +- tests/test_vis.py | 9 +++++---- 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/cadquery/occ_impl/importers/assembly.py b/cadquery/occ_impl/importers/assembly.py index 4126c0ad1..28de735d1 100644 --- a/cadquery/occ_impl/importers/assembly.py +++ b/cadquery/occ_impl/importers/assembly.py @@ -1,5 +1,5 @@ from typing import cast -from path import Path +from pathlib import Path from OCP.TopoDS import TopoDS_Shape from OCP.TCollection import TCollection_ExtendedString @@ -175,7 +175,8 @@ def importXbf(assy: AssemblyProtocol, path: str): app = TDocStd_Application() BinXCAFDrivers.DefineFormat_s(app) - dirname, fname = Path(path).absolute().splitpath() + p = Path(path).absolute() + dirname, fname = str(p.parent), p.name doc = cast( TDocStd_Document, app.Retrieve( @@ -204,7 +205,8 @@ def importXml(assy: AssemblyProtocol, path: str): app = TDocStd_Application() XmlXCAFDrivers.DefineFormat_s(app) - dirname, fname = Path(path).absolute().splitpath() + p = Path(path).absolute() + dirname, fname = str(p.parent), p.name doc = cast( TDocStd_Document, app.Retrieve( diff --git a/environment.yml b/environment.yml index 590179b13..e6941c852 100644 --- a/environment.yml +++ b/environment.yml @@ -16,7 +16,6 @@ dependencies: - ezdxf>=1.3.0 - typing_extensions - nlopt - - path - casadi - runtype - multimethod >=1.11,<2.0 diff --git a/setup.py b/setup.py index 2bcbf98fd..c9f082d64 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,6 @@ "nlopt>=2.9.0,<3.0", "runtype", "casadi", - "path", "trame", "trame-vtk", "trame-components", diff --git a/tests/conftest.py b/tests/conftest.py index d30b369de..705e6f106 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,6 @@ import pytest +import os +from contextlib import contextmanager def pytest_addoption(parser): @@ -20,3 +22,17 @@ def pytest_collection_modifyitems(config, items): for item in items: if "gui" in item.keywords: item.add_marker(skip_gui) + + +@pytest.fixture +def cwd(): + @contextmanager + def _cwd(path): + oldpwd = os.getcwd() + os.chdir(path) + try: + yield + finally: + os.chdir(oldpwd) + + return _cwd diff --git a/tests/test_assembly.py b/tests/test_assembly.py index 2b0b3180d..fe87637cd 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -3,7 +3,7 @@ from itertools import product from math import degrees import copy -from path import Path +from pathlib import Path from pathlib import PurePath import re from pytest import approx @@ -41,7 +41,7 @@ @pytest.fixture(scope="function") def tmpdir(tmp_path_factory): - return Path(tmp_path_factory.mktemp("assembly")) + return tmp_path_factory.mktemp("assembly") @pytest.fixture @@ -1376,18 +1376,18 @@ def test_save(extension, args, nested_assy, nested_assy_sphere): ("stl", ("STL",), {}), ], ) -def test_export(extension, args, kwargs, tmpdir, nested_assy): +def test_export(extension, args, kwargs, tmpdir, nested_assy, cwd): filename = "nested." + extension - with tmpdir: + with cwd(tmpdir): nested_assy.export(filename, *args, **kwargs) assert os.path.exists(filename) -def test_export_vtkjs(tmpdir, nested_assy): +def test_export_vtkjs(tmpdir, nested_assy, cwd): - with tmpdir: + with cwd(tmpdir): nested_assy.export("nested.vtkjs") assert os.path.exists("nested.vtkjs.zip") @@ -1553,7 +1553,7 @@ def test_colors_assy0(assy_fixture, request, tmpdir, kind): """ assy = request.getfixturevalue(assy_fixture) - stepfile = (Path(tmpdir) / assy_fixture).with_suffix(f".{kind}") + stepfile = str((Path(tmpdir) / assy_fixture).with_suffix(f".{kind}")) assy.export(stepfile) assy_i = assy.load(stepfile) @@ -1585,7 +1585,7 @@ def test_colors_assy1(assy_fixture, request, tmpdir, kind): """ assy = request.getfixturevalue(assy_fixture) - stepfile = (Path(tmpdir) / assy_fixture).with_suffix(f".{kind}") + stepfile = str((Path(tmpdir) / assy_fixture).with_suffix(f".{kind}")) assy.export(stepfile) assy_i = assy.load(stepfile) diff --git a/tests/test_examples.py b/tests/test_examples.py index 6f022484e..13ede3213 100755 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -3,7 +3,7 @@ from glob import glob from itertools import chain, count -from path import Path +from pathlib import Path from docutils.parsers.rst import directives from docutils.core import publish_doctree @@ -14,6 +14,11 @@ from cadquery.cq_directive import cq_directive +@pytest.fixture(scope="session") +def tmpdir(tmp_path_factory): + return tmp_path_factory.mktemp("examples") + + def find_examples(pattern="examples/*.py", path=Path("examples")): for p in glob(pattern): @@ -56,10 +61,11 @@ def run(self): @pytest.mark.parametrize( "code, path", chain(find_examples(), find_examples_in_docs()), ids=count(0) ) -def test_example(code, path): +def test_example(code, path, tmpdir, cwd): # build - with path: + with cwd(tmpdir): + # tmpdir is for future use; currently there are no examples that export files res = cqgi.parse(code).build() assert res.exception is None diff --git a/tests/test_exporters.py b/tests/test_exporters.py index c769d124b..055d7698a 100644 --- a/tests/test_exporters.py +++ b/tests/test_exporters.py @@ -4,7 +4,7 @@ # core modules import os import io -from path import Path +from pathlib import Path import re import sys import math diff --git a/tests/test_vis.py b/tests/test_vis.py index a29da7aaf..741bd2ca4 100644 --- a/tests/test_vis.py +++ b/tests/test_vis.py @@ -18,14 +18,15 @@ from vtkmodules.vtkIOImage import vtkPNGWriter from pytest import fixture, raises -from path import Path +import os +from pathlib import Path from typing import List @fixture(scope="module") def tmpdir(tmp_path_factory): - return Path(tmp_path_factory.mktemp("screenshots")) + return tmp_path_factory.mktemp("screenshots") @fixture @@ -138,10 +139,10 @@ def test_show(wp, assy, sk, patch_vtk): show(vtkAxesActor(), [vtkAnnotatedCubeActor()]) -def test_screenshot(wp, tmpdir, patch_vtk): +def test_screenshot(wp, tmpdir, patch_vtk, cwd): # smoke test for now - with tmpdir: + with cwd(tmpdir): show(wp, interact=False, screenshot="img.png", trihedron=False, gradient=False) From d756f8900f595212e80f1ae5c662a90ba9f350d3 Mon Sep 17 00:00:00 2001 From: Lorenz Neureuter Date: Sun, 8 Feb 2026 07:59:09 -0500 Subject: [PATCH 2/5] Update vis screenshot test --- tests/test_vis.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_vis.py b/tests/test_vis.py index 741bd2ca4..695e530de 100644 --- a/tests/test_vis.py +++ b/tests/test_vis.py @@ -17,7 +17,7 @@ from vtkmodules.vtkRenderingAnnotation import vtkAnnotatedCubeActor from vtkmodules.vtkIOImage import vtkPNGWriter -from pytest import fixture, raises +from pytest import fixture, raises, mark import os from pathlib import Path @@ -139,11 +139,18 @@ def test_show(wp, assy, sk, patch_vtk): show(vtkAxesActor(), [vtkAnnotatedCubeActor()]) -def test_screenshot(wp, tmpdir, patch_vtk, cwd): +def test_screenshot(wp, patch_vtk): # smoke test for now + show(wp, interact=False, screenshot="img.png", trihedron=False, gradient=False) + + +@mark.skip(reason="running only smoke test for now") +def test_screenshot_no_patch(wp, tmpdir, cwd): + with cwd(tmpdir): show(wp, interact=False, screenshot="img.png", trihedron=False, gradient=False) + assert (Path(tmpdir) / "img.png").exists() def test_ctrlPts(): From 1f608e99060f55bfe12eaa7647deefd5e2ffc65a Mon Sep 17 00:00:00 2001 From: Lorenz Neureuter Date: Sun, 8 Feb 2026 08:00:31 -0500 Subject: [PATCH 3/5] Use conda-forge for sphinx theme --- environment.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index e6941c852..af3059eee 100644 --- a/environment.yml +++ b/environment.yml @@ -8,7 +8,7 @@ dependencies: - vtk=*=qt* - pyparsing>=3.0.0 - sphinx=9.1.0 -# - sphinx_rtd_theme=3.1.0 + - sphinx_rtd_theme=3.1.0 - mypy - codecov - pytest @@ -30,6 +30,5 @@ dependencies: - trame-vuetify - pip - pip: - - sphinx_rtd_theme==3.1.0 - --editable=. - git+https://github.com/cadquery/black.git@cq From 823ff1129f4e685ac3ac024d9bb960a54b158745 Mon Sep 17 00:00:00 2001 From: Lorenz Neureuter Date: Sun, 8 Feb 2026 08:40:14 -0500 Subject: [PATCH 4/5] Revert use conda-forge for sphinx theme --- environment.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index af3059eee..e6941c852 100644 --- a/environment.yml +++ b/environment.yml @@ -8,7 +8,7 @@ dependencies: - vtk=*=qt* - pyparsing>=3.0.0 - sphinx=9.1.0 - - sphinx_rtd_theme=3.1.0 +# - sphinx_rtd_theme=3.1.0 - mypy - codecov - pytest @@ -30,5 +30,6 @@ dependencies: - trame-vuetify - pip - pip: + - sphinx_rtd_theme==3.1.0 - --editable=. - git+https://github.com/cadquery/black.git@cq From d4f40492cd245294fd7e59b74324df0339a06a99 Mon Sep 17 00:00:00 2001 From: Lorenz Neureuter Date: Sun, 8 Feb 2026 08:45:34 -0500 Subject: [PATCH 5/5] Remove Python 3.8 pytest workaround --- tests/test_assembly.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/tests/test_assembly.py b/tests/test_assembly.py index fe87637cd..2fd46421e 100644 --- a/tests/test_assembly.py +++ b/tests/test_assembly.py @@ -557,18 +557,6 @@ def find_node(node_list, name_path): :param name_path: list of node names (corresponding to path) """ - def purepath_is_relative_to(p0, p1): - """Alternative to PurePath.is_relative_to for Python 3.8 - PurePath.is_relative_to is new in Python 3.9 - """ - try: - if p0.relative_to(p1): - is_relative_to = True - except ValueError: - is_relative_to = False - - return is_relative_to - def get_nodes(node_list, name, parents): if parents: nodes = [] @@ -577,8 +565,7 @@ def get_nodes(node_list, name, parents): [ p for p in node_list - # if p["path"].is_relative_to(parent["path"]) - if purepath_is_relative_to(p["path"], parent["path"]) + if p["path"].is_relative_to(parent["path"]) and len(p["path"].relative_to(parent["path"]).parents) == 1 and re.fullmatch(name, p["name"]) and p not in nodes