Skip to content

Python API Reference

Complete Python API reference for MRRC.

Canonical Type Stubs

The file mrrc/_mrrc.pyi is the ground-truth type reference for the Python extension module. IDEs use it for autocompletion and type checking. If this page and the stub file disagree, the stub file is authoritative.

Core Classes

Record

A MARC bibliographic record containing a leader, control fields, and data fields.

from mrrc import Record, Field, Leader, Subfield

# Create a record with inline fields
record = Record(fields=[
    Field("245", indicators=["1", "0"], subfields=[
        Subfield("a", "Title"),
    ]),
])

# Or build incrementally
record = Record(Leader())
record.add_control_field("001", "123456789")
field = Field("245", "1", "0")
field.add_subfield("a", "Title")
record.add_field(field)

All record accessors (title, author, isbn, …) are read-only properties, matching pymarc.

Enhanced Record wrapper with pymarc-compatible API.

title property

title: Optional[str]

Title from 245 field.

author property

author: Optional[str]

Author from 100/110/111 field.

isbn property

isbn: Optional[str]

ISBN from 020 field.

issn property

issn: Optional[str]

ISSN from 022 field.

subjects property

subjects: List[str]

All subject headings from 6XX subject fields.

location property

location: List[str]

All location fields (852).

notes property

notes: List[str]

All notes from 5xx fields.

publisher property

publisher: Optional[str]

Publisher from 260 or 264 (RDA) field.

uniform_title property

uniform_title: Optional[str]

Uniform title from 130 field.

sudoc property

sudoc: Optional[str]

SuDoc from 086 field.

issn_title property

issn_title: Optional[str]

ISSN title from 222 field.

issnl property

issnl: Optional[str]

ISSN-L from 024 field.

pubyear property

pubyear: Optional[str]

Publication year (returns str, matching pymarc).

series property

series: Optional[str]

Series from 490 field.

physical_description property

physical_description: Optional[str]

Physical description from 300 field.

physicaldescription property

physicaldescription: Optional[str]

Physical description (pymarc-compatible name).

uniformtitle property

uniformtitle: Optional[str]

Uniform title (pymarc-compatible name).

addedentries property

addedentries: list

Added entries from 700/710/711/730 fields (pymarc compatibility).

__init__

__init__(leader: Optional[Leader] = None, *, fields: Optional[List[Field]] = None)

Create a new Record.

Parameters:

Name Type Description Default
leader Optional[Leader]

Optional Leader object (defaults to Leader()).

None
fields Optional[List[Field]]

Optional list of Field objects to add to the record.

None

__getattr__

__getattr__(name: str) -> Any

Delegate attribute access to the inner Rust Record.

__repr__

__repr__() -> str

__str__

__str__() -> str

__contains__

__contains__(tag: str) -> bool

Check if a field with given tag exists in record.

__getitem__

__getitem__(tag: str) -> Field

Get first field with given tag (pymarc compatibility).

Returns a Field instance for both control and data fields. Raises KeyError if the tag is not present in the record.

get_fields

get_fields(*tags: str) -> List[Field]

Get all fields with given tags.

If no tags provided, returns all fields (control + data). Supports multiple tags: record.get_fields('245', '260')

add_field

add_field(*fields: Field) -> None

Add one or more fields to the record.

get

get(tag: str, default=None)

Get first field with given tag, or default (pymarc compatibility).

get_field

get_field(tag: str) -> Optional[Field]

Get first field with given tag.

get_field_or_err

get_field_or_err(tag: str) -> Field

Get first field with given tag, raising :class:mrrc.FieldNotFound (E105) when the tag is not present.

Strict-mode counterpart to :meth:get_field. Use this when an absent field is a programming error worth the typed exception and its diagnostic context (record_control_number, field_tag); use :meth:get_field when None is the expected sentinel.

remove_field

remove_field(*fields: Union[Field, str]) -> None

Remove one or more fields from the record (pymarc compatibility).

A Field argument removes exactly that field: a live handle obtained from this record removes its precise occurrence; a detached field removes the first value-equal field, raising ValueError when nothing matches (as pymarc does). A tag string removes all fields with that tag, control tags included.

remove_fields

remove_fields(*tags: str) -> None

Remove all fields with the given tags (pymarc compatibility).

add_ordered_field

add_ordered_field(*fields: Field) -> None

Add fields maintaining tag sort order (pymarc compatibility).

add_grouped_field

add_grouped_field(*fields: Field) -> None

Add fields after the last field with the same tag (pymarc compatibility).

add_control_field

add_control_field(tag: str, value: str) -> None

Add a control field.

control_field

control_field(tag: str) -> Optional[str]

Get a control field value.

fields

fields() -> List[Field]

Get all fields (control + data), as live handles.

