"""Guards against the transponders<->sim circular import. Transponder models import ``pm3py.sim`` primitives (Transponder/Reader/RFFrame/Medium), so ``pm3py.sim`` must NOT eagerly re-import transponder models at package-init time — its model re-exports are lazy (see ``pm3py/sim/_models.py`` + the package ``__getattr__``), and ``fuzzer.py`` defers its ``iso14443a.base`` import. Each family must therefore import as a *fresh* first import. These run in subprocesses because import-order bugs only show in a clean interpreter. """ import subprocess import sys import pytest # each of these, imported first in a clean interpreter, used to trigger a circular import _FRESH_IMPORTS = [ "pm3py.transponders.hf.iso15693.type5", # the reported failure "pm3py.transponders.hf.iso14443a.base", # the fuzzer-side failure "pm3py.transponders.hf.iso14443b.base", "pm3py.transponders.lf.atmel.t5577", "pm3py.transponders.implants", "pm3py.sim.fuzzer", "pm3py.sim", ] @pytest.mark.parametrize("module", _FRESH_IMPORTS) def test_fresh_import_has_no_cycle(module): r = subprocess.run([sys.executable, "-c", f"import {module}"], capture_output=True, text=True) assert r.returncode == 0, f"importing {module} first failed:\n{r.stderr}" def test_sim_lazy_reexports_resolve(): import pm3py.sim as sim from pm3py.transponders.hf.iso15693.type5 import NfcType5Tag assert sim.NfcType5Tag is NfcType5Tag # lazy re-export == canonical class assert "NfcType5Tag" in dir(sim) assert all(getattr(sim, n, None) is not None for n in sim.__all__) def test_fuzzer_deferred_import_works(): from pm3py.sim.fuzzer import GrammarFuzzer frame = GrammarFuzzer("14443a").generate("SELECT", uid=b"\x04\x11\x22\x33") assert frame.data.hex() == "93700411223304" # CL1, NVB_SELECT, uid, bcc