euromod_linking.compat

Method requirements versus what the model actually provides.

A methodology declares what its runs need — lma_labour_alignment needs the LMA add-on and the LMA_trans extension switch — and this module checks that the model in front of it has them. An unknown add-on is dropped by the engine with only a console message, so left unchecked both simulations complete normally and produce identical output, and the failure surfaces at the end as NoEffectError, after paying for two runs. Answering the question first is why this is called from the validate_only path.

Two kinds of evidence, deliberately unequal.

Capability — authoritative

Whether the model exposes an add-on and an extension switch is cheap and exact: model.addons is a directory listing, and query.system_extension_names reads what the system will actually accept. This is what decides. Where the model cannot be read at all the answer is undeterminable, recorded as a note, never as a failure — “cannot validate” is not “invalid”.

Release — advisory

Which EUROMOD release a model folder holds is not reliably knowable. The documented marker XMLParam/Config/EuromodVersion.txt has writer code in the UI but ships in no real release; the folder name is what the UI itself falls back to (EM_AppContext.GetProjectName feeding <EMVERSION>), and renaming the folder erases it. So a detected release only enriches the message — “the LMA_trans extension is first shipped in J2.54, this model looks like J2.19” — and failing to detect one never blocks anything.

Set EUROMOD_SKIP_COMPAT_CHECK=1 to bypass the check, mirroring the connector’s own EUROMOD_SKIP_VERSION_CHECK.

Attributes

Classes

CompatibilityReport

What one method needs, what this model has, and the gap between them.

Requirement

One thing a method needs from the model, and whether it is there.

Functions

canonical_release(→ str | None)

"..._j2.54" -> "J2.54"; unparseable input comes back stripped.

check_all(→ list[CompatibilityReport])

A report per registered methodology, in registry order.

check_compatibility(→ CompatibilityReport)

Can this methodology actually run against this model and system?

compatibility_matrix(model[, country_code, deep_release])

Which methodologies this model supports, as a DataFrame.

model_release(model_path, *[, deep])

Best-effort (release, source) for a model folder, e.g.

parse_release(release)

Split "J2.54" into a sortable ("J", 2, 54, False), or None.

release_geq(release, floor)

Is release at least floor? None when either cannot be parsed.

Module Contents

class euromod_linking.compat.CompatibilityReport[source]

What one method needs, what this model has, and the gap between them.

problems are hard failures — the run would not do what was asked. notes are advisory: an undeterminable requirement, or a release that looks older than the method’s floor.

Read-only property ok is True when there are no problems; notes never make a report not-ok. This is the field to check.

country_code: str
method: str
min_model_release: str | None = None
model_release: str | None = None
notes: tuple = ()
property ok: bool

True when nothing blocks the run. Notes do not make a report not-ok.

problems: tuple = ()
release_source: str | None = None
requirements: tuple = ()
system_name: str
class euromod_linking.compat.Requirement[source]

One thing a method needs from the model, and whether it is there.

satisfied is tri-state: True, False, or None when the model could not be read — a distinction that matters, because treating “cannot read” as “not present” would refuse to run against a perfectly good model.

detail: str = ''
kind: str
name: str
satisfied: bool | None = None
system: str | None = None
euromod_linking.compat.canonical_release(release: str | None) str | None[source]

"..._j2.54" -> "J2.54"; unparseable input comes back stripped.

Every detection source is run through this, so a report shows one spelling of a release whether it came from a lowercased folder name, a licence file or the change log.

euromod_linking.compat.check_all(system, *, deep_release: bool = False) list[CompatibilityReport][source]

A report per registered methodology, in registry order.

euromod_linking.compat.check_compatibility(system, method, *, deep_release: bool = False) CompatibilityReport[source]

Can this methodology actually run against this model and system?

method is a reference like "lma_labour_alignment" or an MethodSpec. system is a live connector System, from which the country, the model and the model path are reached.

Checks each declared add-on and extension switch against the model, and compares the detected release against the method’s min_model_release. Only the first of those can produce a problem; the release only ever adds a note explaining one. With deep_release the change log is parsed too, at the cost of reading a large workbook.

euromod_linking.compat.compatibility_matrix(model, country_code: str | None = None, *, deep_release: bool = False)[source]

Which methodologies this model supports, as a DataFrame.

One row per (country, system, methodology) with ok, the detected release, and the problems — the answer to “what can I actually run here” without writing a scenario first. Restrict with country_code, since walking every country of a full model means loading every country.

euromod_linking.compat.model_release(model_path: str, *, deep: bool = False)[source]

Best-effort (release, source) for a model folder, e.g. ("J2.19", "folder-name").

Tries the documented version file, then the folder name, then a public release’s licence file; with deep=True it finally parses the change log, which is slow but survives a renamed folder. (None, None) means the release could not be determined — which callers must treat as unknown, never as too old.

euromod_linking.compat.parse_release(release: str | None)[source]

Split "J2.54" into a sortable ("J", 2, 54, False), or None.

The last element is the trailing + of a beta/rolling release, which sorts just above the bare version so J1.86+ is not read as older than J1.86. None for anything that does not look like a release at all — callers treat that as “cannot compare”, not as “older”.

euromod_linking.compat.release_geq(release: str | None, floor: str | None)[source]

Is release at least floor? None when either cannot be parsed.

Fails open — the caller turns None into a note, not a refusal. This is the opposite of euromod.utils._version.version_geq, which gates the engine and so fails closed; here the capability probe is the real check and the release is only there to write a better message.

euromod_linking.compat.RELEASE_RE
euromod_linking.compat.RELEASE_SOURCES = ('version-file', 'folder-name', 'licence-file', 'em-log')