# -*- 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.