Enumerates identically to no-arg :meth:get_fields: repeated control tags (e.g. multiple 007s) yield one field per value.

is_book

is_book() -> bool

Check if this is a book.

is_serial

is_serial() -> bool

Check if this is a serial.

is_music

is_music() -> bool

Check if this is music.

is_audiovisual

is_audiovisual() -> bool

Check if this is audiovisual.

get_linked_fields

get_linked_fields(field: Field) -> List[Field]

Find all 880 fields linked to a given field via subfield $6.

Given a non-880 field that has a $6 linkage subfield, returns all 880 fields whose $6 occurrence number matches. This is pymarc-compatible.

Parameters:

Name Type Description Default
field Field

A Field object with a $6 linkage subfield.

required

Returns:

Type Description
List[Field]

List of linked 880 Field objects (empty if no linkage or no match).

Example
>>> f245 = record.get_fields('245')[0]
>>> linked = record.get_linked_fields(f245)
>>> if linked:
...     print(f"Vernacular title: {linked[0]['a']}")

get_linked_field

get_linked_field(field: Field) -> Optional[Field]

Find the single 880 field linked to a given field via subfield $6.

Like get_linked_fields() but returns only the first match.

Parameters:

Name Type Description Default
field Field

A Field object with a $6 linkage subfield.

required

Returns:

Type Description
Optional[Field]

The linked 880 Field, or None.

get_original_field

get_original_field(field_880: Field) -> Optional[Field]

Find the original field linked from a given 880 field.

Parameters:

Name Type Description Default
field_880 Field

An 880 Field object.

required

Returns:

Type Description
Optional[Field]

The linked original Field, or None.

get_field_pairs

get_field_pairs(tag: str) -> List[Tuple[Field, Optional[Field]]]

Get field pairs of original fields with their linked 880 counterparts.

Parameters:

Name Type Description Default
tag str

The field tag to pair (e.g., '245', '100').

required

Returns:

Type Description
List[Tuple[Field, Optional[Field]]]

List of (original Field, linked 880 Field or None) tuples.

Example
>>> for orig, linked in record.get_field_pairs('245'):
...     print(f"Romanized: {orig['a']}")
...     if linked:
...         print(f"Vernacular: {linked['a']}")

fields_by_indicator

fields_by_indicator(tag: str, *, indicator1: Optional[str] = None, indicator2: Optional[str] = None) -> List[Field]

Get fields matching indicator values.

This is a convenience method for filtering by indicators. For more complex queries, use fields_matching() with a FieldQuery.

Parameters:

Name Type Description Default
tag str

The 3-character field tag to search.

required
indicator1 Optional[str]

Optional first indicator value (None = match any).

None
indicator2 Optional[str]

Optional second indicator value (None = match any).

None

Returns:

Type Description
List[Field]

List of Field objects matching the criteria.

Example
>>> # Find all 650 fields with indicator2='0' (Library of Congress Subject Headings)
>>> lcsh_subjects = record.fields_by_indicator("650", indicator2="0")
>>> for field in lcsh_subjects:
...     print(field["a"])

fields_in_range

fields_in_range(start_tag: str, end_tag: str) -> List[Field]

Get fields within a tag range (inclusive).

Useful for querying groups of related fields, such as all subject fields (600-699) or all added entry fields (700-799).

Parameters:

Name Type Description Default
start_tag str

Start of range (inclusive), e.g., "600".

required
end_tag str

End of range (inclusive), e.g., "699".

required

Returns:

Type Description
List[Field]

List of Field objects within the tag range.

Example
>>> # Find all subject fields (600-699)
>>> subjects = record.fields_in_range("600", "699")
>>> for field in subjects:
...     print(f"{field.tag}: {field['a']}")

fields_matching

fields_matching(query: FieldQuery) -> List[Field]

Get fields matching a FieldQuery.

This method enables complex field matching using the Query DSL. A FieldQuery can combine tag, indicator, and subfield requirements.

Parameters:

Name Type Description Default
query FieldQuery

A FieldQuery object with the matching criteria.

required

Returns:

Type Description
List[Field]

List of Field objects matching all query criteria.

Example
>>> query = FieldQuery().tag("650").indicator2("0").has_subfield("a")
>>> lcsh = record.fields_matching(query)
>>> for field in lcsh:
...     print(field["a"])

fields_matching_range

fields_matching_range(query: TagRangeQuery) -> List[Field]

Get fields matching a TagRangeQuery.

This method finds fields within a tag range that also match indicator and subfield requirements.

Parameters:

Name Type Description Default
query TagRangeQuery

A TagRangeQuery object with range and filter criteria.

required

Returns:

Type Description
List[Field]

List of Field objects matching all query criteria.

