Skip to content

read metadata and cards from tar without extracting multi-dim arrays - #563

Open
lol782 wants to merge 2 commits into
NNPDF:masterfrom
lol782:fix/533-read-card-from-tar
Open

read metadata and cards from tar without extracting multi-dim arrays#563
lol782 wants to merge 2 commits into
NNPDF:masterfrom
lol782:fix/533-read-card-from-tar

Conversation

@lol782

@lol782 lol782 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

closes #533

Implements the cheap card-reading we discussed in #533.

Approach (following your instructions )

  • Added from_raw to TheoryCard, OperatorCard, and Metadata (in runcards.py / metadata.py). Each takes raw yaml, applies the v1/v2 upgrades, then calls from_dict. This is the upgrade logic lifted out of the EKO.theory_card / EKO.operator_card properties.
  • Added load_meta_and_cards_from_tar(path) in runcards.py: opens the archive and pulls only metadata.yaml / theory.yaml / operator.yaml via tarfile.extractfile — the operators are never extracted. Order is metadata → theory → operator (operator upgrade needs the theory dict).
  • Refactored the two EKO properties to reuse from_raw, so folder-reading and tar-reading share one code path.

Points I'd like your view on

  1. Metadata legacy upgrade. v1/v2.update_metadata needs the folder (paths), which the tar path doesn't have. So I kept that legacy 0.13/0.14 upgrade inside Metadata.load and made from_raw do the plain build. Consequence: reading a very old EKO straight from tar won't auto-upgrade its metadata. Fine for modern files — is that an acceptable boundary, or do you want it handled differently?
  2. Function placement. Put load_meta_and_cards_from_tar in runcards.py as you suggested.
  3. Test depth. Added a test that reads the cards from a real archive and checks types + a few values. Let me know if you want stronger assertions.

Existing io tests pass locally and below is the new one.
Screenshot 2026-08-31 180517

@felixhekhorn felixhekhorn added enhancement New feature or request output Output format and management labels Sep 3, 2026

@felixhekhorn felixhekhorn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to run pre-commit

Comment thread src/eko/io/metadata.py
return content

@classmethod
def from_raw(cls, raw: dict) -> "Metadata":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata legacy upgrade

yeah, seems that the update_metadata s take paths - but if you look to the function body they don't do anything with it so I'd say we can just remove them from there, because we definitely need patches here

Comment thread src/eko/io/runcards.py Outdated
raise KeyError(f"'{filename}' not found in {tar.name}")


def load_meta_and_cards_from_tar(path):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function placement

fine for me - any clever suggestion for a shorter function name? 🙃 being explicit is also fine

Comment thread tests/eko/io/test_struct.py Outdated

def test_load_meta_and_cards_from_tar(self, eko_factory: EKOFactory):
"""Load metadata and both YAML cards from a real EKO archive."""
eko = eko_factory.get()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test depth

this is only the current data version - can we please add also all the older as in

for name in ["v1-0.13.tar", "v1-0.14.tar", "v3.tar"]:
? Consider putting this list into a fixture so we can reuse it here and there

Comment thread src/eko/io/runcards.py Outdated
def from_raw(cls, raw: dict, data_version: int, theory_raw)-> "OperatorCard":
"""Build an operator card from a raw dictionary."""
if data_version == 1:
raw =v1.update_operator(raw)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since you are not testing on the full set of versions you did not realize that this is wrong - look:

def update_operator(raw_op: dict, raw_th) -> dict:

there is a reason I put the argument there 🙃

Comment thread src/eko/io/runcards.py

Only this member's bytes are read; nothing else is extracted.
"""
for member in tar.getmembers():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this "costless". If it is, then this is fine.

Otherwise, this function could take a list of filenames so that one can extract all of the files we need in one go.

Comment thread src/eko/io/runcards.py Outdated
raise KeyError(f"'{filename}' not found in {tar.name}")


def load_meta_and_cards_from_tar(path):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def load_meta_and_cards_from_tar(path):
def load_meta_and_cards_from_tar(eko_path):

if we are being explicit, better to be explicit here (so that it is clear to the user that this is the "eko path"

@lol782

lol782 commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

fix the required changes, Apologies for not remembering pre-commit.

  • Operator card bug — from_raw wasn't forwarding the theory dict to v1/v2.update_operator. Fixed (this was the empty-operator failure on legacy versions).
  • Metadata legacy upgrade — removed the unused paths argument from update_metadata in v1.py/v2.py, and moved the version patching into Metadata.from_raw (which was where the legacy case was failing). load now just reads the file and calls from_raw.
  • Tests — added a fixture with the legacy + current archives (v1-0.13.tar, v1-0.14.tar, v3.tar) so both test_legacy.py and the new test reuse it. The new function is now tested across all versions, not just current. Both pass.
  • Runcards — Changed the function logic to improve performance. Previously, it scanned the member list up to three times; now, the number of scans is hardcoded to three.

and also changed the name from load_meta_and_cards_from_tar → read_eko_cards and for test function name too.

@felixhekhorn felixhekhorn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Please mark conversation which you believe to have addressed as resolved, so we know more easily which problems still need to be addressed
  • A simple way for me (and everybody else 🙃 ) to see you are human and not a bot is by seeing you thinking. For example e2b6366 could have been 4 commits - doing in turn what you describe here. Making small self-contained commits is not easy and I openly admit I'm not doing it myself all the time, but I try - and this way you can see me thinking.
  • this will also help with writing more informative commit messages as "fix the required changes" is not very helpful in the long run

Comment thread src/eko/io/metadata.py
raw = v1.update_metadata(raw)
elif version.major == 0 and version.minor == 14:
raw = v2.update_metadata(paths, raw)
raw = v2.update_metadata(raw)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to reuse the new function of course

Comment thread src/eko/io/runcards.py
raise ValueError(f"Unknown mass scheme '{theory.heavy.masses_scheme}'")


def _read_yaml_members(tar, filenames):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the last commit you removed types, e.g. -> dict, but please keep them (and consider adding more)

Comment thread tests/conftest.py
Comment on lines +72 to +73
def legacy_eko_filenames():
return ["v1-0.13.tar", "v1-0.14.tar", "v3.tar"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The "legacy" in the name is not correct since it contains both legacy and current
  2. rather indicate (some way) that they are actual real objects (and not mocked)
  3. of course the path should be here (otherwise it is repeated)

@lol782

lol782 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

wait you're still thinking i am a bot 🥲.

@felixhekhorn

felixhekhorn commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

wait you're still thinking i am a bot 🥲.

nono, of course not - but I want to help you to avoid that impression

PS: and to teach you how to write better commits along the way 🙈

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request output Output format and management

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interface to extract only the *.yaml metadata

3 participants