Skip to content

Commit 8675c67

Browse files
committed
refactor: Standardize deprecation strategy (Phase 6)
- Create standardized deprecation utilities in utils/deprecation.py - Provide consistent @deprecated and @deprecated_class decorators - Update all existing deprecations to use standard approach - Replace inconsistent warnings.warn() calls with decorators - Update CLAUDE.md with new deprecation guidelines - Ensure all deprecations include version, removal version, and replacement This standardizes deprecation handling across the SDK, making it easier for users to understand migration paths and for maintainers to manage deprecated features consistently.
1 parent d4eb229 commit 8675c67

File tree

8 files changed

+371
-47
lines changed

8 files changed

+371
-47
lines changed

CLAUDE.md

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,43 @@ Example approach:
2727
- ❌ DON'T: Remove deprecated features without proper notice period
2828

2929
### Deprecation Process
30-
1. Mark as deprecated with `warnings.warn()` and `@deprecated` decorator
31-
2. Document replacement in deprecation message
30+
1. Use the standardized `@deprecated` decorator from `project_x_py.utils.deprecation`
31+
2. Provide clear reason, version info, and replacement path
3232
3. Keep deprecated feature for at least 2 minor versions
3333
4. Remove only in major version releases (4.0.0, 5.0.0, etc.)
3434

3535
Example:
3636
```python
37-
import warnings
38-
from typing import deprecated
39-
40-
@deprecated("Use new_method() instead. Will be removed in v4.0.0")
37+
from project_x_py.utils.deprecation import deprecated, deprecated_class
38+
39+
# For functions/methods
40+
@deprecated(
41+
reason="Method renamed for clarity",
42+
version="3.1.14", # When deprecated
43+
removal_version="4.0.0", # When it will be removed
44+
replacement="new_method()" # What to use instead
45+
)
4146
def old_method(self):
42-
warnings.warn(
43-
"old_method() is deprecated, use new_method() instead. "
44-
"Will be removed in v4.0.0",
45-
DeprecationWarning,
46-
stacklevel=2
47-
)
4847
return self.new_method()
48+
49+
# For classes
50+
@deprecated_class(
51+
reason="Integrated into TradingSuite",
52+
version="3.1.14",
53+
removal_version="4.0.0",
54+
replacement="TradingSuite"
55+
)
56+
class OldManager:
57+
pass
4958
```
5059

60+
The standardized deprecation utilities provide:
61+
- Consistent warning messages across the SDK
62+
- Automatic docstring updates with deprecation info
63+
- IDE support through the `deprecated` package
64+
- Metadata tracking for deprecation management
65+
- Support for functions, methods, classes, and parameters
66+
5167
## Development Commands
5268

5369
### Package Management (UV)

src/project_x_py/client/trading.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,14 @@ async def main():
6666

6767
import datetime
6868
import logging
69-
import warnings
7069
from datetime import timedelta
7170
from typing import TYPE_CHECKING
7271

7372
import pytz
74-
from deprecated import deprecated # type: ignore
7573

7674
from project_x_py.exceptions import ProjectXError
7775
from project_x_py.models import Position, Trade
76+
from project_x_py.utils.deprecation import deprecated
7877

7978
if TYPE_CHECKING:
8079
from project_x_py.types import ProjectXClientProtocol
@@ -85,8 +84,11 @@ async def main():
8584
class TradingMixin:
8685
"""Mixin class providing trading functionality."""
8786

88-
@deprecated( # type: ignore[misc]
89-
"Use search_open_positions() instead. This method will be removed in v4.0.0."
87+
@deprecated(
88+
reason="Method renamed for API consistency",
89+
version="3.0.0",
90+
removal_version="4.0.0",
91+
replacement="search_open_positions()",
9092
)
9193
async def get_positions(self: "ProjectXClientProtocol") -> list[Position]:
9294
"""
@@ -102,12 +104,7 @@ async def get_positions(self: "ProjectXClientProtocol") -> list[Position]:
102104
Returns:
103105
A list of Position objects representing current holdings.
104106
"""
105-
warnings.warn(
106-
"get_positions() is deprecated, use search_open_positions() instead. "
107-
"This method will be removed in v4.0.0.",
108-
DeprecationWarning,
109-
stacklevel=2,
110-
)
107+
# Deprecation warning handled by decorator
111108
return await self.search_open_positions()
112109

113110
async def search_open_positions(

src/project_x_py/order_manager/tracking.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def on_order_fill(order_data):
5454
from collections.abc import Callable
5555
from typing import TYPE_CHECKING, Any
5656

57+
from project_x_py.utils.deprecation import deprecated
58+
5759
if TYPE_CHECKING:
5860
from project_x_py.types import OrderManagerProtocol
5961

@@ -317,6 +319,12 @@ async def get_tracked_order_status(
317319
async with self.order_lock:
318320
return self.tracked_orders.get(order_id)
319321

322+
@deprecated(
323+
reason="Use TradingSuite.on() with EventType enum for event handling",
324+
version="3.1.0",
325+
removal_version="4.0.0",
326+
replacement="TradingSuite.on(EventType.ORDER_FILLED, callback)",
327+
)
320328
def add_callback(
321329
self,
322330
event_type: str,
@@ -327,9 +335,8 @@ def add_callback(
327335
328336
This method is provided for backward compatibility only and will be removed in v4.0.
329337
"""
330-
logger.warning(
331-
"add_callback is deprecated. Use TradingSuite.on() with EventType enum instead."
332-
)
338+
# Deprecation warning handled by decorator
339+
pass
333340

334341
async def _trigger_callbacks(self, event_type: str, data: Any) -> None:
335342
"""

src/project_x_py/order_tracker.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,24 @@
5757

5858
import asyncio
5959
import logging
60-
import warnings
6160
from types import TracebackType
6261
from typing import TYPE_CHECKING, Any, Union
6362

64-
from typing_extensions import deprecated
65-
6663
from project_x_py.event_bus import EventType
6764
from project_x_py.models import BracketOrderResponse, Order, OrderPlaceResponse
65+
from project_x_py.utils.deprecation import deprecated, deprecated_class
6866

6967
if TYPE_CHECKING:
7068
from project_x_py.trading_suite import TradingSuite
7169

7270
logger = logging.getLogger(__name__)
7371

7472

75-
@deprecated(
76-
"OrderTracker is deprecated. Use TradingSuite.track_order() instead. "
77-
"Will be removed in v4.0.0"
73+
@deprecated_class(
74+
reason="Use TradingSuite.track_order() for integrated order tracking",
75+
version="3.1.14",
76+
removal_version="4.0.0",
77+
replacement="TradingSuite.track_order()",
7878
)
7979
class OrderTracker:
8080
"""
@@ -370,9 +370,11 @@ def is_terminal(self) -> bool:
370370
) # FILLED, CANCELLED, EXPIRED, REJECTED
371371

372372

373-
@deprecated(
374-
"OrderChainBuilder is deprecated. Use TradingSuite.order_chain() instead. "
375-
"Will be removed in v4.0.0"
373+
@deprecated_class(
374+
reason="Use TradingSuite.order_chain() for integrated order chain building",
375+
version="3.1.14",
376+
removal_version="4.0.0",
377+
replacement="TradingSuite.order_chain()",
376378
)
377379
class OrderChainBuilder:
378380
"""
@@ -621,7 +623,10 @@ class OrderLifecycleError(Exception):
621623

622624
# Convenience function for creating order trackers
623625
@deprecated(
624-
"Use TradingSuite.track_order() instead. This function will be removed in v4.0.0."
626+
reason="Use TradingSuite.track_order() for integrated tracking",
627+
version="3.1.14",
628+
removal_version="4.0.0",
629+
replacement="TradingSuite.track_order()",
625630
)
626631
def track_order(
627632
trading_suite: "TradingSuite",
@@ -645,12 +650,7 @@ def track_order(
645650
filled = await tracker.wait_for_fill()
646651
```
647652
"""
648-
warnings.warn(
649-
"track_order() is deprecated, use TradingSuite.track_order() instead. "
650-
"This function will be removed in v4.0.0",
651-
DeprecationWarning,
652-
stacklevel=2,
653-
)
653+
# Deprecation warning handled by decorator
654654
tracker = OrderTracker(trading_suite)
655655
if order:
656656
if isinstance(order, Order | OrderPlaceResponse):

src/project_x_py/orderbook/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ async def on_depth_update(event):
114114
OrderbookAnalysisResponse,
115115
)
116116
from project_x_py.types.stats_types import OrderbookStats
117+
from project_x_py.utils.deprecation import deprecated
117118

118119
__all__ = [
119120
# Types
@@ -464,6 +465,12 @@ async def cleanup(self) -> None:
464465
await super().cleanup()
465466

466467

468+
@deprecated(
469+
reason="Use TradingSuite.create() with orderbook feature for integrated orderbook",
470+
version="3.1.0",
471+
removal_version="4.0.0",
472+
replacement='TradingSuite.create(instrument, features=["orderbook"])',
473+
)
467474
def create_orderbook(
468475
instrument: str,
469476
event_bus: Any,

src/project_x_py/orderbook/base.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ async def on_depth(data):
9191
ProjectXLogger,
9292
handle_errors,
9393
)
94+
from project_x_py.utils.deprecation import deprecated
9495
from project_x_py.utils.stats_tracking import StatsTrackingMixin
9596

9697
logger = ProjectXLogger.get_logger(__name__)
@@ -663,6 +664,12 @@ async def get_order_type_statistics(self) -> dict[str, int]:
663664
async with self.orderbook_lock:
664665
return self.order_type_stats.copy()
665666

667+
@deprecated(
668+
reason="Use TradingSuite.on() with EventType enum for event handling",
669+
version="3.1.0",
670+
removal_version="4.0.0",
671+
replacement="TradingSuite.on(EventType.MARKET_DEPTH_UPDATE, callback)",
672+
)
666673
@handle_errors("add callback", reraise=False)
667674
async def add_callback(self, event_type: str, callback: CallbackType) -> None:
668675
"""
@@ -707,21 +714,23 @@ async def add_callback(self, event_type: str, callback: CallbackType) -> None:
707714
>>> # Events automatically flow through EventBus
708715
"""
709716
async with self._callback_lock:
710-
logger.warning(
711-
"add_callback is deprecated. Use TradingSuite.on() with EventType enum instead."
712-
)
717+
# Deprecation warning handled by decorator
713718
logger.debug(
714719
LogMessages.CALLBACK_REGISTERED,
715720
extra={"event_type": event_type, "component": "orderbook"},
716721
)
717722

723+
@deprecated(
724+
reason="Use TradingSuite.off() with EventType enum for event handling",
725+
version="3.1.0",
726+
removal_version="4.0.0",
727+
replacement="TradingSuite.off(EventType.MARKET_DEPTH_UPDATE, callback)",
728+
)
718729
@handle_errors("remove callback", reraise=False)
719730
async def remove_callback(self, event_type: str, callback: CallbackType) -> None:
720731
"""Remove a registered callback."""
721732
async with self._callback_lock:
722-
logger.warning(
723-
"remove_callback is deprecated. Use TradingSuite.off() with EventType enum instead."
724-
)
733+
# Deprecation warning handled by decorator
725734
logger.debug(
726735
LogMessages.CALLBACK_REMOVED,
727736
extra={"event_type": event_type, "component": "orderbook"},

src/project_x_py/position_manager/tracking.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ async def on_position_closed(data):
6767

6868
from project_x_py.models import Position
6969
from project_x_py.types.trading import PositionType
70+
from project_x_py.utils.deprecation import deprecated
7071

7172
if TYPE_CHECKING:
7273
from asyncio import Lock
@@ -489,6 +490,12 @@ async def _trigger_callbacks(self, event_type: str, data: Any) -> None:
489490

490491
# Legacy callbacks have been removed - use EventBus
491492

493+
@deprecated(
494+
reason="Use TradingSuite.on() with EventType enum for event handling",
495+
version="3.1.0",
496+
removal_version="4.0.0",
497+
replacement="TradingSuite.on(EventType.POSITION_UPDATED, callback)",
498+
)
492499
async def add_callback(
493500
self,
494501
event_type: str,

0 commit comments

Comments
 (0)