Example
>>> # Find all 6XX subjects with indicator2='0' (LCSH) that have subfield 'a'
>>> query = TagRangeQuery("600", "699", indicator2="0", required_subfields=["a"])
>>> subjects = record.fields_matching_range(query)

fields_matching_pattern

fields_matching_pattern(query: SubfieldPatternQuery) -> List[Field]

Get fields matching a SubfieldPatternQuery (regex matching).

This method finds fields where a specific subfield's value matches a regular expression pattern.

Parameters:

Name Type Description Default
query SubfieldPatternQuery

A SubfieldPatternQuery object with tag, subfield, and regex.

required

Returns:

Type Description
List[Field]

List of Field objects where the subfield matches the pattern.

Example
>>> # Find all ISBN-13s (start with 978 or 979)
>>> query = SubfieldPatternQuery("020", "a", r"^97[89]-")
>>> isbn13_fields = record.fields_matching_pattern(query)

fields_matching_value

fields_matching_value(query: SubfieldValueQuery) -> List[Field]

Get fields matching a SubfieldValueQuery (exact or partial string matching).

This method finds fields where a specific subfield's value matches a string exactly or as a substring.

Parameters:

Name Type Description Default
query SubfieldValueQuery

A SubfieldValueQuery object with tag, subfield, value, and match type.

required

Returns:

Type Description
List[Field]

List of Field objects where the subfield matches the value.

Example
>>> # Find exact subject heading "History"
>>> query = SubfieldValueQuery("650", "a", "History")
>>> history_fields = record.fields_matching_value(query)

>>> # Find subjects containing "History" anywhere
>>> query = SubfieldValueQuery("650", "a", "History", partial=True)
>>> related_fields = record.fields_matching_value(query)

to_json

to_json() -> str

Serialize to JSON.

to_xml

to_xml() -> str

Serialize to MARCXML.

to_dublin_core

to_dublin_core() -> str

Serialize to Dublin Core.

to_marcjson

to_marcjson() -> str

Serialize to MARCJSON.

leader

leader() -> Leader

Get the leader.

as_dict

as_dict() -> dict

Return pymarc-compatible MARC-in-JSON dict (code4lib schema).

as_json

as_json(**kwargs) -> str

Serialize to pymarc-compatible MARC-in-JSON string.

as_marc

as_marc() -> bytes

Serialize record to ISO 2709 binary MARC (pymarc compatibility).

as_marc21

as_marc21() -> bytes

Alias for as_marc() (pymarc compatibility).

__eq__

__eq__(other: Any) -> bool

Compare records by content.

__hash__

__hash__() -> int

Hash based on leader.

Dictionary access:

# record['xxx'] raises KeyError if tag is missing
field = record['245']      # Returns Field or raises KeyError

# Use record.get() for safe access (returns None if missing)
field = record.get('245')  # Returns Field or None

Field handles: fields obtained from a record (record[tag], get_field, get_field_or_err, get_fields, fields) are live handles — every read and write goes through to the record, so in-place edits persist, matching pymarc:

record["245"].indicator1 = "1"        # persists
record["245"]["a"] = "New title /"    # persists
record["005"].data = "20260603120000.0"  # control fields too

Two caveats. Handles to the same field are distinct objects (record["245"] is record["245"] is False) that share underlying state — don't compare fields with is or id(). And removing fields invalidates all outstanding handles for that record: any later use raises StaleFieldError; re-fetch the field and retry. Query results (fields_matching, fields_by_indicator, fields_in_range, and related) are snapshots, not handles — edits to them do not write back.

Field

A MARC field — both control fields and data fields use this class.

from mrrc import Field, Subfield

# Create a data field with indicators and subfields inline
field = Field("245", indicators=["1", "0"], subfields=[
    Subfield("a", "Main title :"),
    Subfield("b", "subtitle /"),
    Subfield("c", "by Author."),
])

# Create a control field
field = Field("001", data="12345")

# Or build incrementally
field = Field("245", "1", "0")
field.add_subfield("a", "Main title :")

# Access subfields
print(field["a"])  # "Main title :"
for sf in field.subfields():
    print(f"${sf.code} {sf.value}")

Enhanced Field wrapper with pymarc-compatible API.

Supports both control fields and data fields (like pymarc.Field). Control fields are created with data=: Field('001', data='12345'). Data fields use indicators and subfields as before.

data property writable

data: Optional[str]

Control field data value (pymarc compatibility).

Returns the data string for control fields, None for data fields.

tag property

tag: str

Field tag.

indicator1 property writable

indicator1: str

First indicator.

indicator2 property writable

indicator2: str

Second indicator.

indicators property writable

indicators: Indicators

Get indicators as tuple-like Indicators object (pymarc compatibility).

Example
field.indicators[0]      # First indicator
field.indicators[1]      # Second indicator
ind1, ind2 = field.indicators  # Unpacking

