patchy 3.0.0__tar.gz → 3.1.0__tar.gz
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.
- {patchy-3.0.0 → patchy-3.1.0}/PKG-INFO +1 -1
- {patchy-3.0.0 → patchy-3.1.0}/pyproject.toml +1 -1
- {patchy-3.0.0 → patchy-3.1.0}/pyproject.toml.orig +1 -1
- {patchy-3.0.0 → patchy-3.1.0}/src/patchy/api.py +45 -3
- {patchy-3.0.0 → patchy-3.1.0}/LICENSE +0 -0
- {patchy-3.0.0 → patchy-3.1.0}/README.rst +0 -0
- {patchy-3.0.0 → patchy-3.1.0}/src/patchy/__init__.py +0 -0
- {patchy-3.0.0 → patchy-3.1.0}/src/patchy/cache.py +0 -0
- {patchy-3.0.0 → patchy-3.1.0}/src/patchy/py.typed +0 -0
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
4
|
import inspect
|
|
5
|
+
import linecache
|
|
5
6
|
from collections.abc import Callable
|
|
6
7
|
from functools import wraps
|
|
7
8
|
from textwrap import dedent
|
|
@@ -72,7 +73,7 @@ class temp_patch:
|
|
|
72
73
|
@wraps(decorable)
|
|
73
74
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
74
75
|
with self:
|
|
75
|
-
decorable(*args, **kwargs)
|
|
76
|
+
return decorable(*args, **kwargs)
|
|
76
77
|
|
|
77
78
|
return cast(AnyFunc, wrapper)
|
|
78
79
|
|
|
@@ -185,6 +186,10 @@ def _set_source(func: Callable[..., Any], func_source: str) -> None:
|
|
|
185
186
|
real_func = _get_real_func(func)
|
|
186
187
|
# Figure out any future headers that may be required
|
|
187
188
|
feature_flags = real_func.__code__.co_flags & FEATURE_MASK
|
|
189
|
+
# Compile with a per-function virtual filename, under which the patched
|
|
190
|
+
# source is stored in linecache below. Line numbers relative to the
|
|
191
|
+
# patched source then match, so tools show the right lines.
|
|
192
|
+
filename = f"<patchy: {real_func.__module__}.{real_func.__qualname__}>"
|
|
188
193
|
|
|
189
194
|
class_name = _class_name(func)
|
|
190
195
|
|
|
@@ -193,7 +198,7 @@ def _set_source(func: Callable[..., Any], func_source: str) -> None:
|
|
|
193
198
|
flags: int = 0,
|
|
194
199
|
) -> CodeType | ast.Module:
|
|
195
200
|
result: CodeType | ast.Module = compile(
|
|
196
|
-
code,
|
|
201
|
+
code, filename, "exec", flags=feature_flags | flags, dont_inherit=True
|
|
197
202
|
)
|
|
198
203
|
return result
|
|
199
204
|
|
|
@@ -233,9 +238,36 @@ def _set_source(func: Callable[..., Any], func_source: str) -> None:
|
|
|
233
238
|
else:
|
|
234
239
|
fv_force_use = []
|
|
235
240
|
_ast = _parse(func_source).body[0]
|
|
236
|
-
_ast.
|
|
241
|
+
assert isinstance(_ast, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
242
|
+
_replace_defaults(_ast)
|
|
243
|
+
_ast.body = _ast.body + fv_force_use
|
|
237
244
|
return _def, _ast, fv_body
|
|
238
245
|
|
|
246
|
+
def _replace_defaults(
|
|
247
|
+
func_ast: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
248
|
+
) -> None:
|
|
249
|
+
"""
|
|
250
|
+
Replace default value expressions with None. Only the code object of
|
|
251
|
+
the new function is transplanted onto the original function, so the
|
|
252
|
+
recompiled defaults are discarded and the original ones remain in use.
|
|
253
|
+
The expressions may not even evaluate at patch time, since they can
|
|
254
|
+
refer to names that were only available when the function was
|
|
255
|
+
originally defined, such as class attributes or enclosing function
|
|
256
|
+
locals.
|
|
257
|
+
"""
|
|
258
|
+
args = func_ast.args
|
|
259
|
+
args.defaults = [
|
|
260
|
+
ast.copy_location(ast.Constant(None), default) for default in args.defaults
|
|
261
|
+
]
|
|
262
|
+
args.kw_defaults = [
|
|
263
|
+
(
|
|
264
|
+
None
|
|
265
|
+
if default is None
|
|
266
|
+
else ast.copy_location(ast.Constant(None), default)
|
|
267
|
+
)
|
|
268
|
+
for default in args.kw_defaults
|
|
269
|
+
]
|
|
270
|
+
|
|
239
271
|
def _process_method() -> ast.Module:
|
|
240
272
|
"""
|
|
241
273
|
Wrap the new method in a class to ensure the same mangling as would
|
|
@@ -294,6 +326,16 @@ def _set_source(func: Callable[..., Any], func_source: str) -> None:
|
|
|
294
326
|
# Store the modified source. This used to be attached to the function but
|
|
295
327
|
# that is a bit naughty
|
|
296
328
|
_source_map[real_func] = func_source
|
|
329
|
+
# Store the modified source in linecache, under the virtual filename, so
|
|
330
|
+
# tools that read source through it, like tracebacks, pdb, and
|
|
331
|
+
# inspect.getsource(), show it. Done the same way as doctest and timeit.
|
|
332
|
+
# The None mtime means linecache.checkcache() never drops the entry.
|
|
333
|
+
linecache.cache[filename] = (
|
|
334
|
+
len(func_source),
|
|
335
|
+
None,
|
|
336
|
+
func_source.splitlines(keepends=True),
|
|
337
|
+
filename,
|
|
338
|
+
)
|
|
297
339
|
|
|
298
340
|
|
|
299
341
|
def _get_real_func(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|