python / python/mypy

(Partial) support for dynamically generated types

未关闭
#13,643 4 条评论 3 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

feature
主要语言
Python
星标
20.6k
派生
3.3k
PR 合并指标
PR 指标待抓取

描述

Feature

I don't know about implementation details of mypy, whether it uses an own parser for Python code or could work based on Python's introspection capabilities. However, I think the amount of stuff that mypy could "see" and work with could increase dramatically, if it would actually load a module (thereby running some code that possibly generates some annotations), and only THEN analyze resulting type hints.

Pitch

I am working on a project which heavily uses types (due to heavy use of pydantic), but at the same time is very dynamic.

Example 1:

Sometimes I need something on value level as well as on type level. Currently I use a pattern like that in the module, if I want both mypy to check types and also do some runtime checking to catch misuse:

OpenMode = Literal["r", "r+", "a", "w", "w-", "x"]
_OPEN_MODES = list(get_args(OpenMode))

But there are situations, where going the other way is more natural, i.e. "lift" a value into a type, instead of unpacking it.

Example 1b:

Given a function I wrote and use for dynamically adjusted pydantic models, make_literal,

# evaluates to a TypedDict with a: Literal["foo"] and b: Literal[123]
LiftedValue = make_literal(dict(a="foo", b=123))  

is much more convenient than doing the inverse (having to write out the TypedDict by hand, then unpacking it).

Having this "expansion" work for types defined on value level, so they can be validly understood in following code, would be great.
Of course I understand this is probably impossible to support in an actual annotation, because annotations cannot be assumed to be evaluated in general.

Example 2:

A part of the project "dynamicism" comes from the fact that it is centered around an entry-point based plugin system.

I would like to be able to load some entry points and adjust the __annotations__, so that mypy can pick them up. That would also require that I can tell mypy where the something is coming from, without importing it - because I could basically tell mypy for some entity where the source code lives!

So I would like to have something like that work:

ObjectType = type_from_entrypoint(ep)
my_object: ObjectType = my_fancy_plugin_loader(ep)

or actually:

for p_name, plugin in my_fancy_plugins.items():
  globals()[p_name] =plugin.p_cls
  if TYPE_CHECKING:
     __annotations__[p_name] = Annotated[plugin.p_cls, EntryPoint[plugin.ep]]

So I would simply like that mypy is able to treat classes loaded from entrypoints just like it can make sense of imports - the source code location for entrypoints is easily accessible too! And proper type information is simply one load of the module away.

I think restricting this kind of dynamically added type hinting to "things that automatically are evaluated on module load" would be a natural and good trade-off, because loading a module usually won't run arbitrarily complex or expensive computations, and at the same time it would be immensely powerful.

Now of course this would increase the "risk" of circular imports and affect type checking speed, but sometimes you have to work around that even without using type hints. But I see how this could be an opt-in feature and not something enabled by default.

Or, if this is totally unthinkable for mypy, does anyone know a Python type checker that can do something like that?

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

该 issue 没有指出源文件、测试或实现入口;它提议在类型检查期间评估模块并解析插件入口点。首先与维护者澄清预期的 opt-in 行为和范围,然后找出 mypy 中与类型加载和插件入口点相关的代码。要完成这项工作,需要一份达成共识的设计,以及针对动态生成 annotation 的测试。

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
devtools
Issue 类型
功能
难度
5/5
预计耗时
一周以上
活跃度
停滞
描述清晰度
需要澄清
新手友好度
20/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。