__init__

__init__(tag: str, indicator1: str = ' ', indicator2: str = ' ', *, subfields: Optional[List[Subfield]] = None, indicators: Optional[List[str]] = None, data: Optional[str] = None)

Create a new Field.

Parameters:

Name Type Description Default
tag str

3-character field tag.

required
indicator1 str

First indicator (default ' ').

' '
indicator2 str

Second indicator (default ' ').

' '
subfields Optional[List[Subfield]]

Optional list of Subfield objects to add.

None
indicators Optional[List[str]]

Optional list/tuple of [ind1, ind2], overrides indicator1/indicator2.

None
data Optional[str]

For control fields, the data string value.

None

__getattr__

__getattr__(name: str) -> Any

Delegate attribute access to the inner Rust Field.

__getitem__

__getitem__(code: str) -> Optional[str]

Get first subfield value by code (pymarc compatibility).

Returns None if the subfield code doesn't exist, matching pymarc behavior.

Example
field['a']  # Get first 'a' subfield value
field['z']  # Returns None if 'z' subfield doesn't exist

__setitem__

__setitem__(code: str, value: str) -> None

Set subfield value (replace first occurrence).

get

get(code: str, default: Optional[str] = None) -> Optional[str]

Get first subfield value by code or return default.

__contains__

__contains__(code: str) -> bool

Check if subfield code exists in field.

value

value() -> str

Return the field's value (pymarc compatibility). For control fields, returns the data content. For data fields, returns space-joined subfield values.

format_field

format_field() -> str

Return human-readable text without indicators or subfield codes (pymarc compatibility).

is_control_field

is_control_field() -> bool

Check if this is a control field (pymarc compatibility).

__str__

__str__() -> str

MARC display format (pymarc compatibility).

Data fields: =TAG IND1IND2$aCONTENT$bCONTENT Control fields: =TAG CONTENT

__repr__

__repr__() -> str

Informative repr. Never raises: stale handles say so.

get_subfields

get_subfields(*codes: str) -> List[str]

Get all subfield values for given codes (pymarc compatibility).

Example
field.get_subfields('a', 'b')  # Get all 'a' and 'b' subfield values

delete_subfield

delete_subfield(code: str) -> Optional[str]

Delete first subfield with given code and return its value.

Matches pymarc's Field.delete_subfield() behavior: removes the first subfield with the given code and returns its value, or None if no subfield with that code exists.

subfields_as_dict

subfields_as_dict() -> dict

Return subfields as dictionary mapping code to list of values.

add_subfield

add_subfield(code: str, value: str, pos: Optional[int] = None) -> None

Add a subfield, optionally at a specific position (pymarc compatibility).

subfields

subfields() -> List[Any]

Get all subfields.

subfields_by_code

subfields_by_code(code: str) -> List[str]

Get subfield values by code.

is_subject_field

is_subject_field() -> bool

Check if this is a subject field (6xx).

linkage_occurrence_num

linkage_occurrence_num() -> Optional[str]

Extract the occurrence number from subfield $6 linkage (pymarc compatibility).

__eq__

__eq__(other: Any) -> bool

Compare fields by content.

as_marc

as_marc() -> bytes

Serialize field to ISO 2709 binary format (pymarc compatibility).

as_marc21

as_marc21() -> bytes

Alias for as_marc() (pymarc compatibility).

__hash__

__hash__() -> int

Hash based on tag and first subfield.

convert_legacy_subfields classmethod

convert_legacy_subfields(subfields: list) -> list

Convert legacy pymarc subfield list to Subfield objects.

Converts the old format ['code', 'value', 'code', 'value', ...] to a list of Subfield objects.

ControlField

A backward-compatible subclass of Field for control fields. Prefer Field(tag, data=value) for new code.

from mrrc import ControlField

# Still works for backward compatibility
cf = ControlField("001", "12345")
print(cf.data)              # "12345"
print(cf.is_control_field())  # True
print(isinstance(cf, Field))  # True

Subfield

A subfield within a MARC field.

from mrrc import Subfield

sf = Subfield("a", "value")
print(sf.code)   # "a"
print(sf.value)  # "value"

A subfield within a MARC field.

Subfields are named data elements within fields, consisting of a single-character code and a value.

code instance-attribute

code: str

value instance-attribute

value: str

__new__

__new__(code: str, value: str) -> Subfield

__repr__

__repr__() -> str

__str__

__str__() -> str

__eq__

__eq__(other: object) -> bool

Leader

The 24-byte MARC record header containing metadata.

from mrrc import Leader

leader = Leader()
leader.record_type = "a"           # Language material
leader.bibliographic_level = "m"   # Monograph
leader.character_coding = "a"      # UTF-8

Properties:

