Skip to content

Conversation

@d-chambers
Copy link
Contributor

@d-chambers d-chambers commented Sep 2, 2025

Description

This PR refactors the way DASCore handles deprecating functions/method. Previously, it was a bit ad-hoc, function by function. This PR introduces a utils.deprecated module which contains a deprecate decorator. It also removes DeprecationWarnings issued by deprecated functions in favor of using the decorator.

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes.
  • documented the new feature with docstrings or appropriate doc page.
  • included a test. See testing guidelines.
  • your name has been added to the contributors page (docs/contributors.md).
  • added the "ready_for_review" tag once the PR is ready to be reviewed.

Summary by CodeRabbit

  • New Features

    • Added a centralized deprecation decorator to standardize deprecation messaging.
  • Refactor

    • Replaced scattered inline deprecation warnings with the new decorator; behavior and public signatures unchanged.
  • Deprecations

    • Multiple legacy APIs/functions marked deprecated with removal timelines; clarified a deprecated parameter message.
  • Removals

    • Removed an obsolete compatibility shim module.
  • Tests

    • Added tests for the deprecation decorator; removed a test tied to the deleted shim.
  • Chores

    • Updated SciPy and typing_extensions dependency constraints.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 2, 2025

Walkthrough

Replaces inline deprecation warnings with a new deprecate decorator applied to several public APIs, removes the deprecated dascore.core.schema module, expands one deprecation message in correlate, adds tests for the decorator, and updates dependency constraints. No core runtime logic changes.

Changes

Cohort / File(s) Summary of Changes
Patch & utilities deprecations
dascore/core/patch.py, dascore/transform/fft.py, dascore/utils/patch.py
Replaced inline warnings.warn(...) deprecation calls with @deprecate(...) decorators on multiple functions/properties; updated imports to use dascore.utils.deprecate.deprecate. Signatures and runtime behavior unchanged.
New deprecate utility
dascore/utils/deprecate.py
Added `deprecate(info: str = "", *, since: str
Removed deprecated shim module
dascore/core/schema.py
Deleted the deprecated shim that proxied attributes to dascore.core.attrs and emitted a deprecation warning on access.
Correlate warning text
dascore/proc/correlate.py
Expanded the deprecation message for the lag parameter to state it is deprecated and ignored and to direct users to use Patch.select for lag selection; no functional changes.
Tests added/removed
tests/test_utils/test_deprecated.py, tests/test_core/test_attrs.py
Added tests verifying the deprecate decorator emits a DeprecationWarning on call and that the decorated function docstring includes deprecation text; removed the obsolete test that asserted dascore.core.schema emitted a deprecation.
Dependency constraint update
pyproject.toml
Bumped SciPy minimum from >=1.11.0 to >=1.14.1 and set typing_extensions>=4.12.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant Caller as Caller Code
    participant DepWrapper as @deprecate wrapper
    participant OrigFunc as Original Function

    User->>Caller: call deprecated API
    Caller->>DepWrapper: invoke wrapped function
    Note right of DepWrapper: docstring augmented\n__deprecated__ set\ntyping_extensions.deprecated applied
    DepWrapper->>OrigFunc: forward args/kwargs
    OrigFunc-->>DepWrapper: return result
    DepWrapper-->>Caller: return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I thump my paw: “Old paths, take heed!”
A tidy tag replaces the screed.
Docstrings wear a gentle sign,
Warnings hum when called in line.
I hop away — the branch is fine. 🐇✨


📜 Recent 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 57ff7a1 and a8e8fd6.

📒 Files selected for processing (2)
  • dascore/utils/deprecate.py (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pyproject.toml
  • dascore/utils/deprecate.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: test_code_min_deps (windows-latest, 3.13)
  • GitHub Check: test_code_min_deps (macos-latest, 3.13)
  • GitHub Check: test_code_min_deps (windows-latest, 3.12)
  • GitHub Check: test_code_min_deps (ubuntu-latest, 3.13)
  • GitHub Check: test_code_min_deps (macos-latest, 3.12)
  • GitHub Check: test_code_min_deps (ubuntu-latest, 3.12)
  • GitHub Check: test_code (macos-latest, 3.12)
  • GitHub Check: test_code (windows-latest, 3.12)
  • GitHub Check: test_code (windows-latest, 3.10)
  • GitHub Check: test_code (ubuntu-latest, 3.10)
  • GitHub Check: test_code (macos-latest, 3.10)
  • GitHub Check: test_code (windows-latest, 3.11)
  • GitHub Check: test_code (macos-latest, 3.11)
  • GitHub Check: test_code (ubuntu-latest, 3.12)
  • GitHub Check: test_code (ubuntu-latest, 3.11)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch deprecate_util

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.

@codecov
Copy link

codecov bot commented Sep 2, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.87%. Comparing base (9232dd4) to head (a8e8fd6).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #524   +/-   ##
=======================================
  Coverage   99.87%   99.87%           
=======================================
  Files         122      122           
  Lines        9926     9938   +12     
=======================================
+ Hits         9914     9926   +12     
  Misses         12       12           
Flag Coverage Δ
unittests 99.87% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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: 0

🧹 Nitpick comments (7)
dascore/proc/correlate.py (1)

191-195: Fix deprecation message grammar and point warning at caller.

The message is missing the verb “select,” and the warning should use stacklevel so it points to user code (this function is wrapped by patch_function).

-        msg = (
-            "Correlate lag is deprecated and does nothing. "
-            "Simply use on the output patch."
-        )
-        warnings.warn(msg, DeprecationWarning)
+        msg = (
+            "Parameter 'lag' is deprecated and ignored. "
+            "Use Patch.select on the output patch (e.g., select(lag_time=(...)))."
+        )
+        warnings.warn(msg, category=DeprecationWarning, stacklevel=3)
tests/test_utils/test_deprecated.py (1)

7-7: Redundant import alias.

Drop the no-op “as deprecate”.

-from dascore.utils.deprecate import deprecate as deprecate
+from dascore.utils.deprecate import deprecate
dascore/transform/fft.py (2)

21-26: Make deprecation decorator outermost so IDE/type-checkers see it.

Placing @deprecate outermost ensures the exported symbol carries the typing deprecation and doc augmentation.

-@patch_function()
-@deprecate(
+@deprecate(
     info="The Patch transform rfft is deprecated. Use dft instead.",
     removed_in="0.2.0",
 )
+@patch_function()
 def rfft(patch: PatchType, dim="time") -> PatchType:

30-32: Avoid duplicated deprecation text in the docstring.

The decorator already appends a deprecated section. Trim the manual “DEPRECATED FUNCTION” sentence to keep docs concise.

-    DEPRECATED FUNCTION: Use [dft](`dascore.transform.fourier.dft`) instead.
-    This function is not scaled as detailed in the dascore documentation.
+    See [dft](`dascore.transform.fourier.dft`) instead.
+    Note: this variant is not scaled as detailed in the documentation.
dascore/utils/patch.py (1)

328-336: Consider adding since= for clearer lifecycle and unify with decorator stacklevel.

Adding since="x.y.z" improves user messaging. Also, with the current deprecate() default stacklevel (see utils.deprecate), ensure warnings point to the user call site for non-patch functions like this one.

 @deprecate(
     info=(
         "merge_patches is deprecated. Use spool.chunk instead. "
         "For example, to merge a list of patches you can use: "
         "dascore.spool(patch_list).chunk(time=None) to merge on the time "
         "dimension."
     ),
+    since="0.1.0",
     removed_in="0.2.0",
 )
dascore/utils/deprecate.py (2)

34-36: Docstring nit: sentence case.

Capitalize “It” at the start of the sentence.

-        Short message shown in warnings and editor hints. it is useful to specify
+        Short message shown in warnings and editor hints. It is useful to specify

20-23: Stabilize warning text and make stacklevel configurable.

Using the function object in the message produces noisy, non-deterministic text. Prefer module-qualified name. Also, default stacklevel=2 works for plain functions; when combined with patch_function, making @deprecate outermost (see fft.py comment) preserves correct attribution without hard-coding 3.

 def deprecate(
     info: str = "",
     *,
     since: str | None = None,
     removed_in: str | None = None,
     category: type[Warning] = DeprecationWarning,
+    stacklevel: int = 2,
 ) -> Callable[[F], F]:
@@
-        def wrapper(*args: Any, **kwargs: Any):
-            msg = f"Function or method {func} is deprecated: \n" + message
-            warnings.warn(msg, category=category, stacklevel=3)
+        def wrapper(*args: Any, **kwargs: Any):
+            qual = f"{func.__module__}.{getattr(func, '__qualname__', func.__name__)}"
+            msg = f"{qual} is deprecated. {message}"
+            warnings.warn(msg, category=category, stacklevel=stacklevel)
             return func(*args, **kwargs)

Also applies to: 63-66

📜 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 9232dd4 and f58cf09.

📒 Files selected for processing (8)
  • dascore/core/patch.py (5 hunks)
  • dascore/core/schema.py (0 hunks)
  • dascore/proc/correlate.py (1 hunks)
  • dascore/transform/fft.py (1 hunks)
  • dascore/utils/deprecate.py (1 hunks)
  • dascore/utils/patch.py (2 hunks)
  • tests/test_core/test_attrs.py (0 hunks)
  • tests/test_utils/test_deprecated.py (1 hunks)
💤 Files with no reviewable changes (2)
  • tests/test_core/test_attrs.py
  • dascore/core/schema.py
🧰 Additional context used
🧬 Code graph analysis (4)
tests/test_utils/test_deprecated.py (1)
dascore/utils/deprecate.py (1)
  • deprecate (17-80)
dascore/core/patch.py (1)
dascore/utils/deprecate.py (1)
  • deprecate (17-80)
dascore/transform/fft.py (2)
dascore/utils/deprecate.py (1)
  • deprecate (17-80)
dascore/utils/patch.py (1)
  • patch_function (179-285)
dascore/utils/patch.py (1)
dascore/utils/deprecate.py (1)
  • deprecate (17-80)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: test_code (windows-latest, 3.12)
  • GitHub Check: test_code (windows-latest, 3.10)
  • GitHub Check: test_code (ubuntu-latest, 3.11)
  • GitHub Check: test_code (ubuntu-latest, 3.12)
  • GitHub Check: test_code (macos-latest, 3.10)
  • GitHub Check: test_code (windows-latest, 3.11)
  • GitHub Check: test_code (macos-latest, 3.12)
  • GitHub Check: test_code (macos-latest, 3.11)
  • GitHub Check: test_code (ubuntu-latest, 3.10)
  • GitHub Check: test_code_min_deps (windows-latest, 3.12)
  • GitHub Check: test_code_min_deps (macos-latest, 3.13)
  • GitHub Check: test_code_min_deps (ubuntu-latest, 3.12)
  • GitHub Check: test_code_min_deps (windows-latest, 3.13)
  • GitHub Check: test_code_min_deps (macos-latest, 3.12)
  • GitHub Check: test_code_min_deps (ubuntu-latest, 3.13)
🔇 Additional comments (6)
tests/test_utils/test_deprecated.py (1)

13-22: Fixture signature is incorrect; remove self.

Pytest fixtures must not take self, even when defined inside a class. This will raise at collection/use.

-    @pytest.fixture(scope="class")
-    def deprecated_func(self):
+    @pytest.fixture(scope="class")
+    def deprecated_func():
         """Create a deprecated function."""

Likely an incorrect or invalid review comment.

dascore/core/patch.py (5)

19-19: LGTM! Clean import of the new deprecation decorator.

The import of the new deprecate decorator from dascore.utils.deprecate is properly placed and follows the existing import structure.


275-280: LGTM! Proper application of the deprecation decorator.

The @deprecate decorator is correctly applied to the assign_coords method with an appropriate deprecation message and removal version. The decorator will handle issuing the deprecation warning, replacing the previous inline warning approach.


291-297: LGTM! Consistent deprecation approach.

The iselect method is correctly decorated with the @deprecate decorator, providing clear guidance to users about the replacement (patch.select with samples=True) and the version when it will be removed.


321-327: LGTM! Properly deprecated iresample method.

The iresample method is correctly decorated with the deprecation decorator. The message clearly indicates the replacement method (patch.resample with samples=True) and specifies the removal version.


370-377: Verify deprecation warning stacklevel for tran property
Confirm that using @deprecate(..., stacklevel=3) on a @property still points the warning at the user’s call site (e.g. the line where patch.tran is accessed), not at the internal descriptor or wrapper frame.

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: 3

🧹 Nitpick comments (2)
dascore/utils/deprecate.py (2)

49-51: Typo in docstring (“emmit” → “emit”)

-    def _build_msg(func):
-        """Build the message to emmit."""
+    def _build_msg(func):
+        """Build the message to emit."""

9-9: Preserve callable signatures with ParamSpec/TypeVar to avoid type: ignore

This removes the return-value ignore and gives precise typing for callers.

-from collections.abc import Callable
-from typing import Any, TypeVar
+from collections.abc import Callable
+from typing import Any, ParamSpec, TypeVar, cast
@@
-F = TypeVar("F", bound=Callable[..., Any])
+P = ParamSpec("P")
+R = TypeVar("R")
@@
-def deprecate(
+def deprecate(
     info: str = "",
     *,
     since: str | None = None,
     removed_in: str | None = None,
-) -> Callable[[F], F]:
+) -> Callable[[Callable[P, R]], Callable[P, R]]:
@@
-    def _decorate(func: F) -> F:
+    def _decorate(func: Callable[P, R]) -> Callable[P, R]:
@@
-        def wrapper(*args: Any, **kwargs: Any):
+        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
             warnings.warn(
                 _build_msg(func),
                 category=DeprecationWarning,
                 stacklevel=2,
             )
             return func(*args, **kwargs)
@@
-        return dep(msg)(wrapper)  # type: ignore[return-value]
+        return cast(Callable[P, R], dep(msg)(wrapper))

Also applies to: 13-13, 16-21, 59-63, 75-77

📜 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 f58cf09 and 22f2adb.

📒 Files selected for processing (5)
  • dascore/core/patch.py (5 hunks)
  • dascore/proc/correlate.py (1 hunks)
  • dascore/transform/fft.py (1 hunks)
  • dascore/utils/deprecate.py (1 hunks)
  • tests/test_utils/test_deprecated.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • dascore/proc/correlate.py
  • dascore/core/patch.py
  • dascore/transform/fft.py
  • tests/test_utils/test_deprecated.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: test_code (windows-latest, 3.10)
  • GitHub Check: test_code (windows-latest, 3.12)
  • GitHub Check: test_code (macos-latest, 3.11)
  • GitHub Check: test_code (windows-latest, 3.11)
  • GitHub Check: test_code (macos-latest, 3.10)
  • GitHub Check: test_code_min_deps (ubuntu-latest, 3.13)
  • GitHub Check: test_code (ubuntu-latest, 3.12)
  • GitHub Check: test_code (ubuntu-latest, 3.10)
  • GitHub Check: test_code_min_deps (windows-latest, 3.13)
  • GitHub Check: test_code_min_deps (windows-latest, 3.12)
  • GitHub Check: test_code (ubuntu-latest, 3.11)
  • GitHub Check: test_code_min_deps (macos-latest, 3.13)
  • GitHub Check: test_code_min_deps (macos-latest, 3.12)
  • GitHub Check: test_code_min_deps (ubuntu-latest, 3.12)
🔇 Additional comments (1)
dascore/utils/deprecate.py (1)

16-21: Union syntax requires Python ≥ 3.10

If you still support 3.9, switch to Optional[str]; otherwise confirm minimum Python is ≥ 3.10.

Comment on lines +25 to +27
- Raises a runtime warning when called.
- Annotates the function so editors/type checkers show deprecation.
- Augments the docstring.
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 2, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Docstring promises a runtime warning, but wrapper emits none

Calls currently do not issue DeprecationWarning; tests and users won’t see a warning. Emit one with stacklevel=2 and import warnings.

@@
-from __future__ import annotations
+from __future__ import annotations
@@
-import functools
+import functools
+import warnings
@@
-        def wrapper(*args: Any, **kwargs: Any):
-            return func(*args, **kwargs)
+        def wrapper(*args: Any, **kwargs: Any):
+            warnings.warn(
+                _build_msg(func),
+                category=DeprecationWarning,
+                stacklevel=2,
+            )
+            return func(*args, **kwargs)

Also applies to: 60-63

🤖 Prompt for AI Agents
In dascore/utils/deprecate.py around lines 25-27 (and similarly at 60-63), the
docstring says the decorator should raise a runtime deprecation warning but the
wrapper currently emits none; fix by importing the warnings module and calling
warnings.warn("...deprecated...", DeprecationWarning, stacklevel=2) inside the
wrapper when the deprecated function is invoked (use the decorator's message or
function name for the text), ensuring the warning is emitted at call time with
stacklevel=2 so users/tests see it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This line dep(msg)(wrapper) emits a deprecation warning. Using it how you suggested issues two deprecation warnings.

Copy link
Contributor

Choose a reason for hiding this comment

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

, so adding an additional warnings.warn() call would indeed result in duplicate warnings.

Thank you for the correction - the current implementation is working as intended. The docstring is accurate when it says the decorator "raises a runtime warning when called" because typing_extensions.deprecated handles that automatically.


✏️ Learnings added
Learnt from: d-chambers
PR: DASDAE/dascore#524
File: dascore/utils/deprecate.py:25-27
Timestamp: 2025-09-02T16:36:58.993Z
Learning: The typing_extensions.deprecated decorator automatically emits runtime DeprecationWarning when the decorated function is called, so no additional warnings.warn() calls are needed when using this decorator.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@d-chambers d-chambers merged commit b79a338 into master Sep 2, 2025
20 checks passed
@d-chambers d-chambers deleted the deprecate_util branch September 2, 2025 16:48
@coderabbitai coderabbitai bot mentioned this pull request Sep 20, 2025
5 tasks
@coderabbitai coderabbitai bot mentioned this pull request Dec 20, 2025
4 tasks
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.

2 participants