Skip to content

v0.7.3

Choose a tag to compare

@github-actions github-actions released this 26 Feb 21:48
· 465 commits to main since this release

Added

  • Standard MARCXML support (#15): xml_to_record() and record_to_xml() / to_xml() now produce and parse standard LOC MARCXML. Output includes XML declaration, xmlns="http://www.loc.gov/MARC21/slim" namespace, and tag/ind1/ind2/code as XML attributes (not child elements). Parsing accepts all three namespace forms: default namespace (<record xmlns="...">), prefix namespace (<marc:record xmlns:marc="...">), and no namespace (<record>). New xml_to_records() function parses <collection> wrappers containing multiple records. Rust module renamed from xml to marcxml (record_to_marcxml(), marcxml_to_record(), marcxml_to_records()); Python API names unchanged for pymarc compatibility.
  • get_linked_fields() for 880 alternate script field linkage (#19): New pymarc-compatible record.get_linked_fields(field) returns all 880 fields linked via subfield $6 occurrence matching. Also adds get_linked_field() (singular), get_original_field() (reverse lookup from 880 to original), and get_field_pairs(tag) (original + 880 tuples). Fixed LinkageInfo parser to handle real MARC script identification codes: (2 (Hebrew), (3 (Arabic), $1 (CJK), (N (Cyrillic), (S (Greek). Includes tests for Hebrew, Arabic, Chinese, Russian, and Greek scripts plus linkage across multiple field types (title, author, publisher, subject, series, notes, added entries).
  • MARCReader.backend_type property: Exposes the reader's I/O backend as a string ("rust_file", "cursor", or "python_file") for diagnostics and testing.
  • Dependabot configuration: Enabled automated dependency updates for cargo, uv, and github-actions ecosystems (weekly schedule).
  • GIL release verification tests: Deterministic tests proving py.detach() actually releases the GIL during record parsing. Uses sys.setswitchinterval(100.0) to suppress automatic GIL switching, then verifies a background counter thread makes progress — which can only happen if the GIL is explicitly released. Covers all three backends: rust_file, cursor, and python_file.

Changed

  • Removed 22 duplicate inherent methods from Record (#26): Helper methods like title(), publisher(), isbn(), etc. were defined both as inherent methods on Record and on the RecordHelpers trait. The inherent methods shadowed the trait, causing maintenance burden and confusion. Removed all duplicates; callers now use the RecordHelpers trait (already re-exported in the public API). Rust users may need to add use mrrc::RecordHelpers; if not already imported.
  • Replaced flaky GIL release test with deterministic verification (#3): The test_pathlib_path_threading_equivalent_to_str test used timing-based speedup ratios to verify GIL release, which was unreliable on shared CI runners. Replaced with TestBackendTypeRouting (6 tests asserting each input type routes to the correct backend) and TestPathlibThreadingCorrectness (2 tests verifying parallel reads produce correct results without speedup assertions).
  • quick-xml 0.31 → 0.39 (#7): Migrated MODS XML parser to quick-xml 0.39 API. BytesText::unescape() replaced by decode() with explicit entity resolution via new Event::GeneralRef handler (predefined XML entities and numeric character references). Reader::trim_text() moved to Reader::config_mut().trim_text().
  • pyo3 0.27 → 0.28 (#8): Migrated PyO3 bindings to 0.28 API for free-threaded Python support. Python::with_gil()Python::attach(), Python::assume_gil_acquired()Python::assume_attached(). Added from_py_object annotation to all 10 #[pyclass] types implementing Clone.
  • thiserror 1.0 → 2.0 (#6): Updated error derive macro.
  • nom 7.1 → 8.0 (#5): Updated parser combinator library.
  • ruff 0.14.14 → 0.15.2 (#9): Updated Python linter.
  • GitHub Actions updates: actions/checkout 4 → 6 (#14), codecov/codecov-action 4 → 5 (#13), astral-sh/setup-uv 4 → 7 (#12), actions/download-artifact 4 → 7 (#11), moonrepo/setup-rust 0 → 1 (#10), actions/setup-python 5 → 6 (#24), actions/upload-artifact 4 → 6 (#23), actions/upload-pages-artifact 3 → 4 (#22), actions/cache 4 → 5 (#21), softprops/action-gh-release 1 → 2 (#20).

Fixed

  • Format conversion functions now return wrapped Python objects (#16): marcjson_to_record(), json_to_record(), xml_to_record(), mods_to_record(), and mods_collection_to_records() previously returned raw Rust PyRecord objects. Field subscripting (field['a']), get_subfields(), and leader indexing (leader[9]) all failed on these records. Now all format conversion functions wrap results in the Python Record class, matching the behavior of records from MARCReader.
  • publisher() now checks 264 (RDA) as fallback (#17): Previously only returned $b from field 260. Now falls back to field 264 with indicator2='1' (publication) for RDA-cataloged records. Also updated place_of_publication(), publication_date(), publication_info(), and publication_year() / pubyear() to check 264 as fallback, matching pymarc behavior.
  • subjects() now covers all 6XX fields (#18): Previously only returned $a values from field 650 (topical term). Now matches pymarc's subjects() coverage: 600, 610, 611, 630, 648, 650, 651, 653, 654, 655, 656, 657, 658, 662, 690-691, 696-699. Also updated subjects_with_subdivision() and subjects_with_note() to search all subject fields.
  • Removed flaky timing assertion from test_backend_comparison_1k: The benchmark's assert median_rustfile < median_pythonfile * 2 failed intermittently on shared CI runners due to disk I/O variance. Timing data is still printed for informational purposes.

Documentation

  • Updated Python quick example in docs/index.md to use path string instead of file I/O wrapper, demonstrating the GIL-releasing code path.