-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Static type checking à la mypy #3893
Comments
I will admit that I'm interested in this and it fits into the vision of what we're trying to do, but it would be irresponsible of me to promise or commit to anything yet :) |
I'm not sure if you are aware, but Pylyzer is a Python type checker that's written in Rust. It might be worth seeing if there's a way to have all the desired features with less effort. |
Oh that's interesting! It uses Erg and isn't quite feature complete, I'm not sure if we could use it but it's great to see another Rust Python tool. |
I don't know much about Rust, but would it be possible for them to expose an API that Ruff could call into? If so, the Ruff and Pylyzer teams could work out an API that Pylyzer exposes and Rust could call into? Even if you don't end up using Pylyzer, knowing the API that Ruff needs would be useful if Ruff were to implement its own type analysis. (Just thinking out loud.) |
Is there any dev started on that feature? It would be so awesome to have all the essential python code quality tools in one fast as hell dependency! |
I'm interested in moving forward on this. I would like to begin working on features that could be exposed to ruff-lsp to add more of the traditional LSP capabilities, and it sounds like getting some type checking/inference working is going to be necessary before something like 'textDocument/hover' can be handled (by ruff-lsp) in a useful way. Some input from the maintainers about implementation would be helpful, if this is still something the core team is interested in. |
Thanks for expressing your interest! Unfortunately the scope of this feature is far too large for external contribution, it will require major architectural changes to Ruff and extensive collaboration with our team. We're moving in this direction though. We can mark any related issues that would be good for external contribution with a "help wanted" label, as I'm sure there will be lots of smaller tasks that arise. We're also recently kicked off a rewrite of |
For those interested, looks like initial work for this is here |
please don't make it like mypy, closer to pyright would be much more tolerable. although considering how based the maintainers are here, i'm confident they would know what they are doing |
Curious if there's a tracking issue for the progression of this feature? Would be great to gain some visibility into this (with the caveat that I certainly don't have any expectations around the arrival date). |
An API that I'd love to see from a future typechecker (I feel this is sorely missing with
Given a module `my.module` like:
class Foo:
def call_db(self) -> int:
return 42
class Bar:
foos: list[Foo]
def no_call_db(f: Any) -> Any:
return f
@no_call_db
def f() -> None:
bar = Bar()
for foo in bar.foos:
print(foo.call_db()) I'd like to be able to use the results of typing-checking to perform further static analysis (or maybe even insane runtime stuff/documentation generation). In this case, I'd like to check there are no calls to The API would look something like this: from ruff.typechecker import ast_extended as ast, types, typecheck, parse_ast_with_types
top_level_types = typecheck(Path("."), use_cache=True)
assert top_level_types == {
"my.module.Foo": types.Class(methods={"call_db": types.Method(...)}),
"my.module.no_call_db": types.Function(args=[types.Any], ret=types.Any),
...,
} More useful would be coupling the above with the AST: node = parse_ast_with_types(Path("my/module.py"), use_cache=True)
assert node == ast.FunctionDef(
name="f",
decorator_list=[
ast.Name(
id="no_call_db",
type=types.TypeRef("my.module.no_call_db"),
)
],
body=[
ast.Assign(
targets=[ast.Name(id="bar")],
value=ast.Call(
func=ast.Name(
id="Bar",
type=types.Type(types.TypeRef("my.module.Bar")),
),
args=[],
type=types.TypeRef("my.module.Bar"),
),
type=types.TypeRef("my.module.Bar"),
),
ast.For(
target=ast.Name(
id="foo",
type=types.TypeRef("my.module.Foo"),
),
iter=ast.Attribute(
value=ast.Name(
id="bar",
type=types.TypeRef("my.module.Bar"),
),
attr="foos",
type=types.List(types.TypeRef("my.module.Foo")),
),
body=[
ast.Expr(
value=ast.Call(
func=ast.Name(id="print"),
args=[
ast.Call(
func=ast.Attribute(
value=ast.Name(id="foo"),
attr="call_db",
type=types.MethodRef("my.module.Foo", "call_db"),
),
args=[],
type=types.None_,
)
],
type=types.TypeRef("builtins.print"),
)
)
],
),
],
) A checker for "no db calls where decorator disallows" is then just: def is_decorated_no_call_db(node: ast.AST) -> TypeIs[ast.FunctionDef]:
return (
isinstance(node, ast.FunctionDef)
and any(d.type == types.TypeRef("my.module.no_call_db") for d in node.decorator_list)
)
def is_call_to_call_db(node: ast.AST) -> TypeIs[ast.Call]:
return (
isinstance(child, ast.Call)
and child.func.type == types.MethodRef("my.module.Foo", "call_db")
)
for node in walk(module):
if is_decorated(node) and if any(is_call_to_call_db(child) for child in walk(node)):
yield Error(...) Reliably achieving this kind of thing right now is really difficult as I can't easily eg. hook into |
Would be amazing if Ruff had static type checking. Mypy is a well known solution in this area as well as pytype, pyre, and others.
As a follow up, we could have some way of auto-fixing type annotations.
Thank you for this incredible project 😍
The text was updated successfully, but these errors were encountered: