Skip to content

Mzml Reader

mzmlpy.run.Mzml

Mzml(
    file: str | Path | Any,
    build_index_from_scratch: bool = False,
    gzip_mode: Literal[
        "extract", "indexed", "stream"
    ] = "extract",
    in_memory: bool = True,
    extract_dir: str | Path | None = None,
    spectrum_id_regex: str | None = None,
    chromatogram_id_regex: str | None = None,
)

Reader for mzML files.

Data is lazily loaded, so only the specific sections of the XML file are parsed. The actual data and properties of objects are only parsed when accessed. Use the context manager to ensure proper file handling. The spectra and chromatograms properties return lookup objects that support iteration, indexing, and ID-based access.

Note

A reader is not thread-safe: random access shares a single underlying file handle, so concurrent access from multiple threads on the same Mzml instance will interleave seeks and reads and return corrupt or wrong data. Use one reader per thread.

Parameters:

Name Type Description Default
file str | Path | Any

Path to the mzML file (str or Path) or a file-like object.

required
build_index_from_scratch bool

Build the index from scratch instead of using an existing index.

False
gzip_mode Literal['extract', 'indexed', 'stream']

Strategy for reading gzip-compressed (.mzML.gz) files:

  • "extract" (default): Decompress to a temporary file on disk, then use standard random-access reading.
  • "indexed": Use the rapidgzip library for seekable access to the compressed file without extracting to disk. Requires pip install mzmlpy[rapidgzip].
  • "stream": Stream the file sequentially without building an index. Individual spectrum access re-scans the file from the beginning each time.
'extract'
in_memory bool

Load the entire file into memory for faster access.

True
extract_dir str | Path | None

Directory to store extracted .mzML files when using gzip_mode='extract'. If None (default), a system temp directory is used (<tmpdir>/mzmlpy/). Set this to a custom path to manage extracted files yourself — useful for batch processing where you want to extract all files to one directory and clean up afterward.

None
spectrum_id_regex str | None

Optional regex applied to spectrum IDs to create a secondary lookup key. The first capture group (or full match if no groups) becomes the simplified key. For example, r"scan=(\d+)" lets you look up spectra by scan number (reader.spectra["19"]) instead of the full native ID ("scan=19").

None
chromatogram_id_regex str | None

Optional regex applied to chromatogram IDs to create a secondary lookup key. Works identically to spectrum_id_regex but for chromatograms.

None

Initialize Mzml and parse metadata.

Source code in src/mzmlpy/run.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
def __init__(
    self,
    file: str | Path | Any,
    build_index_from_scratch: bool = False,
    gzip_mode: Literal["extract", "indexed", "stream"] = "extract",
    in_memory: bool = True,
    extract_dir: str | Path | None = None,
    spectrum_id_regex: str | None = None,
    chromatogram_id_regex: str | None = None,
) -> None:
    """Initialize Mzml and parse metadata."""
    self._spectrum_id_regex = spectrum_id_regex
    self._chromatogram_id_regex = chromatogram_id_regex
    self._spectra_lookup: SpectrumLookup | None = None
    self._chromatograms_lookup: ChromatogramLookup | None = None
    self._path: Path | None = None
    file_interface_arg: Any

    if isinstance(file, str | Path):
        self._path = Path(file)
        # Use string representation for internal helpers that expect paths
        path_str = str(self._path)
        self._encoding = _determine_file_encoding(path_str)
        file_interface_arg = path_str
    else:
        # File-like object — must be a readable binary stream. Validate up front so an
        # unsupported input (e.g. an int) raises a clear TypeError instead of an opaque
        # AttributeError from encoding sniffing below.
        if not (hasattr(file, "read") and hasattr(file, "readline")):
            raise TypeError(
                f"Unsupported input type {type(file).__name__!r}: expected a path (str/Path) "
                "or a readable binary file-like object."
            )
        if hasattr(file, "name") and isinstance(file.name, str):
            self._path = Path(file.name)
        self._encoding = _guess_encoding(file)
        file_interface_arg = file

    # Open file
    self._file_object: FileInterface = FileInterface(
        path=file_interface_arg,
        encoding=self._encoding,
        build_index_from_scratch=build_index_from_scratch,
        gzip_mode=gzip_mode,
        in_memory=in_memory,
        extract_dir=str(extract_dir) if extract_dir is not None else None,
    )

    # Parse metadata. If parsing fails, close the file object so a half-constructed
    # reader does not leak extracted temp files or rapidgzip worker threads — the caller
    # never receives the object, so it can never call close() itself.
    try:
        self._root, self.iter, builder = self._parse_metadata()
        # Extract parsed content
        self._content: _MzMLContent = builder.build()
        self.obo_version = builder.obo_version
    except BaseException:
        self._file_object.close()
        raise

