-
-
Notifications
You must be signed in to change notification settings - Fork 32.6k
WIP gh-103741 - PEP 713 - Callable modules #103742
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
""" | ||
PEP 713 Callable Module - def __call__() | ||
""" | ||
|
||
def __call__(value: int = 42, *, power: int = 1): | ||
return value ** power |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
""" | ||
PEP 713 Callable Module - __call__ = func | ||
""" | ||
|
||
def func(value: int = 42, *, power: int = 1): | ||
return value ** power | ||
|
||
__call__ = func |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
""" | ||
PEP 713 Callable Module - __call__ = func | ||
""" | ||
|
||
def func(value: int = 42, *, power: int = 1): | ||
return value ** power | ||
|
||
def __getattr__(name: str): | ||
if name == "__call__": | ||
return func | ||
raise AttributeError |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -945,5 +945,48 @@ def test_function_with_many_args(self): | |
self.assertEqual(l['f'](*range(N)), N//2) | ||
|
||
|
||
class TestPEP590(unittest.TestCase): | ||
def test_sys_not_callable(self): | ||
import sys | ||
|
||
self.assertFalse(callable(sys)) | ||
self.assertFalse(hasattr(sys, "__call__")) | ||
self.assertRaisesRegex(TypeError, "Module 'sys' is not callable", sys) | ||
|
||
def test_sys_add_callable(self): | ||
try: | ||
def exponent(value=42, *, power=1): | ||
return value ** power | ||
|
||
sys.__call__ = exponent | ||
|
||
self.assertTrue(callable(sys)) | ||
self.assertTrue(hasattr(sys, "__call__")) | ||
|
||
self.assertEqual(sys(), 42) | ||
self.assertEqual(sys(3), 3) | ||
self.assertEqual(sys(power=2), 1764) | ||
|
||
finally: | ||
if hasattr(sys, "__call__"): | ||
del sys.__call__ | ||
|
||
def test_callable_modules(self): | ||
from . import callable_module_a, callable_module_b, callable_module_c | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer inlining these instead of polluting the |
||
|
||
for name, module in ( | ||
("via def __call__", callable_module_a), | ||
("via __call__ = func", callable_module_b), | ||
("via __getattr__", callable_module_c), | ||
): | ||
with self.subTest(name): | ||
self.assertTrue(callable(module)) | ||
self.assertTrue(hasattr(module, "__call__")) | ||
|
||
self.assertEqual(module(), 42) | ||
self.assertEqual(module(3), 3) | ||
self.assertEqual(module(power=2), 1764) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1668,6 +1668,9 @@ PyCallable_Check(PyObject *x) | |
{ | ||
if (x == NULL) | ||
return 0; | ||
if (PyModule_CheckExact(x)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This excludes subclasses of ModuleType. |
||
return PyModule_Callable(x); | ||
} | ||
return Py_TYPE(x)->tp_call != NULL; | ||
} | ||
|
||
|
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.
This should be in
Include/cpython/*.h
or in an#ifndef Py_LIMITED_API
block.Or, if you want to add it to the limited API, see docs in the devguide.