Skip to content

fix(serialization): remove monty dependency - #979

Open
njzjz wants to merge 7 commits into
deepmodeling:masterfrom
njzjz:fix/remove-monty-dependency
Open

fix(serialization): remove monty dependency#979
njzjz wants to merge 7 commits into
deepmodeling:masterfrom
njzjz:fix/remove-monty-dependency

Conversation

@njzjz

@njzjz njzjz commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

  • replace monty serialization usage with an internal dpdata.serialization module
  • keep loading existing monty-style JSON numpy arrays and as_dict/from_dict objects
  • remove monty from project/docs dependency declarations and update JSON round-trip tests

Tests

  • ruff check dpdata/ tests/test_json.py tests/test_to_pymatgen_entry.py
  • cd tests && python -m unittest test_json.py test_to_pymatgen_entry.py

Summary by CodeRabbit

  • New Features
    • Added native dump/load helpers for JSON, YAML, and msgpack with automatic format detection and transparent gzip/bzip2 compression.
    • Serialization now round-trips common types (e.g., complex arrays, datetimes, UUIDs, enums, and paths).
    • System dump/load and dict reconstruction now use the new serializer for consistent decoding.
  • Chores
    • Removed monty from runtime dependencies and updated lint rules accordingly.
  • Documentation
    • Updated installation/troubleshooting docs and intersphinx/docs environment config to remove monty.
  • Tests
    • Expanded JSON/YAML serialization coverage with temp-file round-trips, nested objects, and timezone-aware datetimes.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. dependencies Pull requests that update a dependency file dpdata labels Jun 19, 2026
@codspeed-hq

codspeed-hq Bot commented Jun 19, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 2 untouched benchmarks


Comparing njzjz:fix/remove-monty-dependency (acaef94) with master (c77e038)

Open in CodSpeed

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces the monty runtime serialization dependency with dpdata.serialization, integrates JSON/YAML/msgpack handling into System, adds serialization tests, and removes monty from dependencies and documentation configuration.

Changes

Replace monty with dpdata.serialization

Layer / File(s) Summary
Serialization implementation
dpdata/serialization.py
Adds compressed-file handling, format routing, recursive encoding/decoding, and JSON/YAML/msgpack support.
System serialization integration
dpdata/system.py
Updates System.dump, System.load, and System.from_dict to use the local helpers.
Serialization tests and imports
tests/test_json.py, tests/test_to_pymatgen_entry.py
Adds JSON, datetime, nested-object, and YAML tests and switches the pymatgen entry test to the new loadfn.
Dependency and documentation cleanup
pyproject.toml, AGENTS.md, docs/conf.py, docs/environment.yml
Removes monty from dependencies, Ruff configuration, installation guidance, intersphinx mappings, and the docs environment.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant System
  participant dpdata_serialization
  participant FormatBackend
  participant process_decoded
  System->>dpdata_serialization: dumpfn or loadfn
  dpdata_serialization->>FormatBackend: write or read JSON, YAML, or msgpack
  FormatBackend-->>dpdata_serialization: serialized or parsed data
  dpdata_serialization->>process_decoded: decode payload
  process_decoded-->>System: reconstructed data
Loading

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: removing the monty dependency through serialization work.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
dpdata/serialization.py (1)

27-34: Add return type annotation to _open_text.

The function lacks a return type hint, which may cause type checkers to infer imprecise union types for gzip.open/bz2.open even though all code paths return text I/O objects compatible with json.dump and json.load.

Proposed fix
-from typing import Any
+from typing import Any, TextIO, cast
@@
-def _open_text(filename: str | Path, mode: str):
+def _open_text(filename: str | Path, mode: str) -> TextIO:
     path = str(filename)
     lower_path = path.lower()
     if lower_path.endswith((".gz", ".z")):
-        return gzip.open(path, mode, encoding="utf-8")
+        return cast(TextIO, gzip.open(path, mode, encoding="utf-8"))
     if lower_path.endswith(".bz2"):
