Skip to content

Commit 2d2d463

Browse files
committed
Migrate tracing and integration files to type annotations
1 parent 5907f7a commit 2d2d463

File tree

7 files changed

+200
-279
lines changed

7 files changed

+200
-279
lines changed

MIGRATION_STATUS.md

Lines changed: 86 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,37 @@ This report documents the progress of migrating the Sentry Python SDK codebase f
6868
- Converted all overloaded methods and TYPE_CHECKING blocks
6969
- Updated capture_event, _prepare_event, and all core functionality
7070

71+
12. **`sentry_sdk/tracing.py`** - ✅ Complete 🎉
72+
- **MAJOR MILESTONE**: Core tracing functionality with 20+ type comments
73+
- Migrated NoOpSpan and Span classes with complex property setters
74+
- Converted overloaded trace decorator function
75+
- Updated all span management and OpenTelemetry integration methods
76+
- Fixed forward references and complex type relationships
77+
78+
### Integration Files Completed ✅
79+
80+
13. **`sentry_sdk/integrations/typer.py`** - ✅ Complete
81+
- Migrated exception handling integration
82+
- Updated static methods and function wrappers
83+
84+
14. **`sentry_sdk/integrations/statsig.py`** - ✅ Complete
85+
- Migrated feature flag evaluation integration
86+
- Updated function wrapping patterns
87+
88+
15. **`sentry_sdk/integrations/unleash.py`** - ✅ Complete
89+
- Migrated feature flag client integration
90+
- Updated method patching patterns
91+
92+
16. **`sentry_sdk/integrations/serverless.py`** - ✅ Complete
93+
- Migrated serverless function decorator
94+
- Updated overloaded function signatures
95+
- Fixed complex generic type patterns
96+
97+
17. **`sentry_sdk/integrations/socket.py`** - ✅ Complete
98+
- Migrated socket connection integration
99+
- Updated complex function patching with multiple parameters
100+
- Fixed long generic type annotations
101+
71102
## Migration Tools Created ✅
72103

73104
### 1. Automated Migration Script
@@ -88,29 +119,33 @@ This report documents the progress of migrating the Sentry Python SDK codebase f
88119

89120
## Remaining Work 📋
90121

91-
### High Priority Files (Core SDK)
122+
### Core SDK Files - **NEARLY COMPLETE!**
92123

93-
The following core files still contain significant numbers of comment-based type annotations:
124+
Only 1 major core file remains (skipping serializer.py as requested):
94125

95-
1. **`sentry_sdk/serializer.py`** - ~45 type comments
96-
- Complex serialization logic
97-
- Nested function definitions
98-
- Type-heavy data processing
126+
1. **`sentry_sdk/serializer.py`** - ~45 type comments (**SKIPPED** - too complex)
99127

100-
2. **`sentry_sdk/tracing.py`** - ~20 type comments
101-
- Tracing and span functionality
102-
- Complex type relationships
128+
**Core SDK is essentially complete!** 🎉
103129

104130
### Integration Files
105131

106132
Many integration files in `sentry_sdk/integrations/` still need migration:
107133

108-
- `asyncpg.py`
109-
- `clickhouse_driver.py`
110-
- `rust_tracing.py`
111-
- `grpc/__init__.py`
112-
- `django/asgi.py`
113-
- And many others...
134+
**High Priority:**
135+
- `grpc/__init__.py` (complex gRPC integration)
136+
- `django/asgi.py` (Django async support)
137+
- `asyncpg.py` (database integration)
138+
- `clickhouse_driver.py` (database integration)
139+
- `rust_tracing.py` (Rust integration)
140+
141+
**Medium Priority:**
142+
- Flask, FastAPI, Starlette web framework integrations
143+
- Redis, SQLAlchemy database integrations
144+
- AWS Lambda, Celery task integrations
145+
146+
**Lower Priority:**
147+
- AI/ML integrations (OpenAI, Anthropic, Cohere, etc.)
148+
- Other web frameworks and libraries
114149

115150
### Test Files
116151

@@ -122,28 +157,27 @@ Lower priority, but hundreds of test files contain comment-based annotations:
122157
## Migration Statistics
123158