file_path property

file_path: Path | None

Access the file path as a Path object if available.

file_name property

file_name: str

Access the file name as a string.

spectra property

spectra: SpectrumLookup

Access spectra lookup.

Returns the same lookup instance across calls so its next()/reset() cursor and regex _id_map persist — reader.spectra.next() in a loop advances instead of restarting, and ID lookups don't re-scan the file on every access.

chromatograms property

chromatograms: ChromatogramLookup

Access chromatograms lookup.

Returns the same lookup instance across calls (see :meth:spectra).

TIC property

TIC: Chromatogram | None

Access the Total Ion Chromatogram (TIC).

id property

id: str

Access mzML id.

version property

version: str

Access mzML version.

cvs property

cvs: dict[str, CVElement]

Access controlled vocabularies.

file_description property

file_description: FileDescription | None

Access file description.

referenceable_param_groups property

referenceable_param_groups: dict[
    str, ReferenceableParamGroup
]

Access referenceable parameter groups.

softwares property

softwares: dict[str, Software]

Access software list.

instrument_configurations property

instrument_configurations: dict[
    str, InstrumentConfiguration
]

Access instrument configurations.

data_processes property

data_processes: dict[str, DataProcessing]

Access data processing steps.

samples property

samples: dict[str, Sample]

Access sample list.

scan_settings property

scan_settings: dict[str, ScanSetting]

Access scan settings.

run property

run: Run | None

Access run information.

Utilities

mzmlpy.run.peek_spectrum_count

peek_spectrum_count(file: str | Path) -> int | None

Return a file's spectrum count without building a random-access index.

Unlike Mzml(file).spectrum_count, this does not construct a reader or index every spectrum's byte offset — it streams forward just far enough to read the <spectrumList count="N"> opening tag's count attribute (typically a few KB into the file, well before the header content is complete) and stops. Useful for cheaply checking many files (e.g. before deciding which to open fully). Returns None if the file has no spectrumList or the tag has no count attribute.

Note

There is no equally cheap peek_chromatogram_count: per the mzML schema, chromatogramList follows spectrumList, so reaching its opening tag requires streaming past the entire spectrum list first — at that point building the full index via :class:Mzml is a better fit than a "peek."

Source code in src/mzmlpy/run.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def peek_spectrum_count(file: str | Path) -> int | None:
    """Return a file's spectrum count without building a random-access index.

    Unlike ``Mzml(file).spectrum_count``, this does not construct a reader or index every
    spectrum's byte offset — it streams forward just far enough to read the
    ``<spectrumList count="N">`` opening tag's ``count`` attribute (typically a few KB into the
    file, well before the header content is complete) and stops. Useful for cheaply checking many
    files (e.g. before deciding which to open fully). Returns ``None`` if the file has no
    ``spectrumList`` or the tag has no ``count`` attribute.

    Note:
        There is no equally cheap ``peek_chromatogram_count``: per the mzML schema,
        ``chromatogramList`` follows ``spectrumList``, so reaching its opening tag requires
        streaming past the entire spectrum list first — at that point building the full index
        via :class:`Mzml` is a better fit than a "peek."
    """
    path_str = str(file)
    is_gz = path_str.endswith(".gz") or path_str.endswith(".igz")
    file_handle = gzip_open_binary(path_str) if is_gz else open(path_str, "rb")
    try:
        # Read the count off the spectrumList *start* tag, but clear completed elements on their
        # *end* events so that a file with no spectrumList doesn't accumulate the whole tree in
        # memory before returning None.
        for event, element in ElementTree.iterparse(file_handle, events=("start", "end")):
            if event == "start":
                if get_tag(element) == MzMLElement.SPECTRUM_LIST:
                    count = element.attrib.get("count")
                    return int(count) if count is not None else None
            else:
                element.clear()
        return None
    finally:
        file_handle.close()

mzmlpy.util.clear_cache

clear_cache() -> None

Remove all cached files from the mzmlpy temporary directory.

Deletes the <tmpdir>/mzmlpy/ directory and all its contents. This includes extracted .mzML files created by gzip_mode='extract'.

Example::

from mzmlpy import clear_cache
clear_cache()
Source code in src/mzmlpy/util.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def clear_cache() -> None:
    """Remove all cached files from the mzmlpy temporary directory.

    Deletes the ``<tmpdir>/mzmlpy/`` directory and all its contents.
    This includes extracted ``.mzML`` files created by ``gzip_mode='extract'``.

    Example::

        from mzmlpy import clear_cache
        clear_cache()
    """
    cache_dir = _get_cache_dir()
    if os.path.isdir(cache_dir):
        shutil.rmtree(cache_dir)