AttributeError:“str”对象没有属性“errno”

Posted

技术标签:

【中文标题】AttributeError:“str”对象没有属性“errno”【英文标题】:AttributeError: 'str' object has no attribute 'errno' 【发布时间】:2019-09-19 23:23:15 【问题描述】:

我在由asyncio 生成的multiprocessing.Queue 中放置了一个ClientConnectionError 异常。我这样做是为了将 asyncio 中生成的异常传递回另一个线程/进程中的客户端。

我的假设是这个异常发生在反序列化过程中,从队列中读取异常。否则看起来几乎不可能到达。

Traceback (most recent call last):
  File "model_neural_simplified.py", line 318, in <module>
    main(**arg_parser())
  File "model_neural_simplified.py", line 314, in main
    globals()[command](**kwargs)
  File "model_neural_simplified.py", line 304, in predict
    next_neural_data, next_sample = reader.get_next_result()
  File "/project_neural_mouse/src/asyncs3/s3reader.py", line 174, in get_next_result
    result = future.result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 432, in result
    return self.__get_result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "model_neural_simplified.py", line 245, in read_sample
    f_bytes = s3f.read(read_size)
  File "/project_neural_mouse/src/asyncs3/s3reader.py", line 374, in read
    size, b = self._issue_request(S3Reader.READ, (self.url, size, self.position))
  File "/project_neural_mouse/src/asyncs3/s3reader.py", line 389, in _issue_request
    response = self.communication_channels[uuid].get()
  File "/usr/lib/python3.6/multiprocessing/queues.py", line 113, in get
    return _ForkingPickler.loads(res)
  File "/usr/local/lib/python3.6/dist-packages/aiohttp/client_exceptions.py", line 133, in __init__
    super().__init__(os_error.errno, os_error.strerror)
AttributeError: 'str' object has no attribute 'errno'

我想问这个问题有点远,但是有人知道这个问题吗?

Python 3.6.8,aiohttp.__version__ == 3.6.0

更新:

我设法重现了这个问题(感谢 cmets 中的 Samuel 改进了最小可重现测试用例,后来在 bugs.python.org 上的 xtreak 将其进一步提炼为仅用于 pickle 的测试用例):

import pickle

ose = OSError(1, 'unittest')

class SubOSError(OSError):

    def __init__(self, foo, os_error):
        super().__init__(os_error.errno, os_error.strerror)

cce = SubOSError(1, ose)
cce_pickled = pickle.dumps(cce)
pickle.loads(cce_pickled)


./python.exe ../backups/bpo38254.py
Traceback (most recent call last):
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/../backups/bpo38254.py", line 12, in <module>
    pickle.loads(cce_pickled)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/../backups/bpo38254.py", line 8, in __init__
    super().__init__(os_error.errno, os_error.strerror)
AttributeError: 'str' object has no attribute 'errno'

参考资料:

https://github.com/aio-libs/aiohttp/issues/4077 https://bugs.python.org/issue38254

【问题讨论】:

把bug报告贴错地方,移到aiohttp:github.com/aio-libs/aiohttp/issues/4077 您可以直接使用pickle 重现该问题:pickle.loads(pickle.dumps(aiohttp.client_exceptions.ClientConnectorError(connection_key, ose))) 为了让您的示例更具代表性,我建议使用正确的 OSError 初始化,例如OSError(1, "Reason string"),其中1errno 在调试时我发现了一些非常奇怪的东西:ClientConnectorError.__init__ 接收errnoOSError 的第一个参数)作为它自己的第一个参数,它被分配给connection_key!而os_error 是原因字符串,而不是OSError 实例本身。 这可能是由于ClientConnectorErrorOSError 的子类和一些pickle 怪癖(甚至是错误!?) 【参考方案1】:

OSError 有a custom __reduce__ implementation;不幸的是,对于与预期参数不匹配的子类,它不是子类友好的。手动调用__reduce__可以看到酸洗的中间状态:

>>> SubOSError.__reduce__(cce)
(modulename.SubOSError, (1, 'unittest'))

tuple 的第一个元素是要调用的可调用对象,第二个是要传递的参数的 tuple。因此,当它尝试重新创建您的课程时,它会:

modulename.SubOSError(1, 'unittest')

丢失了有关您最初创建时使用的 OSError 的信息。

如果您必须接受与 OSError.__reduce__/OSError.__init__ 期望的参数不匹配的参数,您将需要编写自己的 __reduce__ 覆盖以确保正确信息被腌制。一个简单的版本可能是:

class SubOSError(OSError):

    def __init__(self, foo, os_error):
        self.foo = foo  # Must preserve information for pickling later
        super().__init__(os_error.errno, os_error.strerror)

    def __reduce__(self):
        # Pickle as type plus tuple of args expected by type
        return type(self), (self.foo, OSError(*self.args))

采用这种设计,SubOSError.__reduce__(cce) 现在会返回:

(modulename.SubOSError, (1, PermissionError(1, 'unittest')))

其中tuple 的第二个元素是重新创建实例所需的正确参数(从OSErrorPermissionError 的变化是预期的;OSError 实际上返回基于errno 的自己的子类) .

【讨论】:

【参考方案2】:

此问题已于 2019 年 9 月 25 日在aiohttp 中修复并合并到 master。如果我注意到修复程序进入的版本,我将在未来更新此答案 (请随时在将来会注意包含此更新的版本)

修复的 Git 问题:

https://github.com/aio-libs/aiohttp/issues/4077

【讨论】:

以上是关于AttributeError:“str”对象没有属性“errno”的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy“AttributeError:'str'对象没有属性'c'”

AttributeError 'str' 对象没有属性 'path'

Django 2.2 + AttributeError:'str'对象没有属性'decode'

为啥我得到 AttributeError:'str' 对象没有属性 'strftime'?

AttributeError:'str'对象在构建逻辑回归模型时没有属性'decode'[重复]

Django-channels:AttributeError:'str'对象没有属性'profile'