-
-
Notifications
You must be signed in to change notification settings - Fork 30.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-91053: Add an optional callback that is invoked whenever a function is modified #97834
Closed
Closed
Changes from 2 commits
Commits
Show all changes
152 commits
Select commit
Hold shift + click to select a range
48eb802
Add an optional callback that is invoked whenever a function is modified
mpage 18329a1
Merge branch 'main' into func-modified-cb
mpage 501c4dd
Fix the build on windows
mpage b727aa2
Fix refcounting for the result of vectorcall
mpage e3a8230
Move callback into _PyRuntimeState
mpage b2e20ef
Handle multiple events being dispatched
mpage 0a30690
Add NEWS entry
mpage 73dd809
Make the callback per-interpreter, rather than per-runtime
mpage 4fa1cc8
Call the builtin `id` to return the id for a function that's about to…
mpage 80f49f0
Fix prototype for restore_func_event_callback
mpage 81fcd2c
gh-93357: Start porting asyncio server test cases to IsolatedAsyncioT…
arhadthedev cc60df0
GH-95913: Update what's new in 3.11 for asyncio (#97806)
gvanrossum 39bef03
gh-90301: Doc: Add references to PEP 686 (#96816)
methane de65e64
gh-96448: fix documentation for _thread.lock.acquire (#96449)
dgiger42 c00e5c5
gh-97008: Add a Python implementation of AttributeError and NameError…
ambv 75af114
gh-58451: Add optional delete_on_close parameter to NamedTemporaryFil…
Ev2geny 937cc6d
gh-95913: Move py.exe to appropriate What's New section & refine text…
CAM-Gerlach 1fce505
gh-88355: Fix backslashes in AF_PIPE (#96543)
cousteaulecommandant bb89f15
gh-93738: Documentation C syntax (:c:type:`Py_UNICODE*` -> :c:expr:`P…
AA-Turner 1007409
gh-93738: Documentation C syntax (:c:type:`PyUnicodeObject*` -> :c:ex…
AA-Turner 86e2243
gh-93738: Documentation C syntax (:c:type:`PyBytesObject*` -> :c:expr…
AA-Turner ff43073
gh-93738: Documentation C syntax (:c:type:`PyTupleObject*` -> :c:expr…
AA-Turner b220c7f
gh-93738: Documentation C syntax (:c:type:`PyInterpreterState *` -> :…
AA-Turner 82b2939
gh-93738: Documentation C syntax (:c:type:`PyObject` -> :c:expr:`PyOb…
AA-Turner c9733b2
gh-95913: Copyedit/improve Other Language Changes What's New section …
CAM-Gerlach 9455377
gh-93738: Documentation C syntax (:c:data:`view->obj` -> :c:expr:`vie…
AA-Turner 3d795a2
gh-93738: Documentation C syntax (Use `c:struct`) (#97772)
AA-Turner cb408c6
gh-93738: Documentation C syntax (:c:type:`TYPE` -> :c:expr:`TYPE`) (…
AA-Turner dde4563
gh-93738: Documentation C syntax (:c:type:`FILE` -> :c:expr:`FILE`) (…
AA-Turner 3a134eb
gh-93738: Documentation C syntax (:c:type: to :c:expr:, misc. cases) …
AA-Turner 556e861
gh-95913: Copyedit/improve Implementation Changes What's New section …
CAM-Gerlach 2eeacb0
gh-97837: Change deprecation warning message in `unittest` (#97838)
sobolevn 76e702c
GH-97779: Ensure that *all* frame objects are backed by "complete" fr…
brandtbucher 0ba4aad
GH-91079: Decouple C stack overflow checks from Python recursion chec…
markshannon 86eb98c
gh-97654: Add auto exception chaining example to tutorial (#97703)
smheidrich 83e1296
Add re.VERBOSE flag documentation example (#97678)
athos-ribeiro 2efca9c
gh-97825: fix AttributeError when calling subprocess.check_output(inp…
akulakov ae65da9
gh-93738: Documentation C syntax (:c:type:`PyTypeObject*` -> :c:expr:…
AA-Turner 681e059
GH-96704: Add {Task,Handle}.get_context(), use it in call_exception_h…
gvanrossum 6d4d702
gh-87092: bring compiler code closer to a preprocessing-opt-assembler…
iritkatriel 45b9d05
gh-97661: Improve accuracy of sqlite3.Cursor.fetchone docs (#97662)
jiajunjie c34bef6
gh-74696: Pass root_dir to custom archivers which support it (GH-94251)
serhiy-storchaka 18523e0
gh-97758: Fix a crash in getpath_joinpath() called without arguments …
serhiy-storchaka a47df46
gh-95196: Disable incorrect pickling of the C implemented classmethod…
serhiy-storchaka 2d20cf5
gh-93357: Port test cases to IsolatedAsyncioTestCase, part 2 (#97896)
arhadthedev d1e99a7
gh-93738: Documentation C syntax (Function glob patterns -> literal m…
AA-Turner 129a24f
gh-88050: Fix asyncio subprocess to kill process cleanly when process…
kumaraditya303 4017dd4
GH-95172 Make the same version `versionadded` oneline (#95172)
180909 6eee387
build(deps): bump actions/stale from 5 to 6 (#97701)
dependabot[bot] f200192
gh-91539: improve performance of get_proxies_environment (#91566)
eendebakpt 5da6c6b
gh-93738: Documentation C syntax (:c:type:<C type> -> :c:expr:<C type…
AA-Turner 9a24191
I changed my surname early this year (#96671)
tshepang 0aca8ca
gh-97850: Remove all known instances of module_repr() (#97876)
warsaw 0ee0e4d
docs(typing): add "see PEP 675" to LiteralString (#97926)
simon04 351eda5
gh-65961: Raise `DeprecationWarning` when `__package__` differs from …
brettcannon adc2815
gh-96865: [Enum] fix Flag to use CONFORM boundary (GH-97528)
ethanfurman 3335bbe
GH-88968: Add notes about socket ownership transfers (#97936)
gvanrossum 97a27d8
gh-95691: Doc BufferedWriter and BufferedReader (#95703)
180909 a857648
gh-94808: Cover `PyObject_PyBytes` case with custom `__bytes__` metho…
sobolevn d458682
gh-94808: Cover `PyUnicode_Count` in CAPI (#96929)
sobolevn 79fbf97
gh-97897: Prevent os.mkfifo and os.mknod segfaults with macOS 13 SDK …
ned-deily a0d2d63
gh-95986: Fix the example using match keyword (#95989)
180909 32cc2fd
gh-93738: Disallow pre-v3 syntax in the C domain (#97962)
AA-Turner bf234f5
GH-88050: fix race in closing subprocess pipe in asyncio (#97951)
kumaraditya303 af96109
gh-94808: Coverage: Test that maximum indentation level is handled (#…
mdboom 149ed7f
gh-97943: PyFunction_GetAnnotations should return a borrowed referenc…
larryhastings ff0d621
gh-86482: Document assignment expression need for ()s (#23291)
terryjreedy d68c84f
gh-97781: Apply changes from importlib_metadata 5. (GH-97785)
jaraco aa83510
Add Pynche's move to the What's new in 3.11 (#97974)
warsaw a9a8f2e
gh-94590: add signatures to operator itemgetter, attrgetter, methodca…
eriknw 22986c3
Docs: pin sphinx-lint (GH-97992)
hugovk 9ffbad4
gh-97850: Remove the open issues section from the import reference (#…
brettcannon 7064cd5
gh-65961: Do not rely solely on `__cached__` (GH-97990)
brettcannon 0c970bd
fixes gh-96078: os.sched_yield release the GIL while calling sched_yi…
corona10 078e3ed
gh-97973: Return all necessary information from the tokenizer (GH-97984)
lysnikolaou cea3697
GH-97002: Prevent `_PyInterpreterFrame`s from backing more than one `…
brandtbucher f8e002a
bpo-38693: Use f-strings instead of str.format() within importlib (#1…
gpshead 4f9e610
GH-91052: Add C API for watching dictionaries (GH-31787)
carljm 395bc9a
bpo-35540 dataclasses.asdict now supports defaultdict fields (gh-32056)
kwsp 7eba427
GH-90985: Revert "Deprecate passing a message into cancel()" (#97999)
gvanrossum e896751
Remove extra spaces in custom openSSL documentation. (#93568)
xiaochen7 04a52de
gh-97850: Remove deprecated functions from `importlib.utils` (#97898)
sobolevn bd03a34
Docs: Fix backtick errors found by sphinx-lint (#97998)
hugovk c729f2d
gh-82874: Convert remaining importlib format uses to f-str. (#98005)
gpshead 2781fef
gh-86298: Ensure that __loader__ and __spec__.loader agree in warning…
warsaw 6fe8abf
Doc: sphinx-lint finds two other default roles. (GH-98019)
JulienPalard a1ce6e1
Misc updates to the itertools recipes and tests (GH-98018)
rhettinger 81021bf
gh-71316: Update dis documentation to include changes to jump argumen…
Christopher-Chianelli e75ada1
gh-97983: Revert "Lay the foundation for further work in asyncio.test…
arhadthedev 43583f7
Fix memory leaks in test_capi (#98017)
carljm f100fc3
gh-94808: Cover `%p` in `PyUnicode_FromFormat` (#96677)
sobolevn 603deaf
Add note on capture_output arg to subprocess.run() docstring (#98012)
akulakov bdf0373
Add more syslog tests (GH-97953)
serhiy-storchaka 72bd5b9
gh-96415: Remove `types._cell_factory` from a module namespace (#96416)
sobolevn 5df9a71
gh-64373: Convert `_functools` to Argument Clinic (#96640)
sobolevn ca73c85
Fix a mistake in isSet() deprecated message doc (#95720)
marcmonfort 4ca8bb0
Make _symtable_entry.ste_type's comment consistent wit _Py_block_ty (…
zikcheng 97ae2e9
gh-97669: Move difflib examples to Doc/includes/ (#97964)
vstinner 1096dfa
gh-97955: Migrate `zoneinfo` to Argument Clinic (#97958)
sobolevn e7d9ea0
gh-64921: Clarify wording for open()'s newline arg (#96171)
slateny bd66031
gh-65496: Correct wording on csv's skipinitialspace argument (#96170)
slateny 41b8172
GH-96073: Fix wild replacement in inspect.formatannotation (#96074)
iyume 358e173
Add a warning message about PyOS_snprintf (#95993)
eric-wieser af02efe
gh-96959: Update HTTP links which are redirected to HTTPS (#98039)
180909 8bc17aa
gh-97956: Mention `generate_global_objects.py` in `AC How-To` (#97957)
sobolevn dc2c10e
gh-97923: Always run Ubuntu SSL tests with others in CI (#97940)
sobolevn 9f5d3b5
gh-97646: Change `.js` and `.mjs` files mimetype to conform to RFC 92…
noamcohen97 0085f20
gh-73196: Add namespace/scope clarification for inheritance section (…
slateny 53bc2db
gh-96265: Fix some formatting in faq/design.rst (#96924)
slateny 8ce4739
gh-91708: Revert params note in urllib.parse.urlparse table (#96699)
slateny 053555c
gh-96346: Use double caching for re._compile() (#96347)
serhiy-storchaka 8cb415b
GH-88968: Reject socket that is already used as a transport (#98010)
gvanrossum 7a39f02
gh-97997: Add col_offset field to tokenizer and use that for AST node…
lysnikolaou ca135e1
gh-92886: [clinic.py] raise exception on invalid input instead of ass…
iritkatriel 875841a
gh-96073: fix backticks in NEWS entry (GH-98056)
JelleZijlstra b9bd94d
gh-96288: Add a sentence to `os.mkdir`'s docstring. (#96271)
hagai-helman 864f9b9
gh-61105: Add default param, note on using cookiejar subclass (#95427)
slateny c437b6e
GH-83901: Improve Signature.bind error message for missing keyword-on…
RazerM 5a71338
gh-90085: Remove vestigial -t and -c timeit options (#94941)
hauntsaninja 6444029
gh-94808: Fix regex on exotic platforms (#98036)
JelleZijlstra c42b931
gh-57179: Add note on symlinks for os.walk (#94799)
slateny dcbae74
gh-92886: make test_coroutines pass with -O (assertions off) (GH-98060)
iritkatriel efbc783
gh-92886: make test_ast pass with -O (assertions off) (GH-98058)
iritkatriel 6cd489d
GH-94182: Run the PidfdChildWatcher on the running loop (#94184)
graingert 776f894
GH-98023: Change default child watcher to PidfdChildWatcher on suppor…
kumaraditya303 7bba92f
gh-91052: Add PyDict_Unwatch for unwatching a dictionary (#98055)
carljm e20a190
gh-97822: Fix http.server documentation reference to test() function …
Jason-Y-Z 98b3aca
[doc] Fix broken links to C extensions accelerating stdlib modules (#…
partev 637bf6a
gh-97913 Docs: Add walrus operator to the index (#97921)
hugovk d2abe90
Add `@ezio-melotti` as codeowner for `.github/`. (#98079)
ezio-melotti b31a5ee
GitHub Workflows security hardening (#96492)
sashashura 65d36ba
gh-97922: Run the GC only on eval breaker (#97920)
pablogsal 423a627
gh-68686: Retire eptag ptag scripts (#98064)
nanjekyejoannah 1e204b1
gh-95011: Migrate syslog module to Argument Clinic (GH-95012)
noamcohen97 5723b53
Auto-cancel old builds when new commit pushed to branch (#98009)
hugovk 8e3d19b
GH-94597: deprecate `SafeChildWatcher`, `FastChildWatcher` and `Multi…
kumaraditya303 095c522
Fix link to Lifecycle of a Pull Request in CONTRIBUTING (#98102)
jacobtylerwalls cef2db9
Minor edits to the Descriptor HowTo Guide (GH-24901)
geryogam 4165f80
gh-97841: Add methoddef for _filters_mutated (gh-98115)
corona10 69c00cc
Update whatsnew instructions for GitHub (#98124)
carljm 4bc4dfd
gh-56133: copyreg docs: Clarify function/constructor parameter (#95497)
slateny a15d14e
Fix types in buffer/memoryview docs (#98118)
da-woods ad713e8
gh-98083: Fix URLs in `README.rst` (#98082)
kwsp a87db59
bpo-43564: preserve original exception in args of FTP URLError (#24938)
carljm 096a6a0
doc: remove a misleading statement. (GH-98093)
JulienPalard 70a2708
gh-83940: os docs: Improve wording for getenv/getenvb (#98113)
slateny fbfd13b
gh-94808: Add coverage for bytesarray_setitem (#95802)
mdboom 6d91946
gh-96821: Fix undefined behaviour in `audioop.c` (#96923)
matthiasgoergens 9eda93b
Rename function event callbacks to match dict watchers
mpage ebf15c7
Support multiple watchers
mpage f357352
Rename function events to match naming scheme used by dict watchers
mpage 08877e6
Add documentation for the new C-API functions and types
mpage File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import unittest | ||
from _testcapi import ( | ||
PYFUNC_EVENT_CREATED, | ||
PYFUNC_EVENT_DESTROY, | ||
PYFUNC_EVENT_MODIFY_CODE, | ||
PYFUNC_EVENT_MODIFY_DEFAULTS, | ||
PYFUNC_EVENT_MODIFY_KWDEFAULTS, | ||
restore_func_event_callback, | ||
set_func_event_callback, | ||
) | ||
|
||
|
||
class FuncEventsTest(unittest.TestCase): | ||
def test_func_events_dispatched(self): | ||
event = None | ||
def handle_func_event(*args): | ||
nonlocal event | ||
event = args | ||
set_func_event_callback(handle_func_event) | ||
|
||
try: | ||
def myfunc(): | ||
pass | ||
self.assertEqual(event, (PYFUNC_EVENT_CREATED, myfunc, None)) | ||
myfunc_id = id(myfunc) | ||
|
||
new_code = self.test_func_events_dispatched.__code__ | ||
myfunc.__code__ = new_code | ||
self.assertEqual(event, (PYFUNC_EVENT_MODIFY_CODE, myfunc, new_code)) | ||
|
||
new_defaults = (123,) | ||
myfunc.__defaults__ = new_defaults | ||
self.assertEqual(event, (PYFUNC_EVENT_MODIFY_DEFAULTS, myfunc, new_defaults)) | ||
|
||
new_kwdefaults = {"self": 123} | ||
myfunc.__kwdefaults__ = new_kwdefaults | ||
self.assertEqual(event, (PYFUNC_EVENT_MODIFY_KWDEFAULTS, myfunc, new_kwdefaults)) | ||
|
||
# Clear event's reference to func | ||
event = None | ||
del myfunc | ||
self.assertEqual(event, (PYFUNC_EVENT_DESTROY, myfunc_id, None)) | ||
finally: | ||
restore_func_event_callback() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
#include "parts.h" | ||
|
||
static PyObject *pyfunc_callback = NULL; | ||
static PyFunction_EventCallback orig_callback = NULL; | ||
|
||
static void | ||
call_pyfunc_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) | ||
{ | ||
PyObject *event_obj = PyLong_FromLong(event); | ||
if (event_obj == NULL) { | ||
PyErr_Clear(); | ||
return; | ||
} | ||
if (new_value == NULL) { | ||
new_value = Py_None; | ||
} | ||
Py_INCREF(new_value); | ||
/* Don't expose a function that's about to be destroyed to managed code */ | ||
PyObject *func_or_id = (PyObject *) func; | ||
if (event == PYFUNC_EVENT_DESTROY) { | ||
func_or_id = PyLong_FromLong((long) func); | ||
if (func_or_id == NULL) { | ||
Py_DECREF(new_value); | ||
Py_DECREF(event_obj); | ||
return; | ||
} | ||
} else { | ||
Py_INCREF(func); | ||
} | ||
PyObject *stack[] = {event_obj, func_or_id, new_value}; | ||
PyObject_Vectorcall(pyfunc_callback, stack, 3, NULL); | ||
mpage marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Py_DECREF(new_value); | ||
Py_DECREF(event_obj); | ||
Py_DECREF(func_or_id); | ||
} | ||
|
||
static int | ||
add_event(PyObject *module, const char *name, PyFunction_Event event) | ||
{ | ||
PyObject *value = PyLong_FromLong(event); | ||
if (value == NULL) { | ||
return -1; | ||
} | ||
int ok = PyModule_AddObjectRef(module, name, value); | ||
Py_DECREF(value); | ||
return ok; | ||
} | ||
|
||
static PyObject * | ||
set_func_event_callback(PyObject *self, PyObject *func) | ||
{ | ||
if (!PyFunction_Check(func)) { | ||
PyErr_SetString(PyExc_TypeError, "'func' must be a function"); | ||
return NULL; | ||
} | ||
if (pyfunc_callback != NULL) { | ||
PyErr_SetString(PyExc_RuntimeError, "already set callback"); | ||
return NULL; | ||
} | ||
Py_INCREF(func); | ||
pyfunc_callback = func; | ||
orig_callback = PyFunction_GetEventCallback(); | ||
PyFunction_SetEventCallback(call_pyfunc_callback); | ||
Py_RETURN_NONE; | ||
} | ||
|
||
static PyObject * | ||
restore_func_event_callback() { | ||
if (pyfunc_callback == NULL) { | ||
PyErr_SetString(PyExc_RuntimeError, "nothing to restore"); | ||
return NULL; | ||
} | ||
PyFunction_SetEventCallback(orig_callback); | ||
orig_callback = NULL; | ||
Py_CLEAR(pyfunc_callback); | ||
Py_RETURN_NONE; | ||
} | ||
|
||
static PyMethodDef TestMethods[] = { | ||
{"set_func_event_callback", set_func_event_callback, METH_O}, | ||
{"restore_func_event_callback", restore_func_event_callback, METH_NOARGS}, | ||
{NULL}, | ||
}; | ||
|
||
int | ||
_PyTestCapi_Init_FuncEvents(PyObject *m) { | ||
if (PyModule_AddFunctions(m, TestMethods) < 0) { | ||
return -1; | ||
} | ||
|
||
/* Expose each event as an attribute on the module */ | ||
#define ADD_EVENT(event) \ | ||
if (add_event(m, "PYFUNC_EVENT_" #event, PYFUNC_EVENT_##event)) { \ | ||
return -1; \ | ||
} | ||
FOREACH_FUNC_EVENT(ADD_EVENT); | ||
#undef ADD_EVENT | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we are going to want to support multiple watchers (up to 8, to match dict and type watchers), which means the API here should look like
int PyFunction_AddWatcher
which gives you back an id 0-7 (or -1 and sets an exception if there are no more watcher IDs available), and alsoPyFunction_ClearWatcher(int watcher_id)
to clear a watcher. Can look at the dict watcher API inInclude/cpython/dictobject.h
andPython/dictobject.c
to make it as parallel as we can. (Obviously with the difference that here we aren't doing per-function watching so there's no equivalent toPyDict_Watch
API.)