cinderx 2026.1.16.2__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- __static__/__init__.py +641 -0
- __static__/compiler_flags.py +8 -0
- __static__/enum.py +160 -0
- __static__/native_utils.py +77 -0
- __static__/type_code.py +48 -0
- __strict__/__init__.py +39 -0
- _cinderx.so +0 -0
- cinderx/__init__.py +577 -0
- cinderx/__pycache__/__init__.cpython-314.pyc +0 -0
- cinderx/_asyncio.py +156 -0
- cinderx/compileall.py +710 -0
- cinderx/compiler/__init__.py +40 -0
- cinderx/compiler/__main__.py +137 -0
- cinderx/compiler/config.py +7 -0
- cinderx/compiler/consts.py +72 -0
- cinderx/compiler/debug.py +70 -0
- cinderx/compiler/dis_stable.py +283 -0
- cinderx/compiler/errors.py +151 -0
- cinderx/compiler/flow_graph_optimizer.py +1287 -0
- cinderx/compiler/future.py +91 -0
- cinderx/compiler/misc.py +32 -0
- cinderx/compiler/opcode_cinder.py +18 -0
- cinderx/compiler/opcode_static.py +100 -0
- cinderx/compiler/opcodebase.py +158 -0
- cinderx/compiler/opcodes.py +991 -0
- cinderx/compiler/optimizer.py +547 -0
- cinderx/compiler/pyassem.py +3711 -0
- cinderx/compiler/pycodegen.py +7660 -0
- cinderx/compiler/pysourceloader.py +62 -0
- cinderx/compiler/static/__init__.py +1404 -0
- cinderx/compiler/static/compiler.py +629 -0
- cinderx/compiler/static/declaration_visitor.py +335 -0
- cinderx/compiler/static/definite_assignment_checker.py +280 -0
- cinderx/compiler/static/effects.py +160 -0
- cinderx/compiler/static/module_table.py +666 -0
- cinderx/compiler/static/type_binder.py +2176 -0
- cinderx/compiler/static/types.py +10580 -0
- cinderx/compiler/static/util.py +81 -0
- cinderx/compiler/static/visitor.py +91 -0
- cinderx/compiler/strict/__init__.py +69 -0
- cinderx/compiler/strict/class_conflict_checker.py +249 -0
- cinderx/compiler/strict/code_gen_base.py +409 -0
- cinderx/compiler/strict/common.py +507 -0
- cinderx/compiler/strict/compiler.py +352 -0
- cinderx/compiler/strict/feature_extractor.py +130 -0
- cinderx/compiler/strict/flag_extractor.py +97 -0
- cinderx/compiler/strict/loader.py +827 -0
- cinderx/compiler/strict/preprocessor.py +11 -0
- cinderx/compiler/strict/rewriter/__init__.py +5 -0
- cinderx/compiler/strict/rewriter/remove_annotations.py +84 -0
- cinderx/compiler/strict/rewriter/rewriter.py +975 -0
- cinderx/compiler/strict/runtime.py +77 -0
- cinderx/compiler/symbols.py +1754 -0
- cinderx/compiler/unparse.py +414 -0
- cinderx/compiler/visitor.py +194 -0
- cinderx/jit.py +230 -0
- cinderx/opcode.py +202 -0
- cinderx/static.py +113 -0
- cinderx/strictmodule.py +6 -0
- cinderx/test_support.py +341 -0
- cinderx-2026.1.16.2.dist-info/METADATA +15 -0
- cinderx-2026.1.16.2.dist-info/RECORD +68 -0
- cinderx-2026.1.16.2.dist-info/WHEEL +6 -0
- cinderx-2026.1.16.2.dist-info/licenses/LICENSE +21 -0
- cinderx-2026.1.16.2.dist-info/top_level.txt +5 -0
- opcodes/__init__.py +0 -0
- opcodes/assign_opcode_numbers.py +272 -0
- opcodes/cinderx_opcodes.py +121 -0
cinderx/__init__.py
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
# pyre-strict
|
|
3
|
+
"""High-performance Python runtime extensions."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import gc
|
|
8
|
+
import platform
|
|
9
|
+
import sys
|
|
10
|
+
from os import environ
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# Note!
|
|
14
|
+
#
|
|
15
|
+
# At Meta, this module is currently loaded as part of Lib/site.py in an attempt
|
|
16
|
+
# to get benefits from using it as soon as possible. However
|
|
17
|
+
# Lib/test/test_site.py will assert that site.py does not import too many
|
|
18
|
+
# modules. Be careful with adding import statements here.
|
|
19
|
+
#
|
|
20
|
+
# The plan is to move applications over to using an explicit initialization
|
|
21
|
+
# step rather than Lib/site.py. Once that is done we can add all the imports
|
|
22
|
+
# we want here.
|
|
23
|
+
# ============================================================================
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_import_error: ImportError | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_supported_runtime() -> bool:
|
|
30
|
+
"""
|
|
31
|
+
Check that the current Python runtime will be able to load the _cinderx
|
|
32
|
+
native extension.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
if sys.platform not in ("darwin", "linux"):
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
version = (sys.version_info.major, sys.version_info.minor)
|
|
39
|
+
if version == (3, 14) or version == (3, 15):
|
|
40
|
+
# Can't load the native extension if the GIL is forcibly being disabled. The
|
|
41
|
+
# native extension doesn't support free-threading properly yet.
|
|
42
|
+
return environ.get("PYTHON_GIL") != "0"
|
|
43
|
+
if version == (3, 12):
|
|
44
|
+
return "+meta" in sys.version
|
|
45
|
+
if version == (3, 10):
|
|
46
|
+
return "+cinder" in sys.version
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
# Currently if we try to import _cinderx on runtimes without our internal patches
|
|
52
|
+
# the import will crash. This is meant to go away in the future.
|
|
53
|
+
if not is_supported_runtime():
|
|
54
|
+
raise ImportError(
|
|
55
|
+
f"The _cinderx native extension is not supported for Python version '{sys.version}' on platform '{sys.platform}'"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# pyre-ignore[21]: _cinderx is not a real cpp_python_extension() yet.
|
|
59
|
+
from _cinderx import ( # noqa: F401
|
|
60
|
+
_compile_perf_trampoline_pre_fork,
|
|
61
|
+
_is_compile_perf_trampoline_pre_fork_enabled,
|
|
62
|
+
async_cached_classproperty,
|
|
63
|
+
async_cached_property,
|
|
64
|
+
cached_classproperty,
|
|
65
|
+
cached_property,
|
|
66
|
+
cached_property_with_descr,
|
|
67
|
+
clear_caches,
|
|
68
|
+
clear_classloader_caches,
|
|
69
|
+
disable_parallel_gc,
|
|
70
|
+
enable_parallel_gc,
|
|
71
|
+
freeze_type,
|
|
72
|
+
get_parallel_gc_settings,
|
|
73
|
+
has_parallel_gc,
|
|
74
|
+
immortalize_heap,
|
|
75
|
+
is_immortal,
|
|
76
|
+
strict_module_patch,
|
|
77
|
+
strict_module_patch_delete,
|
|
78
|
+
strict_module_patch_enabled,
|
|
79
|
+
StrictModule,
|
|
80
|
+
watch_sys_modules,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if sys.version_info < (3, 11):
|
|
84
|
+
# In 3.12+ use the versions in the polyfill cinder library instead.
|
|
85
|
+
from _cinderx import (
|
|
86
|
+
_get_entire_call_stack_as_qualnames_with_lineno,
|
|
87
|
+
_get_entire_call_stack_as_qualnames_with_lineno_and_frame,
|
|
88
|
+
clear_all_shadow_caches,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if sys.version_info >= (3, 12):
|
|
92
|
+
from _cinderx import delay_adaptive, get_adaptive_delay, set_adaptive_delay
|
|
93
|
+
|
|
94
|
+
except ImportError as e:
|
|
95
|
+
if "undefined symbol:" in str(e):
|
|
96
|
+
# If we're on a dev build report this as an error, otherwise muddle along with alternative definitions
|
|
97
|
+
# on unsupported Python's.
|
|
98
|
+
from os.path import dirname, exists, join
|
|
99
|
+
|
|
100
|
+
if exists(join(dirname(__file__), ".dev_build")):
|
|
101
|
+
raise ImportError(
|
|
102
|
+
"The _cinderx native extension is not available due to a missing symbol. This is likely a bug you introduced. "
|
|
103
|
+
"Please ensure that the cinderx kernel is being used."
|
|
104
|
+
) from e
|
|
105
|
+
_import_error = e
|
|
106
|
+
|
|
107
|
+
def _compile_perf_trampoline_pre_fork() -> None:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
def _get_entire_call_stack_as_qualnames_with_lineno() -> list[tuple[str, int]]:
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
def _get_entire_call_stack_as_qualnames_with_lineno_and_frame() -> list[
|
|
114
|
+
tuple[str, int, object]
|
|
115
|
+
]:
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
def _is_compile_perf_trampoline_pre_fork_enabled() -> bool:
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
from asyncio import AbstractEventLoop, Future
|
|
122
|
+
from typing import (
|
|
123
|
+
Awaitable,
|
|
124
|
+
Callable,
|
|
125
|
+
Dict,
|
|
126
|
+
final,
|
|
127
|
+
Generator,
|
|
128
|
+
Generic,
|
|
129
|
+
List,
|
|
130
|
+
NoReturn,
|
|
131
|
+
Optional,
|
|
132
|
+
overload,
|
|
133
|
+
Tuple,
|
|
134
|
+
Type,
|
|
135
|
+
TYPE_CHECKING,
|
|
136
|
+
TypeVar,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
_TClass = TypeVar("_TClass")
|
|
140
|
+
_TReturnType = TypeVar("_TReturnType")
|
|
141
|
+
|
|
142
|
+
@final
|
|
143
|
+
class NoValueSet:
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
NO_VALUE_SET = NoValueSet()
|
|
147
|
+
|
|
148
|
+
class _BaseCachedProperty(Generic[_TClass, _TReturnType]):
|
|
149
|
+
fget: Callable[[_TClass], _TReturnType]
|
|
150
|
+
__name__: str
|
|
151
|
+
|
|
152
|
+
def __init__(
|
|
153
|
+
self,
|
|
154
|
+
f: Callable[[_TClass], _TReturnType],
|
|
155
|
+
slot: Optional[Descriptor[_TReturnType]] = None,
|
|
156
|
+
) -> None:
|
|
157
|
+
self.fget: Callable[[_TClass], _TReturnType] = f
|
|
158
|
+
self.__name__ = f.__name__
|
|
159
|
+
self.__doc__: str | None = f.__doc__
|
|
160
|
+
self.slot = slot
|
|
161
|
+
|
|
162
|
+
@overload
|
|
163
|
+
def __get__(
|
|
164
|
+
self, obj: None, cls: Type[_TClass]
|
|
165
|
+
) -> _BaseCachedProperty[_TClass, _TReturnType]: ...
|
|
166
|
+
|
|
167
|
+
@overload
|
|
168
|
+
def __get__(self, obj: _TClass, cls: Type[_TClass]) -> _TReturnType: ...
|
|
169
|
+
|
|
170
|
+
def __get__(
|
|
171
|
+
self, obj: Optional[_TClass], cls: Type[_TClass]
|
|
172
|
+
) -> _BaseCachedProperty[_TClass, _TReturnType] | _TReturnType:
|
|
173
|
+
if obj is None:
|
|
174
|
+
return self
|
|
175
|
+
slot = self.slot
|
|
176
|
+
if slot is not None:
|
|
177
|
+
try:
|
|
178
|
+
res = slot.__get__(obj, cls)
|
|
179
|
+
except AttributeError:
|
|
180
|
+
res = self.fget(obj)
|
|
181
|
+
slot.__set__(obj, res)
|
|
182
|
+
return res
|
|
183
|
+
|
|
184
|
+
result = self.fget(obj)
|
|
185
|
+
obj.__dict__[self.__name__] = result
|
|
186
|
+
return result
|
|
187
|
+
|
|
188
|
+
class _AsyncLazyValueState:
|
|
189
|
+
NotStarted = 0
|
|
190
|
+
Running = 1
|
|
191
|
+
Done = 2
|
|
192
|
+
|
|
193
|
+
_T = TypeVar("_T", covariant=True)
|
|
194
|
+
_TParams = TypeVar("_TParams")
|
|
195
|
+
|
|
196
|
+
# noqa: F401
|
|
197
|
+
import asyncio
|
|
198
|
+
|
|
199
|
+
class _AsyncLazyValue(Awaitable[_T]):
|
|
200
|
+
"""
|
|
201
|
+
This is a low-level class used mainly for two things:
|
|
202
|
+
* It helps to avoid calling a coroutine multiple times, by caching the
|
|
203
|
+
result of a previous call
|
|
204
|
+
* It ensures that the coroutine is called only once
|
|
205
|
+
|
|
206
|
+
_AsyncLazyValue has well defined cancellation behavior in these cases:
|
|
207
|
+
|
|
208
|
+
1. When we have a single task stack (call stack for you JS folks), which is
|
|
209
|
+
awaiting on the AsyncLazyValue
|
|
210
|
+
-> In this case, we mimic the behavior of a normal await. i.e: If the
|
|
211
|
+
task stack gets cancelled, we cancel the coroutine (by raising a
|
|
212
|
+
CancelledError in the underlying future)
|
|
213
|
+
|
|
214
|
+
2. When we have multiple task stacks awaiting on the future.
|
|
215
|
+
We have two sub cases here.
|
|
216
|
+
|
|
217
|
+
2.1. The initial task stack (which resulted in an await of the coroutine)
|
|
218
|
+
gets cancelled.
|
|
219
|
+
-> In this case, we cancel the coroutine, and all the tasks depending
|
|
220
|
+
on it. If we don't do that, we'd have to implement retry logic,
|
|
221
|
+
which is a bad idea in such low level code. Even if we do implement
|
|
222
|
+
retries, there's no guarantee that they would succeed, so it's better
|
|
223
|
+
to just fail here.
|
|
224
|
+
|
|
225
|
+
Also, the number of times this happens is very small (I don't have
|
|
226
|
+
data to prove it, but qualitative arguments suggest this is the
|
|
227
|
+
case).
|
|
228
|
+
|
|
229
|
+
2.2. One of the many task stacks gets cancelled (but not the one which ended
|
|
230
|
+
up awaiting the coroutine)
|
|
231
|
+
-> In this case, we just allow the task stack to be cancelled, but
|
|
232
|
+
the rest of them are processed without being affected.
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
def __init__(
|
|
236
|
+
self,
|
|
237
|
+
# pyre-fixme[31]: Expression `typing.Callable[(_TParams,
|
|
238
|
+
# typing.Awaitable[_T])]` is not a valid type.
|
|
239
|
+
coro_func: Callable[_TParams, Awaitable[_T]],
|
|
240
|
+
# pyre-fixme[11]: Annotation `args` is not defined as a type.
|
|
241
|
+
*args: _TParams.args,
|
|
242
|
+
# pyre-fixme[11]: Annotation `kwargs` is not defined as a type.
|
|
243
|
+
**kwargs: _TParams.kwargs,
|
|
244
|
+
) -> None:
|
|
245
|
+
global asyncio
|
|
246
|
+
# pyre-fixme[31]: Expression `typing.Optional[typing.Callable[(_TParams,
|
|
247
|
+
# typing.Awaitable[_T])]]` is not a valid type.
|
|
248
|
+
self.coro_func: Optional[Callable[_TParams, Awaitable[_T]]] = coro_func
|
|
249
|
+
self.args: Tuple[object, ...] = args
|
|
250
|
+
self.kwargs: Dict[str, object] = kwargs
|
|
251
|
+
self.state: int = _AsyncLazyValueState.NotStarted
|
|
252
|
+
self.res: Optional[_T] = None
|
|
253
|
+
self._futures: List[Future] = []
|
|
254
|
+
self._awaiting_tasks = 0
|
|
255
|
+
|
|
256
|
+
async def _async_compute(self) -> _T:
|
|
257
|
+
futures = self._futures
|
|
258
|
+
try:
|
|
259
|
+
coro_func = self.coro_func
|
|
260
|
+
# lint-fixme: NoAssertsRule
|
|
261
|
+
assert coro_func is not None
|
|
262
|
+
self.res = res = await coro_func(*self.args, **self.kwargs)
|
|
263
|
+
|
|
264
|
+
self.state = _AsyncLazyValueState.Done
|
|
265
|
+
|
|
266
|
+
# pyre-fixme[1001]: Awaitable assigned to `value` is never awaited.
|
|
267
|
+
for value in futures:
|
|
268
|
+
if not value.done():
|
|
269
|
+
value.set_result(self.res)
|
|
270
|
+
|
|
271
|
+
self.args = ()
|
|
272
|
+
self.kwargs.clear()
|
|
273
|
+
del self._futures[:]
|
|
274
|
+
self.coro_func = None
|
|
275
|
+
|
|
276
|
+
return res
|
|
277
|
+
|
|
278
|
+
except (Exception, asyncio.CancelledError) as e:
|
|
279
|
+
# pyre-fixme[1001]: Awaitable assigned to `value` is never awaited.
|
|
280
|
+
for value in futures:
|
|
281
|
+
if not value.done():
|
|
282
|
+
value.set_exception(e)
|
|
283
|
+
self._futures = []
|
|
284
|
+
self.state = _AsyncLazyValueState.NotStarted
|
|
285
|
+
raise
|
|
286
|
+
|
|
287
|
+
def _get_future(self, loop: Optional[AbstractEventLoop]) -> Future:
|
|
288
|
+
if loop is None:
|
|
289
|
+
loop = asyncio.get_event_loop()
|
|
290
|
+
f = asyncio.Future(loop=loop)
|
|
291
|
+
self._futures.append(f)
|
|
292
|
+
self._awaiting_tasks += 1
|
|
293
|
+
return f
|
|
294
|
+
|
|
295
|
+
def __iter__(self) -> _AsyncLazyValue[_T]:
|
|
296
|
+
return self
|
|
297
|
+
|
|
298
|
+
def __next__(self) -> NoReturn:
|
|
299
|
+
raise StopIteration(self.res)
|
|
300
|
+
|
|
301
|
+
def __await__(self) -> Generator[None, None, _T]:
|
|
302
|
+
if self.state == _AsyncLazyValueState.Done:
|
|
303
|
+
# pyre-ignore[7]: Expected `Generator[None, None, Variable[_T](covariant)]`
|
|
304
|
+
# but got `_AsyncLazyValue[Variable[_T](covariant)]`.
|
|
305
|
+
return self
|
|
306
|
+
elif self.state == _AsyncLazyValueState.Running:
|
|
307
|
+
c = self._get_future(None)
|
|
308
|
+
return c.__await__()
|
|
309
|
+
else:
|
|
310
|
+
self.state = _AsyncLazyValueState.Running
|
|
311
|
+
c = self._async_compute()
|
|
312
|
+
return c.__await__()
|
|
313
|
+
|
|
314
|
+
def as_future(self, loop: AbstractEventLoop) -> Future:
|
|
315
|
+
if self.state == _AsyncLazyValueState.Done:
|
|
316
|
+
f = asyncio.Future(loop=loop)
|
|
317
|
+
f.set_result(self.res)
|
|
318
|
+
return f
|
|
319
|
+
elif self.state == _AsyncLazyValueState.Running:
|
|
320
|
+
return self._get_future(loop)
|
|
321
|
+
else:
|
|
322
|
+
if loop is None:
|
|
323
|
+
loop = asyncio.get_event_loop()
|
|
324
|
+
t = loop.create_task(self._async_compute())
|
|
325
|
+
self.state = _AsyncLazyValueState.Running
|
|
326
|
+
# pyre-ignore[16]: Undefined attribute `asyncio.tasks.Task`
|
|
327
|
+
# has no attribute `_source_traceback`.
|
|
328
|
+
if t._source_traceback:
|
|
329
|
+
del t._source_traceback[-1]
|
|
330
|
+
# pyre-fixme[7]: Expected `Future[Any]` but got `Task[_T]`.
|
|
331
|
+
return t
|
|
332
|
+
|
|
333
|
+
_TAwaitableReturnType = TypeVar("_TAwaitableReturnType")
|
|
334
|
+
|
|
335
|
+
class async_cached_property(
|
|
336
|
+
Generic[_TAwaitableReturnType, _TClass],
|
|
337
|
+
_BaseCachedProperty[_TClass, Awaitable[_TAwaitableReturnType]],
|
|
338
|
+
):
|
|
339
|
+
def __init__(
|
|
340
|
+
self,
|
|
341
|
+
f: Callable[[_TClass], _TReturnType],
|
|
342
|
+
slot: Optional[Descriptor[_TReturnType]] = None,
|
|
343
|
+
) -> None:
|
|
344
|
+
super().__init__(f, slot)
|
|
345
|
+
|
|
346
|
+
def __get__(
|
|
347
|
+
self, obj: Optional[_TClass], cls: Type[_TClass]
|
|
348
|
+
) -> (
|
|
349
|
+
_BaseCachedProperty[_TClass, Awaitable[_TAwaitableReturnType]]
|
|
350
|
+
| Awaitable[_TAwaitableReturnType]
|
|
351
|
+
):
|
|
352
|
+
if obj is None:
|
|
353
|
+
return self
|
|
354
|
+
|
|
355
|
+
slot = self.slot
|
|
356
|
+
if slot is not None:
|
|
357
|
+
try:
|
|
358
|
+
res = slot.__get__(obj, cls)
|
|
359
|
+
except AttributeError:
|
|
360
|
+
res = _AsyncLazyValue(self.fget, obj)
|
|
361
|
+
slot.__set__(obj, res)
|
|
362
|
+
return res
|
|
363
|
+
|
|
364
|
+
lazy_value = _AsyncLazyValue(self.fget, obj)
|
|
365
|
+
setattr(obj, self.__name__, lazy_value)
|
|
366
|
+
return lazy_value
|
|
367
|
+
|
|
368
|
+
class async_cached_classproperty(
|
|
369
|
+
Generic[_TAwaitableReturnType, _TClass],
|
|
370
|
+
_BaseCachedProperty[Type[_TClass], Awaitable[_TAwaitableReturnType]],
|
|
371
|
+
):
|
|
372
|
+
def __init__(
|
|
373
|
+
self,
|
|
374
|
+
f: Callable[[_TClass], Awaitable[_TAwaitableReturnType]],
|
|
375
|
+
slot: Optional[Descriptor[Awaitable[_TAwaitableReturnType]]] = None,
|
|
376
|
+
) -> None:
|
|
377
|
+
super().__init__(f, slot)
|
|
378
|
+
self._value: NoValueSet | Awaitable[_TAwaitableReturnType] = NO_VALUE_SET
|
|
379
|
+
|
|
380
|
+
def __get__(
|
|
381
|
+
self, obj: Optional[_TClass], cls: Type[_TClass]
|
|
382
|
+
) -> Awaitable[_TAwaitableReturnType]:
|
|
383
|
+
lazy_value = self._value
|
|
384
|
+
if not isinstance(lazy_value, NoValueSet):
|
|
385
|
+
return lazy_value
|
|
386
|
+
self._value = lazy_value = _AsyncLazyValue(self.fget, cls)
|
|
387
|
+
return lazy_value
|
|
388
|
+
|
|
389
|
+
class cached_classproperty(_BaseCachedProperty[Type[_TClass], _TReturnType]):
|
|
390
|
+
def __init__(
|
|
391
|
+
self,
|
|
392
|
+
f: Callable[[_TClass], _TReturnType],
|
|
393
|
+
slot: Optional[Descriptor[_TReturnType]] = None,
|
|
394
|
+
) -> None:
|
|
395
|
+
super().__init__(f, slot)
|
|
396
|
+
self._value: NoValueSet | _TReturnType = NO_VALUE_SET
|
|
397
|
+
|
|
398
|
+
def __get__(self, obj: Optional[_TClass], cls: Type[_TClass]) -> _TReturnType:
|
|
399
|
+
result = self._value
|
|
400
|
+
if not isinstance(result, NoValueSet):
|
|
401
|
+
return result
|
|
402
|
+
self._value = result = self.fget(cls)
|
|
403
|
+
return result
|
|
404
|
+
|
|
405
|
+
_TClass = TypeVar("_TClass")
|
|
406
|
+
_TReturnType = TypeVar("_TReturnType")
|
|
407
|
+
|
|
408
|
+
if TYPE_CHECKING:
|
|
409
|
+
from abc import ABC
|
|
410
|
+
|
|
411
|
+
@final
|
|
412
|
+
class Descriptor(ABC, Generic[_TReturnType]):
|
|
413
|
+
__name__: str
|
|
414
|
+
__objclass__: Type[object]
|
|
415
|
+
|
|
416
|
+
def __get__(
|
|
417
|
+
self, inst: object, ctx: Optional[Type[object]] = None
|
|
418
|
+
) -> _TReturnType: ...
|
|
419
|
+
|
|
420
|
+
def __set__(self, inst: object, value: _TReturnType) -> None:
|
|
421
|
+
pass
|
|
422
|
+
|
|
423
|
+
def __delete__(self, inst: object) -> None:
|
|
424
|
+
pass
|
|
425
|
+
|
|
426
|
+
class cached_property(_BaseCachedProperty[_TClass, _TReturnType]):
|
|
427
|
+
def __init__(
|
|
428
|
+
self,
|
|
429
|
+
f: Callable[[_TClass], _TReturnType],
|
|
430
|
+
slot: Optional[Descriptor[_TReturnType]] = None,
|
|
431
|
+
) -> None:
|
|
432
|
+
super().__init__(f, slot)
|
|
433
|
+
if slot is not None:
|
|
434
|
+
if (
|
|
435
|
+
type(self) is not cached_property
|
|
436
|
+
and type(self) is not cached_property_with_descr
|
|
437
|
+
):
|
|
438
|
+
raise TypeError(
|
|
439
|
+
"slot can't be used with subtypes of cached_property"
|
|
440
|
+
)
|
|
441
|
+
# pyre-ignore[4]: Missing attribute annotation for __class__
|
|
442
|
+
self.__class__ = cached_property_with_descr
|
|
443
|
+
|
|
444
|
+
class cached_property_with_descr(cached_property[_TClass, _TReturnType]):
|
|
445
|
+
def __set__(self, inst: object, value: _TReturnType) -> None:
|
|
446
|
+
slot = self.slot
|
|
447
|
+
if slot is not None:
|
|
448
|
+
slot.__set__(inst, value)
|
|
449
|
+
else:
|
|
450
|
+
setattr(inst, self.__name__, value)
|
|
451
|
+
|
|
452
|
+
def __delete__(self, inst: object) -> None:
|
|
453
|
+
slot = self.slot
|
|
454
|
+
if slot is not None:
|
|
455
|
+
slot.__delete__(inst)
|
|
456
|
+
else:
|
|
457
|
+
delattr(inst, self.__name__)
|
|
458
|
+
|
|
459
|
+
if sys.version_info < (3, 11):
|
|
460
|
+
|
|
461
|
+
def clear_all_shadow_caches() -> None:
|
|
462
|
+
pass
|
|
463
|
+
|
|
464
|
+
def clear_caches() -> None:
|
|
465
|
+
pass
|
|
466
|
+
|
|
467
|
+
def clear_classloader_caches() -> None:
|
|
468
|
+
pass
|
|
469
|
+
|
|
470
|
+
def disable_parallel_gc() -> None:
|
|
471
|
+
pass
|
|
472
|
+
|
|
473
|
+
def enable_parallel_gc(min_generation: int = 2, num_threads: int = 0) -> None:
|
|
474
|
+
raise RuntimeError(
|
|
475
|
+
"No Parallel GC support because _cinderx did not load correctly"
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
def freeze_type(ty: object) -> object:
|
|
479
|
+
return ty
|
|
480
|
+
|
|
481
|
+
def get_parallel_gc_settings() -> dict[str, int] | None:
|
|
482
|
+
return None
|
|
483
|
+
|
|
484
|
+
def has_parallel_gc() -> bool:
|
|
485
|
+
return False
|
|
486
|
+
|
|
487
|
+
def immortalize_heap() -> None:
|
|
488
|
+
pass
|
|
489
|
+
|
|
490
|
+
def is_immortal(obj: object) -> bool:
|
|
491
|
+
raise RuntimeError(
|
|
492
|
+
"Can't answer whether an object is mortal or immortal from Python code"
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
def strict_module_patch(mod: object, name: str, value: object) -> None:
|
|
496
|
+
pass
|
|
497
|
+
|
|
498
|
+
def strict_module_patch_delete(mod: object, name: str) -> None:
|
|
499
|
+
pass
|
|
500
|
+
|
|
501
|
+
def strict_module_patch_enabled(mod: object) -> bool:
|
|
502
|
+
return False
|
|
503
|
+
|
|
504
|
+
class StrictModule:
|
|
505
|
+
def __init__(self, d: dict[str, object], b: bool) -> None:
|
|
506
|
+
pass
|
|
507
|
+
|
|
508
|
+
def watch_sys_modules() -> None:
|
|
509
|
+
pass
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def maybe_enable_parallel_gc() -> None:
|
|
513
|
+
"""Conditionally enable parallel GC based on environment variables."""
|
|
514
|
+
is_parallel_gc_enabled = environ.get("PARALLEL_GC_ENABLED", "0") == "1"
|
|
515
|
+
if not has_parallel_gc() or not is_parallel_gc_enabled:
|
|
516
|
+
return
|
|
517
|
+
thresholds = gc.get_threshold()
|
|
518
|
+
parallel_gc_threshold_gen0 = int(
|
|
519
|
+
environ.get("PARALLEL_GC_THRESHOLD_GEN0", thresholds[0])
|
|
520
|
+
)
|
|
521
|
+
parallel_gc_threshold_gen1 = int(
|
|
522
|
+
environ.get("PARALLEL_GC_THRESHOLD_GEN1", thresholds[1])
|
|
523
|
+
)
|
|
524
|
+
parallel_gc_threshold_gen2 = int(
|
|
525
|
+
environ.get("PARALLEL_GC_THRESHOLD_GEN2", thresholds[2])
|
|
526
|
+
)
|
|
527
|
+
gc.set_threshold(
|
|
528
|
+
parallel_gc_threshold_gen0,
|
|
529
|
+
parallel_gc_threshold_gen1,
|
|
530
|
+
parallel_gc_threshold_gen2,
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
parallel_gc_num_threads = int(environ.get("PARALLEL_GC_NUM_THREADS", "0"))
|
|
534
|
+
parallel_gc_min_generation = int(environ.get("PARALLEL_GC_MIN_GENERATION", "2"))
|
|
535
|
+
|
|
536
|
+
enable_parallel_gc(
|
|
537
|
+
min_generation=parallel_gc_min_generation,
|
|
538
|
+
num_threads=parallel_gc_num_threads,
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
_is_init: bool = False
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def init() -> None:
|
|
546
|
+
"""Initialize CinderX."""
|
|
547
|
+
global _is_init
|
|
548
|
+
|
|
549
|
+
# Failed to import _cinderx, nothing to initialize.
|
|
550
|
+
if _import_error is not None:
|
|
551
|
+
return
|
|
552
|
+
|
|
553
|
+
# Already initialized.
|
|
554
|
+
if _is_init:
|
|
555
|
+
return
|
|
556
|
+
|
|
557
|
+
maybe_enable_parallel_gc()
|
|
558
|
+
|
|
559
|
+
_is_init = True
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def is_initialized() -> bool:
|
|
563
|
+
"""
|
|
564
|
+
Check if the cinderx extension has been properly initialized.
|
|
565
|
+
"""
|
|
566
|
+
return _is_init
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def get_import_error() -> ImportError | None:
|
|
570
|
+
"""
|
|
571
|
+
Get the ImportError that occurred when _cinderx was imported, if there was
|
|
572
|
+
an error.
|
|
573
|
+
"""
|
|
574
|
+
return _import_error
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
init()
|
|
Binary file
|