Release notes for sbmlutils 0.13.0
We are pleased to release the next version of sbmlutils including the following changes. This release is the result of a full review of the repository (#486, #487, #488, #489, #490). It fixes wrong unit definitions, validation results and several silent failures, hardens code generation, the report server and downloads, makes the package smaller and the release process safer, moves all output of the library to logging and splits the model factory into a package. Code which uses one of the items under breaking changes has to be changed; the items under behaviour changes keep the API but change what is written, reported or raised.
Breaking changes
- library code no longer prints: validation and flatten reports, the output of
create_model(show_sbml=True),ReactionEquation.info(), xpp conversion headers, BioModels query progress, annotation results and Cytoscape information go to thesbmlutils.*loggers at the matching level. Nothing is shown unless the application configures logging or callssbmlutils.log.enable_rich_logging(), see Validation (#489) read_sbmlraises aValueErrorfor a source it cannot read, i.e. a missing file or XML which is not well formed, andsbml_to_model,flatten_sbml,merge_modelsandModelAnnotatorraise aValueErrorfor a document without a model. They used to log the error and continue with an empty document, which failed later with anAttributeErroror wrote an empty model.read_sbmlno longer claims to read URLs, which it never did (#486)ValidationResult.errorsand.warningsholdSBMLErrorInfosnapshots instead oflibsbml.SBMLErrorobjects. The libsbml objects pointed into the error log of the document and returned garbage once the document was freed. The snapshots have the same getters for id, severity, category, line, column, message and package andisError(),isWarning(),isFatal(), butisinstance(e, libsbml.SBMLError)isFalse(#486)antimony_to_sbmltreats aPathas a file and astras a file only if it names an existing file, otherwise as antimony content; invalid antimony raises aValueErrorwith the antimony error, and a missing file raises aFileNotFoundError. A path containing "model" used to be parsed as antimony text, and an error was logged on success while a failure passed silently (#486)- a second rule or a second initial assignment for the same symbol, an annotation entry which is neither an
Annotationnor a tuple,Nonein a list of objects and an unsupported type inModel(objects=...)raise aValueError; they used to produce an invalid model, a duplicated annotation or were dropped in silence (#486) - reaction equations support scientific notation (
1e-3 A => B) andA+Bwithout spaces; more than one reaction arrow,-as a separator and a modifier list which is not a single list at the end raise aValueError, andEquationExceptionis aValueError.A => B => Cused to dropCin silence (#486) - interpolation methods are the
InterpolationMethodStrEnum (the old constants remain as aliases) and an unknown method raises aValueError(#486) - removed, each verified unused:
fbc.set_flux_bounds(broken),fbc.set_boundary_conditions_false,comp.get_submodel_frameworks,replace_elements,replace_element_in_submodels,replaced_by,SBASE_REF_TYPE_*,flatten.flatten_external_model_definitions,Units.create_unit_definitions(deprecated),xpp_helpers.ast_info,converters.mathml.evaluateMathMLwith its helpers and the star import ofmath, andreport.mathml.xslt_cmml2pmml/xslt_pmml2tex, which are the compiledXSLT_CMML2PMML/XSLT_PMML2TEXnow (#488, #489) report.sbmlreport.start_server(path, port)takes the model file, binds 127.0.0.1, serves only that file and returns the running server instead of blocking.create_online_reporttherefore needs an sbml4humans running on the same machine, its defaultserverishttp://localhost:3456, and it opens the current route/report?url=(#488)- the
assignmentsparameter ofSBMLDocumentInfo.compartments(),species()andparameters()is removed, andread_layout_xmldrops its unusedsbml_pathparameter (#486) create_sbmlofLocalParameter,KineticLaw,UserDefinedConstraintComponent,FluxObjective,UncertParameterandUncertSpanrequires themodelit is written into instead of falling back togetModel(), which answers with the wrong model inside a comp model definition.AlgebraicRuleis no longer aRuleWithVariable(#490)pydanticis no longer a dependency of sbmlutils (#487)
Behaviour changes
- unit definitions with a prefix under an exponent or with a magnitude get correct multipliers:
mm**2was written as 1e-3 m²,cm**3as 0.01 m³,10/las 0.1/l and1e-3/minas 16.7/s. Every unit is written as(multiplier * 10^scale * kind)^exponentand checked against pint. A magnitude without a real root raises aValueError(#486) - validation reads only the errors each check added to the error log, reports the read errors of a document once and leaves the error log of the document as it found it. Errors used to be misreported, counted twice or missed, a document with read errors was reported valid, and validating a document twice made the counts grow.
validate_sbmlreports malformed XML as read errors and raises aFileNotFoundErroronly for a path which does not exist and anIsADirectoryErrorfor a directory (#486) flatten_sbmlandmerge_modelsno longer change the working directory, which broke relative paths and is not thread safe; libsbml resolves external model definitions from the absolute location of the document.merge_modelsacceptsstrpaths, honourssbml_levelandsbml_version, no longer changes the dict it is given and points a submodel at the id of its external model definition.create_ExternalModelDefinitiontakes an optionalmodel_refand sets no modelRef by default (#486)- files in directories with non-ASCII characters are read and written on every platform: libsbml cannot open such a path on Windows, so these files are read and written by python and only parsed and serialized by libsbml. External model definitions in such a directory still cannot be resolved on Windows, a libsbml limitation (#486)
- unit strings of reports render magnitudes as numbers (
160 s,2.1 g,mmol/(160 s)) and fractional exponents (s^0.5); 160 s used to render as1minand 2.1 g as2.g(#486) - elements without id or metaid get unique and stable primary keys in
SBMLDocumentInfo, e.g.SpeciesReference:R1/listOfReactants/0, instead of SHA1 digests which collided for identical elements (#486) add_default_flux_boundscreates unique parameter ids (lower_1) instead of duplicating existinglowerandupperids (#486)- the python code generated by
SBML2ODEruns: the math is translated on the libsbml AST (piecewise, logical operators, relations,ln,rem,quotient,xor,INF,NaN), checked against roadrunner. Initial assignments are ignored with a warning; local parameters, function definitions,delayandrateOfraise aNotImplementedError; python keywords used as ids get a trailing underscore; cyclic assignment rules raise aValueErrorinstead of recursing endlessly (#488) - names and units are written on a single line in every generated language, LaTeX escapes ids, names and units, and python, R and julia export raise a
ValueErrorfor ids which are not SIds, so a name can no longer break out of a comment into executable code (#488) download_biomodel_sbmlraises aValueErrorfor a manifest location outsideoutput_dirand creates subdirectories for nested locations (#488)sbmlutils.factoryis a package (_core,units,core_elements,distrib,fbc,comp,model).from sbmlutils.factory import *and every public name are unchanged; names the old module only imported (BQB,SBO,write_sbml,sbml_to_antimony, ...) still resolve with aDeprecationWarningnaming their proper import. Classes name their submodule in__module__and repr, so pickles written by this version cannot be read by older versions; logger names are per submodule, children ofsbmlutils.factory(#490)- the notes of a species reference are normalized like those of every other element, and an invalid SId of a species reference is reported like every other element.
UnitDefinitionhas a readablerepr(#490) sbmlutils.console.consoleno longer records all output in memory (#489)annotate_sbmlraises anOSErrorfor an annotations path which is not a file (#489)- the examples registry moved from
examples/__init__.pytoexamples/registry.py, sopython -m examples.Xno longer warns; the examples enable rich logging in their__main__block (#489)
Fixes
- the tests never reach a running Cytoscape: running the test suite on a machine with Cytoscape open closed its session without saving.
visualize_sbmllogs aCyErrorinstead of raising it, andvisualize_antimonyreturns the network SUID (#486) Interpolatorcompared the method withis, which failed for an equal string, and the cubic spline of unsorted data was wrong (#486)- xpp: the function names of
minandmaxwere swapped and notes were escaped twice (#486) - the online report served the whole directory of the model with listings on all network interfaces, from a server which never stopped in a long running process (#488)
- the hierarchical model of the COMBINE archive example referenced itself instead of the minimal model, so creating the archive failed (#490)
typing.get_type_hintsworks for every public class ofsbmlutils.factory(#490)
Packaging and development
- the sdist holds only the package, tests, examples and the scripts the tests use, 3.6 MB instead of 23 MB, and the tests of an unpacked sdist skip what needs the SBML test suite (#487)
- the package ships
py.typedand theTyping :: Typedclassifier (#487) - dependencies:
lxml>=6.1.0(CVE-2026-41066) andpandas>=2.2.2; the floors ofrich,requestsandmarkdown-it-pyare lowered to verified versions. The tox environmentlowesttests every direct dependency at its floor in CI (#487) uv.lockis committed, Dependabot updates the lock and the pre-commit hooks, and ruff in CI reads its version from the lock (#487)- the release workflow builds without write permissions, publishes with attestations through trusted publishing from a job which only holds
id-token: write, checks the release notes before publishing, and every action is pinned to a commit SHA (#487) - ruff enforces
PERF,PTH,ARG,S,PT,TRY201,PLW0120andPLC0206, and the tests assert what they claim; new pytest markersslowandnetwork(#489) - the model factory is simpler: shared base classes for the rules, class-level authoring hints, one code path for the fields of a species reference, one walk to find the packages of a model and an id index for the rules of a model. The written SBML is unchanged, 890 documents byte for byte (#490)