Property Position Description
record_length 00-04 Record length (5 digits)
record_status 05 Record status (n/c/d)
record_type 06 Type of record (a=language, c=music, etc.)
bibliographic_level 07 Bibliographic level (m=monograph, s=serial)
control_record_type 08 Type of control
character_coding 09 Character coding (space=MARC-8, a=UTF-8)
indicator_count 10 Indicator count (usually 2)
subfield_code_count 11 Subfield code count (usually 2)
data_base_address 12-16 Base address of data
encoding_level 17 Encoding level
cataloging_form 18 Descriptive cataloging form
multipart_level 19 Multipart resource record level
reserved 20-23 Entry map (usually "4500")

AuthorityRecord

A MARC authority record. Returned by AuthorityMARCReader.

from mrrc import AuthorityMARCReader, FieldNotFound

reader = AuthorityMARCReader("authorities.mrc")
record = next(reader)

print(record.heading_text())      # main heading string, or None
for tracing in record.see_from_tracings():
    print("see from:", tracing)

Note: get_fields(tag) returns None when no fields match (unlike Record.get_fields, which returns []).

A MARC authority record. Returned by AuthorityMARCReader.

leader property

leader: Leader

errors property

errors: list[Exception]

Non-fatal errors accumulated while parsing this record. See :attr:Record.errors.

record_type

record_type() -> str

Leader byte 06 (type of record) as a single-character string.

heading

heading() -> Optional[Field]

The main heading field (1XX), or None if absent.

heading_text

heading_text() -> Optional[str]

Subfield a of the main heading field, or None.

see_from_tracings

see_from_tracings() -> List[Field]

See-from tracings (4XX fields).

see_also_tracings

see_also_tracings() -> List[Field]

See-also-from tracings (5XX fields).

notes

notes() -> List[Field]

Note fields: 6XX fields other than the subject tracings 650/651/655.

linking_entries

linking_entries() -> List[Field]

Heading linking entries (7XX fields).

get_fields

get_fields(tag: str) -> Optional[List[Field]]

All fields matching tag, or None when none match.

get_field

get_field(tag: str) -> Optional[Field]

get_field_or_err

get_field_or_err(tag: str) -> Field

Get first field with given tag, raising mrrc.FieldNotFound (E105) when the tag is not present.

get_control_field

get_control_field(tag: str) -> Optional[str]

to_json

to_json() -> str

__repr__

__repr__() -> str

__str__

__str__() -> str
try:
    main = record.get_field_or_err("100")
except FieldNotFound as e:
    print(f"missing {e.field_tag} on record {e.record_control_number}")

HoldingsRecord

A MARC holdings record. Returned by HoldingsMARCReader.

from mrrc import HoldingsMARCReader

reader = HoldingsMARCReader("holdings.mrc")
record = next(reader)

for location in record.locations():       # 852 fields
    print(location)
for caption in record.captions_basic():   # 853 fields
    print(caption)

Note: get_fields(tag) returns None when no fields match (unlike Record.get_fields, which returns []).

A MARC holdings record. Returned by HoldingsMARCReader.

leader property

leader: Leader

errors property

errors: list[Exception]

Non-fatal errors accumulated while parsing this record. See :attr:Record.errors.

record_type

record_type() -> str

Leader byte 06 (type of record) as a single-character string.

locations

locations() -> List[Field]

Location fields (852).

captions_basic

captions_basic() -> List[Field]

Basic caption and pattern fields (853).

captions_supplements

captions_supplements() -> List[Field]

Supplementary-material caption and pattern fields (854).

captions_indexes

captions_indexes() -> List[Field]

Index caption and pattern fields (855).

enumeration_basic

enumeration_basic() -> List[Field]

Basic enumeration and chronology fields (863).

enumeration_supplements

enumeration_supplements() -> List[Field]

Supplementary-material enumeration and chronology fields (864).

enumeration_indexes

enumeration_indexes() -> List[Field]

Index enumeration and chronology fields (865).

textual_holdings_basic

textual_holdings_basic() -> List[Field]

Basic textual holdings fields (866).

textual_holdings_supplements

textual_holdings_supplements() -> List[Field]

Supplementary-material textual holdings fields (867).

textual_holdings_indexes

textual_holdings_indexes() -> List[Field]

Index textual holdings fields (868).

get_fields

get_fields(tag: str) -> Optional[List[Field]]

All fields matching tag, or None when none match.

get_field

get_field(tag: str) -> Optional[Field]

get_field_or_err

get_field_or_err(tag: str) -> Field

Get first field with given tag, raising mrrc.FieldNotFound (E105) when the tag is not present.

get_control_field

get_control_field(tag: str) -> Optional[str]

to_json

to_json() -> str

__repr__

__repr__() -> str

__str__

__str__() -> str

