summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeroen Demeyer <J.Demeyer@UGent.be>2019-04-02 14:03:42 (GMT)
committerPetr Viktorin <encukou@gmail.com>2019-04-02 14:03:42 (GMT)
commitfcef60f59d04c63b3540b4c4886226098c1bacd1 (patch)
tree67f920ddc3764d2b2b99f869bc8aab513cf38859
parent487b73ab39c80157474821ef9083f51e0846bd62 (diff)
downloadcpython-fcef60f59d04c63b3540b4c4886226098c1bacd1.zip
cpython-fcef60f59d04c63b3540b4c4886226098c1bacd1.tar.gz
cpython-fcef60f59d04c63b3540b4c4886226098c1bacd1.tar.bz2
bpo-33261: guard access to __code__ attribute in inspect (GH-6448)
-rw-r--r--Lib/inspect.py23
-rw-r--r--Lib/test/inspect_fodder.py11
-rw-r--r--Lib/test/test_inspect.py1
-rw-r--r--Misc/NEWS.d/next/Library/2018-04-11-11-41-52.bpo-33291.-xLGf8.rst3
4 files changed, 29 insertions, 9 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 8c398bd..d8475c6 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -168,23 +168,30 @@ def isfunction(object):
__kwdefaults__ dict of keyword only parameters with defaults"""
return isinstance(object, types.FunctionType)
+def _has_code_flag(f, flag):
+ """Return true if ``f`` is a function (or a method or functools.partial
+ wrapper wrapping a function) whose code object has the given ``flag``
+ set in its flags."""
+ while ismethod(f):
+ f = f.__func__
+ f = functools._unwrap_partial(f)
+ if not isfunction(f):
+ return False
+ return bool(f.__code__.co_flags & flag)
+
def isgeneratorfunction(obj):
"""Return true if the object is a user-defined generator function.
Generator function objects provide the same attributes as functions.
See help(isfunction) for a list of attributes."""
- obj = functools._unwrap_partial(obj)
- return bool((isfunction(obj) or ismethod(obj)) and
- obj.__code__.co_flags & CO_GENERATOR)
+ return _has_code_flag(obj, CO_GENERATOR)
def iscoroutinefunction(obj):
"""Return true if the object is a coroutine function.
Coroutine functions are defined with "async def" syntax.
"""
- obj = functools._unwrap_partial(obj)
- return bool(((isfunction(obj) or ismethod(obj)) and
- obj.__code__.co_flags & CO_COROUTINE))
+ return _has_code_flag(obj, CO_COROUTINE)
def isasyncgenfunction(obj):
"""Return true if the object is an asynchronous generator function.
@@ -192,9 +199,7 @@ def isasyncgenfunction(obj):
Asynchronous generator functions are defined with "async def"
syntax and have "yield" expressions in their body.
"""
- obj = functools._unwrap_partial(obj)
- return bool((isfunction(obj) or ismethod(obj)) and
- obj.__code__.co_flags & CO_ASYNC_GENERATOR)
+ return _has_code_flag(obj, CO_ASYNC_GENERATOR)
def isasyncgen(object):
"""Return true if the object is an asynchronous generator."""
diff --git a/Lib/test/inspect_fodder.py b/Lib/test/inspect_fodder.py
index ff3f0e4..6675077 100644
--- a/Lib/test/inspect_fodder.py
+++ b/Lib/test/inspect_fodder.py
@@ -80,3 +80,14 @@ try:
raise Exception()
except:
tb = sys.exc_info()[2]
+
+class Callable:
+ def __call__(self, *args):
+ return args
+
+ def as_method_of(self, obj):
+ from types import MethodType
+ return MethodType(self, obj)
+
+custom_method = Callable().as_method_of(42)
+del Callable
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index bc675aa..7d74746 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -146,6 +146,7 @@ class TestPredicates(IsTestBase):
self.istest(inspect.isfunction, 'mod.spam')
self.istest(inspect.isfunction, 'mod.StupidGit.abuse')
self.istest(inspect.ismethod, 'git.argue')
+ self.istest(inspect.ismethod, 'mod.custom_method')
self.istest(inspect.ismodule, 'mod')
self.istest(inspect.isdatadescriptor, 'collections.defaultdict.default_factory')
self.istest(inspect.isgenerator, '(x for x in range(2))')
diff --git a/Misc/NEWS.d/next/Library/2018-04-11-11-41-52.bpo-33291.-xLGf8.rst b/Misc/NEWS.d/next/Library/2018-04-11-11-41-52.bpo-33291.-xLGf8.rst
new file mode 100644
index 0000000..1ffb9dd
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-04-11-11-41-52.bpo-33291.-xLGf8.rst
@@ -0,0 +1,3 @@
+Do not raise AttributeError when calling the inspect functions
+isgeneratorfunction, iscoroutinefunction, isasyncgenfunction on a method
+created from an arbitrary callable. Instead, return False.