Python mypy类型检查未按预期工作。

huangapple go评论112阅读模式
英文:

Python mypy type checking not working as expected

问题

I'm new to python, and am a huge fan of static type checkers. I have some code that handles file uploads with the Bottle framework. See below.

def transcribe_upload(upload: FileUpload) -> Alternative:
  audio:AudioSource = upload_source(upload)
  ...

def upload_source(upload:FileUpload) -> AudioSource:
  ...

I made a really simple mistake and passed upload.file (the file-like object) to upload_source instead of the entire FileUpload object.

def transcribe_upload(upload: FileUpload) -> Alternative:
  audio:AudioSource = upload_source(upload.file) # This is incorrect!

The typechecker didn't catch it. In fact, it doesn't catch ANY incorrect parameter passing to upload_source:

def transcribe_upload(upload: FileUpload) -> Alternative:
  audio:AudioSource = upload_source(4)           # Why isn't mypy giving me an error?
  audio:AudioSource = upload_source(upload.asdf) # Why isn't mypy giving me an error?

What's going on? I tested some basic functions separately and the typechecker caught when I tried to pass a number to a function that wanted a string, and it worked. What am I missing here?

EDIT

@kojiro suggested that FileUpload is equivalent to Any. I think that may be correct. Here is the source for FileUpload. It's imported like this: from bottle import FileUpload.

If that's the case, why is it letting me use FileUpload as if it were a type? (If I mess up the name, like FileUpld it does give me an error).

More importantly, how do I get real types? I suppose the bottle authors have to add them?

英文:

I'm new to python, and am a huge fan of static type checkers. I have some code that handles file uploads with the Bottle framework. See below.

def transcribe_upload(upload: FileUpload) -> Alternative:
  audio:AudioSource = upload_source(upload)
  ...

def upload_source(upload:FileUpload) -> AudioSource:
  ...

I made a really simple mistake and passed upload.file (the file-like object) to upload_source instead of the entire FileUpload object.

def transcribe_upload(upload: FileUpload) -> Alternative:
  audio:AudioSource = upload_source(upload.file) # This is incorrect!

The typechecker didn't catch it. In fact, it doesn't catch ANY incorrect parameter passing to upload_source:

def transcribe_upload(upload: FileUpload) -> Alternative:
  audio:AudioSource = upload_source(4)           # Why isn't mypy giving me an error?
  audio:AudioSource = upload_source(upload.asdf) # Why isn't mypy giving me an error?

What's going on? I tested some basic functions separately and the typechecker caught when I tried to pass a number to a function that wanted a string, and it worked. What am I missing here?

EDIT

@kojiro suggsted that FileUpload is equivalent to Any. I think that may be correct. Here is the source for FileUpload. It's imported like this: from bottle import FileUpload.

If that's the case, why is it letting me use FileUpload as if it were a type? (If I mess up the name, like FileUpld it does give me an error).

More importantly, how do I get real types? I suppose the bottle authors have to add them?

答案1

得分: 3

bottle不是一个静态类型库(它在bottle.py中没有提供类型注释)。它作为一个单一的bottle.py文件的组织形式(而不是一个)还阻止你自己创建一个py.typed,否则它可以让mypy至少获取到类属性、函数/方法签名和全局变量(即使它们没有任何类型信息)。

获得“真实类型”的唯一方法是由bottle维护者添加它们。假设这不太可能发生,你有几个选择:

  1. 生成一个骨架轮廓(Python存根文件,扩展名.pyi),这将有助于基本代码补全。这将捕捉变量、函数和类的存在,但不会捕捉变量和函数签名的类型信息(因为bottle.py本来就没有)。

    骨架轮廓可以使用mypy附带的stubgen工具在大约一秒钟内生成,如果你已经安装了mypy,应该已经拥有这个工具。

  2. 运行pyannotateMonkeyTypebottle的测试套件上收集运行时类型,并使用这些工具生成一个.pyi。这比仅运行stubgen更准确,但最终质量取决于测试套件,特别是对于像bottle的描述符类(如bottle的DictPropertylazy_attribute),很可能会导致mypy发出许多虚假警告。

  3. 使用pytype的类型推断来生成.pyi。这通常会产生更可用的存根,但代价是一个非常长的推断过程,可能会构建一个依赖图并扫描大部分site-packages,如果bottle有很多复杂的第三方依赖,这种方法往往会失败。(编辑:看起来bottle只依赖于Python标准库,如果是这样,那么pytype的推断速度应该相当快)

你应该最终得到一个单独的bottle.pyi文件,你需要将它移动到与bottle.py相同的目录中。这应该位于你虚拟环境的site-packages中。一旦你完成了2或3,mypy应该能够正确地捕捉到类型错误。

英文:

bottle is not a statically-typed library (it offers no type annotations in bottle.py). Its organisation as a single bottle.py (instead of a package) also prevents you from making a py.typed yourself, which would otherwise allow mypy to pick up at least the class attributes, function/method signatures, and global variables (even if they don't have any typing information).

The only way to get "real types" is for the bottle maintainers to add them. Assuming this isn't going to happen, you have several options:

  1. Generate a skeletal outline (Python stub file, extension .pyi) which will aid in basic code completion. This will catch variable, function, and class existence, but not variable and function signature typing information (simply because bottle.py doesn't have any in the first place).

    The skeletal outline can be generated in about a second using mypy's shipped stubgen tool, which you should already have if you've installed mypy.

  2. Collect runtime types by running pyannotate or MonkeyType on bottle's test suite, and use these tools to generate a .pyi. This is more accurate than just running stubgen, but the final quality is at the mercy of the test suite, and is very likely to result in a lot of false positive warnings by mypy, especially with descriptor classes like bottle's DictProperty and lazy_attribute.

  3. Use pytype's type inference to generate a .pyi. This generally results in more usable stubs, at the cost of an extremely long inference procedure that may end up building a dependency graph and scanning most of your site-packages, and will fail quite often if bottle has a lot of complex third-party dependencies. (EDIT: It looks like bottle only depends on the Python standard library, in which case you should get a fairly fast inference by pytype)

You should end up getting a single bottle.pyi that you must move to the same directory as bottle.py. This should be in the site-packages of your virtual environment. Once you've done 2. or 3., mypy should be able to pick up typing errors properly.

huangapple
  • 本文由 发表于 2023年3月7日 01:15:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75653850.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定