-
Notifications
You must be signed in to change notification settings - Fork 28
refactor deprecations into a decorator #524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughReplaces inline deprecation warnings with a new Changes
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
⏰ 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)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
Codecov Report✅ All modified and coverable lines are covered by tests. 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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this 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 deprecatedascore/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.
📒 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
deprecatedecorator fromdascore.utils.deprecateis properly placed and follows the existing import structure.
275-280: LGTM! Proper application of the deprecation decorator.The
@deprecatedecorator is correctly applied to theassign_coordsmethod 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
iselectmethod is correctly decorated with the@deprecatedecorator, 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
iresamplemethod 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 fortranproperty
Confirm that using@deprecate(..., stacklevel=3)on a@propertystill points the warning at the user’s call site (e.g. the line wherepatch.tranis accessed), not at the internal descriptor or wrapper frame.
There was a problem hiding this 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: ignoreThis 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.
📒 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.10If you still support 3.9, switch to Optional[str]; otherwise confirm minimum Python is ≥ 3.10.
| - Raises a runtime warning when called. | ||
| - Annotates the function so editors/type checkers show deprecation. | ||
| - Augments the docstring. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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
deprecatedecorator. It also removesDeprecationWarningsissued by deprecated functions in favor of using the decorator.Checklist
I have (if applicable):
Summary by CodeRabbit
New Features
Refactor
Deprecations
Removals
Tests
Chores