8e6ee31596
ci / test (push) Successful in 6m18s
History-free snapshot of the private development repository at cbe45c5 (2026-09-09). Code, docs and public catalogue only; no plant or job knowledge layers, no client or job names (tools/scrub.py clean). The intro deck is left out of this snapshot because its slide images could not be checked for job drawings.
70 lines
3.3 KiB
Python
70 lines
3.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Makes the Revit-free parts of `vibe` importable on host CPython.
|
|
|
|
`vibe/__init__.py` does `from vibe.helpers import (...)` at module scope, and
|
|
`helpers` imports the Revit API the moment it is loaded. That is correct for
|
|
the extension -- every button script and every `/vibe/exec` snippet gets `q`,
|
|
`p`, `tx` and friends for free -- but it means a plain `import vibe.knowledge`
|
|
from an ordinary CPython interpreter runs `vibe/__init__.py` first and dies
|
|
with `ImportError: No module named Autodesk` before it ever reaches
|
|
`knowledge.py`.
|
|
|
|
Most of `vibe` does not actually touch Revit at module scope. `knowledge.py`,
|
|
`liftexport.py` and `configdump.py` only reach for the Revit API, or for
|
|
`vibe.designspec` / `vibe.ticketqc` (which do), *inside* the functions that
|
|
need a live document -- on purpose, precisely so the rest of the file can be
|
|
exercised without one. `vibe.compat` has no Revit dependency at all.
|
|
|
|
THE TRICK, and why it works
|
|
----------------------------
|
|
We never execute `vibe/__init__.py`. Instead we register a bare, empty module
|
|
object as `sys.modules['vibe']` with its `__path__` pointed at the real
|
|
`WillTools.extension/lib/vibe` directory, then get out of the way. From then
|
|
on, ordinary `import vibe.knowledge` / `from vibe import knowledge` statements
|
|
-- inside test files AND inside the vibe modules themselves, e.g.
|
|
`liftexport.py`'s `from vibe import knowledge` -- are resolved by Python's
|
|
normal import machinery: `vibe` is already in `sys.modules` (so its
|
|
`__init__.py` is never run again, and never run at all), and `vibe.knowledge`
|
|
is found and executed as an ordinary submodule via that `__path__`.
|
|
|
|
USING THIS IN A NEW TEST
|
|
-------------------------
|
|
Nothing to import from this file. Once pytest has loaded this conftest.py
|
|
(automatic for anything under `WillTools.extension/tests/`), plain imports
|
|
just work:
|
|
|
|
from vibe import knowledge
|
|
from vibe import liftexport
|
|
from vibe import configdump
|
|
from vibe import compat
|
|
|
|
A module that reaches for Revit or for `vibe.designspec` / `vibe.ticketqc` at
|
|
MODULE SCOPE (rather than inside a function) will still fail to import this
|
|
way -- that is not a bug in this trick, it is this trick correctly reporting
|
|
that the module has a hard Revit dependency. `vibe.ticketqc` is one such
|
|
module (it does `from Autodesk.Revit.DB import (...)` at the top), and so is
|
|
`vibe.helpers`. Nothing under test here should ever need either.
|
|
|
|
`liftexport.export()` and anything else that needs a live `doc` is out of
|
|
scope for this suite by the same logic -- see the docstring in
|
|
`test_liftexport.py`.
|
|
"""
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
|
|
VIBE_DIR = Path(__file__).resolve().parents[1] / 'lib' / 'vibe'
|
|
assert VIBE_DIR.is_dir(), 'expected to find vibe at {0}'.format(VIBE_DIR)
|
|
|
|
if 'vibe' not in sys.modules:
|
|
_stub = types.ModuleType('vibe')
|
|
_stub.__path__ = [str(VIBE_DIR)] # makes it a package, not a module
|
|
_stub.__file__ = str(VIBE_DIR / '__init__.py')
|
|
sys.modules['vibe'] = _stub
|
|
|
|
# The pyRevit YAML reader is faked in `pyrevit_yaml_fake.py`, NOT here, and
|
|
# the module name is deliberately long: `panellift/tests/` has a conftest of
|
|
# its own, so a whole-repo `python -m pytest` run leaves exactly ONE module
|
|
# named `conftest` importable and it is whichever pytest reached first.
|
|
# `from conftest import ...` inside a test file is therefore a coin flip.
|