Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions cadquery/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,9 @@
UnaryConstraintKind,
BinaryConstraintKind,
)
from .occ_impl.exporters.assembly import (
exportAssembly,
exportCAF,
exportVTKJS,
exportVRML,
exportGLTF,
STEPExportModeLiterals,
)
from .occ_impl.importers.assembly import importStep as _importStep, importXbf, importXml

from .types import UnitLiterals
from .types import UnitLiterals, STEPExportModeLiterals

from .selectors import _expression_grammar as _selector_grammar
from .utils import deprecate, BiDict, instance_of
Expand Down Expand Up @@ -596,6 +588,14 @@ def export(
if mode not in get_args(STEPExportModeLiterals):
raise ValueError(f"Unknown assembly export mode {mode} for STEP")

from .occ_impl.exporters.assembly import (
exportAssembly,
exportCAF,
exportVTKJS,
exportVRML,
exportGLTF,
)

if exportType is None:
t = path.split(".")[-1].upper()
if t in ("STEP", "XML", "XBF", "VRML", "VTKJS", "GLTF", "GLB", "STL"):
Expand Down
28 changes: 18 additions & 10 deletions cadquery/occ_impl/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
Any,
List,
cast,
TYPE_CHECKING,
)
from typing_extensions import Protocol, Self
from math import degrees, radians
Expand Down Expand Up @@ -41,19 +42,14 @@
from OCP.TopoDS import TopoDS_Shape
from OCP.gp import gp_EulerSequence

from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper as vtkMapper,
vtkRenderer,
vtkProp3D,
)

from .geom import Location
from .shapes import Shape, Solid, Compound, GlueLiteral, _set_glue, _set_builder_options
from .exporters.vtk import toString, extractEdgesFaces
from ..cq import Workplane
from ..utils import BiDict

if TYPE_CHECKING:
from vtkmodules.vtkRenderingCore import vtkRenderer, vtkProp3D

# type definitions
AssemblyObjects = Union[Shape, Workplane, None]

Expand Down Expand Up @@ -628,7 +624,10 @@ def toVTKAssy(
linewidth: float = 2,
tolerance: float = 1e-3,
angularTolerance: float = 0.1,
) -> List[vtkProp3D]:
) -> "List[vtkProp3D]":

from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper as vtkMapper
from .exporters.vtk import extractEdgesFaces

rv: List[vtkProp3D] = []

Expand Down Expand Up @@ -675,7 +674,14 @@ def toVTK(
color: Tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0),
tolerance: float = 1e-3,
angularTolerance: float = 0.1,
) -> vtkRenderer:
) -> "vtkRenderer":

from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper as vtkMapper,
vtkRenderer,
)
from .exporters.vtk import extractEdgesFaces

renderer = vtkRenderer()