-        return bz2.open(path, mode, encoding="utf-8")
+        return cast(TextIO, bz2.open(path, mode, encoding="utf-8"))
     return open(path, mode, encoding="utf-8")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dpdata/serialization.py` around lines 27 - 34, The _open_text function is
missing a return type annotation which prevents type checkers from accurately
inferring the type. Add a return type hint to the function signature after the
mode parameter by specifying the appropriate return type that represents a text
I/O object (such as TextIO from the typing module) since all three code
paths—gzip.open, bz2.open, and the built-in open function—all return compatible
text I/O objects when called with encoding="utf-8".

Sources: Linters/SAST tools, Pipeline failures

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@AGENTS.md`:
- Line 135: The individual package installation example for troubleshooting is
incomplete and missing required core dependencies. Update the `uv pip install`
command that currently lists numpy scipy h5py wcmatch to also include lmdb and
msgpack-numpy at the end of the package list. This ensures users following the
troubleshooting step have all necessary core dependencies installed for a
complete setup.
- Line 96: The Core dependencies section in AGENTS.md is incomplete and missing
two dependencies that are listed in pyproject.toml. Update the Core line that
currently reads "Core: numpy>=1.14.3, scipy, h5py, wcmatch" to include lmdb and
msgpack-numpy in the comma-separated dependency list so it matches the complete
set of core dependencies defined in pyproject.toml.
- Line 12: The documentation comment on the `uv pip install -e .` line in
AGENTS.md is incomplete and does not match the actual core dependencies declared
in pyproject.toml. Update the inline comment that lists the core dependencies to
include all six dependencies: numpy, scipy, h5py, wcmatch, lmdb, and
msgpack-numpy. Ensure the comment accurately reflects what is actually installed
by the development mode installation.

In `@dpdata/serialization.py`:
- Around line 149-154: The datetime deserialization logic in the
datetime.datetime class handler is losing timezone information by using
split("+")[0] which strips positive UTC offsets and causes failures on negative
offsets. Replace the current approach with
datetime.datetime.fromisoformat(obj["string"]) as the primary decoder to
properly preserve timezone data, and keep the existing strptime calls as
fallback for backward compatibility with older formats. This ensures
round-tripping of timezone-aware datetimes without converting them to naive
datetimes.

---

Nitpick comments:
In `@dpdata/serialization.py`:
- Around line 27-34: The _open_text function is missing a return type annotation
which prevents type checkers from accurately inferring the type. Add a return
type hint to the function signature after the mode parameter by specifying the
appropriate return type that represents a text I/O object (such as TextIO from
the typing module) since all three code paths—gzip.open, bz2.open, and the
built-in open function—all return compatible text I/O objects when called with
encoding="utf-8".
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1ccd9ec6-d46e-43ed-b7ca-eaa866d12722

📥 Commits

Reviewing files that changed from the base of the PR and between 1b63c9b and 7a6f287.

📒 Files selected for processing (8)
  • AGENTS.md
  • docs/conf.py
  • docs/environment.yml
  • dpdata/serialization.py
  • dpdata/system.py
  • pyproject.toml
  • tests/test_json.py
  • tests/test_to_pymatgen_entry.py
💤 Files with no reviewable changes (3)
  • docs/environment.yml
  • docs/conf.py
  • pyproject.toml

Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread AGENTS.md Outdated
Comment thread dpdata/serialization.py Outdated
@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.88%. Comparing base (713ef68) to head (acaef94).
⚠️ Report is 16 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #979      +/-   ##
==========================================
+ Coverage   86.95%   87.88%   +0.93%     
==========================================
  Files          90       91       +1     
  Lines        8330     9382    +1052     
==========================================
+ Hits         7243     8245    +1002     
- Misses       1087     1137      +50     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@njzjz
njzjz force-pushed the fix/remove-monty-dependency branch from 7a6f287 to e84be01 Compare June 19, 2026 17:53
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

An unexpected error occurred while generating fixes: Not Found - https://docs.github.com/rest/git/refs#get-a-reference

@njzjz
njzjz requested a review from wanghan-iapcm June 19, 2026 18:22
Comment thread dpdata/serialization.py
Preserve timezone-aware datetime values, retain YAML support through a declared backend, and align dependency documentation with project metadata. Add focused regression coverage for datetime and YAML round trips.

Coding-Agent: Codex
Codex-Version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
@njzjz-bot

Copy link
Copy Markdown
Contributor

Addressed the review findings in commit 1d9a382. The _open_text nitpick required no additional change because the current PR head already annotated it as returning TextIO and cast each backend result accordingly.

Validation: focused serialization tests passed; Ruff lint/format and CLI smoke checks passed. The full suite ran 2,032 tests, with 13 errors caused by unavailable optional parmed/rdkit dependencies.

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dpdata/serialization.py`:
- Around line 16-24: Update _detect_format to determine the format from the
filename’s final meaningful suffix rather than substring matches. Remove a
trailing compression suffix before inspection, then map only the resulting .mpk,
.yaml, or .yml suffixes to their formats; otherwise retain the JSON default and
explicit fmt override.
- Around line 141-176: Update process_decoded before invoking a class’s
from_dict so the marker-filtered data mapping is recursively passed through
process_decoded first. Then call cls.from_dict with the decoded nested values,
while preserving the existing Enum handling and fallback behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fba5c27-ccb8-497a-86dc-7dd5aef35fe4

📥 Commits

Reviewing files that changed from the base of the PR and between 7a6f287 and 1d9a382.

📒 Files selected for processing (8)
  • AGENTS.md
  • docs/conf.py
  • docs/environment.yml
  • dpdata/serialization.py
  • dpdata/system.py
  • pyproject.toml
  • tests/test_json.py
  • tests/test_to_pymatgen_entry.py
💤 Files with no reviewable changes (2)
  • docs/conf.py
  • docs/environment.yml
🚧 Files skipped from review as they are similar to previous changes (4)
  • pyproject.toml
  • tests/test_to_pymatgen_entry.py
  • dpdata/system.py
  • tests/test_json.py

Comment thread dpdata/serialization.py
Comment thread dpdata/serialization.py
wanghan-iapcm and others added 2 commits July 16, 2026 22:46
Detect serialization formats from the final meaningful suffix and recursively decode values before passing them to custom from_dict constructors. Add regression tests for both review findings.

Coding-Agent: Codex
Codex-Version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning-Effort: xhigh

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_json.py`:
- Around line 21-30: Add an as_dict method to the NestedSerializable test helper
that returns its stored value in the expected dictionary form, ensuring
to_serializable() traverses and decodes nested numpy/datetime values through the
nested serialization path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 22ba3ebf-ba1f-4b0f-824e-ba86ae3ac2ea

📥 Commits

Reviewing files that changed from the base of the PR and between 267a920 and b1e32a5.

📒 Files selected for processing (2)
  • dpdata/serialization.py
  • tests/test_json.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • dpdata/serialization.py

Comment thread tests/test_json.py
Make the nested serialization helper expose as_dict so the regression test genuinely traverses NumPy and datetime values through the encoder.

Coding-Agent: Codex
Codex-Version: codex-cli 0.144.6
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
Copilot AI review requested due to automatic review settings July 21, 2026 06:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR removes the runtime dependency on monty by introducing an internal dpdata.serialization module and updating core I/O paths and tests to use it, while preserving compatibility with existing monty-style JSON payloads.

Changes:

  • Added dpdata.serialization with JSON/YAML/msgpack dump/load helpers, monty-style numpy/datetime decoding, and gzip/bzip2 support.
  • Switched System.dump/load/from_dict and pymatgen-entry tests to use the new serializer.
  • Updated dependencies/docs to drop monty and add PyYAML, and expanded serialization round-trip tests.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
dpdata/serialization.py New internal serializer to replace monty functionality and preserve monty-style decoding.
dpdata/system.py Routes System dump/load/from_dict through the new serializer.
tests/test_json.py Adds/extends tests for dump/load round-trips and serializer helpers.
tests/test_to_pymatgen_entry.py Uses new loadfn to load monty-style pymatgen objects.
pyproject.toml Removes monty, adds PyYAML, updates import-ban list accordingly.
docs/environment.yml Drops monty from docs environment dependencies.
docs/conf.py Removes monty intersphinx mapping.
AGENTS.md Updates contributor docs to reflect new core dependency set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dpdata/system.py Outdated
Comment thread dpdata/system.py Outdated
Comment thread dpdata/serialization.py
Comment thread tests/test_json.py Outdated
Coding-Agent: Codex
Codex-Version: codex-cli 0.144.6
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
@njzjz
njzjz requested a review from wanghan-iapcm July 22, 2026 17:02
`dpdata/serialization.py` was at 64% line coverage, which is why
codecov/patch is red on this PR. The gaps were the paths that only run on
files the existing tests never write: msgpack, gzip/bz2 streams, the
ruamel fallback, and every encoder branch except ndarray.

Add 35 cases covering format detection and compression-suffix stripping,
text and binary streams for each codec, complex arrays, numpy scalars,
UUID/Path/Enum round trips, the datetime strptime fallbacks for payloads
`fromisoformat` rejects, unknown `@module`/`@class` markers falling back
to plain dicts, JSON/YAML/msgpack round trips including compressed
variants, and the `Invalid format` errors on both dump and load.

The optional-dependency branches are exercised by patching
`importlib.import_module` and `sys.modules`, so PyYAML-missing,
ruamel-missing, and msgpack-missing all raise their intended messages
without changing the test environment.

Coverage of the module is now 100%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@njzjz

njzjz commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