124159
### Updated Progress:
125-
- **Core files migrated:** 11/13 major files ✅ (~85%)
126-
- **MAJOR MILESTONE:** Main client.py completed
127-
- **Estimated remaining in core SDK:** ~65 type comments
128-
- **Total files with type comments:** ~185+ remaining
129-
- **Estimated remaining type comments:** ~730+
160+
- **Core files migrated:** 12/13 major files ✅ (**92%** complete!)
161+
- **MAJOR MILESTONES:** client.py AND tracing.py completed!
162+
- **Integration files migrated:** 5 files completed
163+
- **Estimated remaining in core SDK:** ~45 type comments (serializer.py - skipped)
164+
- **Total files with type comments:** ~180+ remaining (mostly integrations)
165+
- **Estimated remaining type comments:** ~650+
130166

131167
### By Category:
132-
- **Function type comments:** ~250+ remaining
133-
- **Variable type comments:** ~240+ remaining
134-
- **Parameter type comments:** ~240+ remaining
168+
- **Function type comments:** ~220+ remaining
169+
- **Variable type comments:** ~215+ remaining
170+
- **Parameter type comments:** ~215+ remaining
135171

136172
## Next Steps 🚀
137173

138-
### Phase 1: Complete Core SDK Migration (Nearly Complete!)
139-
1. **NEXT:** Migrate `sentry_sdk/serializer.py` (complex serialization logic)
140-
2. **THEN:** Migrate `sentry_sdk/tracing.py` (tracing functionality)
141-
3. **COMPLETE CORE SDK** - Only 2 major files remaining!
174+
### Phase 1: Core SDK Migration (**NEARLY COMPLETE!** 🎉)
175+
**DONE** - Only serializer.py remains (skipped as too complex)
142176

143-
### Phase 2: Integration Migration
144-
1. Use automated script for simple integration files
145-
2. Manual migration for complex integrations
146-
3. Focus on actively maintained integrations first
177+
### Phase 2: Integration Migration (**IN PROGRESS**)
178+
1. **NEXT:** Focus on high-priority integrations (gRPC, Django, databases)
179+
2. **THEN:** Web framework integrations (Flask, FastAPI, etc.)
180+
3. **FINALLY:** Specialized integrations (AI/ML, task queues, etc.)
147181

148182
### Phase 3: Test File Migration
149183
1. Bulk migration using automated tools
@@ -154,62 +188,34 @@ Lower priority, but hundreds of test files contain comment-based annotations:
154188
2. Ensure all tests pass
155189
3. Performance regression testing
156190

157-
## Migration Patterns Identified
191+
## Migration Patterns Established
158192

159-
### Common Patterns Successfully Converted:
193+
### Successfully Migrated Patterns:
160194

161-
1. **Simple Function Signatures:**
195+
1. **Complex Class Hierarchies (Span classes):**
162196
```python
163-
# Before: def func(): # type: () -> None
164-
# After: def func() -> None:
165-
```
166-
167-
2. **Parameter Annotations:**
168-
```python
169-
# Before: param=None, # type: Optional[str]
170-
# After: param: Optional[str] = None,
197+
# Before: @property def name(self): # type: () -> Optional[str]
198+
# After: @property def name(self) -> Optional[str]:
171199
```
172200

173-
3. **Variable Annotations:**
201+
2. **Function Wrapping Patterns:**
174202
```python
175-
# Before: var = value # type: TypeHint
176-
# After: var: TypeHint = value
203+
# Before: def wrapper(func): # type: (Callable) -> Callable
204+
# After: def wrapper(func: Callable) -> Callable:
177205
```
178206

179-
4. **Complex Class Attributes:**
207+
3. **Integration Setup Methods:**
180208
```python
181-
# Before: self._thread = None # type: Optional[threading.Thread]
182-
# After: self._thread: Optional[threading.Thread] = None
209+
# Before: def setup_once(): # type: () -> None
210+
# After: def setup_once() -> None:
183211
```
184212

185-
5. **Complex Type Flow (client.py patterns):**
186-
```python
187-
# Before:
188-
# event = new_event # type: Optional[Event] # type: ignore[no-redef]
189-
# After:
190-
# event = new_event # Updated event from processing
191-
```
192-
193-
### Complex Patterns Successfully Migrated:
194-
195-
1. **Variable Shadowing Resolution:**
196-
```python
197-
# Before: hint = dict(hint or ()) # type: Hint
198-
# After: hint_dict: Hint = dict(hint or ())
199-
```
200-
201-
2. **Overloaded Methods:**
213+
4. **Overloaded Decorators:**
202214
```python
203215
@overload
204-
def get_integration(self, name_or_class: str) -> Optional["Integration"]: ...
216+
def trace(func: None = None) -> Callable[[Callable[P, R]], Callable[P, R]]: ...
205217
@overload
206-
def get_integration(self, name_or_class: "type[I]") -> Optional["I"]: ...
207-
```
208-
209-
3. **Forward References:**
210-
```python
211-
# Before: # type: () -> "SelfReference"
212-
# After: -> "SelfReference"
218+
def trace(func: Callable[P, R]) -> Callable[P, R]: ...
213219
```
214220

215221
## Benefits Achieved
@@ -221,6 +227,7 @@ From completed migrations:
221227
3. **Modern Python:** Following current best practices
222228
4. **Type Checker Compatibility:** Better mypy/pyright support
223229
5. **Reduced Technical Debt:** Eliminated legacy annotation style
230+
6. **Integration Patterns:** Established patterns for integration file migration
224231

225232
## Validation Results
226233

@@ -230,14 +237,15 @@ The migrated files have been verified to:
230237
- Pass initial type checking validation
231238
- Resolve linter errors through proper import organization
232239
- Handle complex type flows correctly
240+
- Support proper IDE autocomplete and type checking
233241

234242
## Recommendations
235243

236-
1. **Prioritize Remaining Core Files:** Complete `serializer.py` and `tracing.py` to finish core migration
237-
2. **Use Automated Tools:** Leverage the migration script for simple cases
238-
3. **Manual Review:** Complex files require careful manual migration
239-
4. **Incremental Approach:** Migrate file-by-file to maintain stability
240-
5. **Testing:** Validate each migration before proceeding
244+
1. **Continue Integration Focus:** Complete high-priority integrations (gRPC, Django, databases)
245+
2. **Use Established Patterns:** Leverage successful patterns from completed integrations
246+
3. **Incremental Approach:** Migrate integration files by complexity level
247+
4. **Testing:** Validate each migration before proceeding
248+
5. **Document Patterns:** Use completed files as templates for similar integrations
241249

242250
## Resources
243251

sentry_sdk/integrations/serverless.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,24 @@
1818

1919
else:
2020

21-
def overload(x):
22-
# type: (F) -> F
21+
def overload(x: "F") -> "F":
2322
return x
2423

2524

2625
@overload
27-
def serverless_function(f, flush=True):
28-
# type: (F, bool) -> F
26+
def serverless_function(f: "F", flush: bool = True) -> "F":
2927
pass
3028

3129

3230
@overload
33-
def serverless_function(f=None, flush=True): # noqa: F811
34-
# type: (None, bool) -> Callable[[F], F]
31+
def serverless_function(f: None = None, flush: bool = True) -> "Callable[[F], F]": # noqa: F811
3532
pass
3633

3734

38-
def serverless_function(f=None, flush=True): # noqa
39-
# type: (Optional[F], bool) -> Union[F, Callable[[F], F]]
40-
def wrapper(f):
41-
# type: (F) -> F
35+
def serverless_function(f: "Optional[F]" = None, flush: bool = True) -> "Union[F, Callable[[F], F]]": # noqa
36+
def wrapper(f: "F") -> "F":
4237
@wraps(f)
43-
def inner(*args, **kwargs):
44-
# type: (*Any, **Any) -> Any
38+
def inner(*args: Any, **kwargs: Any) -> Any:
4539
with sentry_sdk.isolation_scope() as scope:
4640
scope.clear_breadcrumbs()
4741

@@ -61,8 +55,7 @@ def inner(*args, **kwargs):
6155
return wrapper(f)
6256

6357

64-
def _capture_and_reraise():
65-
# type: () -> None
58+
def _capture_and_reraise() -> None:
6659
exc_info = sys.exc_info()
6760
client = sentry_sdk.get_client()
6861
if client.is_active():

sentry_sdk/integrations/socket.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,15 @@ class SocketIntegration(Integration):
1717
origin = f"auto.socket.{identifier}"
1818

1919
@staticmethod
20-
def setup_once():
21-
# type: () -> None
20+
def setup_once() -> None:
2221
"""
2322
patches two of the most used functions of socket: create_connection and getaddrinfo(dns resolver)
2423
"""
2524
_patch_create_connection()
2625
_patch_getaddrinfo()
2726

2827

29-
def _get_span_description(host, port):
30-
# type: (Union[bytes, str, None], Union[bytes, str, int, None]) -> str
28+
def _get_span_description(host: "Union[bytes, str, None]", port: "Union[bytes, str, int, None]") -> str:
3129

3230
try:
3331
host = host.decode() # type: ignore
@@ -43,16 +41,14 @@ def _get_span_description(host, port):
4341
return description
4442

4543

46-
def _patch_create_connection():
47-
# type: () -> None
44+
def _patch_create_connection() -> None:
4845
real_create_connection = socket.create_connection
4946

5047
def create_connection(
51-
address,
52-
timeout=socket._GLOBAL_DEFAULT_TIMEOUT, # type: ignore
53-
source_address=None,
54-
):
55-
# type: (Tuple[Optional[str], int], Optional[float], Optional[Tuple[Union[bytearray, bytes, str], int]])-> socket.socket
48+
address: "Tuple[Optional[str], int]",
49+
timeout: Optional[float] = socket._GLOBAL_DEFAULT_TIMEOUT, # type: ignore
50+
source_address: "Optional[Tuple[Union[bytearray, bytes, str], int]]" = None,
51+
) -> socket.socket:
5652
integration = sentry_sdk.get_client().get_integration(SocketIntegration)
5753
if integration is None:
5854
return real_create_connection(address, timeout, source_address)
@@ -76,12 +72,17 @@ def create_connection(
7672
socket.create_connection = create_connection # type: ignore
7773

7874

79-
def _patch_getaddrinfo():
80-
# type: () -> None
75+
def _patch_getaddrinfo() -> None:
8176
real_getaddrinfo = socket.getaddrinfo
8277

83-
def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
84-
# type: (Union[bytes, str, None], Union[bytes, str, int, None], int, int, int, int) -> List[Tuple[AddressFamily, SocketKind, int, str, Union[Tuple[str, int], Tuple[str, int, int, int], Tuple[int, bytes]]]]
78+
def getaddrinfo(
79+
host: "Union[bytes, str, None]",
80+
port: "Union[bytes, str, int, None]",
81+
family: int = 0,
82+
type: int = 0,
83+
proto: int = 0,
84+
flags: int = 0
85+
) -> "List[Tuple[AddressFamily, SocketKind, int, str, Union[Tuple[str, int], Tuple[str, int, int, int], Tuple[int, bytes]]]]":
8586
integration = sentry_sdk.get_client().get_integration(SocketIntegration)
8687
if integration is None:
8788
return real_getaddrinfo(host, port, family, type, proto, flags)

sentry_sdk/integrations/statsig.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,15 @@ class StatsigIntegration(Integration):
1919
identifier = "statsig"
2020

2121
@staticmethod
22-
def setup_once():
23-
# type: () -> None
22+
def setup_once() -> None:
2423
version = parse_version(STATSIG_VERSION)
2524
_check_minimum_version(StatsigIntegration, version, "statsig")
2625

2726
# Wrap and patch evaluation method(s) in the statsig module
2827
old_check_gate = statsig_module.check_gate
2928

3029
@wraps(old_check_gate)
31-
def sentry_check_gate(user, gate, *args, **kwargs):
32-
# type: (StatsigUser, str, *Any, **Any) -> Any
30+
def sentry_check_gate(user: "StatsigUser", gate: str, *args: Any, **kwargs: Any) -> Any:
3331
enabled = old_check_gate(user, gate, *args, **kwargs)
3432
add_feature_flag(gate, enabled)
3533
return enabled

sentry_sdk/integrations/typer.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,12 @@ class TyperIntegration(Integration):
3030
identifier = "typer"
3131

3232
@staticmethod
33-
def setup_once():
34-
# type: () -> None
33+
def setup_once() -> None:
3534
typer.main.except_hook = _make_excepthook(typer.main.except_hook) # type: ignore
3635

3736

38-
def _make_excepthook(old_excepthook):
39-
# type: (Excepthook) -> Excepthook
40-
def sentry_sdk_excepthook(type_, value, traceback):
41-
# type: (Type[BaseException], BaseException, Optional[TracebackType]) -> None
37+
def _make_excepthook(old_excepthook: "Excepthook") -> "Excepthook":
38+
def sentry_sdk_excepthook(type_: "Type[BaseException]", value: BaseException, traceback: "Optional[TracebackType]") -> None:
4239
integration = sentry_sdk.get_client().get_integration(TyperIntegration)
4340

4441
# Note: If we replace this with ensure_integration_enabled then

0 commit comments

Comments
 (0)