From 56a7df98f21b266d281b510c82d03968446f5140 Mon Sep 17 00:00:00 2001 From: Bill Hlavacek Date: Thu, 10 Sep 2026 11:12:53 -0600 Subject: [PATCH] chore(petab): retire the BnglModel adapter and register_bngl() now that petab 0.9.0 ships the loader, and drop Python 3.11 (#591) petab 0.9.0 (2026-09-07) is the first PyPI release whose known_model_types includes bngl: the native BnglModel loader PyBNF contributed upstream in PEtab-dev/libpetab-python#508 shipped. That is the trigger #591 was gated on. On any 0.9.0 installation register_bngl() already saw bngl registered and returned without rebinding, so the local adapter was dead at runtime and only the tests that constructed it directly still exercised it. Removed: pybnf/petab/bngl_model.py (the BnglModel adapter, register_bngl(), and its _locate_bng2 helper); the pytest-native-bngl CI leg that installed upstream libpetab-python main to reach the native loader ahead of a release; the setup-pybnf action's petab-spec input that leg used; the automodule entry for the removed module; and every register_bngl() call in the tests, the tutorial lessons (12, 13), the lint-clinic fixture generator, and the three real-world make_petab.py scripts. The petab and tests extras now floor at petab >= 0.9, and the action installs the same pin, which test_packaging_metadata already cross-checks. petab 0.9.0 requires Python >= 3.12, so the project floor moves to 3.12: requires-python, the classifier, ruff's target-version, the CI matrix, the packaging test, and the install/cluster docs and batch scripts that named 3.11. Kept: pybnf/petab/_bngl.py, the reader the importer and exporter use (its drift note now points at the shipped upstream module), and pybnf/petab/_bngl_expr.py, the parameter-expression evaluator from #666. The adapter was the evaluator's only consumer, and petab's native BnglModel does not evaluate an expression-valued parameter (get_parameter_value raises NotImplementedError, get_free_parameter_ids_with_values skips it). That is not a regression of this change, since the no-op already routed 0.9.0 users to the native class, but it is the next upstream port; the evaluator stays as its staging copy and its unit tests now drive the evaluator directly. Tests: the adapter's unit tests are replaced by TestNativeBnglModel, which pins the ABC seam the exporter relies on (parameter ids and values, entity namespace, formula symbols, state variables) against petab's class and asserts the native loader is present, taking over the retired CI leg's assert step. The corpus validity test passes an explicit model_id because petab derives one from the file stem and requires a PEtab identifier, which the hyphenated corpus filenames are not. tests/_bngl_differential.py carries its own _locate_bng2. The tutorial tests import BnglModel and _locate_bng2 from petab where they still need them. Locally, under petab 0.9.0 on Python 3.12: every test_petab*.py, the PEtab tutorial tests, the lint clinic, the priors lesson, new-era validation, and the packaging metadata tests pass (730 passed). The pinned ruff gate and the -W docs build are clean. ADR-0026 gains an addendum; CHANGELOG records the floor change and the removal. --- .github/actions/setup-pybnf/action.yml | 26 +-- .github/workflows/tests.yml | 56 +---- CHANGELOG.md | 17 ++ README.md | 2 +- ...del-language-runtime-registered-adapter.md | 26 +++ docs/cluster.rst | 8 +- docs/installation.rst | 12 +- docs/modules/petab.rst | 6 - docs/petab.rst | 31 +-- .../Kozer-2013/egfr_nf/make_petab.py | 2 - .../real-world/Monine-2010/tlbr/make_petab.py | 2 - .../egfr_simpull/make_petab.py | 2 - examples/tcr/tcr_batch.sh | 4 +- .../tutorial/12_petab_roundtrip/README.md | 25 +- .../tutorial/13_petab_lint_clinic/README.md | 13 +- .../regenerate_fixtures.py | 11 +- pybnf/petab/_bngl.py | 23 +- pybnf/petab/_bngl_expr.py | 8 +- pybnf/petab/bngl_model.py | 216 ------------------ pyproject.toml | 30 ++- tests/_bngl_differential.py | 14 +- tests/full_tests/cluster.sh | 2 +- tests/full_tests/cluster_manual.sh | 2 +- tests/test_new_era_validation.py | 4 +- tests/test_packaging_metadata.py | 4 +- tests/test_petab_bngl_corpus.py | 16 +- tests/test_petab_bngl_expr.py | 57 ++--- tests/test_petab_export.py | 184 +++------------ tests/test_petab_formula.py | 2 - tests/test_petab_import.py | 8 +- tests/test_tutorial_lint_clinic.py | 9 +- tests/test_tutorial_petab.py | 5 +- tests/test_tutorial_petab_import.py | 4 - .../test_tutorial_petab_observable_formula.py | 3 - tests/test_tutorial_petab_protocols.py | 5 +- tests/test_tutorial_priors.py | 4 - 36 files changed, 211 insertions(+), 632 deletions(-) delete mode 100644 pybnf/petab/bngl_model.py diff --git a/.github/actions/setup-pybnf/action.yml b/.github/actions/setup-pybnf/action.yml index 13e7c5c9b..52ec221b1 100644 --- a/.github/actions/setup-pybnf/action.yml +++ b/.github/actions/setup-pybnf/action.yml @@ -12,7 +12,7 @@ description: >- inputs: python-version: - description: Python version for the venv (e.g. "3.11", "3.12"). + description: Python version for the venv (e.g. "3.12", "3.13"). required: true bngsim: description: >- @@ -36,24 +36,14 @@ inputs: [jax] extra. required: false default: 'false' - petab-spec: - description: >- - The petab requirement to install. Defaults to stock PyPI petab. A CI leg - overrides it with upstream libpetab-python main - (git+…/PEtab-dev/libpetab-python@main), where the native BnglModel loader - landed (PEtab-dev/libpetab-python#508), so the exporter oracle validates - `language: bngl` problems through petab's OWN native loader rather than the - register_bngl() monkeypatch (ADR-0026). - required: false - default: 'petab>=0.8,<1' cache-suffix: description: >- uv cache namespace suffix. Two jobs that run the SAME python version through this action share one content-addressed cache key (os/arch/python/ pyproject hash + this suffix) and race to save it -- a benign "another job may be creating this cache" warning. Override per job to give each its own - namespace (e.g. the native-BNGL leg also runs 3.12, colliding with the - matrix 3.12 job under a shared suffix). + namespace (e.g. the jax leg also runs 3.12, colliding with the matrix + 3.12 job under a shared suffix). required: false default: 'pybnf' @@ -101,7 +91,6 @@ runs: env: BNGSIM: ${{ inputs.bngsim }} JAX: ${{ inputs.jax }} - PETAB_SPEC: ${{ inputs.petab-spec }} run: | # Build the venv from the setup-python interpreter (see note above), not # a uv-managed one. @@ -141,10 +130,9 @@ runs: # BNG2.pl is installed above, so model-level validation runs here too) AND # the runtime math translator backing the expression observableFormula # path (pybnf[petab], ADR-0035, petab.v2.math); stock PyPI petab carries - # the math grammar, so one install covers both. The exact petab - # requirement is the `petab-spec` input (default stock PyPI; a CI leg - # overrides it with upstream libpetab-python main to run the NATIVE BNGL - # loader, PEtab-dev/libpetab-python#508). + # the math grammar, so one install covers both. petab >= 0.9.0 ships the + # native `language: bngl` loader (PEtab-dev/libpetab-python#508), which + # is what the oracle loads BNGL problems through (#591). # arviz/h5netcdf/h5py unskip the ArviZ InferenceData bridge oracle # (ADR-0055; tests/test_inference_data.py + the tutorial diagnostics beat) # and matplotlib unskips the profile-likelihood plot oracle (#467). These @@ -174,7 +162,7 @@ runs: 'h5netcdf>=1' \ 'h5py' \ 'matplotlib>=3.5' \ - "$PETAB_SPEC" + 'petab>=0.9,<1' # Install pybnf itself without re-resolving deps: on a bngsim: 'false' # leg a re-resolve would pull bngsim in through pyproject.toml and # defeat the point, and everywhere it would re-derive a dependency set diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4c98d2fc4..8d4bd40bd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,11 +11,11 @@ jobs: strategy: fail-fast: false matrix: - # Every supported version (requires-python >= 3.11; classifiers list + # Every supported version (requires-python >= 3.12; classifiers list # through 3.14). CI is free on this public repo, so we test each claimed - # version rather than just the endpoints. + # version rather than just the endpoints. 3.11 left with the petab 0.9 + # floor (#591): petab 0.9.0 requires >= 3.12. include: - - python-version: '3.11' - python-version: '3.12' - python-version: '3.13' # bngsim publishes cp310-cp313 wheels, so 3.14 would fall back to @@ -37,7 +37,7 @@ jobs: bngsim: ${{ matrix.no-bngsim == '1' && 'false' || 'true' }} - name: Warm the arviz version-check stamp - # arviz < 1.0 (the py<3.12 resolution) prints a daily refactor warning on import and + # arviz < 1.0 prints a daily refactor warning on import and # records the date through a helper it calls atomic, which it is not: the temp file has # a FIXED name, `daily_warning.tmp`, so every process shares it. # @@ -84,52 +84,6 @@ jobs: # pull bngsim right back in). run: uv run --no-sync pytest -m "not slow and not recovery" -n auto - # The PEtab v2 export oracle (ADR-0025/0026) validates emitted `language: bngl` - # problems at MODEL level. On the pytest job above (stock PyPI petab) that runs - # through the register_bngl() monkeypatch. This job installs upstream - # libpetab-python main instead -- where the native BnglModel loader landed - # (PEtab-dev/libpetab-python#508) -- so Problem.from_yaml loads BNGL through - # petab's OWN native loader, exercising the collapse-to-no-op path that - # otherwise only ever SKIPS and keeping us green against the merged upstream - # contribution ahead of its first PyPI release (0.8.2 does not yet carry it). - # One Python version is enough; the loader is pure Python. - pytest-native-bngl: - runs-on: ubuntu-latest - name: pytest (native BNGL loader) - steps: - - uses: actions/checkout@v6 - - - uses: ./.github/actions/setup-pybnf - with: - python-version: '3.12' - # This leg is about petab's loader, not simulation: the two oracle - # files it runs are backend-free, so skip the bngsim install (and run - # under PYBNF_NO_BNGSIM=1 below) rather than pay for a wheel nothing - # here exercises. The matrix legs above cover bngsim. - bngsim: 'false' - petab-spec: 'petab @ git+https://github.com/PEtab-dev/libpetab-python.git@main' - # This job also runs 3.12 through setup-pybnf, so without its own - # suffix it shares the matrix py3.12 job's cache key and one save loses - # the reservation (the "another job may be creating this cache" - # warning). Give it a distinct namespace. - cache-suffix: pybnf-native - - # Fail loudly if the upstream build did not actually install (e.g. a - # resolution fallback to stock petab): the native path would silently skip - # otherwise. - - name: Assert petab has native BNGL support - run: | - uv run --no-sync python -c "import petab.v1.models as m; assert 'bngl' in m.known_model_types, 'upstream libpetab-python main is not installed: bngl is not a known petab model type'; print('native BNGL loader present')" - - - name: Run the PEtab oracle through the native loader - env: - PYBNF_NO_BNGSIM: '1' - # The export + import oracles both drive Problem.from_yaml, which loads - # `language: bngl` through petab's own native BnglModel here (the - # register_bngl() monkeypatch collapses to a no-op). This is where the - # TestRegisterBngl native-no-op test actually runs instead of skipping. - run: uv run --no-sync pytest -m "not slow and not recovery" tests/test_petab_export.py tests/test_petab_import.py - # The gradient path (ADR-0059): the HMC reference sampler and the jax-gated # gradient-assembly tests need the optional pybnf[jax] extra (jax + jaxlib + # blackjax). jaxlib is a large install relative to the rest of the dep set, so @@ -155,7 +109,7 @@ jobs: jax: 'true' # This job also runs 3.12 through setup-pybnf, so give it its own cache # namespace to avoid racing the matrix py3.12 job's save (see the - # cache-suffix input / the native-BNGL job above). + # cache-suffix input). cache-suffix: pybnf-jax - name: Run the HMC + jax gradient tests diff --git a/CHANGELOG.md b/CHANGELOG.md index a40048cd0..12f3ccac7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to PyBNF are documented below. This project adheres to ## [Unreleased] +### Changed +- **PyBNF requires Python 3.12 or newer, and the `petab` extra requires petab 0.9 or + newer (#591).** petab 0.9.0 is the first release that loads a `language: bngl` model + natively, through the `BnglModel` loader PyBNF contributed upstream + (PEtab-dev/libpetab-python#508), and it requires Python 3.12. Python 3.11 support is + dropped with it. + +### Removed +- **The `pybnf.petab.bngl_model` module, its `BnglModel` adapter, and the + `register_bngl()` shim that taught older petab releases to load BNGL models (#591).** + petab now does this itself, so PEtab's own validator checks a BNGL-model problem with no + PyBNF code involved; `register_bngl()` had already collapsed to a no-op on petab 0.9.0. + The stand-alone BNGL reader and the parameter-expression evaluator (#666) stay: the + importer and exporter use the reader, and the evaluator is the staging copy for an + upstream port, since petab's native loader does not yet evaluate an expression-valued + parameter. + ### Added - **A stochastic fit now confirms its best fit by running the top parameter sets again (#659).** When a model is stochastic, running it twice with the same parameter values diff --git a/README.md b/README.md index 1a423f1a4..bbca0eb0b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ For documentation, refer to the online documentation at = 0.9`, which floors the project at Python 3.12 +(petab 0.9.0 requires it), so the 3.11 matrix leg and classifier go with it. + +**What stays, and one gap the retirement exposes.** `_bngl.parse_model` stays: it is the +reader the importer and exporter use, and the upstream twin's drift note now points at the +shipped module rather than a branch. `_bngl_expr` (#666, the parameter-expression evaluator) +also stays, though its only consumer was the adapter: petab's native `BnglModel` does not +evaluate an expression-valued parameter (`get_parameter_value` raises `NotImplementedError`, +`get_free_parameter_ids_with_values` skips it), which is the pre-#666 behaviour on the +petab-side validation path. That is not a regression of this change, since the no-op already +routed 0.9.0 users to the native class, but it is the next upstream port, and the evaluator is +kept as its staging copy. `TestNativeBnglModel` in `tests/test_petab_export.py` pins the ABC +seam the exporter relies on against petab's class and asserts the native loader is present, in +place of the retired adapter unit tests and the CI leg's assert step. diff --git a/docs/cluster.rst b/docs/cluster.rst index 391e76748..9d8b3087c 100644 --- a/docs/cluster.rst +++ b/docs/cluster.rst @@ -5,8 +5,8 @@ Running on a cluster PyBNF is designed to run on computing clusters that utilize a shared network filesystem. PyBNF comes with built-in support for clusters running Slurm. It may also be manually configured to run on clusters with other managers (Torque, PBS, etc.). -Installation of PyBNF on a cluster has the same requirements as installation on a workstation, namely Python 3.11 or higher with the pip package manager. This is available on many clusters, -but may require loading a module to access. In Slurm, you can view the available modules with the command ``module avail``, and load the appropriate one with ``module load [modulename]``. Once Python 3.11 or higher and pip are loaded, the same :ref:`installation instructions ` apply as for a standard installation. +Installation of PyBNF on a cluster has the same requirements as installation on a workstation, namely Python 3.12 or higher with the pip package manager. This is available on many clusters, +but may require loading a module to access. In Slurm, you can view the available modules with the command ``module avail``, and load the appropriate one with ``module load [modulename]``. Once Python 3.12 or higher and pip are loaded, the same :ref:`installation instructions ` apply as for a standard installation. Assistance from the cluster administrators may be helpful if any cluster-specific issues arise during installation. @@ -104,8 +104,8 @@ An example batch script -- ``examples/tcr/tcr_batch.sh`` with a single word chan #SBATCH --job-name=pybnf # EDIT THIS LINE for your cluster: load a module (or activate a virtual - # environment) that provides Python 3.11 or newer with PyBNF installed. - module load python/3.11 + # environment) that provides Python 3.12 or newer with PyBNF installed. + module load python/3.12 pybnf -c tcr-ss.conf -t slurm-srun -o diff --git a/docs/installation.rst b/docs/installation.rst index 01184c2bc..9b196d342 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -12,7 +12,7 @@ PyBNF can also be installed on Windows, but functionality on Windows has been le Python ------ -PyBNF requires an installation of Python version 3.11 or higher. +PyBNF requires an installation of Python version 3.12 or higher. This requirement is declared in the package metadata, so installers such as ``pip`` and ``uv`` will refuse to install PyBNF into unsupported Python environments with a clear Python-version error. Linux and Mac @@ -20,11 +20,11 @@ Linux and Mac Python 3 comes built-in on many new Linux and Mac operating systems. To check if you have a supported Python 3, run the command ``python3 --version``. This will print -the version number, or will give an error if you don't have Python 3. Confirm that the version is 3.11 or higher. +the version number, or will give an error if you don't have Python 3. Confirm that the version is 3.12 or higher. Also confirm that your Python 3 has the ``pip`` package manager, which is used to install PyBNF. Run the command ``python3 -m pip``. This will give a help message if you have pip, or an error if not. -If you are missing python3 or pip, an easy way to get them is by installing the `Anaconda`_ Python distribution for Python v3.11 or higher. +If you are missing python3 or pip, an easy way to get them is by installing the `Anaconda`_ Python distribution for Python v3.12 or higher. Instructions for installing on various platforms can be found on the `Anaconda`_ website. .. _windows_install: @@ -34,7 +34,7 @@ Windows Windows does not come with built-in Python, so it must be installed separately. Additionally, if :ref:`BioNetGen ` will be used, Perl installation is required in the same environment as the python installation (i.e., the commands ``python`` and ``perl`` must both work on the same command line). -Our recommended configuration consists of installing `Strawberry Perl`_ and `Anaconda`_ Python 3.11 or higher. The Windows distribution of Anaconda includes the application "Anaconda Prompt", which provides a command line. This is the command line that you should use whenever this documentation refers to the command line or terminal. After installing both Anaconda and Strawberry Perl, a system restart may be required for Anaconda Prompt to find the Perl installation. +Our recommended configuration consists of installing `Strawberry Perl`_ and `Anaconda`_ Python 3.12 or higher. The Windows distribution of Anaconda includes the application "Anaconda Prompt", which provides a command line. This is the command line that you should use whenever this documentation refers to the command line or terminal. After installing both Anaconda and Strawberry Perl, a system restart may be required for Anaconda Prompt to find the Perl installation. For troubleshooting, or more advanced configuration, note that the requirement is to have both Python 3 and Perl on the current path. The current path can be checked with the command ``echo %PATH%`` and set (temporarily) with the command ``set PATH=[newpath]``, where ``[newpath]`` is a semicolon-delimited list of directories to search. @@ -54,7 +54,7 @@ Simply type the following in a terminal: Windows users running Anaconda Python 3 from "Anaconda Prompt" should instead type only ``pip install pybnf``. The above command will use your current version of Python 3 to install the most recent version of PyBNF released on the Python Package Index, along with all required dependencies. -If your Python version is older than 3.11, ``pip`` will stop before installation and report that PyBNF requires Python 3.11 or higher. +If your Python version is older than 3.12, ``pip`` will stop before installation and report that PyBNF requires Python 3.12 or higher. Depending on your Python configuration, the above command may require root access and install PyBNF for all users on the computer. If you don't want to do this, you may add the flag ``--user`` to the end of the command, to install without root access for only the current user. @@ -71,7 +71,7 @@ To run PyBNF once without keeping a tool installation, use: :command:`uvx pybnf --help` -As with ``pip``, ``uv`` will select a Python version compatible with PyBNF or report that Python 3.11 or higher is required. +As with ``pip``, ``uv`` will select a Python version compatible with PyBNF or report that Python 3.12 or higher is required. Installing from source ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/modules/petab.rst b/docs/modules/petab.rst index aca70c98c..901dc592f 100644 --- a/docs/modules/petab.rst +++ b/docs/modules/petab.rst @@ -46,12 +46,6 @@ Measurement formulas .. automodule:: pybnf.petab.formula :members: -BNGL model loader -============================== - -.. automodule:: pybnf.petab.bngl_model - :members: - Importing a problem ============================== diff --git a/docs/petab.rst b/docs/petab.rst index 1b1fa4c13..07b561ddb 100644 --- a/docs/petab.rst +++ b/docs/petab.rst @@ -71,20 +71,23 @@ free parameters, priors, noise models, and data. The BNGL model loader --------------------- -Stock libpetab ships only ``sbml`` and ``pysb`` model loaders, so on its own it -cannot load — or lint — a problem whose model is BNGL. -``pybnf.petab.register_bngl()`` teaches a running ``petab`` to load -``language: bngl`` models. It is idempotent and additive: it routes ``bngl`` to -PyBNF's loader and delegates every other language to the original, leaving -``sbml``/``pysb`` untouched. ``import_job`` and ``export_job`` arrange this for -you; call it yourself only when driving the ``petab`` library directly:: - - from pybnf.petab import register_bngl - - register_bngl() - -With the loader installed, PEtab's own validator (``petab.v2.lint`` / -``petablint``) can check a BNGL-model problem — see the lint-clinic lesson below. +``petab`` (libpetab-python) loads ``language: bngl`` models natively since 0.9.0, +through the ``BnglModel`` loader PyBNF contributed upstream +(PEtab-dev/libpetab-python#508). PEtab's own validator (``petab.v2.lint`` / +``petablint``) therefore checks a BNGL-model problem with no PyBNF code +involved:: + + from petab.v2 import Problem + from petab.v2.lint import lint_problem + + report = lint_problem(Problem.from_yaml("petab/problem.yaml")) + +The model-level check shells out to ``BNG2.pl --check`` when a BioNetGen is on +``BNGPATH`` or ``PATH`` and degrades to "valid" when none is, so validation never +falsely fails for lack of a backend. The ``register_bngl()`` shim that taught +older petab releases the same loader was retired together with the +``petab >= 0.9`` floor of the ``pybnf[petab]`` extra. See the lint-clinic lesson +below. What round-trips ---------------- diff --git a/examples/real-world/Kozer-2013/egfr_nf/make_petab.py b/examples/real-world/Kozer-2013/egfr_nf/make_petab.py index 061ab7afa..cd5d5c6ee 100644 --- a/examples/real-world/Kozer-2013/egfr_nf/make_petab.py +++ b/examples/real-world/Kozer-2013/egfr_nf/make_petab.py @@ -118,11 +118,9 @@ def _lint(problem_yaml: Path) -> tuple[str, str]: try: from petab.v2 import Problem from petab.v2.lint import lint_problem - from pybnf.petab.bngl_model import register_bngl except Exception as exc: # noqa: BLE001 -- petab extra not installed return "skipped", f"petab lint unavailable ({exc!r}); install pybnf[petab] to enforce it" - register_bngl() report = lint_problem(Problem.from_yaml(str(problem_yaml))) has_errors = report.has_errors() if hasattr(report, "has_errors") else bool(report) return ("errors", str(report)) if has_errors else ("clean", "") diff --git a/examples/real-world/Monine-2010/tlbr/make_petab.py b/examples/real-world/Monine-2010/tlbr/make_petab.py index b9b3e8a2b..5f91bcc94 100644 --- a/examples/real-world/Monine-2010/tlbr/make_petab.py +++ b/examples/real-world/Monine-2010/tlbr/make_petab.py @@ -111,11 +111,9 @@ def _lint(problem_yaml: Path) -> tuple[str, str]: try: from petab.v2 import Problem from petab.v2.lint import lint_problem - from pybnf.petab.bngl_model import register_bngl except Exception as exc: # noqa: BLE001 -- petab extra not installed return "skipped", f"petab lint unavailable ({exc!r}); install pybnf[petab] to enforce it" - register_bngl() report = lint_problem(Problem.from_yaml(str(problem_yaml))) has_errors = report.has_errors() if hasattr(report, "has_errors") else bool(report) return ("errors", str(report)) if has_errors else ("clean", "") diff --git a/examples/real-world/Salazar-Cavazos-2019/egfr_simpull/make_petab.py b/examples/real-world/Salazar-Cavazos-2019/egfr_simpull/make_petab.py index e92d195ef..3d0c3fc67 100644 --- a/examples/real-world/Salazar-Cavazos-2019/egfr_simpull/make_petab.py +++ b/examples/real-world/Salazar-Cavazos-2019/egfr_simpull/make_petab.py @@ -111,11 +111,9 @@ def _lint(problem_yaml: Path) -> tuple[str, str]: try: from petab.v2 import Problem from petab.v2.lint import lint_problem - from pybnf.petab.bngl_model import register_bngl except Exception as exc: # noqa: BLE001 -- petab extra not installed return "skipped", f"petab lint unavailable ({exc!r}); install pybnf[petab] to enforce it" - register_bngl() report = lint_problem(Problem.from_yaml(str(problem_yaml))) has_errors = report.has_errors() if hasattr(report, "has_errors") else bool(report) return ("errors", str(report)) if has_errors else ("clean", "") diff --git a/examples/tcr/tcr_batch.sh b/examples/tcr/tcr_batch.sh index 28c69179e..865ce4a16 100644 --- a/examples/tcr/tcr_batch.sh +++ b/examples/tcr/tcr_batch.sh @@ -19,9 +19,9 @@ #SBATCH --job-name=pybnf # EDIT THIS LINE for your cluster: load a module (or activate a virtual environment) -# that provides Python 3.11 or newer with PyBNF installed. Some clusters need nothing +# that provides Python 3.12 or newer with PyBNF installed. Some clusters need nothing # here at all. -module load python/3.11 +module load python/3.12 # Run PyBNF. Use "-t slurm-srun" instead if your cluster's nodes cannot be logged # into over SSH with a key or a password; see "Which ways of starting a run log in to diff --git a/examples/tutorial/12_petab_roundtrip/README.md b/examples/tutorial/12_petab_roundtrip/README.md index 92faa44ce..8262fbae5 100644 --- a/examples/tutorial/12_petab_roundtrip/README.md +++ b/examples/tutorial/12_petab_roundtrip/README.md @@ -4,10 +4,10 @@ [PEtab](https://petab.readthedocs.io) is a community standard for specifying parameter-estimation problems (model + data + observables + parameters) in a -tool-independent way. PyBNF speaks **PEtab v2**, and — uniquely — it can use a -**BNGL** model as the PEtab model, via a small loader it registers into the -`petab` library (`pybnf.petab.bngl_model.register_bngl`). This lesson shows the -full round trip and the validation ("lint") path. +tool-independent way. PyBNF speaks **PEtab v2**, and it can use a **BNGL** +model as the PEtab model: the `petab` library loads `language: bngl` natively +(since petab 0.9.0, through a loader PyBNF contributed upstream). This lesson +shows the full round trip and the validation ("lint") path. ## What a PEtab v2 problem looks like @@ -31,17 +31,15 @@ from pybnf.petab import export_job export_job("bateman_chain_de.conf", "petab/") # run from 02_bateman_chain/ ``` -## Lint it (dogfood the BNGL loader) +## Lint it -Because PyBNF registers a BNGL loader, the standard `petab` validator can load -and check a `language: bngl` problem: +Because `petab` loads BNGL natively, its standard validator can load and check +a `language: bngl` problem: ```python -from pybnf.petab.bngl_model import register_bngl from petab.v2 import Problem from petab.v2.lint import lint_problem -register_bngl() # teach petab about BNGL problem = Problem.from_yaml("petab/problem.yaml") report = lint_problem(problem) assert not report.has_errors() # cross-checks pass @@ -51,10 +49,11 @@ The model-level validity check shells out to `BNG2.pl --check` (the real BNGL validator) when a BioNetGen is available, and degrades gracefully to "valid" when it isn't — so validation never falsely fails for lack of a backend. -> This is the linter we intend to contribute upstream to -> [libpetab-python](https://github.com/PEtab-dev/libpetab-python); exercising it -> across the tutorial models (and the analytical-ODE catalog) is how we build -> confidence in it first. +> This loader was contributed upstream to +> [libpetab-python](https://github.com/PEtab-dev/libpetab-python) +> (PEtab-dev/libpetab-python#508) and ships in petab 0.9.0; exercising it across +> the tutorial models (and the analytical-ODE catalog) is how we built confidence +> in it first. ## Import a PEtab v2 problem → a runnable PyBNF job diff --git a/examples/tutorial/13_petab_lint_clinic/README.md b/examples/tutorial/13_petab_lint_clinic/README.md index a668860f1..f743ef15b 100644 --- a/examples/tutorial/13_petab_lint_clinic/README.md +++ b/examples/tutorial/13_petab_lint_clinic/README.md @@ -1,18 +1,17 @@ # Lesson 13 — A PEtab lint clinic (see the validator catch mistakes) -**Feature:** PEtab v2 validation (`petab.v2.lint`) through PyBNF's BNGL loader · **Difficulty:** ★★☆ +**Feature:** PEtab v2 validation (`petab.v2.lint`) through petab's native BNGL loader · **Difficulty:** ★★☆ Lesson 12 showed the *happy path*: export a job, lint it, get silence. This lesson is the opposite — a **gallery of broken problems**, each with exactly one defect, so you can watch the linter catch each one and learn to read what it says. -Why a whole lesson on *broken* problems? Because PyBNF registers a **BNGL model -loader** into the `petab` library (`pybnf.petab.bngl_model.register_bngl`), the +Why a whole lesson on *broken* problems? Because the `petab` library loads a +**BNGL model** natively (a loader PyBNF contributed upstream to +[libpetab-python](https://github.com/PEtab-dev/libpetab-python), issue #420), the standard `petab.v2` validator can load and check a `language: bngl` problem — and we want hard evidence that petab's lint tasks actually catch the mistakes a -BNGL-native problem can make, *before* we propose the loader upstream to -[libpetab-python](https://github.com/PEtab-dev/libpetab-python) (issue #420). This -clinic is that evidence: one fixture per lint task, each asserted in +BNGL-native problem can make. This clinic is that evidence: one fixture per lint task, each asserted in [`tests/test_tutorial_lint_clinic.py`](../../../tests/test_tutorial_lint_clinic.py). ## The gallery @@ -60,9 +59,7 @@ flagged, so a co-firing sibling is fine. ```python from petab.v2 import Problem from petab.v2.lint import lint_problem -from pybnf.petab.bngl_model import register_bngl -register_bngl() # teach petab about BNGL report = lint_problem(Problem.from_yaml("undefined_observable/problem.yaml")) print(report.has_errors()) # -> True for item in report: diff --git a/examples/tutorial/13_petab_lint_clinic/regenerate_fixtures.py b/examples/tutorial/13_petab_lint_clinic/regenerate_fixtures.py index 2fa714497..e286150a2 100644 --- a/examples/tutorial/13_petab_lint_clinic/regenerate_fixtures.py +++ b/examples/tutorial/13_petab_lint_clinic/regenerate_fixtures.py @@ -6,12 +6,11 @@ baseline that lints without complaint, and a gallery of ``*/`` variants each carrying exactly ONE defect that a specific ``petab.v2.lint`` task must flag. -The point is dogfooding: PyBNF registers a BNGL model loader into ``petab`` -(``pybnf.petab.bngl_model.register_bngl``), so the standard petab validator can -load and check a ``language: bngl`` problem. This clinic proves that with that -loader in place, petab's own lint tasks correctly catch the mistakes a -BNGL-native problem can make -- exactly the confidence we want before proposing -the loader upstream to libpetab-python (issue #420). +The point is dogfooding: ``petab`` loads a ``language: bngl`` model natively +(the loader PyBNF contributed upstream to libpetab-python, issue #420), so the +standard petab validator can load and check a BNGL-native problem. This clinic +proves that petab's own lint tasks correctly catch the mistakes such a problem +can make. The *expected outcome* of each fixture (which lint task flags it, or that it raises at load) is recorded test-side in ``examples/tutorial/_manifest.py`` diff --git a/pybnf/petab/_bngl.py b/pybnf/petab/_bngl.py index 17cfcd471..b92fc7535 100644 --- a/pybnf/petab/_bngl.py +++ b/pybnf/petab/_bngl.py @@ -4,13 +4,14 @@ ``begin/end `` scanner that enumerates the *named entities* of a model (parameters with their values, observables, global functions, molecule types, seed species, compartments) without BNG2.pl, network generation, or ``bngsim``. -It exists so the exporter (:mod:`pybnf.petab.export`) and the PEtab ``Model`` -adapter (:mod:`pybnf.petab.bngl_model`) share *one* reader rather than two that -drift -- the neutral-seam discipline ADR-0025 used for ``PetabParameterRow``. - -Validation needs only *parsing*, never simulation, so this is enough to back the -PEtab ``Model`` ABC (the one method that wants more, ``is_valid``, shells out to -``BNG2.pl --check`` separately; see :mod:`pybnf.petab.bngl_model`). The entity +It exists so the exporter (:mod:`pybnf.petab.export`) and the importer +(:mod:`pybnf.petab.import_`) share *one* reader rather than two that drift -- the +neutral-seam discipline ADR-0025 used for ``PetabParameterRow``. + +Validation needs only *parsing*, never simulation, so this was enough to back the +PEtab ``Model`` ABC while PyBNF carried its own adapter (the one method that wants +more, ``is_valid``, shells out to ``BNG2.pl --check``; that adapter now lives +upstream, see the drift note). The entity sets were fixed against BNG2.pl's ``Perl2/`` modules, not the PySB analogy: expression symbols are exactly the ``ParamList`` (parameters, observables, global functions), and compartments are *not* expression symbols (ADR-0026). @@ -21,9 +22,11 @@ ``species`` block alias (``begin species`` = ``begin seed species``), the seed- species ``$`` clamp marker, and the observable/function/compartment line shapes. -**Drift note (#420 Step B):** this reader has an upstream twin — the standalone, -pybnf-free port now merged into ``libpetab-python`` main -(``petab/v1/models/bngl_model.py``, PEtab-dev/libpetab-python#508). The two carry +**Drift note (#420 Step B, #591):** this reader has an upstream twin — the +standalone, pybnf-free port shipped in ``petab`` since 0.9.0 +(``petab/v1/models/bngl_model.py``, PEtab-dev/libpetab-python#508), which now backs +the PEtab ``Model`` ABC for ``language: bngl``; PyBNF's local adapter and its +``register_bngl()`` shim were retired with the ``petab >= 0.9`` floor. The two carry the *same* entity-enumeration semantics and grammar hardening; any change here (e.g. a block alias or pattern-modifier rule) must be ported upstream, guarded by the mirrored grammar-hardening tests on both sides. diff --git a/pybnf/petab/_bngl_expr.py b/pybnf/petab/_bngl_expr.py index 57ddfcb2a..ae4ca95b7 100644 --- a/pybnf/petab/_bngl_expr.py +++ b/pybnf/petab/_bngl_expr.py @@ -38,8 +38,12 @@ with the obvious injection problem. This module is deliberately self-contained: stdlib only, and no imports from -the rest of PyBNF, so that it can move to ``libpetab-python`` alongside the -BNGL model adapter it serves (see #591, #420 Step B). +the rest of PyBNF, so that it can move to ``libpetab-python``. Its consumer, the +local ``BnglModel`` adapter, was retired in #591 once petab 0.9.0 shipped the +loader upstream, and petab's native ``BnglModel`` does not yet evaluate an +expression-valued parameter (``get_parameter_value`` raises +``NotImplementedError`` and ``get_free_parameter_ids_with_values`` skips it), so +this evaluator is the staging copy for that upstream port (#666, #420 Step B). """ from __future__ import annotations diff --git a/pybnf/petab/bngl_model.py b/pybnf/petab/bngl_model.py deleted file mode 100644 index 88e4e992c..000000000 --- a/pybnf/petab/bngl_model.py +++ /dev/null @@ -1,216 +0,0 @@ -"""A PEtab ``Model`` adapter for BNGL, registered into ``petab`` at runtime (ADR-0026). - -``petab`` 0.8.x ships only ``sbml``/``pysb`` model loaders, so ``petablint`` cannot -load a ``language: bngl`` problem -- it dies at model-load before any table check. -:class:`BnglModel` is the missing loader: a :class:`petab.v1.models.model.Model` -backed by PyBNF's own stdlib BNGL reader (:mod:`pybnf.petab._bngl`), so the PEtab v2 -exporter's emitted problems validate at *model* level (the ~5 model-cross checks -ADR-0025 had to exclude), with no BNG2.pl needed for the enumeration. - -This is **Step A** of issue #420 -- a local reference implementation. :func:`register_bngl` -teaches a running ``petab`` about BNGL by rebinding ``petab.v2.core.model_factory`` -(petab's factory is a hardcoded ``if/elif`` with no plugin hook). **Step B** upstreams -the same class to ``PEtab-dev/libpetab-python`` as a peer of ``PySBModel``; once that -lands, :func:`register_bngl` collapses to a no-op. - -Validation needs only *parsing*, so every ABC method is backed by the parsed entity -sets -- except :meth:`BnglModel.is_valid`, which shells out to ``BNG2.pl --check`` (the -real validator) when a BNG2.pl is locatable, and degrades gracefully to ``True`` when -it is not. This module imports ``petab`` and is therefore **not** imported by -``pybnf.petab.__init__``; it is reached only from the test tier / an explicit -``register_bngl()`` call, keeping core dependency-free (ADR-0025). -""" - -import os -import shutil -import subprocess -import warnings -from pathlib import Path - -from petab.v1.models.model import Model - -from ._bngl import parse_model -from ._bngl_expr import evaluate_parameters_partial - -#: BNGL model type, as used in a PEtab v2 yaml file as ``language``. -MODEL_TYPE_BNGL = 'bngl' - - -class BnglModel(Model): - """PEtab ``Model`` wrapper for a BNGL model (validation-grade; no simulation).""" - - type_id = MODEL_TYPE_BNGL - - def __init__(self, entities, model_id, path=None): - super().__init__() - self._entities = entities - self._model_id = model_id - self._path = Path(path) if path is not None else None - self._resolved_parameters = None - - @staticmethod - def from_file(filepath_or_buffer, model_id=None, base_path=None): - path = Path(filepath_or_buffer) - if base_path is not None and not path.is_absolute(): - path = Path(base_path) / path - text = path.read_text(encoding='utf-8', errors='replace') - return BnglModel(parse_model(text), model_id=model_id or path.stem, path=path) - - def to_file(self, filename=None): - target = Path(filename) if filename is not None else self._path - if target is None: - raise ValueError("No filename given and this BnglModel has no source path.") - target.write_text(self._entities.text, encoding='utf-8') - - @property - def model_id(self): - return self._model_id - - # -- parameters --------------------------------------------------------- - - def get_parameter_ids(self): - return list(self._entities.parameters) - - def _parameter_values(self): - """``(values, errors)`` for the parameters block, computed once and cached. - - A parameters block is arithmetic over other parameters, so this needs - no BNG2.pl and no network generation; see :mod:`pybnf.petab._bngl_expr`. - Resolution is *partial*: one unusable definition costs that parameter - and whatever depends on it, not the whole block. A real model can carry - a construct we do not evaluate (an NFsim ``TFUN``, say), and taking the - other 16 parameters down with it would be a worse failure than the one - #666 set out to fix. - """ - if self._resolved_parameters is None: - self._resolved_parameters = evaluate_parameters_partial( - dict(self._entities.parameters) - ) - return self._resolved_parameters - - def get_parameter_value(self, id_): - if id_ not in self._entities.parameters: - raise ValueError(f"Parameter {id_} does not exist.") - values, errors = self._parameter_values() - if id_ in values: - return values[id_] - raise ValueError( - f"Parameter '{id_}' has an expression value " - f"'{self._entities.parameters[id_]}' that could not be evaluated: " - f"{errors[id_]}" - ) - - def get_free_parameter_ids_with_values(self): - # Expression-valued parameters used to be skipped here, which lost them - # from the PEtab problem with nothing said (#666). They are resolved - # now, and anything still unusable is named in a warning rather than - # disappearing -- but it no longer takes the rest of the block with it. - values, errors = self._parameter_values() - if errors: - detail = '; '.join(f'{name} ({errors[name]})' for name in sorted(errors)) - warnings.warn( - f"Model {self._model_id!r}: {len(errors)} of " - f"{len(self._entities.parameters)} parameters could not be evaluated " - f"and are omitted from the PEtab problem: {detail}", - stacklevel=2, - ) - return [(name, values[name]) - for name in self._entities.parameters if name in values] - - def get_valid_parameters_for_parameter_table(self): - return list(self._entities.parameters) - - # -- model-entity namespaces (verified against BNG2.pl; ADR-0026) ------- - - def has_entity_with_id(self, entity_id): - """Any declared identifier: the full BNGL model-entity namespace.""" - e = self._entities - return (entity_id in e.parameters - or entity_id in e.observable_names - or entity_id in e.function_names - or entity_id in e.molecule_type_names - or entity_id in e.compartment_names - or entity_id in e.seed_species) - - def symbol_allowed_in_observable_formula(self, id_): - """The BNG ``ParamList``: parameters, observables, global functions only.""" - e = self._entities - return (id_ in e.parameters - or id_ in e.observable_names - or id_ in e.function_names) - - def get_valid_ids_for_condition_table(self): - return list(self._entities.parameters) + list(self._entities.compartment_names) - - def is_state_variable(self, id_): - """A species. At validation grade, only the concrete ``seed species`` are known - (the full set is a network-generation product, out of scope; ADR-0026).""" - return id_ in self._entities.seed_species - - # -- validity ----------------------------------------------------------- - - def is_valid(self): - """``BNG2.pl --check`` (real validation, no network gen) when locatable, else - ``True`` -- never a false failure where no BNG backend is available.""" - bng2 = _locate_bng2() - if bng2 is None or self._path is None: - return True - try: - result = subprocess.run( - [bng2, '--check', str(self._path)], - capture_output=True, text=True, timeout=120, - cwd=str(self._path.parent), - ) - except (OSError, subprocess.SubprocessError): - return True # a tooling hiccup must not masquerade as an invalid model - return result.returncode == 0 - - -def _locate_bng2(): - """A path to ``BNG2.pl`` via ``BNGPATH`` or ``PATH``, or ``None`` if unavailable.""" - bngpath = os.environ.get('BNGPATH') - if bngpath: - candidate = Path(bngpath) / 'BNG2.pl' - if candidate.is_file(): - return str(candidate) - return shutil.which('BNG2.pl') - - -def register_bngl(): - """Teach a running ``petab`` to load ``language: bngl`` models via :class:`BnglModel`. - - Idempotent and additive: rebinds ``petab.v2.core.model_factory`` to route ``bngl`` - to :class:`BnglModel` and delegate every other language to the captured original - (so ``sbml``/``pysb`` are untouched), and registers ``'bngl'`` as a known model - type. A guarded permanent rebind (no teardown) -- the temporary local stand-in for - Step B's upstream loader (ADR-0026). - """ - import petab.v1.models as _models - import petab.v2.core as _v2core - - # If petab already supports BNGL natively (a libpetab-python build that - # shipped the upstream loader -- #420 Step B), do nothing: its model_factory - # already routes 'bngl' to a native BnglModel that supersedes this local - # stand-in. This is the "collapse to a no-op" ADR-0026 anticipated; it is - # what lets the exporter tests dogfood the native loader. We distinguish - # native support (bngl present, our wrapper never installed) from our own - # prior registration (the sentinel below) so re-entry stays idempotent. - already_registered = hasattr(_v2core, '_pybnf_orig_model_factory') - if MODEL_TYPE_BNGL in _models.known_model_types and not already_registered: - return - - _models.known_model_types.add(MODEL_TYPE_BNGL) - - original = getattr(_v2core, '_pybnf_orig_model_factory', None) - if original is None: - original = _v2core.model_factory - _v2core._pybnf_orig_model_factory = original - - def _model_factory(filepath_or_buffer, model_language, model_id=None, base_path=None): - if model_language == MODEL_TYPE_BNGL: - return BnglModel.from_file( - filepath_or_buffer, model_id=model_id, base_path=base_path) - return original( - filepath_or_buffer, model_language, model_id=model_id, base_path=base_path) - - _v2core.model_factory = _model_factory diff --git a/pyproject.toml b/pyproject.toml index 1374b5c63..6ca09de7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "pybnf" dynamic = ["version"] description = "An application for parallel fitting of BioNetGen and SBML models using metaheuristics" readme = { file = "README.md", content-type = "text/markdown" } -requires-python = ">=3.11" +requires-python = ">=3.12" license = "BSD-3-Clause" license-files = ["LICENSE"] authors = [ @@ -25,7 +25,6 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", @@ -54,10 +53,12 @@ antimony = [ # (petab.v2.math). OPTIONAL runtime extra -- only the expression import/export path # imports petab; the bare-name observableFormula path and core stay dependency-free # (ADR-0019). Stock PyPI petab carries the math grammar, so this is a normal extra a -# user installs with `pip install pybnf[petab]` (the test extra below overrides the -# pin with upstream libpetab-python main for the BnglModel validation oracle). +# user installs with `pip install pybnf[petab]`. The 0.9 floor is the release that +# ships the native `language: bngl` loader (PEtab-dev/libpetab-python#508), so +# PyBNF no longer carries its own BnglModel adapter / register_bngl() shim (#591); +# petab 0.9.0 also sets the project's Python floor, since it requires >= 3.12. petab = [ - "petab>=0.8,<1", + "petab>=0.9,<1", ] # The gradient-based reference sampler (job_type = hmc, ADR-0059): blackjax NUTS on # the analytical model's JAX log-density, for evaluating PyBNF's gradient-free @@ -112,19 +113,14 @@ tests = [ # PEtab v2 export oracle (ADR-0025/0026) + the math translator (ADR-0035): the # exporter tests validate emitted problems against petab's own checks, and the # formula tests use petab.v2.math. Test-only -- core stays dependency-free. - # Stock PyPI petab, so this extra (and therefore the published wheel's - # Requires-Dist) carries NO direct URL reference -- PyPI rejects any distribution - # whose metadata names a `git+` ref. Upstream libpetab-python main, where the - # native `language: bngl` loader merged (PEtab-dev/libpetab-python#508 -- it - # validates the exporter oracle at MODEL level rather than via the register_bngl() - # monkeypatch), is NOT pinned here: it is injected only on the dedicated CI leg - # through the setup-pybnf action's `petab-spec` input (see - # .github/workflows/tests.yml). The latest PyPI petab (0.8.2) does not yet carry - # BNGL, so register_bngl() stays a working fallback for stock petab; once a - # release ships it, that override -- and the monkeypatch -- go away. The + # Stock PyPI petab, same floor as the runtime extra above: 0.9.0 is the first + # release with the native `language: bngl` loader, so the exporter oracle loads + # BNGL problems through petab's OWN BnglModel (the local adapter, its + # register_bngl() monkeypatch, and the CI leg that used to install upstream + # libpetab-python main to reach the native loader are all gone, #591). The # oracle/formula tests `pytest.importorskip('petab')`, so this is not a hard test # requirement. - "petab>=0.8,<1", + "petab>=0.9,<1", # ArviZ InferenceData bridge oracle (ADR-0055): tests/test_inference_data.py # builds real containers and round-trips them through netCDF, so the bridge is # exercised in CI rather than skipped. importorskip('arviz') keeps it non-fatal @@ -172,7 +168,7 @@ include = ["pybnf*"] version = { attr = "pybnf.__version__" } [tool.ruff] -target-version = "py311" +target-version = "py312" # Pins ruff's default rule set (pycodestyle E4/E7/E9 + pyflakes F) so local runs # and CI agree. Line length (E501) and the broader style/lint families are # intentionally left off for v1; revisit once the codebase is reformatted. diff --git a/tests/_bngl_differential.py b/tests/_bngl_differential.py index f12c10b1d..88b7adc5f 100644 --- a/tests/_bngl_differential.py +++ b/tests/_bngl_differential.py @@ -13,9 +13,10 @@ python tests/_bngl_differential.py -Needs a BNG2.pl on ``BNGPATH``/``PATH`` (reuses ``bngl_model._locate_bng2``). +Needs a BNG2.pl on ``BNGPATH``/``PATH`` (:func:`_locate_bng2` below). """ +import os import re import shutil import subprocess @@ -24,7 +25,16 @@ from pathlib import Path from pybnf.petab._bngl import parse_model -from pybnf.petab.bngl_model import _locate_bng2 + + +def _locate_bng2(): + """A path to ``BNG2.pl`` via ``BNGPATH`` or ``PATH``, or ``None`` if unavailable.""" + bngpath = os.environ.get('BNGPATH') + if bngpath: + candidate = Path(bngpath) / 'BNG2.pl' + if candidate.is_file(): + return str(candidate) + return shutil.which('BNG2.pl') # Blocks that define model entities; everything else (actions, directives) is # dropped before handing the model to BNG2.pl so no network is generated. diff --git a/tests/full_tests/cluster.sh b/tests/full_tests/cluster.sh index e56b52520..4ee5d0779 100644 --- a/tests/full_tests/cluster.sh +++ b/tests/full_tests/cluster.sh @@ -23,7 +23,7 @@ # Uncomment and edit one of these lines, or add your own: # source /path/to/your/virtualenv/bin/activate # conda activate your-env-name -# module load python/3.11 +# module load python/3.12 # # Example (edit the path): # source $HOME/path/to/pybnf-env/bin/activate diff --git a/tests/full_tests/cluster_manual.sh b/tests/full_tests/cluster_manual.sh index 5a3cc3fbf..f6103b7ab 100644 --- a/tests/full_tests/cluster_manual.sh +++ b/tests/full_tests/cluster_manual.sh @@ -32,7 +32,7 @@ THREADS_PER_WORKER=1 # Uncomment and edit one of these lines, or add your own: # source /path/to/your/virtualenv/bin/activate # conda activate your-env-name -# module load python/3.11 +# module load python/3.12 # # Example (edit the path): # source $HOME/path/to/pybnf-env/bin/activate diff --git a/tests/test_new_era_validation.py b/tests/test_new_era_validation.py index bf7ec29ec..c184aa6bf 100644 --- a/tests/test_new_era_validation.py +++ b/tests/test_new_era_validation.py @@ -66,15 +66,13 @@ def _petab_validation_errors(problem_yaml): The model-level external oracle, identical to ``test_petab_export``'s: load the whole problem via ``Problem.from_yaml`` (the real petablint path -- ``model_factory`` -> - ``BnglModel.from_file`` -> ``BNG2.pl --check``, after ``register_bngl()``), then run + ``BnglModel.from_file`` -> ``BNG2.pl --check``, through petab's native BNGL loader), then run **every** ``default_validation_task`` (the model-cross checks included). """ pytest.importorskip('petab.v2') from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - from pybnf.petab.bngl_model import register_bngl - register_bngl() problem = Problem.from_yaml(str(problem_yaml)) assert type(problem.model).__name__ == 'BnglModel' # the BNGL loader ran errors = [] diff --git a/tests/test_packaging_metadata.py b/tests/test_packaging_metadata.py index c43b95c2d..660fb624a 100644 --- a/tests/test_packaging_metadata.py +++ b/tests/test_packaging_metadata.py @@ -17,7 +17,7 @@ def test_project_metadata_declares_python_floor_and_bngsim_dependency(): metadata = tomllib.loads(pyproject_path.read_text()) project = metadata['project'] - assert project['requires-python'] == '>=3.11' + assert project['requires-python'] == '>=3.12' # bngsim >= 0.15.0 is bought by a CONTRACT, not by a feature PyBNF wants. lanl/bngsim#431 # publishes `event_sensitivities` as a real capabilities() feature key. Until it existed # PyBNF read `effective_ic_sensitivity` as a WITNESS for the same thing (ADR-0119), which @@ -68,7 +68,7 @@ def _requirements_quoted_in(text): The CI action is a shell script embedded in YAML, so its requirements are ordinary single-quoted shell words -- in the `uv pip install` argument list, in the BNGSIM_SPEC - and JAX_SPEC arrays, and in the `petab-spec` input's default. Quotes are matched + and JAX_SPEC arrays. Quotes are matched within a single line and comment lines are dropped, because the surrounding YAML prose is full of apostrophes ("pyproject.toml's", "the action's") that otherwise pair up with each other and swallow the real strings. diff --git a/tests/test_petab_bngl_corpus.py b/tests/test_petab_bngl_corpus.py index 1ce401fae..ee522a1d8 100644 --- a/tests/test_petab_bngl_corpus.py +++ b/tests/test_petab_bngl_corpus.py @@ -3,8 +3,8 @@ Runs the ``writeModel`` differential (:mod:`tests._bngl_differential`) over a curated set of *public* community BNGL models committed under ``tests/petab_fixtures/bngl_corpus/``, asserting our reader -(:func:`pybnf.petab._bngl.parse_model`, the one backing the ``BnglModel`` PEtab -linter) enumerates the same entities BNG2.pl's canonical parse does. This locks in +(:func:`pybnf.petab._bngl.parse_model`, the twin of the reader behind petab's +native ``BnglModel``) enumerates the same entities BNG2.pl's canonical parse does. This locks in the parity the corpus differential established -- so a future "simplification" of the reader that silently drops line-continuation, line-label, alias, or ``$``-clamp handling fails here. @@ -20,9 +20,8 @@ import pytest -from pybnf.petab.bngl_model import BnglModel, _locate_bng2 - from . import _bngl_differential as diff +from ._bngl_differential import _locate_bng2 _FIXTURES = Path(__file__).parent / 'petab_fixtures' / 'bngl_corpus' _BNG2 = _locate_bng2() @@ -62,6 +61,9 @@ def test_reader_agrees_with_bng2(model): @pytest.mark.parametrize('model', _curated(), ids=lambda p: p.stem) def test_curated_models_are_valid(model): - # Exercises BnglModel.is_valid() (a real `BNG2.pl --check`) over real models, - # complementing the faked-subprocess unit tests: every curated model is valid. - assert BnglModel.from_file(model).is_valid() is True + # Exercises petab's native BnglModel.is_valid() (a real `BNG2.pl --check`) + # over real models: every curated model is valid. The id is given explicitly + # because petab derives it from the file stem and requires a PEtab identifier, + # which the hyphenated corpus filenames are not; the check is about the model. + bngl_model = pytest.importorskip('petab.v1.models.bngl_model') + assert bngl_model.BnglModel.from_file(model, model_id='m').is_valid() is True diff --git a/tests/test_petab_bngl_expr.py b/tests/test_petab_bngl_expr.py index abf0a3b8e..21c7101d9 100644 --- a/tests/test_petab_bngl_expr.py +++ b/tests/test_petab_bngl_expr.py @@ -28,7 +28,6 @@ evaluate_parameters, evaluate_parameters_partial, ) -from pybnf.petab.bngl_model import BnglModel #: ``(expression, value BNG2.pl computes)``. Self-contained, so each one can be #: dropped straight into a parameters block. @@ -207,14 +206,14 @@ def test_expression_valued_parameter_is_resolved(): end parameters end model """ - m = BnglModel(parse_model(text), model_id='demo') + declared = parse_model(text).parameters + values = evaluate_parameters(declared) - assert m.get_parameter_value('kon') == pytest.approx(0.1 / (5.0 * 6.022e23 * 1e-12)) + assert values['kon'] == pytest.approx(0.1 / (5.0 * 6.022e23 * 1e-12)) - # The parameter used to be dropped from this list entirely. - ids = [name for name, _ in m.get_free_parameter_ids_with_values()] - assert ids == list(m.get_parameter_ids()) - assert 'kon' in ids + # The parameter used to be dropped entirely; every declared name resolves. + assert list(values) == list(declared) + assert 'kon' in values def test_declaration_order_does_not_matter(): @@ -317,40 +316,12 @@ def test_one_unevaluable_parameter_does_not_take_down_the_model(): end parameters end model """ - m = BnglModel(parse_model(text), model_id='demo') - with pytest.warns(UserWarning, match='could not be evaluated'): - pairs = dict(m.get_free_parameter_ids_with_values()) - assert pairs == {'good1': 2.0, 'good2': 6.0} - - -def test_unevaluable_parameter_surfaces_from_the_model(): - """The adapter reports the failure instead of dropping the parameter.""" - text = """ -begin model -begin parameters - a b -end parameters -end model -""" - m = BnglModel(parse_model(text), model_id='demo') - with pytest.raises(ValueError, match='could not be evaluated'): - m.get_parameter_value('a') - - -def test_missing_parameter_still_raises_value_error(): - text = """ -begin model -begin parameters - a 1 -end parameters -end model -""" - m = BnglModel(parse_model(text), model_id='demo') - with pytest.raises(ValueError, match='does not exist'): - m.get_parameter_value('nope') + values, errors = evaluate_parameters_partial(parse_model(text).parameters) + assert values == {'good1': 2.0, 'good2': 6.0} + assert set(errors) == {'bad'} -def test_fully_resolvable_model_warns_about_nothing(): +def test_fully_resolvable_block_reports_no_errors(): text = """ begin model begin parameters @@ -359,8 +330,6 @@ def test_fully_resolvable_model_warns_about_nothing(): end parameters end model """ - m = BnglModel(parse_model(text), model_id='demo') - import warnings as _w - with _w.catch_warnings(): - _w.simplefilter('error') - assert dict(m.get_free_parameter_ids_with_values()) == {'a': 2.0, 'b': 6.0} + values, errors = evaluate_parameters_partial(parse_model(text).parameters) + assert values == {'a': 2.0, 'b': 6.0} + assert errors == {} diff --git a/tests/test_petab_export.py b/tests/test_petab_export.py index f885684b6..cf4ce1f69 100644 --- a/tests/test_petab_export.py +++ b/tests/test_petab_export.py @@ -5,8 +5,9 @@ Its contracts, by strength of oracle: 1. **The external oracle, at model level.** petab's own validation, run on the whole - problem loaded via ``Problem.from_yaml`` after ``register_bngl()`` installs the - ``BnglModel`` loader (ADR-0026). The exported ``demo`` problem must pass **every** + problem loaded via ``Problem.from_yaml`` through petab's native ``BnglModel`` + loader (ADR-0026; upstream since petab 0.9.0, #591). The exported ``demo`` problem + must pass **every** ``default_validation_task`` -- including the model-cross checks (``CheckModel`` et al.) ADR-0025 had to exclude -- with no errors. 2. **The measurement pivot is exact.** Every long measurement cell equals the source @@ -83,8 +84,8 @@ def _petab_validation_errors(problem_yaml): """Load a problem via the real petablint path and return its ERROR-level issues. The model-level external oracle: ``Problem.from_yaml`` exercises - ``model_factory -> BnglModel -> BNG2.pl --check`` (after ``register_bngl()``, - which is a no-op on a petab that ships BNGL natively, #420 Step B), then runs + ``model_factory -> BnglModel -> BNG2.pl --check`` through petab's native BNGL + loader (upstream since petab 0.9.0; #420 Step B, #591), then runs **every** ``default_validation_task`` -- the model-cross checks included. Returns ``[(task, message), ...]``; empty means a clean problem. """ @@ -92,8 +93,6 @@ def _petab_validation_errors(problem_yaml): from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - from pybnf.petab.bngl_model import register_bngl - register_bngl() problem = Problem.from_yaml(str(problem_yaml)) assert type(problem.model).__name__ == 'BnglModel' # the BNGL loader ran errors = [] @@ -305,25 +304,14 @@ def test_full_petab_validation_is_clean(self, exported): # Problem.from_yaml (the real petablint path -- exercises model_factory -> # BnglModel.from_file -> BNG2.pl --check), run ALL tasks. pytest.importorskip('petab.v2') # the v2 typed-table API the oracle needs - import petab.v1.models as models - import petab.v2.core as v2core from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - # register_bngl() teaches a stock petab about BNGL, and is a no-op on a - # petab that ships it natively (#420 Step B) -- so this same path validates - # against both. We dogfood the fork: when petab is native (bngl known and - # our wrapper never installed), the loaded model must be petab's OWN - # BnglModel, not our local stand-in. - native = ('bngl' in models.known_model_types - and not hasattr(v2core, '_pybnf_orig_model_factory')) - from pybnf.petab.bngl_model import register_bngl - register_bngl() - + # petab loads `language: bngl` through its own native BnglModel (upstream + # since petab 0.9.0; #420 Step B, #591): the loaded model must be petab's. problem = Problem.from_yaml(str(exported / 'problem.yaml')) assert type(problem.model).__name__ == 'BnglModel' # the BNGL loader ran - if native: - assert type(problem.model).__module__.startswith('petab.') + assert type(problem.model).__module__.startswith('petab.') errors = [] for task in default_validation_tasks: @@ -1520,8 +1508,6 @@ def _petab_multimodel_validation_errors(problem_yaml): from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - from pybnf.petab.bngl_model import register_bngl - register_bngl() problem = Problem.from_yaml(str(problem_yaml)) assert len(problem.models) > 1 # the multi-model problem loaded errors = [] @@ -2080,33 +2066,43 @@ def test_bnglmodel_state_variable_ignores_the_clamp(self): # variable under its bare id (is_state_variable drives CheckModel's species # cross-checks in petablint). pytest.importorskip('petab') - from pybnf.petab._bngl import parse_model - from pybnf.petab.bngl_model import BnglModel + from petab.v1.models.bngl_model import BnglModel, parse_bngl model = BnglModel( - parse_model('begin seed species\n $A() 100\nend seed species\n'), + parse_bngl('begin seed species\n $A() 100\nend seed species\n'), model_id='m') assert model.is_state_variable('A()') assert not model.is_state_variable('$A()') # --------------------------------------------------------------------------- -# 4'. The BnglModel adapter ABC, unit-tested directly (ADR-0026 -- the model-level -# guarantees the table oracle now checks externally, asserted method by method). +# 4'. petab's native BnglModel, pinned at the ABC seam the exporter relies on +# (ADR-0026). PyBNF's local adapter and its register_bngl() monkeypatch were +# retired in #591 once petab 0.9.0 shipped the loader upstream; these are the +# model-level semantics the table oracle checks externally, asserted method by +# method, as verified against BNG2.pl when the adapter was written. # --------------------------------------------------------------------------- -class TestBnglModel: +class TestNativeBnglModel: @pytest.fixture def model(self, tmp_path): - # A BnglModel parsed from the exported (cleaned, numeric-nominal) demo model -- - # the same file Problem.from_yaml loads. + # petab's BnglModel parsed from the exported (cleaned, numeric-nominal) demo + # model -- the same file Problem.from_yaml loads. pytest.importorskip('petab') - from pybnf.petab.bngl_model import BnglModel + from petab.v1.models.bngl_model import BnglModel from pybnf.petab.export import export_job out = tmp_path / 'p' export_job(DEMO_CONF, out) return BnglModel.from_file(out / DEMO_MODEL) + def test_petab_ships_the_bngl_loader_natively(self): + # The #591 trigger, pinned: stock petab (>= 0.9.0) knows `bngl`. Were this + # to fail, the installed petab predates the native loader and every + # `language: bngl` oracle in this file would raise "Unknown model format". + pytest.importorskip('petab') + import petab.v1.models as models + assert 'bngl' in models.known_model_types + def test_parameter_ids_and_values(self, model): # The exported model is carried verbatim (ADR-0034), so the parameter values are # the model's real nominals (parabola_v2.bngl: v1 0.5, v2 1, v3 3), not the old @@ -2141,130 +2137,6 @@ def test_is_state_variable_is_seed_species_only(self, model): assert not model.is_state_variable('v1') # a parameter is not a species assert not model.is_state_variable('x') # nor is an observable - def test_expression_valued_parameter_is_evaluated(self): - # Superseded by #666: an expression RHS used to raise NotImplementedError, - # and get_free_parameter_ids_with_values dropped the parameter without - # saying so. A parameters block is arithmetic over other parameters, so - # it is resolved here without BNG2.pl; see pybnf.petab._bngl_expr, whose - # semantics are pinned against a real BNG2.pl in test_petab_bngl_expr.py. - pytest.importorskip('petab') - from pybnf.petab._bngl import parse_model - from pybnf.petab.bngl_model import BnglModel - ent = parse_model( - "begin parameters\n base 2\n k_on 2*base\nend parameters\n") - model = BnglModel(ent, model_id='m') - assert model.get_parameter_value('base') == 2.0 # numeric RHS -> float - assert model.get_parameter_value('k_on') == 4.0 # expression RHS -> resolved - assert dict(model.get_free_parameter_ids_with_values()) == { - 'base': 2.0, 'k_on': 4.0, - } - - # -- is_valid: both contract paths pinned (#437) -------------------------- - # The contract (ADR-0026): shell to `BNG2.pl --check` when a BNG2.pl is - # locatable and the model has a path; otherwise degrade to True -- never a - # false failure where no BNG backend is available. Both paths are exercised - # by faking _locate_bng2 / subprocess.run, so neither needs a real BNG2.pl. - - def test_is_valid_true_when_bng2_not_locatable(self, model, monkeypatch): - # Degrade-to-True: no BNG2.pl on BNGPATH/PATH -> True even though `model` - # has a real source path to check. - import pybnf.petab.bngl_model as bm - monkeypatch.setattr(bm, '_locate_bng2', lambda: None) - assert model.is_valid() is True - - def test_is_valid_true_when_model_has_no_path(self, monkeypatch): - # An in-memory BnglModel has no file to --check; degrade to True even when a - # BNG2.pl IS locatable (nothing to hand it). - pytest.importorskip('petab') - import pybnf.petab.bngl_model as bm - from pybnf.petab._bngl import parse_model - monkeypatch.setattr(bm, '_locate_bng2', lambda: '/fake/BNG2.pl') - m = bm.BnglModel( - parse_model('begin parameters\n k 1\nend parameters\n'), model_id='m') - assert m.is_valid() is True - - def test_is_valid_shells_to_bng2_and_maps_returncode(self, model, monkeypatch): - # BNG2.pl-present path: is_valid is exactly `BNG2.pl --check ` exiting 0. - import pybnf.petab.bngl_model as bm - monkeypatch.setattr(bm, '_locate_bng2', lambda: '/fake/BNG2.pl') - seen = {} - - class _Result: - def __init__(self, rc): - self.returncode = rc - - def fake_run(cmd, **kwargs): - seen['cmd'] = cmd - return _Result(seen['rc']) - - monkeypatch.setattr(bm.subprocess, 'run', fake_run) - seen['rc'] = 0 - assert model.is_valid() is True - seen['rc'] = 1 - assert model.is_valid() is False - assert seen['cmd'][:2] == ['/fake/BNG2.pl', '--check'] # real invocation shape - - def test_is_valid_true_when_bng2_invocation_errors(self, model, monkeypatch): - # A tooling hiccup (OSError/SubprocessError from the subprocess) must not - # masquerade as an invalid model. - import pybnf.petab.bngl_model as bm - monkeypatch.setattr(bm, '_locate_bng2', lambda: '/fake/BNG2.pl') - - def boom(cmd, **kwargs): - raise OSError('perl not found') - - monkeypatch.setattr(bm.subprocess, 'run', boom) - assert model.is_valid() is True - - -class TestRegisterBngl: - - @staticmethod - def _petab_is_native(): - """True iff petab already supports BNGL (the #420 Step B fork) and we - have not installed our own wrapper -- i.e. register_bngl is a no-op.""" - import petab.v1.models as models - import petab.v2.core as v2core - return ('bngl' in models.known_model_types - and not hasattr(v2core, '_pybnf_orig_model_factory')) - - def test_native_support_makes_register_a_noop(self): - # Against a petab that ships BNGL natively (the dogfooded fork branch), - # register_bngl must leave model_factory untouched so the native loader - # wins -- the collapse-to-no-op of ADR-0026. - pytest.importorskip('petab.v2') - import petab.v2.core as v2core - from pybnf.petab.bngl_model import register_bngl - - if not self._petab_is_native(): - pytest.skip("petab does not support BNGL natively in this env") - - before = v2core.model_factory - register_bngl() - assert v2core.model_factory is before # not rebound - assert not hasattr(v2core, '_pybnf_orig_model_factory') # no sentinel - - def test_idempotent_guarded_rebind(self): - # Against a stock petab without native BNGL, register_bngl installs an - # idempotent wrapper that routes 'bngl' and delegates everything else. - pytest.importorskip('petab.v2') - import petab.v1.models as models - import petab.v2.core as v2core - from pybnf.petab.bngl_model import register_bngl - - if self._petab_is_native(): - pytest.skip("petab supports BNGL natively; register_bngl is a no-op") - - register_bngl() - wrapper = v2core.model_factory - captured = v2core._pybnf_orig_model_factory - assert wrapper is not captured # wrapper installed, original captured - assert 'bngl' in models.known_model_types - - register_bngl() # second call must not re-wrap - assert v2core._pybnf_orig_model_factory is captured - assert v2core.model_factory is not captured - # --------------------------------------------------------------------------- # Shared fixtures for the new-era condition tests (above) + the surrogate-mapping unit @@ -2311,8 +2183,6 @@ def _assert_petab_clean(exported): from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - from pybnf.petab.bngl_model import register_bngl - register_bngl() problem = Problem.from_yaml(str(exported / 'problem.yaml')) assert type(problem.model).__name__ == 'BnglModel' # the BNGL loader ran errors = [] diff --git a/tests/test_petab_formula.py b/tests/test_petab_formula.py index 1d4ad3c04..354fe04b3 100644 --- a/tests/test_petab_formula.py +++ b/tests/test_petab_formula.py @@ -273,8 +273,6 @@ def test_imported_expression_problem_passes_petab_validation(self, tmp_path): from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - from pybnf.petab.bngl_model import register_bngl - register_bngl() src = _write_crafted_src(tmp_path) p1, imported, p2 = tmp_path / 'p1', tmp_path / 'imp', tmp_path / 'p2' export_job(src / 'job.conf', p1, inline_functions=True) diff --git a/tests/test_petab_import.py b/tests/test_petab_import.py index 19f1de871..97ab82e5e 100644 --- a/tests/test_petab_import.py +++ b/tests/test_petab_import.py @@ -226,8 +226,6 @@ def test_imported_problem_passes_full_petab_validation(self, imported): from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - from pybnf.petab.bngl_model import register_bngl - register_bngl() _, _, petab2, _ = imported problem = Problem.from_yaml(str(petab2 / 'problem.yaml')) assert type(problem.model).__name__ == 'BnglModel' @@ -548,8 +546,6 @@ def test_imported_problem_passes_full_petab_validation(self, imported): from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - from pybnf.petab.bngl_model import register_bngl - register_bngl() _, _, petab2, _ = imported problem = Problem.from_yaml(str(petab2 / 'problem.yaml')) errors = [type(t).__name__ for t in default_validation_tasks @@ -860,8 +856,6 @@ def test_imported_problem_passes_full_petab_validation(self, imported): from petab.v2 import Problem from petab.v2.lint import ValidationIssueSeverity, default_validation_tasks - from pybnf.petab.bngl_model import register_bngl - register_bngl() _, _, petab2, _ = imported problem = Problem.from_yaml(str(petab2 / 'problem.yaml')) errors = [type(t).__name__ for t in default_validation_tasks @@ -1945,7 +1939,7 @@ def test_imported_boehm_reexports_to_clean_petab(self, tmp_path): assert obs[o]['noisePlaceholders'] == '' assert params[f'sd_{o}']['estimate'] == 'true' # The external oracle: the re-exported (SBML) problem validates via the real petablint - # path (register_bngl is a no-op for an SBML model). + # path. problem = Problem.from_yaml(str(out / 'problem.yaml')) errors = [type(t).__name__ for t in default_validation_tasks if (i := t.run(problem)) is not None diff --git a/tests/test_tutorial_lint_clinic.py b/tests/test_tutorial_lint_clinic.py index d7743f7a6..abbf3db20 100644 --- a/tests/test_tutorial_lint_clinic.py +++ b/tests/test_tutorial_lint_clinic.py @@ -4,8 +4,8 @@ (export a tutorial conf -> lint it clean), this module proves it is *loud* on broken ones: a gallery of tiny BNGL-native PEtab v2 fixtures, each carrying exactly one defect, and the assertion that the standard ``petab.v2`` validator -- -loading the ``language: bngl`` model through PyBNF's registered loader -(``register_bngl``) -- reacts as recorded in ``examples/tutorial/_manifest.py``: +loading the ``language: bngl`` model through petab's native BNGL loader -- +reacts as recorded in ``examples/tutorial/_manifest.py``: * ``clean`` -> ``lint_problem`` finds no errors; * ``error`` -> ``lint_problem`` finds errors, and the expected ``Check`` task @@ -33,10 +33,7 @@ from petab.v2 import Problem from petab.v2.lint import lint_problem, ValidationIssueSeverity -from pybnf.petab.bngl_model import register_bngl, _locate_bng2 - -# Teach petab to load `language: bngl` problems (idempotent). -register_bngl() +from petab.v1.models.bngl_model import _locate_bng2 _REPO_ROOT = Path(__file__).resolve().parents[1] _CLINIC = _REPO_ROOT / 'examples' / 'tutorial' / '13_petab_lint_clinic' diff --git a/tests/test_tutorial_petab.py b/tests/test_tutorial_petab.py index 1983131d5..0c52324a3 100644 --- a/tests/test_tutorial_petab.py +++ b/tests/test_tutorial_petab.py @@ -24,10 +24,7 @@ from petab.v2.lint import lint_problem from pybnf.petab import export_job, import_job -from pybnf.petab.bngl_model import BnglModel, register_bngl, _locate_bng2 - -# Teach petab to load `language: bngl` problems (idempotent). -register_bngl() +from petab.v1.models.bngl_model import BnglModel, _locate_bng2 _REPO_ROOT = Path(__file__).resolve().parents[1] _TUT = _REPO_ROOT / 'examples' / 'tutorial' diff --git a/tests/test_tutorial_petab_import.py b/tests/test_tutorial_petab_import.py index c5d708686..c6f4649ad 100644 --- a/tests/test_tutorial_petab_import.py +++ b/tests/test_tutorial_petab_import.py @@ -30,10 +30,6 @@ from petab.v2.lint import lint_problem from pybnf.petab import import_job -from pybnf.petab.bngl_model import register_bngl - -# Teach petab to load `language: bngl` problems (idempotent). -register_bngl() _REPO_ROOT = Path(__file__).resolve().parents[1] _LESSON = _REPO_ROOT / 'examples' / 'tutorial' / '20_petab_observable_parameters' diff --git a/tests/test_tutorial_petab_observable_formula.py b/tests/test_tutorial_petab_observable_formula.py index dd2243a0f..0f2a532d7 100644 --- a/tests/test_tutorial_petab_observable_formula.py +++ b/tests/test_tutorial_petab_observable_formula.py @@ -34,14 +34,11 @@ from petab.v2.lint import lint_problem from pybnf.petab import export_job, import_job -from pybnf.petab.bngl_model import register_bngl from pybnf.parse import ploop from pybnf.data import Data from . import recovery_harness as H -register_bngl() # teach petab to load `language: bngl` problems (idempotent) - _REPO_ROOT = Path(__file__).resolve().parents[1] _LESSON = _REPO_ROOT / 'examples' / 'tutorial' / '34_petab_observable_formula' _YAML = _LESSON / 'problem.yaml' diff --git a/tests/test_tutorial_petab_protocols.py b/tests/test_tutorial_petab_protocols.py index e5d809b36..892d48c4a 100644 --- a/tests/test_tutorial_petab_protocols.py +++ b/tests/test_tutorial_petab_protocols.py @@ -28,10 +28,7 @@ from petab.v2.lint import lint_problem from pybnf.petab import export_job, import_job -from pybnf.petab.bngl_model import BnglModel, register_bngl, _locate_bng2 - -# Teach petab to load `language: bngl` problems (idempotent). -register_bngl() +from petab.v1.models.bngl_model import BnglModel, _locate_bng2 _REPO_ROOT = Path(__file__).resolve().parents[1] _LESSON = _REPO_ROOT / 'examples' / 'tutorial' / '29_petab_protocols' diff --git a/tests/test_tutorial_priors.py b/tests/test_tutorial_priors.py index 8d6d40376..263379a3d 100644 --- a/tests/test_tutorial_priors.py +++ b/tests/test_tutorial_priors.py @@ -26,12 +26,8 @@ from petab.v2.lint import lint_problem from pybnf.petab import import_job -from pybnf.petab.bngl_model import register_bngl from pybnf.petab.parameters import read_parameter_table, free_parameter_from_row -# Teach petab to load `language: bngl` problems (idempotent). -register_bngl() - _REPO_ROOT = Path(__file__).resolve().parents[1] _LESSON = _REPO_ROOT / 'examples' / 'tutorial' / '15_petab_priors' _MANIFEST = _REPO_ROOT / 'examples' / 'tutorial' / '_manifest.py'