Reader/Writer Classes

mrrc provides a dedicated reader per MARC record type. All three share the ISO 2709 binary format and the iteration protocol; they differ in the record type they yield and their constructor keywords.

Reader Yields Use for
MARCReader Record Bibliographic records
AuthorityMARCReader AuthorityRecord Authority records
HoldingsMARCReader HoldingsRecord Holdings records

There are no dedicated authority/holdings writers; MARCWriter serializes any record's fields to ISO 2709.

MARCReader

Reads MARC records from files with GIL-released I/O for parallelism.

MARCReader(file_obj, to_unicode=True, permissive=False, recovery_mode="strict")
from mrrc import MARCReader

# From file path (recommended for performance)
for record in MARCReader("records.mrc"):
    print(record.title)

# From file object
with open("records.mrc", "rb") as f:
    for record in MARCReader(f):
        print(record.title)

# From bytes
data = open("records.mrc", "rb").read()
for record in MARCReader(data):
    print(record.title)

Input Types:

Type Description
str or Path File path (pure Rust I/O, best performance)
bytes or bytearray In-memory data
File object Python file-like object

Keyword Arguments:

Kwarg Type Default Description
to_unicode bool True Accepted for pymarc compatibility. mrrc always converts MARC-8 to UTF-8; passing False emits a warning but has no effect.
permissive bool False When True, yields None for records that fail to parse instead of raising, matching pymarc's permissive behavior.
recovery_mode str "strict" Controls how malformed records are handled (see below). Cannot be combined with permissive=True.

Recovery Modes:

Instead of skipping bad records entirely (like permissive=True), recovery_mode attempts to salvage valid fields from damaged records:

Mode Behavior
"strict" Raise on any malformation (default).
"lenient" Attempt to recover, salvage valid fields from damaged records.
"permissive" Very lenient — accept partial data even from severely malformed records.
# Skip bad records (pymarc-compatible)
for record in MARCReader("bad.mrc", permissive=True):
    if record is None:
        continue
    print(record.title)

# Salvage partial data from malformed records
for record in MARCReader("bad.mrc", recovery_mode="lenient"):
    print(f"Got {len(record.get_fields())} fields")

Note: permissive=True and recovery_mode other than "strict" cannot be combined — they represent different error-handling strategies. Use permissive=True for pymarc-compatible "skip bad records" behavior, or recovery_mode for mrrc's "salvage what you can" approach.

Thread Safety:

  • NOT thread-safe - each thread needs its own reader
  • GIL released during record parsing for parallelism
  • Use ThreadPoolExecutor with separate readers per thread

MARCWriter

Writes MARC records to files.

from mrrc import MARCWriter

with MARCWriter("output.mrc") as writer:
    writer.write(record)

MARC Writer wrapper.

__init__

__init__(file_obj: Any)

Create a new MARC writer.

write

write(record: Record) -> None

Write a record.

write_record

write_record(record: Record) -> None

Write a record (alias for write).

close

close() -> None

Close the writer.

__enter__

__enter__()

Context manager support.

__exit__

__exit__(exc_type, exc_val, exc_tb)

Context manager support.

AuthorityMARCReader

Reads MARC authority records, yielding AuthorityRecord. Same ISO 2709 binary format and iteration protocol as MARCReader, with a smaller keyword set. The record source (path, bytes, or file object) is positional; recovery_mode and validation_level are keyword-only and follow the MARCReader recovery modes, except recovery_mode defaults to "permissive" here rather than "strict".

from mrrc import AuthorityMARCReader

# From a path, bytes, or file object — like MARCReader
for record in AuthorityMARCReader("authorities.mrc"):
    print(record.heading_text())

Reader for MARC authority records.

__new__

__new__(source: Any, *, recovery_mode: str = 'permissive', validation_level: str = 'structural') -> AuthorityMARCReader

__iter__

__iter__() -> Iterator[AuthorityRecord]

__next__

__next__() -> AuthorityRecord

__enter__

__enter__() -> AuthorityMARCReader

__exit__

__exit__(_exc_type: Any, _exc_val: Any, _exc_tb: Any) -> bool

read_record

read_record() -> Optional[AuthorityRecord]

HoldingsMARCReader

Reads MARC holdings records, yielding HoldingsRecord. Same shape and keyword semantics as AuthorityMARCReader (recovery_mode defaults to "permissive").

from mrrc import HoldingsMARCReader

for record in HoldingsMARCReader("holdings.mrc"):
    for location in record.locations():
        print(location)

Reader for MARC holdings records.

__new__

__new__(source: Any, *, recovery_mode: str = 'permissive', validation_level: str = 'structural') -> HoldingsMARCReader

__iter__

__iter__() -> Iterator[HoldingsRecord]

__next__

__next__() -> HoldingsRecord

