test_py3_compat
Revit-hosted engine-portability tests for the Python 3 migration.
Run from Revit via the pyRevit DevTools "Py3 Compat Tests" buttons
(doc-project context). One button runs on the attached IronPython engine,
its twin carries a python3 shebang and runs the same suite on
CPython, so results can be compared per engine.
The suite exercises the compatibility hotspots cataloged in IRONPYTHON_TO_PYTHON3_ANALYSIS.md sections 4.3-4.5 and 6.1: Python-2-only idioms, IronPython-only CLR loading, out/ref marshaling, and heterogeneous sorting. A failing test is a coverage signal, not necessarily a regression: it marks a spot the current engine does not support yet.
Static counterpart: dev/scripts/check_py3_compat.py (no Revit needed).
Attributes
FAMILY_FILE = None
module-attribute
FAMILY_UTILS_FILE = None
module-attribute
CORE_MODULES = ['pyrevit', 'pyrevit.compat', 'pyrevit.coreutils', 'pyrevit.coreutils.envvars', 'pyrevit.coreutils.pyutils', 'pyrevit.forms', 'pyrevit.framework', 'pyrevit.output', 'pyrevit.revit', 'pyrevit.script']
module-attribute
RPW_MODULES = ['rpw', 'rpw.db', 'rpw.ui.forms']
module-attribute
INTEROP_MODULES = ['pyrevit.interop.dxf', 'pyrevit.interop.ifc']
module-attribute
INTEROP_NATIVE_MODULES = ['pyrevit.interop.adc', 'pyrevit.interop.rhino']
module-attribute
TEST_NATIVE_INTEROP = False
module-attribute
Classes
ImportTests
Bases: TestCase
Every supported module must be importable on the running engine.
Methods:
test_requests_backend_matches_engine()
IronPython uses the CLR HTTP shim; CPython uses requests.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_vendored_requests_imports()
Direct requests imports remain available on every Python 3 engine.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_tool_dependency_imports()
Bundled packages used by Keynotes, Excel tools, and Revit Server import.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_vendored_requests_wraps_invalid_json()
Malformed JSON raises the documented Requests exception.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_core_module_imports()
Core pyrevit modules import cleanly on this engine.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_interop_module_imports()
Interop modules import cleanly (needs the framework CLR shim).
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_framework_asm_file_reference()
framework.add_reference_to_file loads assemblies on any engine.
Uses an extensionless path to a managed assembly pyRevit already ships, exercising the extension-resolution branch of the shim.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_interop_native_module_imports()
Interop modules that load native/external binaries (opt-in).
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
Py2IdiomTests
Bases: TestCase
Runtime behavior of the section 4.3 syntax residuals.
Methods:
test_basewrapper_repr()
ElementWrapper repr works (crashes on .iteritems under Py3).
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_forms_paramdef_truthiness()
ParamDef instances stay truthy on Python 3 engines (bool).
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_forms_listitem_truthiness()
TemplateListItem truthiness follows checked state (bool).
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
RpwCompatibilityTests
Bases: TestCase
The bundled RevitPythonWrapper imports and forms work on IronPython.
Methods:
test_rpw_module_imports()
RPW's public namespaces import under the selected IronPython engine.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_rpw_combobox_sorts_dictionary_options()
Dictionary options materialize before sorting on Python 3 engines.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_rpw_ipy3_wpf_loader()
RPW exposes the WPF LoadComponent helper through the IPY3 engine.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_rpw_ipy3_flexform_construction()
RPW forms used by shipped tools can instantiate without showing UI.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_rpw_ipy3_wraps_project_information()
RPW wraps a live Revit element without Python 2 conversion paths.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
SortingTests
Bases: TestCase
Heterogeneous-data sorting (the Settings.smartbutton fix pattern).
Methods:
test_envvars_sortable_by_name()
Env vars dict sorts by str-coerced key without comparing values.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
QueryStringLookupTests
Bases: TestCase
String-identifier lookups in revit.db.query (isinstance str checks).
Methods:
setUp()
Require an open project document for name-based Revit queries.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_get_param_by_name()
get_param resolves a parameter passed by name string.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_get_category_by_name()
get_category resolves a category passed by name string.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
FamilyLoaderTests
Bases: TestCase
The Load Families tool preserves Revit's direct load result.
Methods:
test_overwrite_result_is_not_derived_from_symbols()
FamilyLoader returns the helper's result for overwrite loads.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
OutParamMarshalingTests
Bases: TestCase
The two clr.Reference out/ref sites (sections 4.5 / 6.1).
Methods:
setUp()
Require an open project document for Revit API marshaling tests.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_load_family_out_param()
create.load_family_with_result marshals the out-param family reference.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_load_family_result_preserves_refusal_status()
Existing symbols do not turn a refused family load into success.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_load_family_symbol_out_param()
create.load_family_symbol marshals the out-param symbol reference.
Two host-API details shape this test.
The symbol is checked against DB.FamilySymbol rather than through
rpw: rpw's Element constructor always returns the base
rpw.db.Element wrapper, never the specific subclass, so an
isinstance check against rpw.db.family.FamilySymbol cannot pass
on any input. Revit hands back a generated subclass (this family's type
is an AnnotationSymbolType) whose MRO still derives from
FamilySymbol, so the host-type check is the one that expresses the
claim.
That same generated subclass is why the type name cannot be read as
.Name: the member is present in the binding but raises
AttributeError on read, on IronPython and CPython alike. The
symbol-name parameter is the stable read for a family type name.
Source code in pyrevitlib/pyrevit/unittests/test_py3_compat.py
test_curve_intersect_out_param()
geom.intersect_curves marshals intersection results.
Exercises the Curve.Intersect wrapper on pure geometry, with no element creation or view dependency. Works across both host API generations (out-param overload and CurveIntersectResultOption).