Skip to content

Conversation

rlskoeser
Copy link
Member

@rlskoeser rlskoeser commented Aug 29, 2025

To address #142

  • Updated __repr__ methods for Undate, UndateInterval, and UnDelta to return a string that documents object initialization options and can be used with eval()
  • Updated tests for the new format and to confirm that eval() results in an equivalent object

Summary by CodeRabbit

  • New Features

    • UnDelta is now available from the package root (e.g., undate.UnDelta).
  • Refactor

    • repr for Undate, UndateInterval, and UnDelta now produce fully-qualified, constructor-style strings with deterministic field ordering.
  • Tests

    • Tests updated to verify the new repr formats and that eval(repr(obj)) round-trips to equivalent objects.
  • Documentation

    • README examples revised to show constructor-style reprs, explicit calendars/labels, and adjusted REPL formatting.

Copy link
Contributor

coderabbitai bot commented Aug 29, 2025

Warning

Rate limit exceeded

@rlskoeser has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 18 minutes and 48 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8b30880 and 3656437.

📒 Files selected for processing (1)
  • src/undate/undate.py (3 hunks)

Walkthrough

Adds UnDelta to the public API and changes repr of UnDelta, UndateInterval, and Undate to fully-qualified, eval-friendly constructor-style strings; README and tests updated to assert eval(repr(obj)) round-trips. No public signatures or control-flow changes.

Changes

Cohort / File(s) Summary
Public API exposure
src/undate/__init__.py
Imports UnDelta from undate.date and adds it to __all__; __all__ reformatted as a multi-line list.
Representation updates
src/undate/date.py, src/undate/interval.py, src/undate/undate.py
Rewrites __repr__ for UnDelta, UndateInterval, and Undate to return fully-qualified, parameterized forms (e.g., undate.UnDelta(...), undate.UndateInterval(...), undate.Undate(...)) intended to be eval-able; no signature changes.
Tests updated for eval-friendly reprs
tests/test_date.py, tests/test_interval.py, tests/test_undate.py
Update expectations to module-qualified constructor-style reprs, import top-level undate where needed, and add/adjust assertions that eval(repr(obj)) == obj and str(eval(repr(obj))) == str(obj).
Docs / Examples
README.md
Example outputs and REPL snippets changed to show fully-qualified constructor-style reprs; minor formatting and link adjustments in examples and prompts.

Sequence Diagram(s)

(omitted — changes are representation/output formatting only, no control-flow or runtime interaction changes)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • robcast
  • jdamerow
  • ColeDCrawford

Poem

A rabbit in code with a hop and a thunk,
I changed how reprs show — neat, clear, and shrunk.
UnDelta now waves from the package tree,
Undate and Interval wear constructor glee.
Eval me, I bounce back — a carrot-shaped spree. 🥕

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/revise-repr

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@rlskoeser rlskoeser changed the title Revise __repr__ so they result in a string that can be used with eval() Revise __repr__ methods to return strings that can be used with eval() Aug 29, 2025
Copy link

codecov bot commented Aug 29, 2025

Codecov Report

❌ Patch coverage is 95.34884% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.69%. Comparing base (7170433) to head (3656437).
⚠️ Report is 8 commits behind head on develop.

Files with missing lines Patch % Lines
src/undate/undate.py 80.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #144      +/-   ##
===========================================
- Coverage    98.72%   98.69%   -0.04%     
===========================================
  Files           38       38              
  Lines         1956     1986      +30     
===========================================
+ Hits          1931     1960      +29     
- Misses          25       26       +1     

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (8)
tests/test_date.py (1)

233-247: Strengthen repr round-trip test to cover whole object

You validate endpoints and module import; also assert eval(repr(obj)) reconstructs an UnDelta with the same bounds.

Apply:

         feb_undelt = UnDelta(28, 29)
         assert repr(feb_undelt) == "undate.UnDelta(28,29)"
         # can't compare directly because uncertain deltas aren't equal,