__enter__

__enter__() -> HoldingsMARCReader

__exit__

__exit__(_exc_type: Any, _exc_val: Any, _exc_tb: Any) -> bool

read_record

read_record() -> Optional[HoldingsRecord]

Query DSL

Composable query objects passed to the Record.fields_matching* methods to select fields by tag, indicators, tag range, or subfield value/pattern.

from mrrc import FieldQuery, SubfieldValueQuery

# Fields with tag 650 and indicator2 = "0"
q = FieldQuery().tag("650").indicator2("0")
for field in record.fields_matching(q):
    print(field)

# Fields whose subfield $a equals "History"
for field in record.fields_matching_value(
    SubfieldValueQuery("650", "a", "History")
):
    print(field)

FieldQuery

Query builder for matching fields by tag, indicators, and subfields.

__init__

__init__() -> None

tag

tag(tag: str) -> FieldQuery

indicator1

indicator1(indicator: Optional[str] = None) -> FieldQuery

indicator2

indicator2(indicator: Optional[str] = None) -> FieldQuery

has_subfield

has_subfield(code: str) -> FieldQuery

has_subfields

has_subfields(codes: List[str]) -> FieldQuery

tag_range

tag_range(start_tag: str, end_tag: str) -> TagRangeQuery

TagRangeQuery

Query for fields within a tag range.

start_tag property

start_tag: str

end_tag property

end_tag: str

indicator1 property

indicator1: Optional[str]

indicator2 property

indicator2: Optional[str]

__new__

__new__(start_tag: str, end_tag: str, *, indicator1: Optional[str] = None, indicator2: Optional[str] = None, required_subfields: Optional[List[str]] = None) -> TagRangeQuery

tag_in_range

tag_in_range(tag: str) -> bool

SubfieldPatternQuery

Query for fields where a subfield matches a regex pattern.

tag property

tag: str

subfield_code property

subfield_code: str

pattern property

pattern: str

The regex pattern string this query matches against.

negate property

negate: bool

__new__

__new__(tag: str, subfield_code: str, pattern: str, *, negate: bool = False) -> SubfieldPatternQuery

SubfieldValueQuery

Query for fields where a subfield matches a value.

tag property

tag: str

subfield_code property

subfield_code: str

value property

value: str

partial property

partial: bool

negate property

negate: bool

__new__

__new__(tag: str, subfield_code: str, value: str, *, partial: bool = False, negate: bool = False) -> SubfieldValueQuery

Format Conversion

Record Methods

# JSON formats
json_str = record.to_json()
marcjson_str = record.to_marcjson()

# pymarc-compatible serialization
json_str = record.as_json()     # pymarc MARC-in-JSON format
record_dict = record.as_dict()  # pymarc-compatible dict

# MARCXML
xml_str = record.to_xml()

# Other XML-based formats
mods_str = record.to_mods()
dc_str = record.to_dublin_core()

# Binary (ISO 2709)
marc_bytes = record.as_marc()   # returns bytes
marc_bytes = record.as_marc21() # alias

Module Functions

import mrrc

# Parse from JSON
record = mrrc.json_to_record(json_str)

# Parse from MARCXML
record = mrrc.xml_to_record(xml_str)

# Parse MARCXML collection (multiple records)
records = mrrc.xml_to_records(collection_xml_str)

# Parse from MODS XML
record = mrrc.mods_to_record(mods_xml)

# Parse MODS collection (multiple records)
records = mrrc.mods_collection_to_records(mods_collection_xml)

# Convert to CSV
csv_str = mrrc.record_to_csv(record)
csv_str = mrrc.records_to_csv(records)

# Convenience functions
records = mrrc.parse_xml_to_array(xml_str)
records = mrrc.parse_json_to_array(json_str)
mrrc.map_records(func, reader)

Constants

from mrrc import (
    LEADER_LEN,           # 24
    DIRECTORY_ENTRY_LEN,  # 12
    END_OF_FIELD,         # '\x1e'
    END_OF_RECORD,        # '\x1d'
    SUBFIELD_INDICATOR,   # '\x1f'
    MARC_XML_NS,          # MARCXML namespace URI
    MARC_XML_SCHEMA,      # MARCXML schema URI
)

BIBFRAME Conversion

Convert MARC to BIBFRAME 2.0 RDF.

from mrrc import marc_to_bibframe, BibframeConfig

# Basic conversion
config = BibframeConfig()
graph = marc_to_bibframe(record, config)

# With custom base URI
config.set_base_uri("http://library.example.org/")
graph = marc_to_bibframe(record, config)

# Serialize to different formats
turtle = graph.serialize("turtle")
rdfxml = graph.serialize("rdf-xml")
jsonld = graph.serialize("jsonld")
ntriples = graph.serialize("ntriples")

