我正试图找出错误的原因:https://github.com/numba/numba/issues/3027
似乎对于某些较旧的ubuntu安装,编译递归numba函数会使sys.stdout重置。我已验证sys is not being reloaded,而必须是某处正在分配给sys。因此,如果要分配sys名称空间的成员,我想安排一个断点。这可能吗?
重新分配:sys.__setattr__
,__builtins__.setattr
似乎不成功:
import sys
def f(*args, **kw):
print >>sys.stderr, args, kw
raise Exception("Break here") # Never reached
print >>sys.stderr, sys.__setattr__
sys.__setattr__ = f
__builtins__.setattr = f
print >>sys.stderr, sys.__setattr__ #Changed
sys.stdout = 12345 # Should cause exception.
此外,sys名称空间还有一些不可思议的地方:
pprint.pprint(dir(sys)) # No __setattr__ here
print "__setattr__" in dir(sys) # False. Nope
# But this:
print >>sys.stderr, sys.__setattr__ # <method-wrapper '__setattr__' of module object at 0x7f8065b6cbb0>
答案 0 :(得分:2)
__setattr__
是special method:
对于自定义类,只有在对对象的类型(而不是在对象的实例字典中)进行定义的情况下,才能保证对特殊方法的隐式调用可以正常工作。
Python并未在任何地方准确记录哪些特殊方法会跳过实例字典,实际上,这确实取决于每个实现,但是(至少在CPython和PyPy中)__setattr__
直接进入类型。
请注意,PEP 562在Python 3.7上为__getattr__
上的__dir__
和ModuleType
添加了一种特殊情况,但不包括__setattr__
。
因此,分配sys.__setattr__
无效。
这也是'__setattr__' in dir(sys)
为False的原因-就像对于任何非类型的东西一样。 dir
函数不会返回在类中找到的属性(或从基类继承的属性)。如果要检查属性,通常使用hasattr(sys, '__setattr__')
-或更好的方法是尝试访问该属性(因为即使使用自定义__getattribute__
创建的动态属性,该属性也可以使用)。 / p>
此外,这意味着设置断点的位置将为types.ModuleType.__setattr__
(或type(sys).__setattr__
,即同一位置)。但这在CPython中不起作用,因为这是内置类型(实际上只是从object.__setattr__
继承的)上的C函数插槽,而不是Python方法。
有两种传统的解决方法。不能保证它们都与内置模块一起使用。通过快速测试(使用CPython 3.7),第一个有效,但第二个无效。但是请在您自己的Python实现/版本上尝试它们。
首先,您可以创建一个子类:
class HookedModuleType(types.ModuleType):
def __setattr__(self, name, value):
print(f'{self.__name__}.__setattr__({name}, {value})')
return super().__setattr__(name, value)
…,然后重新键入模块:
mymodule.__class__ = HookedModuleType
或者,由于ModuleType
不会覆盖__setattr__
的默认行为,因此它只是从object
继承而来,这意味着它所做的全部都是self.__dict__[name] = value
设置的。因此,您可以编写一个dict
来拦截__setitem__
并获得相同的效果:
class HookedDict(dict):
def __setitem__(self, key, value):
print(f'{self._name}.__setitem__({key}, {value})')
return super().__setitem__(key, value)
mymodule.__dict__ = HookedDict(mymodule.__dict__)
mymodule.__dict__._name = mymodule.__name__
如果这些都不起作用,则必须创建一个稍微复杂一些的类来代理实际的模块对象:
class ModuleProxy(object):
def __init__(self, module):
object.__setattr__(self, '_module', module)
def __getattr__(self, name):
return getattr(self._module, name)
def __delattr__(self, name):
delattr(self._module, name)
def __setattr__(self, name, value):
print(f'{self._module.__name__}.__setattr__({name}, {value})')
setattr(self._module, name, value)
...,然后用该模块的代理替换该模块:
sys = sys.modules['sys'] = ModuleProxy(sys)
这个错误更容易出错,在某些情况下可能会导致一些奇怪的行为,但是它并不依赖于任何非保证的行为,并且似乎可以在CPython 3.7、3.6和2.7和PyPy 5.10 / 3.5和5.10 / 2.7(显然2.7需要将f字符串更改为format
调用)。
答案 1 :(得分:2)
代理或包装器类将起作用:
class DebugModule(object):
def __init__(self, module):
self.__dict__["_module"] = module
def __getattr__(self, name):
return getattr(self._module, name)
def __setattr__(self, name, value):
setattr(self._module, name, value)
print("{}.{} = {}".format(self._module.__name__, name, repr(value)))
用法:
import sys
sys = DebugModule(sys)