-        # but compare vlaues
+        # but compare values
         assert eval(repr(feb_undelt.days.lower)) == feb_undelt.days.lower
         assert eval(repr(feb_undelt.days.upper)) == feb_undelt.days.upper
+        # round-trip the whole object
+        rt = eval(repr(feb_undelt))
+        assert isinstance(rt, UnDelta)
+        assert (rt.days.lower, rt.days.upper) == (feb_undelt.days.lower, feb_undelt.days.upper)
tests/test_interval.py (4)

59-61: Pass explicit globals to eval() for clarity and safety

Relying on a function-scope import to make undate visible to eval() works, but is brittle. Passing an explicit globals dict is clearer and avoids surprises.

-        assert eval(repr(closed_interval)) == closed_interval
+        assert eval(repr(closed_interval), {"undate": undate}) == closed_interval
@@
-        assert eval(repr(fancy_epoch)) == fancy_epoch
+        assert eval(repr(fancy_epoch), {"undate": undate}) == fancy_epoch
@@
-        assert eval(repr(open_interval)) == open_interval
+        assert eval(repr(open_interval), {"undate": undate}) == open_interval

Also applies to: 69-69, 76-76, 85-85


63-67: Reduce brittleness of exact repr string assertions

Exact string equality ties tests to argument ordering/spacing decisions. Since eval-roundtrip is already asserted, consider relaxing these to structural checks (e.g., startswith, substrings) or keep only the eval equality.

Also applies to: 73-75, 82-84


71-76: Add a label-with-quotes case to ensure proper escaping in repr

Exercise escaping to guarantee eval(repr(...)) survives embedded quotes and non-ASCII.

         fancy_epoch = UndateInterval(Undate(2022), Undate(2023), label="Fancy Epoch")
         assert (
             repr(fancy_epoch)
             == f'undate.UndateInterval(earliest={repr(fancy_epoch.earliest)}, latest={repr(fancy_epoch.latest)}, label="Fancy Epoch")'
         )
-        assert eval(repr(fancy_epoch)) == fancy_epoch
+        assert eval(repr(fancy_epoch), {"undate": undate}) == fancy_epoch
+
+        tricky_label = 'Fancy "Epoch" — v2'
+        tricky = UndateInterval(Undate(2022), Undate(2023), label=tricky_label)
+        assert eval(repr(tricky), {"undate": undate}) == tricky

78-85: Also cover latest-only (open-start) repr

You cover earliest-only; mirror it with a latest-only case for symmetry.

         open_interval = UndateInterval(
             Undate(33),
         )
         assert (
             repr(open_interval)
             == f"undate.UndateInterval(earliest={repr(open_interval.earliest)})"
         )
-        assert eval(repr(open_interval)) == open_interval
+        assert eval(repr(open_interval), {"undate": undate}) == open_interval
+
+        latest_only = UndateInterval(latest=Undate(2023))
+        assert (
+            repr(latest_only)
+            == f"undate.UndateInterval(latest={repr(latest_only.latest)})"
+        )
+        assert eval(repr(latest_only), {"undate": undate}) == latest_only
tests/test_undate.py (3)

31-34: Bind eval() to the undate module explicitly

Make the dependency on undate explicit and avoid relying on scope rules. Apply to all eval calls in this test.

-        assert eval(repr(nov2022)) == nov2022
+        assert eval(repr(nov2022), {"undate": undate}) == nov2022
@@
-        assert eval(repr(nov2022_labeled)) == nov2022_labeled
+        assert eval(repr(nov2022_labeled), {"undate": undate}) == nov2022_labeled
@@
-        assert eval(repr(islamic_date)) == islamic_date
+        assert eval(repr(islamic_date), {"undate": undate}) == islamic_date
@@
-        assert str(eval(repr(unknown_year))) == str(unknown_year)
+        assert str(eval(repr(unknown_year), {"undate": undate})) == str(unknown_year)