Closed the codecov/patch gap in acaef94.

dpdata/serialization.py was at 64%. The uncovered lines were the paths no existing test reaches: msgpack dump/load, the gzip/bz2 streams, _open_binary entirely, the ruamel fallback and its RuntimeError, complex-array encode/decode, UUID/Path/Enum encoding, the datetime strptime fallbacks, the unknown-@module branch, and TypeError: Invalid format.

tests/test_serialization.py adds 35 cases and takes the module to 100%.

Two notes on how the harder branches are reached:

  • The optional-dependency errors are triggered by patching importlib.import_module and sys.modules rather than by uninstalling anything, so PyYAML-missing, ruamel-missing, and msgpack-missing each assert their own message with the test environment untouched.
  • The strptime fallbacks needed inputs that fromisoformat actually rejects. On Python 3.11+ a space separator is accepted, so "2020-01-02 03:04:05.678900" never reaches them; unpadded fields do, hence "2020-1-2 3:4:5.678900". The split("+")[0] line is covered by "2020-1-2 3:4:5+00:00", which also documents that the fallback silently drops an offset it cannot parse.

python -m unittest discover — 2201 passed, 43 skipped.

@wanghan-iapcm wanghan-iapcm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Correcting my earlier review first: I claimed YAML dump/load "worked out of the box" under monty. It did not. monty.serialization.dumpfn sets cls=MontyEncoder only in the json branch, so on master System.dump("x.yaml") raises TypeError: YAML.dump() got an unexpected keyword argument 'indent' (and without indent, RepresenterError: cannot represent an object: np.int64(2)), while loadfn returns a raw CommentedMap rather than a System. So the System-level YAML path never worked, and this PR genuinely fixes it.

The dependency half of my concern was real and is properly addressed: PyYAML is in the core dependencies array, monty is fully removed, and JSON output is byte-identical to monty's, so files round-trip in both directions with older dpdata.

Three things below I would like resolved before merge -- the silent .xz behavior in particular.

Comment thread pyproject.toml
'monty',
'scipy',
'h5py',
'PyYAML',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This line is the actual fix, but nothing in the suite guards it.

_yaml_dump / _yaml_load are byte-identical before and after this PR, so the only pre-fix/post-fix difference is this declaration -- and a round-trip test running inside an already-provisioned interpreter cannot observe a metadata declaration. CI installs .[test,amber,ase,pymatgen], and pymatgen -> monty -> ruamel.yaml, so a YAML backend is always importable; I confirmed the YAML tests pass on the pre-fix commit and fail only when both backends are blocked with an import hook. So the added "regression test" passes on the broken code too.

A cheap genuine guard already almost exists: .github/workflows/test_import.yml does a core-only uv pip install --system . and then just runs python -c 'import dpdata'. Extending that to an actual .yaml dump/load would fail pre-fix and pass post-fix.

Comment thread dpdata/system.py
def dump(self, filename: str, indent: int = 4):
"""Dump .json or .yaml file."""
from monty.serialization import dumpfn
"""Dump a JSON, YAML, or MessagePack file."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two things on this line.

indent is forwarded unconditionally, but _detect_format routes .mpk into msgpack.dump(obj, fp, **kwargs), so System.dump("x.mpk") raises TypeError: Packer.__init__() got an unexpected keyword argument 'indent' -- while this PR newly changes the docstring from "Dump .json or .yaml file" to advertise MessagePack. dumpfn(s.as_dict(), "x.mpk") works when called directly, so gating indent to the text formats is enough.

Separately, please add a System-level YAML test. Both current YAML tests use plain dicts, so the indent=4 + numpy-payload + System-reconstruction path this PR actually repairs has no coverage. A TestYamlDumpLoad mirroring the existing TestJsonDumpLoad is about 12 lines, errors 15/15 on master, and passes here -- a proper regression guard for the fix.

Comment thread dpdata/serialization.py
return "json"


def _open_text(filename: str | Path, mode: str) -> TextIO:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This drops .xz / .lzma, which monty's zopen handled, and it fails silently in both directions:

  • dumpfn(obj, "x.json.xz") now writes plain JSON under an .xz name -- file reports "JSON text data" where master reports "XZ compressed data, checksum CRC64". No error, no warning.
  • Reading a .xz file written by an older dpdata raises UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfd in position 0.

Silent wrong output is worse than a hard failure here. Either add the lzma branch alongside .gz/.z/.bz2, or raise on an unrecognized compression suffix so the loss cannot go unnoticed.

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

Labels

dependencies Pull requests that update a dependency file dpdata size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants