test_config_api_contract
Runtime tests for the Python config API contract.
Pins the behavior clauses of docs/config-api.md that source analysis
cannot see, all of them on a typed section reached through
user_config.core:
- a declared C# property wins over a raw option of the same name, on read and on write;
- a name that is not a declared property is stored as a raw option rather than
rejected - which is what makes
user_config.core.set_option()necessary and what makes the snake_case spelling silently ineffective; - the escape hatches are symmetric with
ConfigSection, so a value written through one reads back through the other.
The typed-property/raw-option precedence is the sharp edge called out in the
contract: user_config.core.rocket_mode = True succeeds, writes a key no
reader looks at, and leaves RocketMode alone. These tests make that
explicit so adopting strictness later is a visible diff rather than a silent
behavior change.
The fakes model the C# contract - the wrapper asks the section's CLR type whether a name belongs to the schema, and writes declared names through the service while undeclared ones become raw options. They are hermetic: no user config, no disk, no labs assemblies, so this runs identically under IronPython 2/3 and CPython.
Run from Revit via the pyRevit DevTools "Config Module Tests" button.
Classes
TypedSectionContractTests
Bases: TestCase
A declared property wins over a raw option of the same name.
Methods:
setUp()
Build a writable [core] wrapper over a fake typed section.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_typed_property_is_read_from_the_section()
PascalCase is the canonical spelling, so it must resolve to the schema.
test_typed_property_write_reaches_the_service()
A typed write is not a raw-option write; it has to reach the store.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_typed_property_beats_a_raw_option_of_the_same_name()
A stale raw key from a hand-edited file must not shadow the schema.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_typed_property_write_does_not_create_a_raw_option()
Nothing lands under the property name; the key on disk is the section's.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_snake_case_spelling_is_stored_as_a_raw_option()
The sharp edge: the write succeeds silently and never reaches RocketMode.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_unknown_attribute_read_raises_only_when_no_raw_option_exists()
Absence is the only raise; permissiveness is what makes the hatch work.
test_raw_option_is_readable_by_attribute()
A stored raw option answers to attribute access, as a script expects.
test_raw_option_write_bypasses_the_service()
A raw write is stored verbatim and never applied as a section record.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
EscapeHatchParityTests
Bases: TestCase
A typed section and a ConfigSection share one decode and one store.
Methods:
setUp()
Pair a [core] wrapper with a ConfigSection over the same store.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_value_written_through_one_reads_through_the_other()
Extensions hold whichever of the two a given entry point handed them.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_value_written_through_a_section_reads_through_the_typed_section()
A script's own section and a built-in section cannot diverge.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_typed_property_write_is_visible_to_the_raw_reader()
A typed write lands under the section key, so a raw reader sees it.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_legacy_bool_reads_the_same_through_both()
One decoder serves both, so neither reports a legacy False as truthy.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
ReadOnlyContractTests
Bases: TestCase
An admin-locked config drops writes rather than reporting a false success.
Methods:
setUp()
Build a read-only [core] wrapper over a fake typed section.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_typed_property_write_is_skipped()
save_changes skips the flush, so a write accepted here would be a lie.
Source code in pyrevitlib/pyrevit/unittests/test_config_api_contract.py
test_snake_case_write_is_skipped_too()
The raw fallback is a write path too, so it is dropped by the same guard.
test_existing_values_still_read()
Read-only means writes are dropped, not that the config goes dark.