Skip to content

EMDB().get_entry() fails for EMD-6483 due to BaseMapFile abstract class instantiation in mask parsing #5

@shuuul

Description

@shuuul

Hi, thanks for maintaining this package.

I ran into an issue when calling EMDB().get_entry("EMD-6483"). The API request itself succeeds, but the Python wrapper fails while parsing the returned entry metadata.

Minimal reproduction

from emdb.client import EMDB

entry = EMDB().get_entry("EMD-6483")

Observed error

Traceback (most recent call last):
  File ".../site-packages/emdb/client.py", line 40, in get_entry
    return EMDBEntry.from_api(data, client=self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../site-packages/emdb/models/entry.py", line 118, in from_api
    mask_file = MaskFile.from_api(m)
                ^^^^^^^^^^^^^^^^^^^^
  File ".../site-packages/emdb/models/files.py", line 222, in from_api
    map_file=BaseMapFile.from_api(data.get("mask_details", {})) if data.get("mask_details") else None,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../site-packages/emdb/models/files.py", line 108, in from_api
    return cls(
           ^^^^
TypeError: Can't instantiate abstract class BaseMapFile without an implementation for abstract method 'source_path'

During handling of the above exception, another exception occurred:

emdb.exceptions.EMDBAPIError: EMDB API Error: Failed to retrieve entry EMD-6483: Can't instantiate abstract class BaseMapFile without an implementation for abstract method 'source_path'

Environment

Python: 3.12
Package: emdb-api-wrapper / emdb

I observed this through a downstream dependency (cryopy.database.get_contour_level), but the minimal reproduction above fails directly with this package.

Expected behavior

EMDB().get_entry("EMD-6483") should return an EMDBEntry object successfully.

The raw EMDB API response for this entry appears to contain a valid contour level:

/map/contour_list/contour[0]/level = 0.03
/map/contour_list/contour[0]/primary = True

So the entry metadata is accessible from the API, but the wrapper fails during object construction.

Possible cause

The failure seems to happen while parsing mask metadata:

# emdb/models/files.py
map_file = (
    BaseMapFile.from_api(data.get("mask_details", {}))
    if data.get("mask_details")
    else None
)

BaseMapFile is an abstract class because it has an abstract source_path property, so calling BaseMapFile.from_api(...) eventually attempts to instantiate an abstract class.

Maybe mask_details should be parsed into a concrete map-file class, or MaskFile.map_file should use a non-abstract concrete model for mask map details.

Why this matters

This breaks downstream workflows that only need fields such as primary_map.contour_list, because get_entry() parses the full entry and fails on unrelated mask metadata before the caller can access the contour level.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions