0
# Utility Functions
1
2
Core utility functions for directory management, deprecation warnings, async support, and platform-specific operations. These utilities are used throughout the Jupyter ecosystem to provide consistent behavior and functionality.
3
4
## Capabilities
5
6
### Directory Management
7
8
Utilities for creating and managing directories with proper permissions and error handling.
9
10
```python { .api }
11
def ensure_dir_exists(path: str | Path, mode: int = 0o777) -> None:
12
"""Ensure that a directory exists.
13
14
If it doesn't exist, try to create it, protecting against race conditions
15
if another process is doing the same. The default permissions are
16
determined by the current umask.
17
18
Args:
19
path: Directory path to create
20
mode: Directory permissions (default: 0o777, modified by umask)
21
22
Raises:
23
OSError: If directory cannot be created or path exists but is not a directory
24
"""
25
```
26
27
**Usage Example:**
28
29
```python
30
from jupyter_core.utils import ensure_dir_exists
31
from pathlib import Path
32
33
# Ensure a config directory exists
34
config_dir = Path.home() / ".jupyter" / "custom"
35
ensure_dir_exists(config_dir, mode=0o700) # User-only permissions
36
37
# Safe to use even if directory already exists
38
ensure_dir_exists(config_dir) # No error if already exists
39
```
40
41
### Deprecation Support
42
43
Utilities for generating deprecation warnings with proper stack level calculation to point to the actual calling code rather than internal frames.
44
45
```python { .api }
46
def deprecation(message: str, internal: str | list[str] = "jupyter_core/") -> None:
47
"""Generate a deprecation warning targeting the first frame that is not 'internal'.
48
49
Automatically calculates the correct stack level to ensure the warning
50
points to user code rather than internal library code.
51
52
Args:
53
message: Deprecation warning message
54
internal: String or list of strings identifying internal code paths
55
(if these appear in frame filenames, frames are considered internal)
56
"""
57
```
58
59
**Usage Example:**
60
61
```python
62
from jupyter_core.utils import deprecation
63
64
def old_function():
65
deprecation(
66
"old_function is deprecated, use new_function instead",
67
internal=["jupyter_core/", "mypackage/internal/"]
68
)
69
# Function implementation...
70
71
# When called by user code, warning points to user's call site
72
old_function() # Warning shows this line, not internal deprecation() call
73
```
74
75
### Async Utilities
76
77
Functions for managing asyncio event loops and running async code in sync contexts.
78
79
```python { .api }
80
def ensure_event_loop(prefer_selector_loop: bool = False) -> asyncio.AbstractEventLoop:
81
"""Ensure an asyncio event loop exists and return it.
82
83
Gets the current event loop, or creates a new one if none exists.
84
Handles platform-specific loop selection.
85
86
Args:
87
prefer_selector_loop: On Windows, prefer SelectorEventLoop over default
88
(useful for Tornado compatibility)
89
90
Returns:
91
asyncio.AbstractEventLoop: Current or newly created event loop
92
"""
93
94
def run_sync(coro: Callable[..., Awaitable[T]]) -> Callable[..., T]:
95
"""Wraps coroutine in a function that blocks until it has executed.
96
97
Allows calling async functions from sync code. Handles both cases
98
where an event loop is already running (uses background thread) and
99
where no loop exists (runs loop directly).
100
101
Args:
102
coro: Coroutine function to wrap
103
104
Returns:
105
Callable: Synchronous wrapper function
106
"""
107
108
async def ensure_async(obj: Awaitable[T] | T) -> T:
109
"""Convert a non-awaitable object to a coroutine if needed.
110
111
Handles functions that may return either sync values or awaitables,
112
ensuring consistent async handling.
113
114
Args:
115
obj: Object that may or may not be awaitable
116
117
Returns:
118
T: The result, whether it was originally sync or async
119
"""
120
```
121
122
**Usage Example:**
123
124
```python
125
from jupyter_core.utils import run_sync, ensure_event_loop
126
import asyncio
127
128
# Wrap an async function to be callable from sync code
129
@run_sync
130
async def async_operation(data):
131
await asyncio.sleep(1)
132
return f"Processed: {data}"
133
134
# Can now call async function synchronously
135
result = async_operation("test data")
136
print(result) # "Processed: test data"
137
138
# Ensure event loop exists
139
loop = ensure_event_loop()
140
print(f"Loop type: {type(loop)}")
141
```
142
143
### Internal Async Classes
144
145
Internal classes that support the async utilities. These are typically not used directly but are part of the implementation.
146
147
```python { .api }
148
class _TaskRunner:
149
"""A task runner that runs an asyncio event loop on a background thread.
150
151
Used internally by run_sync() to handle cases where an event loop
152
is already running in the current thread.
153
"""
154
155
def __init__(self) -> None:
156
"""Initialize the task runner."""
157
158
def run(self, coro) -> Any:
159
"""Synchronously run a coroutine on a background thread.
160
161
Args:
162
coro: Coroutine to execute
163
164
Returns:
165
Any: Result of coroutine execution
166
"""
167
```
168
169
### Stack Inspection Utilities
170
171
Internal utilities for inspecting the call stack, used by the deprecation system.
172
173
```python { .api }
174
def _get_frame(level: int) -> FrameType | None:
175
"""Get the frame at the given stack level.
176
177
Uses sys._getframe if available (faster) or falls back to inspect.stack.
178
179
Args:
180
level: Stack level to retrieve (0 = current frame)
181
182
Returns:
183
FrameType | None: Frame object or None if level is invalid
184
"""
185
186
def _external_stacklevel(internal: list[str]) -> int:
187
"""Find the stacklevel of the first frame that doesn't contain internal strings.
188
189
Used by deprecation() to calculate the correct stacklevel for warnings.
190
191
Args:
192
internal: List of strings that identify internal code paths
193
194
Returns:
195
int: Stack level of first external frame
196
"""
197
```
198
199
## Type Support
200
201
Type variables and aliases used throughout the utility functions.
202
203
```python { .api }
204
from typing import Any, Awaitable, Callable, TypeVar
205
from types import FrameType
206
from contextvars import ContextVar
207
import asyncio
208
209
# Type variable for generic async functions
210
T = TypeVar("T")
211
212
# Context variable for tracking event loops
213
_loop: ContextVar[asyncio.AbstractEventLoop | None]
214
215
# Internal registry for task runners
216
_runner_map: dict[str, _TaskRunner]
217
```
218
219
## Platform-Specific Behavior
220
221
### Windows
222
- **Event Loop Selection**: Can prefer SelectorEventLoop over default ProactorEventLoop for Tornado compatibility
223
- **Thread Naming**: Uses descriptive thread names for background async runners
224
- **Frame Inspection**: Handles differences in stack frame availability
225
226
### Unix/Linux/macOS
227
- **Event Loop**: Uses default event loop policy for the platform
228
- **Thread Safety**: Proper handling of event loops across threads
229
- **Signal Handling**: Respects platform signal handling in async contexts
230
231
## Integration with Jupyter Ecosystem
232
233
These utilities are used throughout Jupyter components:
234
235
### Directory Management
236
- **Config directories**: Ensuring Jupyter config directories exist with proper permissions
237
- **Data directories**: Creating kernel spec directories, extension directories
238
- **Runtime directories**: Setting up directories for kernels, servers
239
- **Custom directories**: User-defined locations for notebooks, kernels
240
241
### Deprecation Warnings
242
- **API Changes**: Warning about deprecated function signatures
243
- **Configuration Changes**: Alerting to deprecated config options
244
- **Path Changes**: Warning about legacy path usage patterns
245
- **Import Changes**: Deprecating old module locations
246
247
### Async Support
248
- **Kernel Management**: Running async kernel operations from sync contexts
249
- **Server Integration**: Supporting both sync and async server implementations
250
- **Widget Communication**: Handling async widget comm messages
251
- **Extension Loading**: Supporting async extension initialization
252
253
## Error Handling
254
255
The utilities include robust error handling:
256
257
- **Directory Creation**: Race condition protection, permission error handling
258
- **Stack Inspection**: Fallback methods when sys._getframe unavailable
259
- **Event Loop Management**: Cleanup on application exit, thread safety
260
- **Async Conversion**: Handling already-awaited coroutines, runtime errors
261
262
This comprehensive error handling ensures the utilities work reliably across different Python implementations, platforms, and usage contexts.