【问题标题】:The unbearable opaqueness of time.struct_timetime.struct_time 难以忍受的不透明性
【发布时间】:2018-03-17 14:43:01
【问题描述】:

为什么 pylint 和 IDE 的智能感知功能无法识别 time.struct_time 的实例?以下代码包含对类的存在/不存在属性、命名元组和类命名元组time.struct_time 的一些简单测试。在 pylint、IntelliJ 和 VSCode 中,一切都按预期工作——除了time.struct_time 之外,在每种情况下都会报告对缺失属性的访问——它在任何这些工具中都不会产生警告或错误。为什么他们不知道它是什么以及它的属性是什么?

import time
from collections import namedtuple

t = time.localtime()
e = t.tm_mday
e = t.bad # this is not reported by linters or IDEs. 

class Clz:
    cvar = 'whee'

    def __init__(self):
        self.ivar = 'whaa'

o = Clz()
e = Clz.cvar
e = o.ivar
e = Clz.bad
e = o.bad

Ntup = namedtuple('Ntup', 'thing')
n = Ntup(thing=3)
e = n.thing
e = n.bad

问题的上下文是pipenv中的以下最近的错误 -

# Halloween easter-egg.          
if ((now.tm_mon == 10) and (now.tm_day == 30)) 

显然,pass 路径从未经过测试,但似乎典型的静态分析工具在这里也无济于事。这对于标准库中的类型来说很奇怪。

(可以在https://github.com/kennethreitz/pipenv/commit/033b969d094ba2d80f8ae217c8c604bc40160b03 看到完整的修复)

【问题讨论】:

  • 可能是因为time.struct_time 在 C 中,所以他们无法检查其来源是否有有效属性。
  • 当然,这可能与它有关,但我认为这还不够。这些工具不会被每种 C 实现的类型所混淆。他们对列表等非常满意。
  • 他们需要对time.struct_time 不存在的列表进行特定处理。
  • 再一次,这听起来很合理,但完全是推测性的。为什么不会到位?这些东西不会自动生成吗?那还缺什么?等等
  • 如果您尝试转到提到def localtime(seconds=None): # real signature unknown; restored from __doc__ 的函数定义。因此,如果不知道真正的签名,那么 lint 将如何工作?

标签: python-3.x visual-studio-code pycharm pylint


【解决方案1】:

time.struct_time 是用 C 定义的对象,这意味着它不能被静态自省。自动补全软件可以解析 Python 代码并对哪些类和命名元组支持做出合理的猜测,但它们不能对 C 定义的对象执行此操作。

大多数系统使用的解决方法是生成存根文件;通常通过在运行时内省对象(导入模块并记录找到的属性)。例如,CodeIntel(Komodo IDE 的一部分)使用XML file format called CIX。但是,这更容易出错,因此这样的系统谨慎起见,并且不会明确地将未知属性标记为错误。

如果您使用 Python 3 进行编码,您可以考虑使用 type hinting。对于 C 扩展,您仍然需要存根文件,但社区现在非常擅长维护这些文件。标准库存根文件保存在project called typeshed

您必须在项目中添加类型提示:

#!/usr/bin/env python3
import time
from collections import namedtuple


t: time.struct_time = time.localtime()
e: int = t.tm_mday
e = t.bad  # this is not reported by linters or IDEs.


class Clz:
    cvar: str = 'whee'
    ivar: str

    def __init__(self) -> None:
        self.ivar = 'whaa'


o = Clz()
s = Clz.cvar
s = o.ivar
s = Clz.bad
s = o.bad

Ntup = namedtuple('Ntup', 'thing')
n = Ntup(thing=3)
e = n.thing
e = n.bad

但是flake8 tool 结合flake8-mypy plugin 会检测到错误属性:

$ flake8 test.py
test.py:8:5: T484 "struct_time" has no attribute "bad"
test.py:22:5: T484 "Clz" has no attribute "bad"
test.py:23:5: T484 "Clz" has no attribute "bad"
test.py:28:5: T484 "Ntup" has no attribute "bad"

PyCharm 也建立在这项工作之上,也许可以检测到相同的无效使用。当然是directly supports pyi files

【讨论】:

  • 我认为这在 pylint 的情况下实际上是不准确的,正如其中一位评论者所建议的那样(有点不经意间)。 Pylint 没有得到这些不是因为结构的 C'ness,而是因为它只是不(也不容易)知道很多关于返回类型的开始。它在lensplit 等的返回上失败。它确实知道内置对象的外观,但它不知道某些东西(它自己没有解析)给你一个。 typeshed 声称被 IntelliJ 使用,但不及格 struct_time 但它是进一步挖掘的好指针。
  • @pvg: pylint 没有存根文件。
  • 我明白了,但无论返回是否是 C 结构,pylint 都会出错。它还远远不足以被 C 中定义的东西所难倒。当然,它可以在运行时自省内置函数,我想它会这样做(或者至少是 pre-flgihts)。但同样,这无济于事。另一方面,我确实有机会快速浏览一下 typeshed,它包含这些特定调用和结构的准确存根,因此这是一个值得推荐的调查方向。
  • 不,pylint 不检查返回类型;它还不够复杂。 Flake8,没有 mypy 插件,也不会。
猜你喜欢
  • 1970-01-01
  • 2015-05-10
  • 1970-01-01
  • 2017-08-04
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
  • 2014-01-09
  • 1970-01-01
相关资源
最近更新 更多