Also applies to: 42-42, 48-48, 52-52, 61-61


44-49: Add a label-with-quotes/non-ASCII case

Ensure proper escaping and eval-roundtrip when labels contain quotes or Unicode.

         nov2022_labeled = Undate(2022, 11, 7, label="A Special Day")
         assert (
             repr(nov2022_labeled)
             == 'undate.Undate(year=2022, month=11, day=7, label="A Special Day", calendar="Gregorian")'
         )
-        assert eval(repr(nov2022_labeled)) == nov2022_labeled
+        assert eval(repr(nov2022_labeled), {"undate": undate}) == nov2022_labeled
+
+        tricky_label = 'A "Special" Day — ©'
+        tricky = Undate(2022, 11, 7, label=tricky_label)
+        assert eval(repr(tricky), {"undate": undate}) == tricky

54-62: Assert repr idempotency for unknown components

Unknown parts aren’t equal, but repr should still be stable across a round-trip.

-        assert str(eval(repr(unknown_year))) == str(unknown_year)
+        assert str(eval(repr(unknown_year), {"undate": undate})) == str(unknown_year)
+        assert repr(eval(repr(unknown_year), {"undate": undate})) == repr(unknown_year)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c6b97ff and 23620c7.

📒 Files selected for processing (7)
  • src/undate/__init__.py (1 hunks)
  • src/undate/date.py (1 hunks)
  • src/undate/interval.py (1 hunks)
  • src/undate/undate.py (1 hunks)
  • tests/test_date.py (1 hunks)
  • tests/test_interval.py (1 hunks)
  • tests/test_undate.py (1 hunks)
🔇 Additional comments (5)
src/undate/date.py (1)

145-149: LGTM: eval-friendly UnDelta.repr

Format is deterministic and round-trips via eval with endpoints only. Matches PR intent.

src/undate/undate.py (1)

253-263: Confirm intent: converter not preserved in repr

Round-tripping via eval() will always construct with the default converter, losing any non-default converter passed at init. If that’s intentional, consider documenting it; otherwise, we can include a converter hint (e.g., by name) and extend init to accept it.

Do you want me to draft a follow-up that accepts converter="ISO8601" (by name) in init and incorporates it into repr?

src/undate/__init__.py (1)

3-14: LGTM: UnDelta exported at package root

This enables undate.UnDelta in repr strings and eval round-trips as intended.

tests/test_undate.py (2)

35-43: LGTM: canonical, eval-friendly repr for fully-known Gregorian date

The exact string and round-trip checks look solid.


50-53: LGTM: alternate calendar repr is round-trippable

Good coverage of non-Gregorian calendar emission and reconstruction.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
README.md (2)

124-138: Use the standard code-fence language for REPL examples ("pycon").

"Python console" is not a recognized lexer on most renderers; use "pycon" for correct highlighting of >>> prompts.

-```Python console
+```pycon

187-198: Minor formatting nit in REPL prompt spacing.

There’s an extra space after the REPL prompt on Line 196; remove for consistency.

->>>  Undate.parse("1800/1900", format="EDTF")
+>>> Undate.parse("1800/1900", format="EDTF")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 23620c7 and 8dfe28b.

📒 Files selected for processing (1)
  • README.md (4 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[grammar] ~236-~236: There might be a mistake here.
Context: ...date-python/tree/main/examples/) in this repository. ## Documentation Project d...

(QB_NEW_EN)

🔇 Additional comments (2)
README.md (2)

161-175: Repr outputs look correct and align with eval-friendly constructor style.

Interval examples now show fully-qualified undate.UndateInterval(...) with nested undate.Undate(...); consistent with the PR goal.


215-231: Calendar examples and sorted output match new repr semantics.

Objects display explicit calendar and labels; sort order aligns with earliest dates across calendars.

@rlskoeser rlskoeser force-pushed the feature/revise-repr branch from 3656437 to 8b30880 Compare August 29, 2025 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant