`inspect.getsource` 来自字符串中定义的函数? `s="def f(): return 5"`
`inspect.getsource` from a function defined in a string? `s="def f(): return 5"`
给定一个内联定义的函数,如何让 getsource
提供输出? - 这是为了测试,这是我正在尝试的事情:
from importlib.util import module_from_spec, spec_from_loader
_locals = module_from_spec(
spec_from_loader("helper", loader=None, origin="str") # loader=MemoryInspectLoader
)
exec(
'def f(): return "foo"',
_locals.__dict__,
)
f = getattr(_locals, "f")
setattr(f, "__loader__", MemoryInspectLoader)
根据我的尝试,它看起来像一个 linecache
问题:
from importlib.abc import Loader
class MemoryInspectLoader(Loader):
def get_code(self): raise NotImplementedError()
但从未出现错误。从 getsource(f)
,我刚得到:
In [2]: import inspect
...: inspect.getsource(f)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-3-1348c7a45f75> in <module>
----> 1 inspect.getsource(f)
/usr/lib/python3.8/inspect.py in getsource(object)
983 or code object. The source code is returned as a single string. An
984 OSError is raised if the source code cannot be retrieved."""
--> 985 lines, lnum = getsourcelines(object)
986 return ''.join(lines)
987
/usr/lib/python3.8/inspect.py in getsourcelines(object)
965 raised if the source code cannot be retrieved."""
966 object = unwrap(object)
--> 967 lines, lnum = findsource(object)
968
969 if istraceback(object):
/usr/lib/python3.8/inspect.py in findsource(object)
796 lines = linecache.getlines(file)
797 if not lines:
--> 798 raise OSError('could not get source code')
799
800 if ismodule(object):
OSError: could not get source code
如何让 getsource
在 Python 3.6+ 中使用内联定义的函数?
这是我的解决方案:
import os.path
import sys
import tempfile
from importlib.util import module_from_spec, spec_from_loader
from types import ModuleType
from typing import Any, Callable
class ShowSourceLoader:
def __init__(self, modname: str, source: str) -> None:
self.modname = modname
self.source = source
def get_source(self, modname: str) -> str:
if modname != self.modname:
raise ImportError(modname)
return self.source
def make_function(s: str) -> Callable[..., Any]:
filename = tempfile.mktemp(suffix='.py')
modname = os.path.splitext(os.path.basename(filename))[0]
assert modname not in sys.modules
# our loader is a dummy one which just spits out our source
loader = ShowSourceLoader(modname, s)
spec = spec_from_loader(modname, loader, origin=filename)
module = module_from_spec(spec)
# the code must be compiled so the function's code object has a filename
code = compile(s, mode='exec', filename=filename)
exec(code, module.__dict__)
# inspect.getmodule(...) requires it to be in sys.modules
sys.modules[modname] = module
return module.f
import inspect
func = make_function('def f(): print("hi")')
print(inspect.getsource(func))
输出:
$ python3 t.py
def f(): print("hi")
这里有一些微妙而不幸的地方:
- 它需要在
sys.modules
中注入一些东西(inspect.getsource
总是在那里寻找 inspect.getmodule
)
- 我构建的
__loader__
是假的,如果你正在做任何其他需要正常工作的事情 __loader__
这可能会因此而中断
- 内联记录了其他异常情况
顺便说一句,您最好以其他方式保留原始来源,而不是通过几个全局变量(sys.modules
、linecache
、__loader__
等回旋镖.)
不太确定我是否答对了问题。
但是如果你有以下代码:
class MemoryInspectLoader(Loader):
def get_code(self): raise NotImplementedError()
您可以使用dill提取函数体。
from dill.source import getsource
print(getsource(MemoryInspectLoader.get_code))
将输出:
def get_code(self): raise NotImplementedError()
this SO 答案中也有说明。
Monkey 补丁 linecache.getlines 以使 inspect.getsource() 与来自 exec() 的代码一起工作。
当您查看错误堆栈时,它会停在 inspect.py 中的 findsource() 处。当你查看the code of findsource()时,你会看到一个提示:
# Allow filenames in form of "<something>" to pass through.
# `doctest` monkeypatches `linecache` module to enable
# inspection, so let `linecache.getlines` to be called.
然后,如果您查看 this test function,您就会明白它的含义。您可以临时更改核心 Python 功能之一以满足您的目的。
无论如何,这是解决方案:
import linecache
import inspect
def exec_getsource(code):
getlines = linecache.getlines
def monkey_patch(filename, module_globals=None):
if filename == '<string>':
return code.splitlines(keepends=True)
else:
return getlines(filename, module_globals)
linecache.getlines = monkey_patch
try:
exec(code)
#you can now use inspect.getsource() on the result of exec() here
finally:
linecache.getlines = getlines
给定一个内联定义的函数,如何让 getsource
提供输出? - 这是为了测试,这是我正在尝试的事情:
from importlib.util import module_from_spec, spec_from_loader
_locals = module_from_spec(
spec_from_loader("helper", loader=None, origin="str") # loader=MemoryInspectLoader
)
exec(
'def f(): return "foo"',
_locals.__dict__,
)
f = getattr(_locals, "f")
setattr(f, "__loader__", MemoryInspectLoader)
根据我的尝试,它看起来像一个 linecache
问题:
from importlib.abc import Loader
class MemoryInspectLoader(Loader):
def get_code(self): raise NotImplementedError()
但从未出现错误。从 getsource(f)
,我刚得到:
In [2]: import inspect
...: inspect.getsource(f)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-3-1348c7a45f75> in <module>
----> 1 inspect.getsource(f)
/usr/lib/python3.8/inspect.py in getsource(object)
983 or code object. The source code is returned as a single string. An
984 OSError is raised if the source code cannot be retrieved."""
--> 985 lines, lnum = getsourcelines(object)
986 return ''.join(lines)
987
/usr/lib/python3.8/inspect.py in getsourcelines(object)
965 raised if the source code cannot be retrieved."""
966 object = unwrap(object)
--> 967 lines, lnum = findsource(object)
968
969 if istraceback(object):
/usr/lib/python3.8/inspect.py in findsource(object)
796 lines = linecache.getlines(file)
797 if not lines:
--> 798 raise OSError('could not get source code')
799
800 if ismodule(object):
OSError: could not get source code
如何让 getsource
在 Python 3.6+ 中使用内联定义的函数?
这是我的解决方案:
import os.path
import sys
import tempfile
from importlib.util import module_from_spec, spec_from_loader
from types import ModuleType
from typing import Any, Callable
class ShowSourceLoader:
def __init__(self, modname: str, source: str) -> None:
self.modname = modname
self.source = source
def get_source(self, modname: str) -> str:
if modname != self.modname:
raise ImportError(modname)
return self.source
def make_function(s: str) -> Callable[..., Any]:
filename = tempfile.mktemp(suffix='.py')
modname = os.path.splitext(os.path.basename(filename))[0]
assert modname not in sys.modules
# our loader is a dummy one which just spits out our source
loader = ShowSourceLoader(modname, s)
spec = spec_from_loader(modname, loader, origin=filename)
module = module_from_spec(spec)
# the code must be compiled so the function's code object has a filename
code = compile(s, mode='exec', filename=filename)
exec(code, module.__dict__)
# inspect.getmodule(...) requires it to be in sys.modules
sys.modules[modname] = module
return module.f
import inspect
func = make_function('def f(): print("hi")')
print(inspect.getsource(func))
输出:
$ python3 t.py
def f(): print("hi")
这里有一些微妙而不幸的地方:
- 它需要在
sys.modules
中注入一些东西(inspect.getsource
总是在那里寻找inspect.getmodule
) - 我构建的
__loader__
是假的,如果你正在做任何其他需要正常工作的事情__loader__
这可能会因此而中断 - 内联记录了其他异常情况
顺便说一句,您最好以其他方式保留原始来源,而不是通过几个全局变量(sys.modules
、linecache
、__loader__
等回旋镖.)
不太确定我是否答对了问题。
但是如果你有以下代码:
class MemoryInspectLoader(Loader):
def get_code(self): raise NotImplementedError()
您可以使用dill提取函数体。
from dill.source import getsource
print(getsource(MemoryInspectLoader.get_code))
将输出:
def get_code(self): raise NotImplementedError()
this SO 答案中也有说明。
Monkey 补丁 linecache.getlines 以使 inspect.getsource() 与来自 exec() 的代码一起工作。 当您查看错误堆栈时,它会停在 inspect.py 中的 findsource() 处。当你查看the code of findsource()时,你会看到一个提示:
# Allow filenames in form of "<something>" to pass through.
# `doctest` monkeypatches `linecache` module to enable
# inspection, so let `linecache.getlines` to be called.
然后,如果您查看 this test function,您就会明白它的含义。您可以临时更改核心 Python 功能之一以满足您的目的。
无论如何,这是解决方案:
import linecache
import inspect
def exec_getsource(code):
getlines = linecache.getlines
def monkey_patch(filename, module_globals=None):
if filename == '<string>':
return code.splitlines(keepends=True)
else:
return getlines(filename, module_globals)
linecache.getlines = monkey_patch
try:
exec(code)
#you can now use inspect.getsource() on the result of exec() here
finally:
linecache.getlines = getlines