Expand Down Expand Up @@ -725,6 +731,8 @@ def toJSON(
Export an object to a structure suitable for converting to VTK.js JSON.
"""

from .exporters.vtk import toString

rv = []

for shape, _, loc, col_ in assy:
Expand Down
24 changes: 21 additions & 3 deletions cadquery/occ_impl/exporters/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import IO, Optional, Union, cast, Dict, Any, Iterable
from importlib import import_module
from typing import IO, Optional, Union, cast, Dict, Any, Iterable, TYPE_CHECKING
from typing_extensions import Literal

from OCP.VrmlAPI import VrmlAPI
Expand All @@ -10,8 +11,10 @@
from .json import JsonMesh
from .amf import AmfWriter
from .threemf import ThreeMFWriter
from .dxf import exportDXF, exportDXFProjection, DxfDocument
from .vtk import exportVTP

if TYPE_CHECKING:
from .dxf import exportDXF, exportDXFProjection, DxfDocument
from .vtk import exportVTP


class ExportTypes:
Expand Down Expand Up @@ -110,6 +113,8 @@ def export(
tmfw.write3mf(f)

elif exportType == ExportTypes.DXF:
from .dxf import exportDXF

exportDXF(w, fname, **opt)

elif exportType == ExportTypes.STEP:
Expand All @@ -128,6 +133,8 @@ def export(
VrmlAPI.Write_s(shape.wrapped, fname)

elif exportType == ExportTypes.VTP:
from .vtk import exportVTP

exportVTP(shape, fname, tolerance, angularTolerance)

elif exportType == ExportTypes.BREP:
Expand All @@ -138,3 +145,14 @@ def export(

else:
raise ValueError("Unknown export type")


# lazy: importing these here would load ezdxf and vtk with cadquery
def __getattr__(name):
if name in ("dxf", "vtk", "assembly"):
return import_module(f".{name}", __name__)
if name in ("exportDXF", "exportDXFProjection", "DxfDocument"):
return getattr(import_module(".dxf", __name__), name)
if name == "exportVTP":
return import_module(".vtk", __name__).exportVTP
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
6 changes: 1 addition & 5 deletions cadquery/occ_impl/exporters/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from tempfile import TemporaryDirectory
from shutil import make_archive
from typing import Optional
from typing_extensions import Literal

from vtkmodules.vtkIOExport import vtkJSONSceneExporter, vtkVRMLExporter
from vtkmodules.vtkRenderingCore import vtkRenderWindow
Expand Down Expand Up @@ -50,17 +49,14 @@
from ..assembly import AssemblyProtocol, toCAF, toVTK, toFusedCAF
from ..geom import Location
from ..shapes import Shape, Compound
from ...types import UnitLiterals
from ...types import UnitLiterals, STEPExportModeLiterals


class ExportModes:
DEFAULT = "default"
FUSED = "fused"


STEPExportModeLiterals = Literal["default", "fused"]


def exportAssembly(
assy: AssemblyProtocol,
path: str,
Expand Down
11 changes: 10 additions & 1 deletion cadquery/occ_impl/importers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from importlib import import_module
from math import pi
from typing import List, Literal

Expand All @@ -7,7 +8,6 @@

from ... import cq
from ..shapes import Shape
from .dxf import _importDXF
from ...types import UnitLiterals

RAD2DEG = 360.0 / (2 * pi)
Expand Down Expand Up @@ -133,6 +133,15 @@ def importDXF(
:param include: a list of layer names to import
"""

from .dxf import _importDXF

faces = _importDXF(filename, tol, exclude, include)

return cq.Workplane("XY").newObject(faces)


# lazy: importing dxf here would load ezdxf with cadquery

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this is needed.

def __getattr__(name):
if name == "dxf":
return import_module(".dxf", __name__)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
10 changes: 7 additions & 3 deletions cadquery/occ_impl/shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
Self,
TypeAlias,
TypeVar,
TYPE_CHECKING,
cast as tcast,
overload,
Type,
Expand All @@ -20,9 +21,6 @@

from warnings import warn

from vtkmodules.vtkCommonDataModel import vtkPolyData
from vtkmodules.vtkFiltersCore import vtkTriangleFilter, vtkPolyDataNormals

from OCP.ShapeBuild import ShapeBuild_ReShape

from .geom import Vector, VectorLike, BoundBox, Plane, Location, Matrix
Expand All @@ -36,6 +34,9 @@
from ..utils import multimethod, multidispatch, mypyclassmethod
from ..types import UnitLiterals

if TYPE_CHECKING:
from vtkmodules.vtkCommonDataModel import vtkPolyData

# change default OCCT logging level
from OCP.Message import Message, Message_Gravity

Expand Down Expand Up @@ -1711,6 +1712,9 @@ def toVtkPolyData(
Convert shape to vtkPolyData
"""

# vtk must be loaded before getVtkPolyData(), which returns None otherwise
from vtkmodules.vtkFiltersCore import vtkTriangleFilter, vtkPolyDataNormals

vtk_shape = IVtkOCC_Shape(self.wrapped)
shape_data = IVtkVTK_ShapeData()
shape_mesher = IVtkOCC_ShapeMesher()
Expand Down
3 changes: 2 additions & 1 deletion cadquery/sketch.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
)
from .occ_impl.geom import Location, Vector
from .occ_impl.exporters import export
from .occ_impl.importers.dxf import _importDXF
from .occ_impl.sketch_solver import (
SketchConstraintSolver,
ConstraintKind,
Expand Down Expand Up @@ -232,6 +231,8 @@ def importDXF(
Import a DXF file and construct face(s)
"""

from .occ_impl.importers.dxf import _importDXF

res = Compound.makeCompound(_importDXF(filename, tol, exclude, include))

return self.face(res, angle, mode, tag)
Expand Down
1 change: 1 addition & 0 deletions cadquery/types.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Union, Literal

UnitLiterals = Literal["MM", "CM", "M", "KM", "INCH", "FT", "MI", "UM", "NM"]
STEPExportModeLiterals = Literal["default", "fused"]
Real = Union[int, float]
26 changes: 26 additions & 0 deletions tests/test_lazy_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
import subprocess
import sys

import cadquery


def test_import_does_not_load_ezdxf_or_vtk():
# a fresh interpreter: this process has ezdxf and vtk loaded by other tests
code = (
"import sys, cadquery as cq;"
"print({m.split('.')[0] for m in sys.modules} & {'ezdxf', 'vtkmodules'});"
# the submodules stay reachable through the packages
"cq.exporters.assembly.ExportModes, cq.exporters.dxf, cq.exporters.vtk, cq.importers.dxf;"
"print('ok')"
)
# run next to the package under test; only stdout is checked, since the
# interpreter may crash on exit on Windows (#1911)
out = subprocess.run(
[sys.executable, "-u", "-c", code],
cwd=os.path.dirname(os.path.dirname(cadquery.__file__)),
capture_output=True,
text=True,
)

assert out.stdout.split() == ["set()", "ok"], out.stdout + out.stderr
Loading