BibframeConfig

Configuration for BIBFRAME conversion.

Controls how MARC records are converted to BIBFRAME entities and how the resulting RDF graph is serialized.

Examples:

Default configuration::

config = mrrc.BibframeConfig()

Custom configuration::

config = mrrc.BibframeConfig()
config.set_base_uri("http://example.org/")
config.set_output_format("turtle")
config.set_authority_linking(True)

base_uri property

base_uri: str | None

Get the current base URI.

output_format property

output_format: str

Get the current output format.

authority_linking property

authority_linking: bool

Get the current authority linking setting.

include_bflc property

include_bflc: bool

Get the current BFLC extension setting.

strict property

strict: bool

Get the current strict mode setting.

fail_fast property

fail_fast: bool

Get the current fail-fast setting.

__init__

__init__() -> None

__repr__

__repr__() -> str

set_base_uri

set_base_uri(uri: str) -> None

Set the base URI for generated resources.

When set, entities are given minted URIs like {base}/work/{id}. When not set, blank nodes are used.

set_output_format

set_output_format(format: str) -> None

Set the output format for RDF serialization.

Parameters:

Name Type Description Default
format str

One of: "rdf-xml", "jsonld", "turtle", "ntriples"

required

Raises:

Type Description
ValueError

If format is not recognized

set_authority_linking

set_authority_linking(enabled: bool) -> None

Enable or disable linking to external authority URIs.

set_include_bflc

set_include_bflc(enabled: bool) -> None

Enable or disable BFLC extensions.

set_strict

set_strict(enabled: bool) -> None

Enable or disable strict validation mode.

set_fail_fast

set_fail_fast(enabled: bool) -> None

Enable or disable fail-fast error handling.

RdfGraph

The RDF graph produced by marc_to_bibframe. Use len(graph) for the triple count and serialize(format) to emit RDF in "turtle", "rdf-xml", "jsonld", or "ntriples".

An RDF graph containing BIBFRAME triples.

Wraps the RDF graph produced by MARC-to-BIBFRAME conversion and provides serialization to various RDF formats.

Examples:

::

record = mrrc.Record(leader=mrrc.Leader())
config = mrrc.BibframeConfig()
graph = mrrc.marc_to_bibframe(record, config)

print(f"Graph has {len(graph)} triples")
turtle = graph.serialize("turtle")

__init__

__init__() -> None

__repr__

__repr__() -> str

__len__

__len__() -> int

Get the number of triples in the graph.

is_empty

is_empty() -> bool

Check if the graph is empty.

serialize

serialize(format: str) -> str

Serialize the graph to a string in the specified format.

Parameters:

Name Type Description Default
format str

One of: "rdf-xml", "jsonld", "turtle", "ntriples"

required

Returns:

Type Description
str

The serialized RDF as a string

Raises:

Type Description
ValueError

If format is not recognized or serialization fails

parse staticmethod

parse(data: str, format: str) -> RdfGraph

Parse an RDF graph from a string.

Parameters:

Name Type Description Default
data str

The RDF data as a string

required
format str

One of: "rdf-xml", "jsonld", "turtle", "ntriples"

required

Returns:

Type Description
RdfGraph

A new RdfGraph instance

Raises:

Type Description
ValueError

If format is not recognized or parsing fails

triples

triples() -> list[tuple[str, str, str]]

Get all triples as a list of (subject, predicate, object) tuples.

Parallel Processing

Lower-level utilities for splitting and parsing large MARC files across threads. Most callers should use MARCReader with a ThreadPoolExecutor; these are the building blocks underneath.

RecordBoundaryScanner

Scanner for finding record boundaries in MARC data.

__init__

__init__() -> None

scan

scan(data: bytes) -> List[tuple[int, int]]

scan_limited

scan_limited(data: bytes, limit: int) -> List[tuple[int, int]]

count_records

count_records(data: bytes) -> int

clear

clear() -> None

capacity

capacity() -> int

ProducerConsumerPipeline

Pipeline for parallel record processing.

__init__

__init__() -> None

from_file staticmethod

from_file(path: str, buffer_size: Optional[int] = None, channel_capacity: Optional[int] = None) -> ProducerConsumerPipeline

next

next() -> Optional[Record]

try_next

try_next() -> Optional[Record]

Exceptions

from mrrc import MrrcException, MarcError

try:
    for record in MARCReader("bad.mrc"):
        pass
except MrrcException as e:
    print(f"MRRC error: {e}")
except MarcError as e:
    print(f"MARC error: {e}")

The exception hierarchy:

  • MrrcException — base exception for all mrrc errors
  • MarcError — MARC-specific errors (parsing, validation)
  • StaleFieldError — a live field handle was invalidated by field removal; re-fetch the field from the record and retry

See Also