├── crates ├── zuban_benchmark │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ └── benches │ │ ├── parsa_python.rs │ │ └── zuban_python.rs ├── zuban_python │ ├── tests │ │ ├── integration │ │ │ └── main.rs │ │ ├── mypylike │ │ │ ├── packages │ │ │ │ └── with_star_imports │ │ │ │ │ └── with_star_imports │ │ │ │ │ ├── py.typed │ │ │ │ │ ├── x.py │ │ │ │ │ ├── y.py │ │ │ │ │ ├── z.py │ │ │ │ │ └── __init__.py │ │ │ └── tests │ │ │ │ ├── type-comments.test │ │ │ │ ├── language-server.test │ │ │ │ ├── formatting.test │ │ │ │ ├── async.test │ │ │ │ ├── django.test │ │ │ │ ├── type-guards.test │ │ │ │ ├── unions.test │ │ │ │ ├── references.test │ │ │ │ ├── selection_ranges.test │ │ │ │ └── renames.test │ │ └── blackbox │ │ │ ├── from_jedi_python_files │ │ │ ├── import_tree │ │ │ │ ├── mod2.py │ │ │ │ ├── pkg │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── mod1.py │ │ │ │ │ └── base.py │ │ │ │ ├── rename1.py │ │ │ │ ├── flow_import.py │ │ │ │ ├── globals.py │ │ │ │ ├── mod1.py │ │ │ │ ├── recurse_class2.py │ │ │ │ ├── random.py │ │ │ │ ├── references.py │ │ │ │ ├── rename2.py │ │ │ │ ├── __init__.py │ │ │ │ ├── recurse_class1.py │ │ │ │ ├── classes.py │ │ │ │ └── invisible_pkg.py │ │ │ ├── namespace1 │ │ │ │ └── pkg1 │ │ │ │ │ └── pkg2 │ │ │ │ │ └── mod1.py │ │ │ ├── namespace2 │ │ │ │ └── pkg1 │ │ │ │ │ └── pkg2 │ │ │ │ │ └── mod2.py │ │ │ ├── stub_folder │ │ │ │ ├── stub_only_folder │ │ │ │ │ ├── python_only.py │ │ │ │ │ ├── __init__.pyi │ │ │ │ │ ├── nested_stub_only.pyi │ │ │ │ │ ├── nested_with_stub.py │ │ │ │ │ └── nested_with_stub.pyi │ │ │ │ ├── with_stub_folder │ │ │ │ │ ├── python_only.py │ │ │ │ │ ├── nested_with_stub.py │ │ │ │ │ ├── nested_with_stub.pyi │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── __init__.pyi │ │ │ │ │ └── nested_stub_only.pyi │ │ │ │ ├── stub_only.pyi │ │ │ │ ├── with_stub.pyi │ │ │ │ └── with_stub.py │ │ │ ├── fixture_module.py │ │ │ ├── __init__.py │ │ │ ├── ns_path.py │ │ │ ├── pep0593_annotations.py │ │ │ ├── sys_path.py │ │ │ ├── fstring.py │ │ │ ├── conftest.py │ │ │ ├── complex.py │ │ │ ├── completion.py │ │ │ ├── positional_only_params.py │ │ │ ├── keywords.py │ │ │ ├── context.py │ │ │ ├── pep0484_decorators.py │ │ │ ├── parser.py │ │ │ ├── named_expression.py │ │ │ ├── pep0604.py │ │ │ ├── pep0484_overload.py │ │ │ ├── inheritance.py │ │ │ ├── async_.py │ │ │ ├── lambdas.py │ │ │ ├── pep0526_variables.py │ │ │ ├── recursion.py │ │ │ ├── isinstance.py │ │ │ ├── stubs.py │ │ │ ├── named_param.py │ │ │ ├── dynamic_params.py │ │ │ └── ordering.py │ │ │ ├── python_files │ │ │ ├── dunder_all_file.py │ │ │ ├── dunder_all_file_i.pyi │ │ │ ├── simple_stub.pyi │ │ │ ├── generics.py │ │ │ ├── union.py │ │ │ ├── temp.py │ │ │ ├── instance.py │ │ │ ├── additional_django.py │ │ │ ├── list.py │ │ │ ├── keyword_arguments.py │ │ │ ├── unreachable_no_crash.py │ │ │ ├── closure.py │ │ │ └── callable.py │ │ │ └── LICENSE.txt │ ├── src │ │ ├── type_helpers │ │ │ ├── mod.rs │ │ │ └── bound_method.rs │ │ ├── type_ │ │ │ └── utils.rs │ │ ├── selection_ranges.rs │ │ └── file │ │ │ ├── file_state.rs │ │ │ └── mod.rs │ ├── Cargo.toml │ └── docs │ │ └── ideas.md ├── zuban │ ├── src │ │ ├── bin │ │ │ ├── zmypy.rs │ │ │ ├── zubanls.rs │ │ │ └── zuban.rs │ │ └── lib.rs │ └── Cargo.toml ├── parsa_python │ ├── tests │ │ └── snapshots │ │ │ ├── test_grammar__empty.snap │ │ │ ├── test_grammar__terminal_error_recovery.snap │ │ │ ├── test_grammar__very_simple.snap │ │ │ ├── test_grammar__json.snap │ │ │ ├── test_grammar__nested_call.snap │ │ │ ├── test_grammar__import_name_fails.snap │ │ │ ├── test_grammar__with_items_single_items_parens.snap │ │ │ ├── test_grammar__keyword_recovery.snap │ │ │ ├── test_grammar__match_underscore_with_as.snap │ │ │ ├── test_grammar__string_literals_in_strings.snap │ │ │ ├── test_grammar__import_from_fails.snap │ │ │ ├── test_grammar__operators.snap │ │ │ ├── test_grammar__simple_error_recovery.snap │ │ │ ├── test_grammar__bytes.snap │ │ │ ├── test_grammar__with_items_simple.snap │ │ │ ├── test_grammar__semicolon.snap │ │ │ ├── test_grammar__completion_on_func_call1.snap │ │ │ ├── test_grammar__invalid_syntax.snap │ │ │ ├── test_grammar__del_stmt.snap │ │ │ ├── test_grammar__import_from_normal.snap │ │ │ ├── test_grammar__match_simple.snap │ │ │ ├── test_grammar__assignments_aug.snap │ │ │ ├── test_grammar__terminal_and_nonterminal_error_recovery.snap │ │ │ ├── test_grammar__simple.snap │ │ │ ├── test_grammar__import_names.snap │ │ │ ├── test_grammar__completion_on_func_call2.snap │ │ │ ├── test_grammar__import_from_dotted.snap │ │ │ ├── test_grammar__assignments_annotation.snap │ │ │ ├── test_grammar__calls.snap │ │ │ ├── test_grammar__soft_keyword_underscore.snap │ │ │ ├── test_grammar__dict_literal1.snap │ │ │ ├── test_grammar__lambda_simple.snap │ │ │ ├── test_grammar__assignments_star.snap │ │ │ ├── test_grammar__cls.snap │ │ │ ├── test_grammar__complex_dedents_no_crash.snap │ │ │ ├── test_grammar__assignments_with_call.snap │ │ │ ├── test_grammar__exception_group.snap │ │ │ ├── test_grammar__for_stmt.snap │ │ │ ├── test_grammar__lambda_kwargs.snap │ │ │ ├── test_grammar__assignments_setitem.snap │ │ │ └── test_grammar__comprehension_simple.snap │ └── Cargo.toml ├── licensing │ ├── new_openssl_key.sh │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── scripts │ ├── parso_speed.py │ ├── cpython_parser_speed.py │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── zubanls │ ├── src │ │ ├── lib.rs │ │ ├── panic_hooks.rs │ │ └── notebooks.rs │ └── Cargo.toml ├── parsa │ ├── Cargo.toml │ └── src │ │ └── backtracking.rs ├── utils │ ├── Cargo.toml │ └── src │ │ └── panic_context.rs ├── logging_config │ └── Cargo.toml ├── test_utils │ └── Cargo.toml ├── parsa_python_cst │ ├── Cargo.toml │ └── src │ │ └── bytes.rs ├── vfs │ ├── Cargo.toml │ └── src │ │ ├── glob_abs_path.rs │ │ └── path.rs ├── primer │ └── Cargo.toml ├── config │ └── Cargo.toml └── zmypy │ └── Cargo.toml ├── deploy └── pypi │ ├── zuban │ ├── zuban │ │ ├── third_party │ │ └── __init__.py │ ├── .gitignore │ ├── test-maturin-build.sh │ ├── about.toml │ ├── pre-maturin-build.sh │ ├── README.rst │ └── pyproject.toml │ └── zubanls │ ├── .gitignore │ ├── zubanls │ └── __init__.py │ ├── README.rst │ ├── deploy-master.sh │ └── setup.py ├── .gitignore ├── rustfmt.toml ├── .github ├── workflows │ ├── debug_ci.yml │ ├── debug_wheels.yml │ ├── ci.yml │ ├── release.yml │ └── tests.yml └── pull_request_template.md ├── scripts ├── release.sh ├── ci-test.sh └── install-locally.sh ├── .gitmodules ├── README.md └── Cargo.toml /crates/zuban_benchmark/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /deploy/pypi/zuban/zuban/third_party: -------------------------------------------------------------------------------- 1 | ../../../../third_party -------------------------------------------------------------------------------- /crates/zuban_python/tests/integration/main.rs: -------------------------------------------------------------------------------- 1 | mod signature_tests; 2 | -------------------------------------------------------------------------------- /deploy/pypi/zubanls/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | zubanls.egg-info/ 4 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/packages/with_star_imports/with_star_imports/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/mod2.py: -------------------------------------------------------------------------------- 1 | from . import mod1 as fake 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/namespace1/pkg1/pkg2/mod1.py: -------------------------------------------------------------------------------- 1 | mod1_name = 'mod1' 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/namespace2/pkg1/pkg2/mod2.py: -------------------------------------------------------------------------------- 1 | mod2_name = 'mod2' 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/packages/with_star_imports/with_star_imports/x.py: -------------------------------------------------------------------------------- 1 | from_star_import = 1 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/packages/with_star_imports/with_star_imports/y.py: -------------------------------------------------------------------------------- 1 | from_star_import2 = 1 2 | -------------------------------------------------------------------------------- /crates/zuban/src/bin/zmypy.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | zuban::execute_zuban_executable_with_subcommand("mypy") 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.vscode 3 | # insta creates these files 4 | *.snap.new 5 | # For cargo-about 6 | /licenses.html 7 | -------------------------------------------------------------------------------- /crates/zuban/src/bin/zubanls.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | zuban::execute_zuban_executable_with_subcommand("server") 3 | } 4 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/stub_only_folder/python_only.py: -------------------------------------------------------------------------------- 1 | in_python = '' 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/with_stub_folder/python_only.py: -------------------------------------------------------------------------------- 1 | in_python = '' 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/pkg/__init__.py: -------------------------------------------------------------------------------- 1 | a = list 2 | 3 | from math import * 4 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/stub_only_folder/__init__.pyi: -------------------------------------------------------------------------------- 1 | in_stub_only_folder: int 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/dunder_all_file.py: -------------------------------------------------------------------------------- 1 | __all__ = ('attr_x',) 2 | 3 | attr_x = 1 4 | attr_y = 1 5 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/pkg/mod1.py: -------------------------------------------------------------------------------- 1 | a = 1.0 2 | 3 | from ..random import foobar 4 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/rename1.py: -------------------------------------------------------------------------------- 1 | """ used for renaming tests """ 2 | 3 | abc = 3 4 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/stub_only_folder/nested_stub_only.pyi: -------------------------------------------------------------------------------- 1 | in_stub_only: int 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/dunder_all_file_i.pyi: -------------------------------------------------------------------------------- 1 | __all__ = ('attr_x',) 2 | 3 | attr_x = 1 4 | attr_y = 1 5 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/simple_stub.pyi: -------------------------------------------------------------------------------- 1 | import import_tree 2 | 3 | #? ["import_tree"] 4 | import_tree 5 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/pkg/base.py: -------------------------------------------------------------------------------- 1 | class MyBase: 2 | def f1(self): 3 | pass 4 | -------------------------------------------------------------------------------- /deploy/pypi/zuban/zuban/__init__.py: -------------------------------------------------------------------------------- 1 | raise Exception("You should be using the zubanls executable instead of import this in Python") 2 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/flow_import.py: -------------------------------------------------------------------------------- 1 | if name: 2 | env = 1 3 | else: 4 | env = 2 5 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/stub_only_folder/nested_with_stub.py: -------------------------------------------------------------------------------- 1 | in_python = '' 2 | in_both = '' 3 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/stub_only_folder/nested_with_stub.pyi: -------------------------------------------------------------------------------- 1 | in_stub: int 2 | in_both: float 3 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/with_stub_folder/nested_with_stub.py: -------------------------------------------------------------------------------- 1 | in_python = '' 2 | in_both = '' 3 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/with_stub_folder/nested_with_stub.pyi: -------------------------------------------------------------------------------- 1 | in_stub: int 2 | in_both: float 3 | -------------------------------------------------------------------------------- /deploy/pypi/zuban/.gitignore: -------------------------------------------------------------------------------- 1 | # Typically used with `./build.sh ... develop` 2 | /venv 3 | 4 | # Generated by the build 5 | /licenses.html 6 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/globals.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def something(): 4 | global foo 5 | foo = 3 6 | -------------------------------------------------------------------------------- /deploy/pypi/zubanls/zubanls/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is an alias package for zuban. Just use that instead. 3 | """ 4 | from zuban import * 5 | 6 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/mod1.py: -------------------------------------------------------------------------------- 1 | a = 1 2 | from import_tree.random import a as c 3 | 4 | foobarbaz = 3.0 5 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/with_stub_folder/__init__.py: -------------------------------------------------------------------------------- 1 | in_with_stub_both_folder = 5 2 | in_with_stub_python_folder = 8 3 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/recurse_class2.py: -------------------------------------------------------------------------------- 1 | from . import recurse_class1 2 | 3 | class C(recurse_class1.C): 4 | pass 5 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/with_stub_folder/__init__.pyi: -------------------------------------------------------------------------------- 1 | in_with_stub_both_folder: str 2 | in_with_stub_stub_folder: float 3 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Both options are currently unstable, but we might enable them in the future. 2 | # imports_granularity = "Crate" 3 | # group_imports = "StdExternalCrate" 4 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/random.py: -------------------------------------------------------------------------------- 1 | """ 2 | Here because random is also a builtin module. 3 | """ 4 | a = set 5 | 6 | foobar = 0 7 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/references.py: -------------------------------------------------------------------------------- 1 | from ..usages import usage_definition 2 | 3 | 4 | def x(): 5 | usage_definition() 6 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/rename2.py: -------------------------------------------------------------------------------- 1 | """ used for renaming tests """ 2 | 3 | 4 | from import_tree.rename1 import abc 5 | 6 | abc 7 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/with_stub_folder/nested_stub_only.pyi: -------------------------------------------------------------------------------- 1 | if 1: 2 | in_stub_only: int 3 | else: 4 | in_stub_only: int 5 | -------------------------------------------------------------------------------- /deploy/pypi/zuban/test-maturin-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu -o pipefail -x 3 | 4 | pip install zuban --no-index --find-links dist/ --force-reinstall 5 | zuban --help 6 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/__init__.py: -------------------------------------------------------------------------------- 1 | a = '' 2 | 3 | from . import invisible_pkg 4 | 5 | the_pkg = invisible_pkg 6 | 7 | invisible_pkg = 1 8 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/recurse_class1.py: -------------------------------------------------------------------------------- 1 | from . import recurse_class2 2 | 3 | class C(recurse_class2.C): 4 | def a(self): 5 | pass 6 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/stub_only.pyi: -------------------------------------------------------------------------------- 1 | in_stub_only: int 2 | 3 | 4 | class Foo(Bar): 5 | pass 6 | 7 | 8 | class Bar: 9 | pass 10 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__empty.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-1 6 | Endmarker: 1-1 "" 7 | 8 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/fixture_module.py: -------------------------------------------------------------------------------- 1 | # Exists only for completion/pytest.py 2 | import pytest 3 | 4 | @pytest.fixture 5 | def my_module_fixture(): 6 | return 1.0 7 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/with_stub.pyi: -------------------------------------------------------------------------------- 1 | in_with_stub_both: str 2 | in_with_stub_stub: float 3 | 4 | 5 | def stub_function(x: int, y: float) -> str: 6 | pass 7 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/packages/with_star_imports/with_star_imports/z.py: -------------------------------------------------------------------------------- 1 | # This exists, but should never be imported, because it's considered local for 2 | # other packages. 3 | from_star_import_not_public = 1 4 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/type-comments.test: -------------------------------------------------------------------------------- 1 | [case tuple_without_parentheses] 2 | a, b = 1, "" # type: int, str 3 | reveal_type(a) # N: Revealed type is "int" 4 | reveal_type(b) # N: Revealed type is "str" 5 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/__init__.py: -------------------------------------------------------------------------------- 1 | """ needed for some modules to test against packages. """ 2 | 3 | some_variable = 1 4 | 5 | 6 | from . import imports 7 | #? int() 8 | imports.relative() 9 | -------------------------------------------------------------------------------- /crates/licensing/new_openssl_key.sh: -------------------------------------------------------------------------------- 1 | openssl genpkey -algorithm Ed25519 -text \ 2 | | sed 's/ //g' \ 3 | | rg priv: -A 7 \ 4 | | sed ':a;N;$!ba;s/:\n/:/g' \ 5 | | sed 's/pub:/pub: /' \ 6 | | sed 's/priv:/priv: /' 7 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/generics.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar 2 | 3 | T = TypeVar("T") 4 | 5 | 6 | def foo(y: T) -> T: 7 | ... 8 | 9 | 10 | #? int() 11 | foo(1) 12 | 13 | #? str() 14 | foo("") 15 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/union.py: -------------------------------------------------------------------------------- 1 | if 1: 2 | a = 3 3 | else: 4 | a = "foo" 5 | 6 | #? int() 7 | a 8 | 9 | if bool(): 10 | a = 3 11 | else: 12 | a = "foo" 13 | 14 | #? int() str() 15 | a 16 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stub_folder/with_stub.py: -------------------------------------------------------------------------------- 1 | in_with_stub_both = 5 2 | in_with_stub_python = 8 3 | 4 | 5 | def stub_function(x: float, y): 6 | """ 7 | Python docstring 8 | """ 9 | return 1 10 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/classes.py: -------------------------------------------------------------------------------- 1 | blub = 1 2 | 3 | class Config2(): 4 | pass 5 | 6 | 7 | class BaseClass(): 8 | mode = Config2() 9 | if isinstance(whaat, int): 10 | mode2 = whaat 11 | -------------------------------------------------------------------------------- /deploy/pypi/zuban/about.toml: -------------------------------------------------------------------------------- 1 | accepted = [ 2 | "Apache-2.0", 3 | "MIT", 4 | "ISC", 5 | "BSD-3-Clause", 6 | "CC0-1.0", 7 | "MPL-2.0", 8 | "Unicode-3.0", 9 | "Python-2.0.1", 10 | ] 11 | 12 | private = { ignore = true } 13 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/temp.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | a = "" 4 | 5 | #? str() 6 | a 7 | 8 | #? str() 9 | b = a.upper() 10 | 11 | #? str() 12 | b 13 | 14 | def x(a): 15 | return a 16 | 17 | #? int() 18 | x(1) 19 | #? str() 20 | x("") 21 | -------------------------------------------------------------------------------- /.github/workflows/debug_ci.yml: -------------------------------------------------------------------------------- 1 | name: Debug CI 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | tests: 8 | uses: ./.github/workflows/tests.yml 9 | with: 10 | all_tests: true 11 | debug_ssh_session: true 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/language-server.test: -------------------------------------------------------------------------------- 1 | [case no_errors_in_unreachable] 2 | # flags: --warn-unreachable 3 | 4 | import typing 5 | def foo() -> typing.Never: ... 6 | 7 | foo() 8 | abc = 1 # E: Statement is unreachable 9 | 1() 10 | abc 11 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/instance.py: -------------------------------------------------------------------------------- 1 | class Foo: 2 | def __init__(self, a): 3 | self.a = a 4 | 5 | x = 3 6 | 7 | #? int() 8 | Foo.x 9 | #? 10 | Foo.a 11 | #? int() 12 | Foo().x 13 | #? 14 | Foo().a 15 | #? int() 16 | Foo(1).a 17 | -------------------------------------------------------------------------------- /deploy/pypi/zubanls/README.rst: -------------------------------------------------------------------------------- 1 | ############### 2 | ZubanLS / Zuban 3 | ############### 4 | 5 | This package is an alias for the zuban package. 6 | 7 | Just use `pip install zuban`, this package does nothing, expect having zuban as 8 | a dependency. To start the language server use `zuban server`. 9 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu -o pipefail 3 | 4 | cd "$(dirname "$0")" 5 | 6 | # path is the default level 7 | LEVEL="${1:-patch}" 8 | 9 | cargo install cargo-release --version 0.25.17 10 | cargo release --workspace --tag-prefix '' --no-publish --execute "$LEVEL" 11 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/import_tree/invisible_pkg.py: -------------------------------------------------------------------------------- 1 | """ 2 | It should not be possible to import this pkg except for the import_tree itself, 3 | because it is overwritten there. (It would be possible with a sys.path 4 | modification, though). 5 | """ 6 | 7 | foo = 1.0 8 | -------------------------------------------------------------------------------- /crates/scripts/parso_speed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import parso 3 | from timeit import timeit 4 | 5 | code = open('/home/dave/source/stuff_jedi/quickfix_huge.py').read()*10 6 | 7 | x = parso.parse(code) 8 | print(len(x.children)) 9 | 10 | #print(timeit(lambda: parso.parse(code), number=5)) 11 | -------------------------------------------------------------------------------- /crates/zubanls/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod capabilities; 2 | mod notebooks; 3 | mod notification_handlers; 4 | mod panic_hooks; 5 | mod request_handlers; 6 | mod semantic_tokens; 7 | mod server; 8 | 9 | pub use crate::server::{ 10 | GLOBAL_NOTIFY_EVENT_COUNTER, run_server, run_server_with_custom_connection, 11 | }; 12 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__terminal_error_recovery.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-3 6 | Error(stmt): 1-2 7 | Error(ErrorToken): 1-2 "?" 8 | stmt: 2-3 9 | Newline: 2-3 "\n" 10 | Endmarker: 3-3 "" 11 | 12 | -------------------------------------------------------------------------------- /deploy/pypi/zubanls/deploy-master.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | BASE_DIR=$(dirname $(readlink -f "$0")) 6 | cd $BASE_DIR 7 | 8 | # Package and upload to PyPI 9 | rm -rf dist/ 10 | echo `pwd` 11 | python3 setup.py bdist_wheel 12 | # Maybe do a pip install twine before. 13 | twine upload dist/* 14 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/additional_django.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class CustomField[T1, T2](models.CharField[T1, T2]): 4 | pass 5 | 6 | class BusinessModel(models.Model): 7 | custom_field = CustomField() 8 | 9 | def f(inst: BusinessModel): 10 | #? str() 11 | inst.custom_field 12 | -------------------------------------------------------------------------------- /crates/scripts/cpython_parser_speed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from ast import parse 4 | from timeit import timeit 5 | from parser import suite 6 | 7 | code = open('/home/dave/source/stuff_jedi/quickfix_huge.py').read()*10 8 | 9 | #x = suite(code) 10 | #x = parse(code) 11 | #compile(code, 'foo', 'exec') 12 | #print(timeit(lambda: suite(code), number=5)) 13 | -------------------------------------------------------------------------------- /deploy/pypi/zuban/pre-maturin-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu -o pipefail 3 | 4 | cd "$(dirname "$0")" 5 | 6 | BASE_DIR="../../.." 7 | WORKSPACE_TOML="$BASE_DIR/Cargo.toml" 8 | 9 | # Use --debug to speed up compilation 10 | cargo install --locked --debug cargo-about 11 | cargo about generate -o licenses.html about.hbs --fail --manifest-path "$WORKSPACE_TOML" 12 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/list.py: -------------------------------------------------------------------------------- 1 | #? int() str() 2 | [1,""][0] 3 | #? int() str() 4 | [1,""][1] 5 | #? str() int() 6 | [1,""][x] 7 | 8 | foo: list[int] = "" 9 | 10 | #? list() 11 | foo 12 | 13 | #? int() 14 | foo[0] 15 | 16 | #? int() 17 | foo.pop() 18 | 19 | bar = list([1]) 20 | 21 | #? list() 22 | bar 23 | 24 | foo = list([1, 2]) 25 | #? int() 26 | foo[0] 27 | -------------------------------------------------------------------------------- /deploy/pypi/zuban/README.rst: -------------------------------------------------------------------------------- 1 | ######################### 2 | The Zuban Language Server 3 | ######################### 4 | 5 | A Mypy-compatible Python Language Server and type checker built in Rust. 6 | 7 | More infos: https://zubanls.com 8 | 9 | Docs 10 | ==== 11 | 12 | https://docs.zubanls.com 13 | 14 | License 15 | ======= 16 | 17 | AGPL, https://github.com/zubanls/zuban/blob/master/LICENSE 18 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__very_simple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-2 6 | stmt: 0-2 7 | simple_stmts: 0-2 8 | simple_stmt: 0-1 9 | star_expressions: 0-1 10 | expression: 0-1 11 | atom: 0-1 12 | Number: 0-1 "1" 13 | Newline: 1-2 "\n" 14 | Endmarker: 2-2 "" 15 | 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "typeshed"] 2 | path = third_party/typeshed 3 | url = https://github.com/python/typeshed 4 | [submodule "crates/zuban_python/tests/mypylike/mypy"] 5 | path = crates/zuban_python/tests/mypylike/mypy 6 | url = https://github.com/davidhalter/mypy 7 | [submodule "third_party/django-stubs"] 8 | path = third_party/django-stubs 9 | url = https://github.com/typeddjango/django-stubs 10 | -------------------------------------------------------------------------------- /crates/parsa/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parsa" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | lazy_static.workspace = true 15 | utils.workspace = true 16 | 17 | [dev-dependencies] 18 | regex.workspace = true 19 | -------------------------------------------------------------------------------- /crates/utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "utils" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | fnv.workspace = true 15 | dirs.workspace = true 16 | serde.workspace = true 17 | postcard = { version = "*", features = ["use-std"] } 18 | -------------------------------------------------------------------------------- /crates/logging_config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "logging_config" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | anyhow.workspace = true 15 | tracing.workspace = true 16 | tracing-subscriber.workspace = true 17 | tracing-appender.workspace = true 18 | -------------------------------------------------------------------------------- /crates/test_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_utils" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | vfs.workspace = true 15 | utils.workspace = true 16 | 17 | lazy_static.workspace = true 18 | regex.workspace = true 19 | tracing.workspace = true 20 | -------------------------------------------------------------------------------- /crates/scripts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scripts" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | parsa_python.workspace = true 17 | zuban_python.workspace = true 18 | -------------------------------------------------------------------------------- /crates/licensing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "licensing" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | utils.workspace = true 15 | 16 | anyhow.workspace = true 17 | clap.workspace = true 18 | serde.workspace = true 19 | serde_json.workspace = true 20 | 21 | ed25519-dalek = "*" 22 | -------------------------------------------------------------------------------- /crates/parsa_python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parsa_python" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | parsa.workspace = true 15 | lazy_static.workspace = true 16 | regex.workspace = true 17 | 18 | [dev-dependencies] 19 | insta.workspace = true 20 | utils.workspace = true 21 | -------------------------------------------------------------------------------- /crates/parsa_python_cst/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parsa_python_cst" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | parsa_python.workspace = true 15 | utils.workspace = true 16 | 17 | num-bigint.workspace = true 18 | num-traits.workspace = true 19 | tracing.workspace = true 20 | -------------------------------------------------------------------------------- /crates/vfs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vfs" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | utils.workspace = true 15 | 16 | anyhow.workspace = true 17 | tracing.workspace = true 18 | crossbeam-channel.workspace = true 19 | notify.workspace = true 20 | glob = "*" 21 | same-file = "*" 22 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/ns_path.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | from os.path import dirname 4 | 5 | sys.path.insert(0, os.path.join(dirname(__file__), 'namespace2')) 6 | sys.path.insert(0, os.path.join(dirname(__file__), 'namespace1')) 7 | 8 | #? ['mod1'] 9 | import pkg1.pkg2.mod1 10 | 11 | #? ['mod2'] 12 | import pkg1.pkg2.mod2 13 | 14 | #? ['mod1_name'] 15 | pkg1.pkg2.mod1.mod1_name 16 | 17 | #? ['mod2_name'] 18 | pkg1.pkg2.mod2.mod2_name 19 | -------------------------------------------------------------------------------- /crates/primer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "primer" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | zmypy.workspace = true 15 | test_utils.workspace = true 16 | 17 | clap.workspace = true 18 | logging_config.workspace = true 19 | 20 | [features] 21 | zuban_debug = ["zmypy/zuban_debug"] 22 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/packages/with_star_imports/with_star_imports/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import * 2 | from .x import * 3 | # This import is stupid, because it cycles, but let's keep it in here 4 | from with_star_imports import * 5 | from with_star_imports.y import * 6 | 7 | def no_star_import(): 8 | from .z import * 9 | 10 | class WithStarImportClass: 11 | from .z import * 12 | 13 | # A symbol that is also present in the stdlib 14 | class MIMEMessage: ... 15 | -------------------------------------------------------------------------------- /scripts/ci-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu -o pipefail -x 3 | 4 | # Simply show the current rust version 5 | rustup show 6 | 7 | RUST_BACKTRACE=1 cargo test --locked 8 | 9 | # On Windows pipefail causes problems with the command below, so deactivate it temporarily 10 | set +o pipefail 11 | # Check that the server ends, because no license was found 12 | #(cargo run --bin zubanls 2>&1 || true) | tee >(cat >&2) | grep -Eq 'license.json": (No such file or directory|The system cannot find the path)' 13 | set -o pipefail 14 | -------------------------------------------------------------------------------- /crates/zuban/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zuban" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | logging_config.workspace = true 15 | zmypy.workspace = true 16 | zubanls.workspace = true 17 | 18 | anyhow.workspace = true 19 | clap.workspace = true 20 | tracing.workspace = true 21 | 22 | [features] 23 | zuban_debug = ["zmypy/zuban_debug", "zubanls/zuban_debug"] 24 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/pep0593_annotations.py: -------------------------------------------------------------------------------- 1 | # python >= 3.9 2 | 3 | from typing import Annotated 4 | 5 | # This is just a dummy and very meaningless thing to use with to the Annotated 6 | # type hint 7 | class Foo: 8 | pass 9 | 10 | class A: 11 | pass 12 | 13 | 14 | def annotated_function_params( 15 | basic: Annotated[str, Foo()], 16 | obj: A, 17 | annotated_obj: Annotated[A, Foo()], 18 | ): 19 | #? str() 20 | basic 21 | 22 | #? A() 23 | obj 24 | 25 | #? A() 26 | annotated_obj 27 | -------------------------------------------------------------------------------- /scripts/install-locally.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu -o pipefail 3 | 4 | cd "$(dirname "$0")" 5 | rm -f ../target/wheels/* 6 | cd ../deploy/pypi/zuban 7 | 8 | if [ $# -eq 0 ]; then 9 | echo "Creating a dev build (for release builds pass --release)" 10 | else 11 | echo "Creating a build with maturin build ${@:1}" 12 | fi 13 | 14 | if [ ! -f licenses.html ]; then 15 | bash pre-maturin-build.sh 16 | fi 17 | 18 | maturin build "${@:1}" 19 | 20 | 21 | pip install ../../../target/wheels/zuban-*.whl --force-reinstall --break-system-packages 22 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/keyword_arguments.py: -------------------------------------------------------------------------------- 1 | # Additional tests to the ones in from_jedi_python_files/named_param.py 2 | def foo(abcdef, abcdgg, abcdhh): 3 | pass 4 | 5 | #? 8 ["abcdef=", "abcdgg=", "abcdhh="] 6 | foo(abcd).a = 1 7 | #? 8 ["abcdef=", "abcdgg=", "abcdhh="] 8 | foo(abcd.a = 1 9 | #? 9 [] 10 | foo([abcd]) 11 | #? 14 [] 12 | foo([abcd, abcd]) 13 | #? 14 [] 14 | foo([abcd, abc) 15 | #? 14 [] 16 | foo([abcd, abc 17 | #? 14 [] 18 | foo(abcdef.abc) 19 | #? 14 [] 20 | foo(abcdef.abc) 21 | #? 17 [] 22 | foo(1, abcdef.abc) 23 | #? 17 24 | foo(1, abcdef.abc 25 | -------------------------------------------------------------------------------- /crates/config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | vfs.workspace = true 15 | 16 | anyhow.workspace = true 17 | rust-ini = { version = "0.21.0", features = ["inline-comment"], git = "https://github.com/davidhalter-archive/rust-ini.git", branch="indented-multiline-values" } 18 | regex.workspace = true 19 | tracing.workspace = true 20 | toml_edit.workspace = true 21 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/sys_path.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import os 4 | from os.path import dirname 5 | 6 | sys.path.insert(0, '../../jedi') 7 | sys.path.append(os.path.join(dirname(__file__), 'thirdparty')) 8 | 9 | # modifications, that should fail: 10 | # syntax err 11 | sys.path.append('a' +* '/thirdparty') 12 | 13 | #? ['inference'] 14 | import inference 15 | 16 | #? ['inference_state_function_cache'] 17 | inference.inference_state_fu 18 | 19 | # Those don't work because dirname and abspath are not properly understood. 20 | #? ['jedi_'] 21 | import jedi_ 22 | 23 | #? ['el'] 24 | jedi_.el 25 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/unreachable_no_crash.py: -------------------------------------------------------------------------------- 1 | raise 1 2 | # Everything after this is considered unreachable and should still work 3 | 4 | class Test(): 5 | def __init__(self, testing): 6 | if isinstance(testing, str): 7 | self.testing = testing 8 | else: 9 | self.testing = 10 10 | 11 | def boo(self): 12 | if isinstance(self.testing, str): 13 | # TODO this is wrong, it should only be str. 14 | # jedi-diff: #? str() int() 15 | #? str() 16 | self.testing 17 | #? Test() 18 | self 19 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/fstring.py: -------------------------------------------------------------------------------- 1 | class Foo: 2 | bar = 1 3 | 4 | #? 10 int() 5 | f'{Foo.bar}' 6 | #? 10 ['bar'] 7 | f'{Foo.bar}' 8 | #? 10 int() 9 | Fr'{Foo.bar' 10 | #? 10 ['bar'] 11 | Fr'{Foo.bar' 12 | #? int() 13 | Fr'{Foo.bar 14 | #? ['bar'] 15 | Fr'{Foo.bar 16 | # jedi-diff: #? ['Exception'] 17 | #? ['Exception', 'ExceptionGroup'] 18 | F"{Excepti 19 | 20 | #? 8 Foo 21 | Fr'a{Foo.bar' 22 | #? str() 23 | Fr'sasdf' 24 | 25 | #? 7 str() 26 | Fr'''sasdf''' + '' 27 | 28 | #? ['upper'] 29 | f'xyz'.uppe 30 | 31 | 32 | #? 3 [] 33 | f'f' 34 | 35 | # Github #1248 36 | #? int() 37 | {"foo": 1}[f"foo"] 38 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | - [ ] I () own the content in this Pull Request. Neither my employer 7 | or anyone else has rights to this content. I here by grant to Dave Halter 8 | (the owner of Zuban) a perpetual, worldwide, non-exclusive, no-charge, 9 | royalty-free, irrevocable copyright license to reproduce, prepare derivative 10 | works of, publicly display, publicly perform, sublicense, sell and distribute 11 | my contributions and such derivative works. 12 | -------------------------------------------------------------------------------- /crates/zuban_benchmark/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zuban_benchmark" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dev-dependencies] 14 | criterion = "*" 15 | utils.workspace = true 16 | parsa_python_cst.workspace = true 17 | zuban_python.workspace = true 18 | config.workspace = true 19 | vfs.workspace = true 20 | test_utils.workspace = true 21 | 22 | [[bench]] 23 | name = "parsa_python" 24 | harness = false 25 | 26 | [[bench]] 27 | name = "zuban_python" 28 | harness = false 29 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/formatting.test: -------------------------------------------------------------------------------- 1 | [case format_method_with_type_var] 2 | from typing import TypeVar, Generic 3 | 4 | T = TypeVar("T") 5 | 6 | class Foo(Generic[T]): 7 | def foo(self, x: int, y: T) -> T: ... 8 | def bar(self, x: int, y: list[T]) -> list[T]: ... 9 | 10 | 11 | reveal_type(Foo().foo) # N: Revealed type is "def (x: int, y: Never) -> Never" 12 | reveal_type(Foo[str]().foo) # N: Revealed type is "def (x: int, y: str) -> str" 13 | 14 | reveal_type(Foo().bar) # N: Revealed type is "def (x: int, y: list[Never]) -> list[Never]" 15 | reveal_type(Foo[str]().bar) # N: Revealed type is "def (x: int, y: list[str]) -> list[str]" 16 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__json.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-9 6 | stmt: 0-9 7 | simple_stmts: 0-9 8 | simple_stmt: 0-8 9 | star_expressions: 0-8 10 | expression: 0-8 11 | atom: 0-8 12 | Keyword: 0-1 "{" 13 | dict_content: 1-7 14 | dict_key_value: 1-7 15 | expression: 1-4 16 | atom: 1-4 17 | Name: 1-4 "foo" 18 | Keyword: 4-5 ":" 19 | expression: 6-7 20 | atom: 6-7 21 | Number: 6-7 "1" 22 | Keyword: 7-8 "}" 23 | Newline: 8-9 "\n" 24 | Endmarker: 9-9 "" 25 | 26 | -------------------------------------------------------------------------------- /crates/zmypy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zmypy" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | config.workspace = true 15 | licensing.workspace = true 16 | vfs.workspace = true 17 | zuban_python.workspace = true 18 | 19 | anyhow.workspace = true 20 | clap.workspace = true 21 | colored.workspace = true 22 | tracing.workspace = true 23 | 24 | [dev-dependencies] 25 | test_utils.workspace = true 26 | logging_config.workspace = true 27 | 28 | [features] 29 | zuban_debug = ["zuban_python/zuban_debug"] 30 | -------------------------------------------------------------------------------- /.github/workflows/debug_wheels.yml: -------------------------------------------------------------------------------- 1 | name: Debug wheels 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | wheels: 8 | uses: ./.github/workflows/wheels.yml 9 | secrets: inherit 10 | 11 | list-wheels: 12 | needs: [wheels] 13 | runs-on: ubuntu-24.04 14 | defaults: 15 | run: 16 | shell: bash 17 | working-directory: ${{ github.workspace }} 18 | steps: 19 | - name: "Download Wheels from GitHub Artifacts" 20 | uses: actions/download-artifact@v4 21 | with: 22 | pattern: wheels-* 23 | path: wheels 24 | merge-multiple: true 25 | - name: List all builded wheels 26 | run: 27 | ls -alh wheels/ 28 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/async.test: -------------------------------------------------------------------------------- 1 | [case await_non_awaitable] 2 | class A: 3 | def __await__(self) -> int: ... 4 | async def foo(a: A) -> None: 5 | await a # E: Incompatible types in "await" (actual type "A", expected type "Awaitable[Any]") 6 | 7 | [case await_invalid_generator_type] 8 | class X: 9 | def __await__(self) -> tuple[int]: ... 10 | async def foo() -> None: 11 | await X() # E: Incompatible types in "await" (actual type "X", expected type "Awaitable[Any]") 12 | 13 | [case async_context_should_be_correct] 14 | import asyncio 15 | 16 | def get_sum(number: int) -> int: 17 | return sum(_ for _ in range(number)) 18 | 19 | asyncio.gather(asyncio.to_thread(get_sum, 5)) 20 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__nested_call.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-10 6 | stmt: 1-10 7 | simple_stmts: 1-10 8 | simple_stmt: 1-9 9 | star_expressions: 1-9 10 | expression: 1-9 11 | primary: 1-9 12 | primary: 1-7 13 | atom: 1-4 14 | Name: 1-4 "foo" 15 | Keyword: 4-5 "(" 16 | arguments: 5-6 17 | named_expression: 5-6 18 | expression: 5-6 19 | atom: 5-6 20 | Number: 5-6 "1" 21 | Keyword: 6-7 ")" 22 | Keyword: 7-8 "(" 23 | Keyword: 8-9 ")" 24 | Newline: 9-10 "\n" 25 | Endmarker: 10-10 "" 26 | 27 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/closure.py: -------------------------------------------------------------------------------- 1 | def foo(y): 2 | def bar(): 3 | return y 4 | return bar 5 | 6 | 7 | x = foo(1)() 8 | ##? int() 9 | x 10 | 11 | z = foo("") 12 | ##? str() 13 | z() 14 | 15 | def foo2(y): 16 | def decorator(): 17 | def bar(): 18 | return y 19 | 20 | x = bar 21 | return x 22 | return decorator 23 | 24 | 25 | class Foo: 26 | def __init__(self, a): 27 | self.a = a 28 | 29 | x = 3 30 | 31 | ##? Foo 32 | foo2(Foo)()() 33 | ##? int() 34 | foo2(1)()() 35 | ##? str() 36 | foo2("")()() 37 | 38 | 39 | def executor(cls): 40 | def wrapper(x): 41 | return cls(x) 42 | return wrapper 43 | 44 | ##? Foo() 45 | executor(Foo)(1) 46 | ##? int() 47 | executor(Foo)(1).a 48 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/conftest.py: -------------------------------------------------------------------------------- 1 | # Exists only for completion/pytest.py 2 | 3 | import pytest 4 | 5 | 6 | @pytest.fixture() 7 | def my_other_conftest_fixture(): 8 | return 1.0 9 | 10 | 11 | @pytest.fixture() 12 | def my_conftest_fixture(my_other_conftest_fixture): 13 | return my_other_conftest_fixture 14 | 15 | 16 | def my_not_existing_fixture(): 17 | return 3 # Just a normal function 18 | 19 | 20 | @pytest.fixture() 21 | def inheritance_fixture(): 22 | return '' 23 | 24 | 25 | @pytest.fixture 26 | def capsysbinary(capsysbinary): 27 | #? ['close'] 28 | capsysbinary.clos 29 | return capsysbinary 30 | 31 | 32 | # used when fixtures are defined in multiple files 33 | pytest_plugins = [ 34 | "completion.fixture_module", 35 | ] 36 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/complex.py: -------------------------------------------------------------------------------- 1 | """ Mostly for stupid error reports of @dbrgn. :-) """ 2 | 3 | import time 4 | 5 | class Foo(object): 6 | global time 7 | asdf = time 8 | 9 | def asdfy(): 10 | return Foo 11 | 12 | xorz = getattr(asdfy()(), 'asdf') 13 | # jedi-diff: #? time 14 | #? 15 | xorz 16 | 17 | 18 | 19 | def args_returner(*args): 20 | return args 21 | 22 | 23 | #? tuple() 24 | args_returner(1)[:] 25 | # jedi-diff #? int() 26 | #? 27 | args_returner(1)[:][0] 28 | 29 | 30 | def kwargs_returner(**kwargs): 31 | return kwargs 32 | 33 | 34 | # TODO This is not really correct, needs correction probably at some point, but 35 | # at least it doesn't raise an error. 36 | #? int() 37 | kwargs_returner(a=1)[:] 38 | #? 39 | kwargs_returner(b=1)[:][0] 40 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/completion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Special cases of completions (typically special positions that caused issues 3 | with value parsing. 4 | """ 5 | 6 | def pass_decorator(func): 7 | return func 8 | 9 | 10 | def x(): 11 | return ( 12 | 1, 13 | #? ["tuple"] 14 | tuple 15 | ) 16 | 17 | # Comment just somewhere 18 | 19 | 20 | class MyClass: 21 | @pass_decorator 22 | def x(foo, 23 | #? 5 [] 24 | tuple, 25 | ): 26 | return 1 27 | 28 | 29 | if x: 30 | pass 31 | #? ['else'] 32 | else 33 | 34 | try: 35 | pass 36 | #? ['except', 'Exception'] 37 | except 38 | 39 | try: 40 | pass 41 | #? 6 ['except', 'Exception'] 42 | except AttributeError: 43 | pass 44 | #? ['finally'] 45 | finally 46 | 47 | for x in y: 48 | pass 49 | #? ['else'] 50 | else 51 | -------------------------------------------------------------------------------- /crates/zuban/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::process::{Command, exit}; 3 | 4 | pub fn execute_zuban_executable_with_subcommand(mut subcommand: &str) { 5 | // Collect args except the binary name 6 | let mut args: Vec = env::args().skip(1).collect(); 7 | 8 | let mut zuban_path = env::current_exe().expect("failed to get current exe path"); 9 | // replace "zubanls"/"zmypy" with "zuban" 10 | zuban_path.set_file_name("zuban"); 11 | 12 | if args.len() == 1 && (args[0] == "--version" || args[0] == "-V") { 13 | args.clear(); 14 | subcommand = "--version" 15 | } 16 | 17 | // Run "./zuban server " 18 | let status = Command::new(zuban_path) 19 | .arg(subcommand) 20 | .args(args) 21 | .status() 22 | .expect("failed to execute zuban"); 23 | exit(status.code().unwrap_or(1)); 24 | } 25 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/positional_only_params.py: -------------------------------------------------------------------------------- 1 | # python >= 3.8 2 | 3 | def positional_only_call(a, /, b): 4 | #? str() 5 | a 6 | #? int() 7 | b 8 | if UNDEFINED: 9 | return a 10 | else: 11 | return b 12 | 13 | 14 | #? int() str() 15 | positional_only_call('', 1) 16 | 17 | 18 | def positional_only_call2(a, /, b=3): 19 | if UNDEFINED: 20 | return a 21 | else: 22 | return b 23 | 24 | #? int() 25 | positional_only_call2(1) 26 | #? int() 27 | positional_only_call2(SOMETHING_UNDEFINED) 28 | #? str() 29 | positional_only_call2(SOMETHING_UNDEFINED, '') 30 | 31 | # Maybe change this? Because it's actually not correct 32 | # jedi-diff: #? int() str() 33 | #? str() 34 | positional_only_call2(a=1, b='') 35 | # jedi-diff: #? tuple str() 36 | #? str() 37 | positional_only_call2(b='', a=tuple) 38 | -------------------------------------------------------------------------------- /crates/zuban_python/src/type_helpers/mod.rs: -------------------------------------------------------------------------------- 1 | mod bound_method; 2 | mod callable; 3 | mod class; 4 | mod function; 5 | mod instance; 6 | mod overload; 7 | mod typing; 8 | 9 | pub(crate) use bound_method::{BoundMethod, BoundMethodFunction}; 10 | pub(crate) use callable::{Callable, FuncLike}; 11 | pub(crate) use class::{ 12 | Class, ClassExecutionResult, ClassLookupOptions, MroIterator, TypeOrClass, cache_class_name, 13 | check_type_var_variance_validity_for_type, 14 | }; 15 | pub(crate) use function::{ 16 | FirstParamKind, FirstParamProperties, Function, GeneratorType, is_private, 17 | }; 18 | pub(crate) use instance::{ 19 | Instance, InstanceLookupOptions, LookupDetails, execute_isinstance, execute_issubclass, 20 | execute_super, 21 | }; 22 | pub(crate) use overload::{OverloadResult, OverloadedFunction}; 23 | pub(crate) use typing::{execute_assert_type, execute_cast, execute_reveal_type}; 24 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/keywords.py: -------------------------------------------------------------------------------- 1 | 2 | #? ['raise'] 3 | raise 4 | 5 | #? ['Exception'] 6 | except 7 | 8 | #? [] 9 | b + continu 10 | 11 | #? [] 12 | b + continue 13 | 14 | #? ['continue'] 15 | b; continue 16 | 17 | #? ['continue'] 18 | b; continu 19 | 20 | #? [] 21 | c + pass 22 | 23 | #? [] 24 | a + pass 25 | 26 | #? ['pass'] 27 | b; pass 28 | 29 | # ----------------- 30 | # Keywords should not appear everywhere. 31 | # ----------------- 32 | 33 | #? [] 34 | with open() as f 35 | #? [] 36 | def i 37 | #? [] 38 | class i 39 | 40 | #? [] 41 | continue i 42 | 43 | # More syntax details, e.g. while only after newline, but not after semicolon, 44 | # continue also after semicolon 45 | #? ['while'] 46 | while 47 | #? [] 48 | x while 49 | #? [] 50 | x; while 51 | #? ['continue'] 52 | x; continue 53 | 54 | #? [] 55 | and 56 | #? ['and'] 57 | x and 58 | #? [] 59 | x * and 60 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/context.py: -------------------------------------------------------------------------------- 1 | class Base(): 2 | myfoobar = 3 3 | 4 | 5 | class X(Base): 6 | def func(self, foo): 7 | pass 8 | 9 | 10 | class Y(X): 11 | def actual_function(self): 12 | pass 13 | 14 | #? [] 15 | def actual_function 16 | #? ['func'] 17 | def f 18 | 19 | #? ['__doc__'] 20 | __doc__ 21 | #? [] 22 | def __doc__ 23 | 24 | #? [] 25 | def __class__ 26 | #? ['__class__'] 27 | __class__ 28 | 29 | 30 | #? ['__repr__'] 31 | def __repr__ 32 | 33 | #? [] 34 | def mro 35 | 36 | #? ['myfoobar'] 37 | myfoobar 38 | 39 | #? [] 40 | myfoobar 41 | 42 | # ----------------- 43 | # Inheritance 44 | # ----------------- 45 | 46 | class Super(): 47 | enabled = True 48 | if enabled: 49 | yo_dude = 4 50 | 51 | class Sub(Super): 52 | #? ['yo_dude'] 53 | yo_dud 54 | -------------------------------------------------------------------------------- /crates/zuban_python/src/type_/utils.rs: -------------------------------------------------------------------------------- 1 | use super::Instance; 2 | use crate::{ 3 | arguments::Args, 4 | inference_state::InferenceState, 5 | inferred::Inferred, 6 | matching::{OnTypeError, ResultContext}, 7 | }; 8 | 9 | pub(crate) fn method_with_fallback<'db, 'x, T>( 10 | i_s: &InferenceState<'db, '_>, 11 | args: &dyn Args<'db>, 12 | result_context: &mut ResultContext, 13 | on_type_error: OnTypeError, 14 | td: T, 15 | name: &str, 16 | handler: fn(i_s: &InferenceState<'db, '_>, td: T, args: &dyn Args<'db>) -> Option, 17 | fallback_instance: impl FnOnce() -> Instance<'x>, 18 | ) -> Inferred { 19 | handler(i_s, td, args).unwrap_or_else(|| { 20 | fallback_instance() 21 | .type_lookup(i_s, |issue| args.add_issue(i_s, issue), name) 22 | .into_inferred() 23 | .execute_with_details(i_s, args, result_context, on_type_error) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/pep0484_decorators.py: -------------------------------------------------------------------------------- 1 | """ Pep-0484 type hinted decorators """ 2 | 3 | from typing import Callable 4 | 5 | 6 | def decorator(func): 7 | def wrapper(*a, **k): 8 | return str(func(*a, **k)) 9 | return wrapper 10 | 11 | 12 | def typed_decorator(func: Callable[..., int]) -> Callable[..., str]: 13 | ... 14 | 15 | # Functions 16 | 17 | @decorator 18 | def plain_func() -> int: 19 | return 4 20 | 21 | #? str() 22 | plain_func() 23 | 24 | 25 | @typed_decorator 26 | def typed_func() -> int: 27 | return 4 28 | 29 | #? str() 30 | typed_func() 31 | 32 | 33 | # Methods 34 | 35 | class X: 36 | @decorator 37 | def plain_method(self) -> int: 38 | return 4 39 | 40 | @typed_decorator 41 | def typed_method(self) -> int: 42 | return 4 43 | 44 | inst = X() 45 | 46 | #? str() 47 | inst.plain_method() 48 | 49 | #? str() 50 | inst.typed_method() 51 | -------------------------------------------------------------------------------- /crates/zuban_python/src/selection_ranges.rs: -------------------------------------------------------------------------------- 1 | use crate::{Document, InputPosition, PositionInfos, debug, file::File as _}; 2 | 3 | impl<'project> Document<'project> { 4 | pub fn selection_ranges( 5 | &self, 6 | position: InputPosition, 7 | ) -> anyhow::Result, PositionInfos<'project>)>> 8 | { 9 | let db = &self.project.db; 10 | let file = db.loaded_python_file(self.file_index); 11 | let pos = file.line_column_to_byte(position)?; 12 | let ranges = file.tree.selection_ranges(pos.byte); 13 | debug!( 14 | "Position for selection ranges {}->{position:?}", 15 | file.file_path(db), 16 | ); 17 | Ok(ranges.map(|range| { 18 | ( 19 | file.byte_to_position_infos(db, range.start), 20 | file.byte_to_position_infos(db, range.end), 21 | ) 22 | })) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/zubanls/src/panic_hooks.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::Cell, panic::PanicHookInfo}; 2 | 3 | type PanicHook = Box) + 'static>; 4 | 5 | thread_local! { 6 | static HOOK: Cell> = const { Cell::new(None) }; 7 | } 8 | 9 | pub struct RestorePanicHook(); 10 | 11 | impl Drop for RestorePanicHook { 12 | fn drop(&mut self) { 13 | HOOK.with(|cell| cell.take()); 14 | } 15 | } 16 | 17 | pub fn enter(new_hook: PanicHook) -> RestorePanicHook { 18 | // Remove the previous hook and set a new one for now 19 | let default_hook = std::panic::take_hook(); 20 | std::panic::set_hook(Box::new(move |panic_info| { 21 | HOOK.with(|cell| match cell.take() { 22 | Some(new_hook) => new_hook(panic_info), 23 | None => default_hook(panic_info), 24 | }) 25 | })); 26 | HOOK.with(move |cell| { 27 | cell.set(Some(new_hook)); 28 | }); 29 | RestorePanicHook() 30 | } 31 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Issues with the parser and not the type inference should be part of this file. 3 | """ 4 | 5 | class IndentIssues(): 6 | """ 7 | issue jedi-vim#288 8 | Which is really a fast parser issue. It used to start a new block at the 9 | parentheses, because it had problems with the indentation. 10 | """ 11 | def one_param( 12 | self, 13 | ): 14 | return 1 15 | 16 | def with_param( 17 | self, 18 | y): 19 | return y 20 | 21 | 22 | 23 | #? int() 24 | IndentIssues().one_param() 25 | 26 | #? str() 27 | IndentIssues().with_param('') 28 | 29 | 30 | """ 31 | Just because there's a def keyword, doesn't mean it should not be able to 32 | complete to definition. 33 | """ 34 | definition = 0 35 | #? ['definition'] 36 | str(def 37 | 38 | 39 | # It might be hard to determine the value 40 | class Foo(object): 41 | @property 42 | #? ['str'] 43 | def bar(x=str 44 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/django.test: -------------------------------------------------------------------------------- 1 | [case django_models_nullable] 2 | from django.db import models 3 | class Foo(models.Model): 4 | not_nullable = models.CharField() 5 | nullable = models.CharField(null=True) 6 | 7 | def f(x: Foo): 8 | reveal_type(x.not_nullable) # N: Revealed type is "str" 9 | reveal_type(x.nullable) # N: Revealed type is "str | None" 10 | reveal_type(x.pk) # N: Revealed type is "Any" 11 | 12 | x.not_nullable = 1 13 | x.nullable = 1 14 | 15 | x.not_nullable = '2' 16 | x.nullable = '2' 17 | 18 | x.not_nullable = b'3' # E: Incompatible types in assignment (expression has type "bytes", variable has type "str | int | Combinable") 19 | x.nullable = b'3' # E: Incompatible types in assignment (expression has type "bytes", variable has type "str | int | Combinable | None") 20 | 21 | x.not_nullable = None # E: Incompatible types in assignment (expression has type "None", variable has type "str | int | Combinable") 22 | x.nullable = None 23 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/named_expression.py: -------------------------------------------------------------------------------- 1 | # For assignment expressions / named expressions / walrus operators / whatever 2 | # they are called. 3 | 4 | # python >= 3.8 5 | b = (a:=1, a) 6 | 7 | #? int() 8 | b[0] 9 | #? int() 10 | b[1] 11 | 12 | # Should not fail 13 | b = ('':=1,) 14 | 15 | #? int() 16 | b[0] 17 | 18 | def test_assignments(): 19 | match = '' 20 | #? str() 21 | match 22 | #? 8 int() 23 | if match := 1: 24 | #? int() 25 | match 26 | #? int() 27 | match 28 | 29 | def test_assignments2(): 30 | class Foo: 31 | match = '' 32 | #? str() 33 | Foo.match 34 | # jedi-diff: #? 13 35 | #? 13 str() 36 | if Foo.match := 1: 37 | #? str() 38 | Foo.match 39 | #? str() 40 | Foo.match 41 | 42 | #? str() 43 | y 44 | #? 16 str() 45 | if y := Foo.match: 46 | #? str() 47 | y 48 | #? str() 49 | y 50 | 51 | #? 8 str() 52 | if z := Foo.match: 53 | pass 54 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__import_name_fails.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-30 6 | Error(stmt): 1-12 7 | Error(simple_stmts): 1-12 8 | Error(simple_stmt): 1-12 9 | Error(import_name): 1-12 10 | Keyword: 1-7 "import" 11 | Error(dotted_as_names): 8-12 12 | dotted_as_name: 8-11 13 | name_def: 8-11 14 | Name: 8-11 "foo" 15 | Keyword: 11-12 "," 16 | stmt: 12-13 17 | Newline: 12-13 "\n" 18 | Error(stmt): 13-29 19 | Error(simple_stmts): 13-29 20 | Error(simple_stmt): 13-29 21 | Error(import_name): 13-29 22 | Keyword: 13-19 "import" 23 | Error(dotted_as_names): 20-29 24 | dotted_as_name: 20-23 25 | name_def: 20-23 26 | Name: 20-23 "foo" 27 | Keyword: 23-24 "," 28 | dotted_as_name: 25-28 29 | name_def: 25-28 30 | Name: 25-28 "bar" 31 | Keyword: 28-29 "," 32 | stmt: 29-30 33 | Newline: 29-30 "\n" 34 | Endmarker: 30-30 "" 35 | 36 | -------------------------------------------------------------------------------- /crates/zubanls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zubanls" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | licensing.workspace = true 15 | logging_config.workspace = true 16 | config.workspace = true 17 | vfs.workspace = true 18 | zuban_python.workspace = true 19 | 20 | anyhow.workspace = true 21 | clap.workspace = true 22 | crossbeam-channel.workspace = true 23 | lazy_static.workspace = true 24 | lsp-server = "0.7.7" 25 | lsp-types.workspace = true 26 | notify.workspace = true 27 | rayon.workspace = true 28 | serde.workspace = true 29 | serde_json.workspace = true 30 | tracing.workspace = true 31 | tracing-subscriber.workspace = true 32 | shellexpand.workspace = true 33 | serial_test = "*" 34 | fluent-uri = "0.1.4" 35 | urlencoding = "*" 36 | 37 | [dev-dependencies] 38 | test_utils.workspace = true 39 | 40 | [features] 41 | zuban_debug = ["zuban_python/zuban_debug"] 42 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__with_items_single_items_parens.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 41 4 | expression: tree_to_string(tree) 5 | --- 6 | file: 0-30 7 | stmt: 1-30 8 | with_stmt: 1-30 9 | Keyword: 1-5 "with" 10 | with_items: 6-24 11 | with_item: 6-11 12 | expression: 6-11 13 | atom: 6-11 14 | Keyword: 6-7 "(" 15 | named_expression: 7-10 16 | expression: 7-10 17 | atom: 7-10 18 | Name: 7-10 "foo" 19 | Keyword: 10-11 ")" 20 | Keyword: 11-12 "," 21 | with_item: 13-23 22 | expression: 13-16 23 | atom: 13-16 24 | Name: 13-16 "bar" 25 | Keyword: 17-19 "as" 26 | name_def: 20-23 27 | Name: 20-23 "baz" 28 | Keyword: 23-24 ":" 29 | block: 26-30 30 | simple_stmts: 26-30 31 | simple_stmt: 26-29 32 | star_expressions: 26-29 33 | expression: 26-29 34 | atom: 26-29 35 | Keyword: 26-29 "..." 36 | Newline: 29-30 "\n" 37 | Endmarker: 30-30 "" 38 | -------------------------------------------------------------------------------- /crates/zuban_python/src/file/file_state.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] // TODO remove this 2 | use parsa_python_cst::CodeIndex; 3 | use vfs::{FileIndex, NormalizedPath}; 4 | 5 | use crate::{ 6 | InputPosition, PythonProject, 7 | database::Database, 8 | diagnostics::Diagnostic, 9 | lines::{BytePositionInfos, PositionInfos}, 10 | }; 11 | 12 | pub trait File: std::fmt::Debug { 13 | fn file_index(&self) -> FileIndex; 14 | 15 | fn line_column_to_byte(&self, input: InputPosition) -> anyhow::Result; 16 | fn byte_to_position_infos<'db>( 17 | &'db self, 18 | db: &'db Database, 19 | byte: CodeIndex, 20 | ) -> PositionInfos<'db>; 21 | 22 | fn file_path<'db>(&self, db: &'db Database) -> &'db NormalizedPath { 23 | db.file_path(self.file_index()) 24 | } 25 | fn code(&self) -> &str; 26 | 27 | fn diagnostics<'db>(&'db self, db: &'db Database) -> Box<[Diagnostic<'db>]>; 28 | fn invalidate_full_db(&mut self, project: &PythonProject); 29 | fn has_super_file(&self) -> bool; 30 | } 31 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/type-guards.test: -------------------------------------------------------------------------------- 1 | [case check_guard_type_errors] 2 | from typing_extensions import TypeGuard 3 | 4 | def foo(a: object, b: int) -> TypeGuard[bytes]: pass 5 | 6 | a = object() 7 | 8 | if foo(a, ""): # E: Argument 2 to "foo" has incompatible type "str"; expected "int" 9 | reveal_type(a) # N: Revealed type is "bytes" 10 | 11 | [case check_guard_type_errors_with_overloads] 12 | from typing import overload, Union 13 | from typing_extensions import TypeGuard 14 | 15 | @overload 16 | def foo(a: object, b: int) -> TypeGuard[bytes]: pass 17 | @overload 18 | def foo(a: object, b: bytes) -> TypeGuard[bytes]: pass 19 | def foo(a, b): ... 20 | 21 | a = object() 22 | 23 | if foo(a, ""): # E: No overload variant of "foo" matches argument types "object", "str" \ 24 | # N: Possible overload variants: \ 25 | # N: def foo(a: object, b: int) -> TypeGuard[bytes] \ 26 | # N: def foo(a: object, b: bytes) -> TypeGuard[bytes] 27 | reveal_type(a) # N: Revealed type is "object" 28 | -------------------------------------------------------------------------------- /crates/zuban_benchmark/benches/parsa_python.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group, criterion_main}; 2 | 3 | use parsa_python_cst::Tree; 4 | 5 | fn parse_file(copies: usize) { 6 | let some_code = utils::dedent( 7 | r#" 8 | import os 9 | def x(a, b, c): 10 | try: 11 | return a 12 | except Exception: 13 | return [1, 2, 3, 4, 5, 6] 14 | class C: 15 | x: int 16 | y = 1 # type: int 17 | z: dict[int, list[str]] = {1: ["", r''], 2: [f""" {1} """]} 18 | def f(self, x: list[str], y: int): ... 19 | "#, 20 | ); 21 | assert_eq!(some_code.len(), 262); 22 | Tree::parse(some_code.repeat(copies).into()); 23 | } 24 | 25 | fn bench_parser(c: &mut Criterion) { 26 | c.bench_function("parse big file", |b| b.iter(|| parse_file(1000))); 27 | c.bench_function("parse small file", |b| b.iter(|| parse_file(1))); 28 | } 29 | 30 | // Register the benchmarks 31 | criterion_group!(benches, bench_parser); 32 | criterion_main!(benches); 33 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | workflow_dispatch: 8 | all_tests: 9 | required: true 10 | description: "Run more expensive tests" 11 | default: "full" 12 | type: choice 13 | options: 14 | - "full" 15 | - "partial" 16 | 17 | 18 | concurrency: 19 | group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | cargo-fmt: 24 | name: "cargo fmt" 25 | runs-on: ubuntu-24.04 26 | timeout-minutes: 10 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | submodules: recursive 31 | persist-credentials: false 32 | - name: "Install Rust toolchain" 33 | run: rustup component add rustfmt 34 | - run: cargo fmt --all --check 35 | 36 | tests: 37 | uses: ./.github/workflows/tests.yml 38 | with: 39 | all_tests: ${{ inputs.all_tests == 'full' || false }} 40 | secrets: inherit 41 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: New Release 2 | 3 | # We only want one release process running for each tag. 4 | concurrency: 5 | group: ${{ github.workflow }}-${{ github.ref }} 6 | cancel-in-progress: true 7 | 8 | permissions: 9 | id-token: write 10 | contents: read 11 | 12 | on: 13 | push: 14 | tags: 15 | - 'v[0-9]*' 16 | workflow_dispatch: 17 | 18 | jobs: 19 | tests: 20 | uses: ./.github/workflows/tests.yml 21 | with: 22 | all_tests: true 23 | secrets: inherit 24 | 25 | build: 26 | needs: [tests] 27 | uses: ./.github/workflows/wheels.yml 28 | secrets: inherit 29 | 30 | publish: 31 | needs: [build] 32 | runs-on: ubuntu-24.04 33 | steps: 34 | - name: "Download Wheels from GitHub Artifacts" 35 | uses: actions/download-artifact@v4 36 | with: 37 | pattern: wheels-* 38 | path: wheels 39 | merge-multiple: true 40 | - name: Publish Wheel to PyPI 41 | uses: pypa/gh-action-pypi-publish@release/v1 42 | with: 43 | packages-dir: "wheels/" 44 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__keyword_recovery.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-23 6 | stmt: 1-5 7 | simple_stmts: 1-5 8 | simple_stmt: 1-4 9 | star_expressions: 1-4 10 | expression: 1-4 11 | atom: 1-4 12 | Name: 1-4 "foo" 13 | Newline: 4-5 "\n" 14 | Error(stmt): 5-8 15 | Error(function_def): 5-8 16 | Keyword: 5-8 "def" 17 | stmt: 8-9 18 | Newline: 8-9 "\n" 19 | stmt: 9-13 20 | simple_stmts: 9-13 21 | simple_stmt: 9-12 22 | star_expressions: 9-12 23 | expression: 9-12 24 | atom: 9-12 25 | Name: 9-12 "bar" 26 | Newline: 12-13 "\n" 27 | Error(stmt): 13-17 28 | Error(Keyword): 13-17 "else" 29 | Error(stmt): 17-18 30 | Error(Keyword): 17-18 ":" 31 | stmt: 18-19 32 | Newline: 18-19 "\n" 33 | stmt: 19-23 34 | simple_stmts: 19-23 35 | simple_stmt: 19-22 36 | star_expressions: 19-22 37 | expression: 19-22 38 | atom: 19-22 39 | Name: 19-22 "baz" 40 | Newline: 22-23 "\n" 41 | Endmarker: 23-23 "" 42 | 43 | -------------------------------------------------------------------------------- /crates/zuban_python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zuban_python" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | publish = false 7 | homepage.workspace = true 8 | authors.workspace = true 9 | 10 | [lints] 11 | workspace = true 12 | 13 | [dependencies] 14 | parsa_python_cst.workspace = true 15 | utils.workspace = true 16 | vfs.workspace = true 17 | 18 | anyhow.workspace = true 19 | colored.workspace = true 20 | config.workspace = true 21 | dirs.workspace = true 22 | lazy_static.workspace = true 23 | num-bigint.workspace = true 24 | num-traits.workspace = true 25 | regex.workspace = true 26 | serde.workspace = true 27 | serde_json.workspace = true 28 | tracing.workspace = true 29 | lsp-types.workspace = true 30 | rayon.workspace = true 31 | which = "*" 32 | 33 | [dev-dependencies] 34 | clap.workspace = true 35 | test_utils.workspace = true 36 | logging_config.workspace = true 37 | shlex = "*" 38 | 39 | [features] 40 | zuban_debug = [] 41 | 42 | [[test]] 43 | name = "blackbox" 44 | harness = false 45 | 46 | [[test]] 47 | name = "mypylike" 48 | harness = false 49 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/pep0604.py: -------------------------------------------------------------------------------- 1 | from pep0484_generic_parameters import list_t_to_list_t 2 | 3 | list_of_ints_and_strs: list[int | str] 4 | 5 | # Test that unions are handled 6 | x2 = list_t_to_list_t(list_of_ints_and_strs)[0] 7 | #? int() str() 8 | x2 9 | 10 | for z in list_t_to_list_t(list_of_ints_and_strs): 11 | #? int() str() 12 | z 13 | 14 | 15 | from pep0484_generic_passthroughs import ( 16 | typed_variadic_tuple_generic_passthrough, 17 | ) 18 | 19 | variadic_tuple_str_int: tuple[int | str, ...] 20 | 21 | for m in typed_variadic_tuple_generic_passthrough(variadic_tuple_str_int): 22 | #? str() int() 23 | m 24 | 25 | 26 | def func_returns_byteslike() -> bytes | bytearray: 27 | pass 28 | 29 | #? bytes() bytearray() 30 | func_returns_byteslike() 31 | 32 | 33 | pep604_optional_1: int | str | None 34 | pep604_optional_2: None | bytes 35 | 36 | #? int() str() types.NoneType() 37 | pep604_optional_1 38 | 39 | #? types.NoneType() bytes() 40 | pep604_optional_2 41 | 42 | 43 | pep604_in_str: "int | bytes" 44 | 45 | #? int() bytes() 46 | pep604_in_str 47 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__match_underscore_with_as.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-36 8 | Error(stmt): 1-29 9 | Error(match_stmt): 1-29 10 | Keyword: 1-6 "match" 11 | subject_expr: 7-10 12 | named_expression: 7-10 13 | expression: 7-10 14 | atom: 7-10 15 | Name: 7-10 "foo" 16 | Keyword: 10-11 ":" 17 | Newline: 11-12 "\n" 18 | Indent: 16-16 "" 19 | Error(case_block): 16-29 20 | Keyword: 16-20 "case" 21 | Error(open_sequence_pattern): 21-29 22 | Error(pattern): 21-29 23 | name_def: 21-24 24 | Name: 21-24 "bar" 25 | Keyword: 25-27 "as" 26 | Error(pattern_capture_target): 28-29 27 | Keyword: 28-29 "_" 28 | Error(stmt): 29-30 29 | Error(Keyword): 29-30 ":" 30 | stmt: 31-36 31 | simple_stmts: 31-36 32 | simple_stmt: 31-35 33 | pass_stmt: 31-35 34 | Keyword: 31-35 "pass" 35 | Newline: 35-36 "\n" 36 | Error(stmt): 36-36 37 | Error(Dedent): 36-36 "" 38 | Endmarker: 36-36 "" 39 | -------------------------------------------------------------------------------- /crates/zuban_python/docs/ideas.md: -------------------------------------------------------------------------------- 1 | # Ideas 2 | 3 | ## Maybe document the dunder stuff 4 | 5 | ``` 6 | __package__; __file__; 7 | __doc__; __name__; __qualname__; __module__; __defaults__; __code__; 8 | __globals__; __closure__; __annotations__; __kwdefaults__; 9 | __init__; __new__; __dict__; __class__; __slots__; __weakref__; __self__; __bases__; 10 | __init_subclass__; __mro_entries__; __prepare__; 11 | __instancecheck__; __subclasscheck__; (on type) 12 | __del__ 13 | __class_getitem__ 14 | __get__; __set__; __delete__; __set_name__ 15 | __getitem__; __setitem__; __delitem__; __missing__; 16 | __enter__; __exit__; 17 | __await__; __aiter__; __anext__; __aenter__; __aexit__; 18 | coroutine.send; coroutine.throw; coroutine.close 19 | Expected to match object implementation 20 | __repr__; __str__; __bytes__; __format__; 21 | __contains__; __reversed__; __len__; __length_hint__; __iter__ 22 | __complex__; __int__; __float__; __index__; 23 | __round__; __trunc__; __floor__; __ceil__; 24 | __lt__; __le__; __eq__; __ne__; __gt__; __gt__; __hash__; __bool__; 25 | __getattr__; __getattribute__; __setattr__; __delattr__; __dir__; 26 | ``` 27 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__string_literals_in_strings.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-19 6 | stmt: 1-7 7 | simple_stmts: 1-7 8 | simple_stmt: 1-6 9 | star_expressions: 1-6 10 | expression: 1-6 11 | atom: 1-6 12 | strings: 1-6 13 | String: 1-6 "'\"\"\"'" 14 | Newline: 6-7 "\n" 15 | stmt: 7-13 16 | simple_stmts: 7-13 17 | simple_stmt: 7-12 18 | star_expressions: 7-12 19 | expression: 7-12 20 | atom: 7-12 21 | strings: 7-12 22 | String: 7-12 "\"'''\"" 23 | Newline: 12-13 "\n" 24 | stmt: 13-16 25 | simple_stmts: 13-16 26 | simple_stmt: 13-15 27 | star_expressions: 13-15 28 | expression: 13-15 29 | atom: 13-15 30 | strings: 13-15 31 | String: 13-15 "\"\"" 32 | Newline: 15-16 "\n" 33 | stmt: 16-19 34 | simple_stmts: 16-19 35 | simple_stmt: 16-18 36 | star_expressions: 16-18 37 | expression: 16-18 38 | atom: 16-18 39 | strings: 16-18 40 | String: 16-18 "''" 41 | Newline: 18-19 "\n" 42 | Endmarker: 19-19 "" 43 | 44 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__import_from_fails.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-44 6 | Error(stmt): 1-19 7 | Error(simple_stmts): 1-19 8 | Error(simple_stmt): 1-19 9 | Error(import_from): 1-19 10 | Keyword: 1-5 "from" 11 | Keyword: 6-7 "." 12 | Keyword: 8-14 "import" 13 | Error(import_from_targets): 15-19 14 | import_from_as_name: 15-18 15 | name_def: 15-18 16 | Name: 15-18 "foo" 17 | Keyword: 18-19 "," 18 | stmt: 19-20 19 | Newline: 19-20 "\n" 20 | Error(stmt): 20-43 21 | Error(simple_stmts): 20-43 22 | Error(simple_stmt): 20-43 23 | Error(import_from): 20-43 24 | Keyword: 20-24 "from" 25 | Keyword: 25-26 "." 26 | Keyword: 27-33 "import" 27 | Error(import_from_targets): 34-43 28 | import_from_as_name: 34-37 29 | name_def: 34-37 30 | Name: 34-37 "foo" 31 | Keyword: 37-38 "," 32 | import_from_as_name: 39-42 33 | name_def: 39-42 34 | Name: 39-42 "bar" 35 | Keyword: 42-43 "," 36 | stmt: 43-44 37 | Newline: 43-44 "\n" 38 | Endmarker: 44-44 "" 39 | 40 | -------------------------------------------------------------------------------- /crates/vfs/src/glob_abs_path.rs: -------------------------------------------------------------------------------- 1 | use crate::{AbsPath, VfsHandler}; 2 | 3 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] 4 | pub struct GlobAbsPath { 5 | pattern: glob::Pattern, 6 | } 7 | 8 | impl GlobAbsPath { 9 | pub fn new(vfs: &dyn VfsHandler, current_dir: &AbsPath, path: &str) -> anyhow::Result { 10 | let p = vfs.normalize_rc_path(vfs.absolute_path(current_dir, path)); 11 | let pattern = glob::Pattern::new(&p)?; 12 | Ok(Self { pattern }) 13 | } 14 | 15 | pub fn matches(&self, vfs: &dyn VfsHandler, path: &AbsPath) -> bool { 16 | // TODO this does not correctly use VFS's separator 17 | self.pattern.matches_with( 18 | path, 19 | glob::MatchOptions { 20 | case_sensitive: vfs.is_case_sensitive(), 21 | require_literal_separator: true, 22 | ..Default::default() 23 | }, 24 | ) 25 | } 26 | 27 | pub fn maybe_simple_path(&self) -> Option<&str> { 28 | let original = self.pattern.as_str(); 29 | (!original.contains(['?', '[', ']', '*'])).then_some(original) 30 | } 31 | 32 | pub fn as_str(&self) -> &str { 33 | self.pattern.as_str() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/scripts/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unreachable_code, unused_imports)] 2 | use std::{fs, path::PathBuf}; 3 | 4 | fn main() -> std::io::Result<()> { 5 | /* 6 | let mut project = zuban_python::Project::new("foo".to_owned()); 7 | let script = zuban_python::Script::new( 8 | &mut project, 9 | Some("/foo/bar.py".to_owned()), 10 | Some("foo\n".to_owned()), 11 | ); 12 | let defs = script.infer_definition( 13 | &|name| (name.kind(), name.name().to_owned()), 14 | zuban_python::Position::Byte(1), 15 | ); 16 | for (kind, name) in defs { 17 | dbg!(kind, name); 18 | } 19 | */ 20 | 21 | Ok(()) 22 | 23 | /* 24 | let file = "/home/dave/source/stuff_jedi/quickfix_huge.py"; 25 | let contents = fs::read_to_string(file)?; 26 | 27 | let c = contents.repeat(10); 28 | let start = std::time::Instant::now(); 29 | parsa_python::parse(c); 30 | eprintln!("elapsed {:?}", start.elapsed()); 31 | 32 | for _ in 0..10 { 33 | let c = contents.repeat(10); 34 | let start = std::time::Instant::now(); 35 | parsa_python::parse(c); 36 | eprintln!("elapsed {:?}", start.elapsed()); 37 | } 38 | Ok(()) 39 | */ 40 | } 41 | -------------------------------------------------------------------------------- /crates/zuban_benchmark/benches/zuban_python.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group, criterion_main}; 2 | 3 | use config::ProjectOptions; 4 | use vfs::PathWithScheme; 5 | use zuban_python::{Mode, Project}; 6 | 7 | fn check_independent_files(file_count: usize) { 8 | let some_code = utils::dedent( 9 | r#" 10 | def foo(x: int) -> int: 11 | return x 12 | "#, 13 | ); 14 | let mut po = ProjectOptions::default(); 15 | po.settings.typeshed_path = Some(test_utils::typeshed_path()); 16 | let mut project = Project::without_watcher(po, Mode::TypeCheckingOnly); 17 | for i in 0..file_count { 18 | let vfs = project.vfs_handler(); 19 | let path = PathWithScheme::with_file_scheme( 20 | vfs.normalize_rc_path(vfs.unchecked_abs_path(&format!("/bench-test/test{i}.py"))), 21 | ); 22 | project.store_in_memory_file(path, some_code.clone().into()) 23 | } 24 | } 25 | 26 | fn bench_type_checking(c: &mut Criterion) { 27 | c.bench_function("1 file", |b| b.iter(|| check_independent_files(1))); 28 | c.bench_function("1000 files", |b| b.iter(|| check_independent_files(1000))); 29 | } 30 | 31 | // Register the benchmarks 32 | criterion_group!(benches, bench_type_checking); 33 | criterion_main!(benches); 34 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__operators.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-37 6 | stmt: 1-37 7 | simple_stmts: 1-37 8 | simple_stmt: 1-36 9 | star_expressions: 1-36 10 | expression: 1-36 11 | bitwise_or: 1-36 12 | bitwise_or: 1-6 13 | atom: 1-2 14 | Name: 1-2 "f" 15 | Keyword: 3-4 "|" 16 | atom: 5-6 17 | Name: 5-6 "d" 18 | Keyword: 7-8 "|" 19 | bitwise_and: 9-36 20 | sum: 9-14 21 | atom: 9-10 22 | Name: 9-10 "c" 23 | Keyword: 11-12 "+" 24 | atom: 13-14 25 | Number: 13-14 "5" 26 | Keyword: 15-16 "&" 27 | term: 17-36 28 | term: 17-22 29 | atom: 17-18 30 | Number: 17-18 "2" 31 | Keyword: 19-20 "*" 32 | atom: 21-22 33 | Number: 21-22 "3" 34 | Keyword: 23-24 "*" 35 | power: 25-36 36 | atom: 25-26 37 | Number: 25-26 "4" 38 | Keyword: 27-29 "**" 39 | power: 30-36 40 | atom: 30-31 41 | Number: 30-31 "5" 42 | Keyword: 32-34 "**" 43 | atom: 35-36 44 | Number: 35-36 "6" 45 | Newline: 36-37 "\n" 46 | Endmarker: 37-37 "" 47 | 48 | -------------------------------------------------------------------------------- /deploy/pypi/zubanls/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, find_packages 4 | 5 | __AUTHOR__ = 'David Halter' 6 | __AUTHOR_EMAIL__ = 'info@zubanls.com' 7 | 8 | readme = open('README.rst').read() 9 | 10 | setup( 11 | name='zubanls', 12 | version='0.0.2', 13 | description='Alias for Zuban - use pip install zuban instead', 14 | author=__AUTHOR__, 15 | author_email=__AUTHOR_EMAIL__, 16 | include_package_data=True, 17 | maintainer=__AUTHOR__, 18 | maintainer_email=__AUTHOR_EMAIL__, 19 | url='https://github.com/zubanls/zuban', 20 | license='Proprietary', 21 | keywords='typechecking mypy static analysis autocompletion', 22 | long_description=readme, 23 | packages=find_packages(exclude=['test']), 24 | python_requires='>=3.4', 25 | install_requires=['zubanls>=0.0.0,<0.1.0'], 26 | classifiers=[ 27 | 'Development Status :: 1 - Planning', 28 | 'Environment :: Plugins', 29 | 'Intended Audience :: Developers', 30 | 'License :: Other/Proprietary License', 31 | 'Programming Language :: Python :: 3', 32 | 'Topic :: Software Development :: Libraries :: Python Modules', 33 | 'Topic :: Text Editors :: Integrated Development Environments (IDE)', 34 | 'Topic :: Utilities', 35 | 'Typing :: Typed', 36 | ], 37 | ) 38 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__simple_error_recovery.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-15 6 | Error(stmt): 1-8 7 | Error(simple_stmts): 1-8 8 | Error(simple_stmt): 1-8 9 | Error(star_expressions): 1-8 10 | Error(expression): 1-8 11 | Error(ternary): 1-8 12 | Error(disjunction): 1-8 13 | Error(conjunction): 1-8 14 | Error(inversion): 1-8 15 | Error(comparison): 1-8 16 | Error(bitwise_or): 1-8 17 | Error(bitwise_xor): 1-8 18 | Error(bitwise_and): 1-8 19 | Error(shift_expr): 1-8 20 | Error(sum): 1-8 21 | sum: 1-6 22 | atom: 1-2 23 | Name: 1-2 "a" 24 | Keyword: 3-4 "+" 25 | atom: 5-6 26 | Number: 5-6 "3" 27 | Keyword: 7-8 "+" 28 | stmt: 8-9 29 | Newline: 8-9 "\n" 30 | stmt: 9-15 31 | simple_stmts: 9-15 32 | simple_stmt: 9-14 33 | assignment: 9-14 34 | star_targets: 9-10 35 | name_def: 9-10 36 | Name: 9-10 "b" 37 | Keyword: 11-12 "=" 38 | star_expressions: 13-14 39 | expression: 13-14 40 | atom: 13-14 41 | Number: 13-14 "3" 42 | Newline: 14-15 "\n" 43 | Endmarker: 15-15 "" 44 | 45 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__bytes.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-47 6 | stmt: 1-13 7 | simple_stmts: 1-13 8 | simple_stmt: 1-12 9 | star_expressions: 1-12 10 | expression: 1-12 11 | atom: 1-12 12 | bytes: 1-12 13 | Bytes: 1-12 "b'asdf\\n\\''" 14 | Newline: 12-13 "\n" 15 | stmt: 13-23 16 | simple_stmts: 13-23 17 | simple_stmt: 13-22 18 | star_expressions: 13-22 19 | expression: 13-22 20 | atom: 13-22 21 | bytes: 13-22 22 | Bytes: 13-22 "br'\\asdf'" 23 | Newline: 22-23 "\n" 24 | stmt: 23-35 25 | simple_stmts: 23-35 26 | simple_stmt: 23-34 27 | star_expressions: 23-34 28 | expression: 23-34 29 | atom: 23-34 30 | bytes: 23-34 31 | Bytes: 23-34 "Br'asdf\"\"\"'" 32 | Newline: 34-35 "\n" 33 | stmt: 35-40 34 | simple_stmts: 35-40 35 | simple_stmt: 35-39 36 | star_expressions: 35-39 37 | expression: 35-39 38 | atom: 35-39 39 | bytes: 35-39 40 | Bytes: 35-39 "bR''" 41 | Newline: 39-40 "\n" 42 | stmt: 40-47 43 | simple_stmts: 40-47 44 | simple_stmt: 40-46 45 | star_expressions: 40-46 46 | expression: 40-46 47 | atom: 40-46 48 | bytes: 40-46 49 | Bytes: 40-46 "BR'\\''" 50 | Newline: 46-47 "\n" 51 | Endmarker: 47-47 "" 52 | 53 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__with_items_simple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 41 4 | expression: tree_to_string(tree) 5 | --- 6 | file: 0-48 7 | stmt: 1-22 8 | with_stmt: 1-22 9 | Keyword: 1-5 "with" 10 | with_items: 6-17 11 | with_item: 6-16 12 | expression: 6-9 13 | atom: 6-9 14 | Name: 6-9 "foo" 15 | Keyword: 10-12 "as" 16 | name_def: 13-16 17 | Name: 13-16 "bar" 18 | Keyword: 16-17 ":" 19 | block: 18-22 20 | simple_stmts: 18-22 21 | simple_stmt: 18-21 22 | star_expressions: 18-21 23 | expression: 18-21 24 | atom: 18-21 25 | Keyword: 18-21 "..." 26 | Newline: 21-22 "\n" 27 | stmt: 22-48 28 | with_stmt: 22-48 29 | Keyword: 22-26 "with" 30 | with_items: 27-43 31 | with_item: 27-37 32 | expression: 27-30 33 | atom: 27-30 34 | Name: 27-30 "foo" 35 | Keyword: 31-33 "as" 36 | name_def: 34-37 37 | Name: 34-37 "bar" 38 | Keyword: 37-38 "," 39 | with_item: 39-42 40 | expression: 39-42 41 | atom: 39-42 42 | Name: 39-42 "baz" 43 | Keyword: 42-43 ":" 44 | block: 44-48 45 | simple_stmts: 44-48 46 | simple_stmt: 44-47 47 | star_expressions: 44-47 48 | expression: 44-47 49 | atom: 44-47 50 | Keyword: 44-47 "..." 51 | Newline: 47-48 "\n" 52 | Endmarker: 48-48 "" 53 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/LICENSE.txt: -------------------------------------------------------------------------------- 1 | All rights reserved by Dave Halter. Some files originally had an MIT License: 2 | 3 | All contributions towards Jedi are MIT licensed. 4 | 5 | ------------------------------------------------------------------------------- 6 | The MIT License (MIT) 7 | 8 | Copyright (c) <2022> 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/unions.test: -------------------------------------------------------------------------------- 1 | [case union_simplification_with_none_strict_optional] 2 | from typing import TypeVar, Generic, Union 3 | 4 | T = TypeVar('T') 5 | 6 | def get(default: T) -> Union[int, T]: ... 7 | 8 | reveal_type(get(None)) # N: Revealed type is "int | None" 9 | reveal_type(get("")) # N: Revealed type is "int | str" 10 | 11 | [case merge_nested_unions] 12 | # This is copied from testFlattenTypeAliasWhenAliasedAsUnion, because we he had 13 | # issues with union definitions. 14 | from typing import Union 15 | 16 | T1 = int 17 | T2 = T1 | float 18 | T3 = T2 | complex 19 | T4 = T3 | int 20 | 21 | def bar(a: T4, b: T4) -> T4: 22 | return a + b 23 | 24 | [case in_operator_with_union_non_match] 25 | zz: str | bytes 26 | 1.0 in zz # E: Unsupported operand types for in ("float" and "Union[str, bytes]") 27 | 28 | [case in_operator_with_never] 29 | from typing import Never 30 | zz: Never 31 | 1.0 in zz 32 | 33 | [case type_union_assignments] 34 | from typing import Type 35 | x: Type[int | str] 36 | y: Type[str] | Type[int] 37 | a: Type[int | bytes] 38 | b: Type[int] | Type[bytes] 39 | 40 | if bool(): 41 | x = y 42 | a = y # E: Incompatible types in assignment (expression has type "type[str] | type[int]", variable has type "type[int | bytes]") 43 | b = x # E: Incompatible types in assignment (expression has type "type[str] | type[int]", variable has type "type[int] | type[bytes]") 44 | if bool(): 45 | y = x 46 | -------------------------------------------------------------------------------- /crates/licensing/README.md: -------------------------------------------------------------------------------- 1 | # Licensing 2 | 3 | This module is here for managing licenses. We currently use ed25519 keys. 4 | Nothing else is supported. Please note that the security is not 5 | very important at the moment, because we only use it for signing licenses. 6 | Licenses are probably easier circumvented by modifying the original binary to 7 | not check for licenses in the first place, since Zuban doesn't rely on web 8 | services. 9 | 10 | ## Creating Licenses 11 | 12 | To create a license use a command like 13 | 14 | ``` 15 | ZUBAN_SIGNING_KEY=`cat` cargo run -p licensing create --name Dave --email info@zubanls.com --company Squirrels 16 | ``` 17 | 18 | and store it at: 19 | 20 | - Linux: `/home//.config/zuban/license.json` 21 | - Windows: `C:\Users\\AppData\Roaming\zuban\license.json` 22 | - Mac: `/Users//Library/Application Support/zuban/license.json` 23 | 24 | Make sure to **never** leak the private key. It should **never** be part of the **shell history** or somehow be provided **as an argument to a process** (which would leak it when looking at processes). 25 | 26 | ## Initializing Private/Public Keys 27 | 28 | To create a new key with OpenSSL use `./new_openssl_key.sh`. Once you have a 29 | few licenses (and please save them somewhere safe!), you can use 30 | 31 | ``` 32 | cat | rg pub | sed 's/pub: //' | xargs -n 1 cargo run -q hex-to-rust-byte-literals 33 | ``` 34 | 35 | to be able to paste these public keys to the Rust binary. 36 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__semicolon.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-49 8 | stmt: 1-11 9 | simple_stmts: 1-11 10 | simple_stmt: 1-9 11 | assignment: 1-9 12 | star_targets: 1-5 13 | name_def: 1-5 14 | Name: 1-5 "temp" 15 | Keyword: 6-7 "=" 16 | star_expressions: 8-9 17 | expression: 8-9 18 | atom: 8-9 19 | Number: 8-9 "1" 20 | Keyword: 9-10 ";" 21 | Newline: 10-11 "\n" 22 | stmt: 11-49 23 | class_def: 11-49 24 | Keyword: 11-16 "class" 25 | name_def: 17-18 26 | Name: 17-18 "A" 27 | Keyword: 18-19 ":" 28 | block: 19-49 29 | Newline: 19-20 "\n" 30 | Indent: 24-24 "" 31 | stmt: 24-30 32 | simple_stmts: 24-30 33 | simple_stmt: 24-29 34 | assignment: 24-29 35 | star_targets: 24-25 36 | name_def: 24-25 37 | Name: 24-25 "a" 38 | Keyword: 26-27 "=" 39 | star_expressions: 28-29 40 | expression: 28-29 41 | atom: 28-29 42 | Number: 28-29 "3" 43 | Newline: 29-30 "\n" 44 | stmt: 47-49 45 | simple_stmts: 47-49 46 | simple_stmt: 47-48 47 | star_expressions: 47-48 48 | expression: 47-48 49 | atom: 47-48 50 | Name: 47-48 "a" 51 | Newline: 48-49 "\n" 52 | Dedent: 49-49 "" 53 | Endmarker: 49-49 "" 54 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__completion_on_func_call1.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-6 8 | Error(stmt): 0-5 9 | Error(simple_stmts): 0-5 10 | Error(simple_stmt): 0-5 11 | Error(star_expressions): 0-5 12 | Error(expression): 0-5 13 | Error(ternary): 0-5 14 | Error(disjunction): 0-5 15 | Error(conjunction): 0-5 16 | Error(inversion): 0-5 17 | Error(comparison): 0-5 18 | Error(bitwise_or): 0-5 19 | Error(bitwise_xor): 0-5 20 | Error(bitwise_and): 0-5 21 | Error(shift_expr): 0-5 22 | Error(sum): 0-5 23 | Error(term): 0-5 24 | Error(factor): 0-5 25 | Error(power): 0-5 26 | Error(await_primary): 0-5 27 | Error(primary): 0-5 28 | primary: 0-4 29 | atom: 0-1 30 | Name: 0-1 "f" 31 | Keyword: 1-2 "(" 32 | arguments: 2-3 33 | named_expression: 2-3 34 | expression: 2-3 35 | atom: 2-3 36 | Number: 2-3 "3" 37 | Keyword: 3-4 ")" 38 | Keyword: 4-5 "." 39 | stmt: 5-6 40 | Newline: 5-6 "\n" 41 | Endmarker: 6-6 "" 42 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__invalid_syntax.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-30 6 | stmt: 1-30 7 | function_def: 1-30 8 | Keyword: 1-4 "def" 9 | name_def: 5-6 10 | Name: 5-6 "f" 11 | function_def_parameters: 6-8 12 | Keyword: 6-7 "(" 13 | Keyword: 7-8 ")" 14 | Keyword: 8-9 ":" 15 | block: 9-30 16 | Newline: 9-10 "\n" 17 | Indent: 14-14 "" 18 | Error(stmt): 14-18 19 | Error(simple_stmts): 14-18 20 | simple_stmt: 14-18 21 | star_expressions: 14-18 22 | expression: 14-18 23 | atom: 14-18 24 | Keyword: 14-15 "(" 25 | tuple_content: 15-17 26 | named_expression: 15-16 27 | expression: 15-16 28 | atom: 15-16 29 | Name: 15-16 "x" 30 | Keyword: 16-17 "," 31 | Keyword: 17-18 ")" 32 | Error(stmt): 19-21 33 | Error(Keyword): 19-21 "+=" 34 | stmt: 22-24 35 | simple_stmts: 22-24 36 | simple_stmt: 22-23 37 | star_expressions: 22-23 38 | expression: 22-23 39 | atom: 22-23 40 | Number: 22-23 "1" 41 | Newline: 23-24 "\n" 42 | stmt: 28-30 43 | simple_stmts: 28-30 44 | simple_stmt: 28-29 45 | star_expressions: 28-29 46 | expression: 28-29 47 | atom: 28-29 48 | Name: 28-29 "z" 49 | Newline: 29-30 "\n" 50 | Dedent: 30-30 "" 51 | Endmarker: 30-30 "" 52 | 53 | -------------------------------------------------------------------------------- /crates/vfs/src/path.rs: -------------------------------------------------------------------------------- 1 | use std::{path::Path, rc::Rc, sync::Arc}; 2 | 3 | #[derive(Debug, PartialEq, Eq, Hash)] 4 | #[repr(transparent)] 5 | pub struct AbsPath(str); 6 | 7 | impl AbsPath { 8 | /* 9 | pub(crate) fn new(x: &str) -> &Self { 10 | // SAFETY: `AbsPath` is repr(transparent) over `str` 11 | unsafe { std::mem::transmute(x) } 12 | } 13 | */ 14 | 15 | pub(crate) fn new_arc(x: Arc) -> Arc { 16 | // SAFETY: `AbsPath` is repr(transparent) over `str` 17 | unsafe { std::mem::transmute(x) } 18 | } 19 | 20 | pub fn contains_sub_file(&self, path: &str) -> bool { 21 | Path::new(path).starts_with(Path::new(&self.0)) 22 | } 23 | } 24 | 25 | impl ToOwned for AbsPath { 26 | type Owned = Arc; 27 | 28 | fn to_owned(&self) -> Self::Owned { 29 | self.into() 30 | } 31 | } 32 | 33 | impl From<&AbsPath> for Arc { 34 | #[inline] 35 | fn from(s: &AbsPath) -> Arc { 36 | let x: Rc = s.0.into(); 37 | unsafe { std::mem::transmute(x) } 38 | } 39 | } 40 | 41 | impl std::ops::Deref for AbsPath { 42 | type Target = str; 43 | 44 | fn deref(&self) -> &Self::Target { 45 | &self.0 46 | } 47 | } 48 | 49 | impl AsRef for AbsPath { 50 | fn as_ref(&self) -> &Path { 51 | Path::new(&self.0) 52 | } 53 | } 54 | 55 | impl std::fmt::Display for AbsPath { 56 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 57 | self.0.fmt(f) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__del_stmt.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-46 6 | stmt: 1-9 7 | simple_stmts: 1-9 8 | simple_stmt: 1-8 9 | del_stmt: 1-8 10 | Keyword: 1-4 "del" 11 | del_targets: 5-8 12 | name_def: 5-8 13 | Name: 5-8 "foo" 14 | Newline: 8-9 "\n" 15 | stmt: 9-18 16 | simple_stmts: 9-18 17 | simple_stmt: 9-17 18 | del_stmt: 9-17 19 | Keyword: 9-12 "del" 20 | del_targets: 13-17 21 | name_def: 13-16 22 | Name: 13-16 "foo" 23 | Keyword: 16-17 "," 24 | Newline: 17-18 "\n" 25 | stmt: 18-35 26 | simple_stmts: 18-35 27 | simple_stmt: 18-34 28 | del_stmt: 18-34 29 | Keyword: 18-21 "del" 30 | del_targets: 22-34 31 | name_def: 22-25 32 | Name: 22-25 "foo" 33 | Keyword: 25-26 "," 34 | t_primary: 27-34 35 | atom: 27-30 36 | Name: 27-30 "bar" 37 | Keyword: 30-31 "." 38 | name_def: 31-34 39 | Name: 31-34 "baz" 40 | Newline: 34-35 "\n" 41 | stmt: 35-46 42 | simple_stmts: 35-46 43 | simple_stmt: 35-45 44 | del_stmt: 35-45 45 | Keyword: 35-38 "del" 46 | del_targets: 39-45 47 | t_primary: 39-45 48 | atom: 39-42 49 | Name: 39-42 "foo" 50 | Keyword: 42-43 "[" 51 | named_expression: 43-44 52 | expression: 43-44 53 | atom: 43-44 54 | Number: 43-44 "1" 55 | Keyword: 44-45 "]" 56 | Newline: 45-46 "\n" 57 | Endmarker: 46-46 "" 58 | 59 | -------------------------------------------------------------------------------- /crates/utils/src/panic_context.rs: -------------------------------------------------------------------------------- 1 | //! A micro-crate to enhance panic messages with context info. 2 | //! 3 | //! FIXME: upstream to ? 4 | 5 | use std::{cell::RefCell, panic, sync::Once}; 6 | 7 | pub fn enter(context: String) -> PanicContext { 8 | static ONCE: Once = Once::new(); 9 | ONCE.call_once(PanicContext::init); 10 | 11 | with_ctx(|ctx| ctx.push(context)); 12 | PanicContext { _priv: () } 13 | } 14 | 15 | #[must_use] 16 | pub struct PanicContext { 17 | _priv: (), 18 | } 19 | 20 | impl PanicContext { 21 | #[allow(clippy::print_stderr)] 22 | fn init() { 23 | let default_hook = panic::take_hook(); 24 | #[allow(deprecated)] 25 | let hook = move |panic_info: &panic::PanicInfo<'_>| { 26 | with_ctx(|ctx| { 27 | if !ctx.is_empty() { 28 | eprintln!("Panic context:"); 29 | for frame in ctx.iter() { 30 | eprintln!("> {frame}\n"); 31 | } 32 | } 33 | default_hook(panic_info); 34 | }); 35 | }; 36 | panic::set_hook(Box::new(hook)); 37 | } 38 | } 39 | 40 | impl Drop for PanicContext { 41 | fn drop(&mut self) { 42 | with_ctx(|ctx| assert!(ctx.pop().is_some())); 43 | } 44 | } 45 | 46 | fn with_ctx(f: impl FnOnce(&mut Vec)) { 47 | thread_local! { 48 | static CTX: RefCell> = const { RefCell::new(Vec::new()) }; 49 | } 50 | CTX.with(|ctx| f(&mut ctx.borrow_mut())); 51 | } 52 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__import_from_normal.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-74 8 | stmt: 1-21 9 | simple_stmts: 1-21 10 | simple_stmt: 1-20 11 | import_from: 1-20 12 | Keyword: 1-5 "from" 13 | dotted_import_name: 6-9 14 | Name: 6-9 "foo" 15 | Keyword: 10-16 "import" 16 | import_from_targets: 17-20 17 | import_from_as_name: 17-20 18 | name_def: 17-20 19 | Name: 17-20 "bar" 20 | Newline: 20-21 "\n" 21 | stmt: 21-48 22 | simple_stmts: 21-48 23 | simple_stmt: 21-47 24 | import_from: 21-47 25 | Keyword: 21-25 "from" 26 | dotted_import_name: 26-29 27 | Name: 26-29 "foo" 28 | Keyword: 30-36 "import" 29 | import_from_targets: 37-47 30 | import_from_as_name: 37-47 31 | Name: 37-40 "bar" 32 | Keyword: 41-43 "as" 33 | name_def: 44-47 34 | Name: 44-47 "baz" 35 | Newline: 47-48 "\n" 36 | stmt: 48-74 37 | simple_stmts: 48-74 38 | simple_stmt: 48-73 39 | import_from: 48-73 40 | Keyword: 48-52 "from" 41 | dotted_import_name: 53-64 42 | dotted_import_name: 53-60 43 | dotted_import_name: 53-56 44 | Name: 53-56 "foo" 45 | Keyword: 56-57 "." 46 | Name: 57-60 "bar" 47 | Keyword: 60-61 "." 48 | Name: 61-64 "baz" 49 | Keyword: 65-71 "import" 50 | import_from_targets: 72-73 51 | Keyword: 72-73 "*" 52 | Newline: 73-74 "\n" 53 | Endmarker: 74-74 "" 54 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__match_simple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-57 8 | stmt: 1-57 9 | match_stmt: 1-57 10 | Keyword: 1-6 "match" 11 | subject_expr: 7-10 12 | named_expression: 7-10 13 | expression: 7-10 14 | atom: 7-10 15 | Name: 7-10 "foo" 16 | Keyword: 10-11 ":" 17 | Newline: 11-12 "\n" 18 | Indent: 16-16 "" 19 | case_block: 16-32 20 | Keyword: 16-20 "case" 21 | pattern: 21-26 22 | sequence_pattern: 21-26 23 | Keyword: 21-22 "[" 24 | open_sequence_pattern: 22-25 25 | pattern: 22-25 26 | literal_pattern: 22-25 27 | strings: 22-25 28 | String: 22-25 "'a'" 29 | Keyword: 25-26 "]" 30 | Keyword: 26-27 ":" 31 | block: 28-32 32 | simple_stmts: 28-32 33 | simple_stmt: 28-31 34 | star_expressions: 28-31 35 | expression: 28-31 36 | atom: 28-31 37 | Keyword: 28-31 "..." 38 | Newline: 31-32 "\n" 39 | case_block: 36-57 40 | Keyword: 36-40 "case" 41 | pattern: 41-42 42 | wildcard_pattern: 41-42 43 | Keyword: 41-42 "_" 44 | Keyword: 42-43 ":" 45 | block: 43-57 46 | Newline: 43-44 "\n" 47 | Indent: 52-52 "" 48 | stmt: 52-57 49 | simple_stmts: 52-57 50 | simple_stmt: 52-56 51 | pass_stmt: 52-56 52 | Keyword: 52-56 "pass" 53 | Newline: 56-57 "\n" 54 | Dedent: 57-57 "" 55 | Dedent: 57-57 "" 56 | Endmarker: 57-57 "" 57 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__assignments_aug.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-37 6 | stmt: 1-10 7 | simple_stmts: 1-10 8 | simple_stmt: 1-9 9 | assignment: 1-9 10 | single_target: 1-4 11 | name_def: 1-4 12 | Name: 1-4 "foo" 13 | augassign: 5-7 14 | Keyword: 5-7 "+=" 15 | star_expressions: 8-9 16 | expression: 8-9 17 | atom: 8-9 18 | Number: 8-9 "3" 19 | Newline: 9-10 "\n" 20 | stmt: 10-23 21 | simple_stmts: 10-23 22 | simple_stmt: 10-22 23 | assignment: 10-22 24 | single_target: 10-17 25 | t_primary: 10-17 26 | atom: 10-13 27 | Name: 10-13 "foo" 28 | Keyword: 13-14 "." 29 | name_def: 14-17 30 | Name: 14-17 "bar" 31 | augassign: 18-20 32 | Keyword: 18-20 "|=" 33 | star_expressions: 21-22 34 | expression: 21-22 35 | atom: 21-22 36 | Number: 21-22 "3" 37 | Newline: 22-23 "\n" 38 | Error(stmt): 23-31 39 | Error(simple_stmts): 23-31 40 | simple_stmt: 23-31 41 | star_expressions: 23-31 42 | expression: 23-26 43 | atom: 23-26 44 | Name: 23-26 "foo" 45 | Keyword: 26-27 "," 46 | expression: 28-31 47 | atom: 28-31 48 | Name: 28-31 "bar" 49 | Error(stmt): 32-34 50 | Error(Keyword): 32-34 "+=" 51 | stmt: 35-37 52 | simple_stmts: 35-37 53 | simple_stmt: 35-36 54 | star_expressions: 35-36 55 | expression: 35-36 56 | atom: 35-36 57 | Number: 35-36 "3" 58 | Newline: 36-37 "\n" 59 | Endmarker: 37-37 "" 60 | 61 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/references.test: -------------------------------------------------------------------------------- 1 | [case simple_references] 2 | #? --codepoint-column 19 references 3 | with open("x") as x: 4 | x.read() 5 | 6 | [out] 7 | __main__.py:2:references -> __main__.py:2:18; __main__.py:3:4 8 | 9 | [case simple_references_no_declarations] 10 | #? --codepoint-column 19 references --no-include-declarations 11 | with open("x") as x: 12 | x.read() 13 | 14 | [out] 15 | __main__.py:2:references -> __main__.py:3:4 16 | 17 | [case file_references_import_name] 18 | #? references 19 | import foo 20 | 21 | [file foo.py] 22 | [file foo.pyi] 23 | 24 | [out] 25 | __main__.py:2:references -> foo.py:1:0; foo.pyi:1:0; __main__.py:2:7 26 | 27 | [case file_references_no_declarations] 28 | #? references --no-include-declarations 29 | import foo 30 | 31 | [file foo.py] 32 | 33 | [out] 34 | __main__.py:2:references -> __main__.py:2:7 35 | 36 | [case file_references_import_from1] 37 | #? references 38 | from bar import foo 39 | 40 | [file bar/foo.py] 41 | [file bar/foo.pyi] 42 | 43 | [out] 44 | __main__.py:2:references -> bar/foo.py:1:0; bar/foo.pyi:1:0; __main__.py:2:16 45 | 46 | [case file_references_import_from2] 47 | #? --codepoint-column 5 references 48 | from bar import foo 49 | 50 | [file bar/__init__.py] 51 | [file bar/foo.py] 52 | [file bar/foo.pyi] 53 | 54 | [out] 55 | __main__.py:2:references -> bar/__init__.py:1:0; __main__.py:2:5 56 | 57 | [case builtin_usages_check_workspace] 58 | #? references 59 | str 60 | [out] 61 | __main__.py:2:references -> builtins.pyi:476:6; __main__.py:2:0 62 | 63 | [case builtin_usages_only_check_file] 64 | #? references --only-check-file 65 | str 66 | [out] 67 | __main__.py:2:references -> __main__.py:2:0 68 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | all_tests: 7 | required: true 8 | type: boolean 9 | debug_ssh_session: 10 | required: false 11 | type: boolean 12 | 13 | defaults: 14 | run: 15 | shell: bash 16 | 17 | jobs: 18 | cargo-test-debug: 19 | strategy: 20 | matrix: 21 | os: 22 | - macos-latest 23 | - ubuntu-24.04 24 | - windows-2022 25 | exclude: 26 | # Avoid Mac here for now, because the CI is costly (10x of linux) 27 | - os: ${{ !inputs.all_tests && 'macos-latest' }} 28 | runs-on: ${{ matrix.os }} 29 | timeout-minutes: 20 30 | steps: 31 | - uses: actions/checkout@v4 32 | with: 33 | submodules: recursive 34 | persist-credentials: false 35 | - uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 36 | - name: Setup tmate session 37 | uses: mxschmitt/action-tmate@v3 38 | if: ${{ inputs.debug_ssh_session }} 39 | with: 40 | limit-access-to-actor: true 41 | - name: "Run tests" 42 | run: | 43 | ./scripts/ci-test.sh 44 | 45 | cargo-test-linux-release: 46 | if: ${{ inputs.all_tests }} 47 | name: "cargo test (linux, release)" 48 | runs-on: ubuntu-24.04 49 | timeout-minutes: 20 50 | steps: 51 | - uses: actions/checkout@v4 52 | with: 53 | submodules: recursive 54 | persist-credentials: false 55 | - uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 56 | - name: "Run tests" 57 | run: | 58 | rustup show 59 | cargo test --release --locked 60 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__terminal_and_nonterminal_error_recovery.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-21 6 | stmt: 1-5 7 | simple_stmts: 1-5 8 | simple_stmt: 1-4 9 | star_expressions: 1-4 10 | expression: 1-4 11 | atom: 1-4 12 | Name: 1-4 "foo" 13 | Newline: 4-5 "\n" 14 | Error(stmt): 5-10 15 | Error(simple_stmts): 5-10 16 | Error(simple_stmt): 5-10 17 | Error(star_expressions): 5-10 18 | Error(expression): 5-10 19 | Error(ternary): 5-10 20 | Error(disjunction): 5-10 21 | Error(conjunction): 5-10 22 | Error(inversion): 5-10 23 | Error(comparison): 5-10 24 | Error(bitwise_or): 5-10 25 | Error(bitwise_xor): 5-10 26 | Error(bitwise_and): 5-10 27 | Error(shift_expr): 5-10 28 | Error(sum): 5-10 29 | atom: 5-8 30 | Keyword: 5-6 "(" 31 | named_expression: 6-7 32 | expression: 6-7 33 | atom: 6-7 34 | Number: 6-7 "1" 35 | Keyword: 7-8 ")" 36 | Keyword: 9-10 "+" 37 | Error(stmt): 11-12 38 | Error(ErrorToken): 11-12 "?" 39 | stmt: 13-17 40 | simple_stmts: 13-17 41 | simple_stmt: 13-16 42 | star_expressions: 13-16 43 | expression: 13-16 44 | factor: 13-16 45 | Keyword: 13-14 "+" 46 | atom: 15-16 47 | Name: 15-16 "c" 48 | Newline: 16-17 "\n" 49 | stmt: 17-21 50 | simple_stmts: 17-21 51 | simple_stmt: 17-20 52 | star_expressions: 17-20 53 | expression: 17-20 54 | atom: 17-20 55 | Name: 17-20 "bar" 56 | Newline: 20-21 "\n" 57 | Endmarker: 21-21 "" 58 | 59 | -------------------------------------------------------------------------------- /crates/zuban_python/src/file/mod.rs: -------------------------------------------------------------------------------- 1 | mod diagnostics; 2 | mod file_state; 3 | mod flow_analysis; 4 | mod imports; 5 | mod inference; 6 | mod isinstance; 7 | mod name_binder; 8 | mod name_resolution; 9 | mod python_file; 10 | mod type_computation; 11 | mod type_var_finder; 12 | mod utils; 13 | 14 | pub(crate) use diagnostics::{OVERLAPPING_REVERSE_TO_NORMAL_METHODS, check_multiple_inheritance}; 15 | pub(crate) use file_state::File; 16 | pub(crate) use flow_analysis::{FLOW_ANALYSIS, RedefinitionResult, process_unfinished_partials}; 17 | use inference::Inference; 18 | pub(crate) use inference::{first_defined_name, first_defined_name_of_multi_def}; 19 | pub(crate) use name_binder::{ 20 | FUNC_TO_RETURN_OR_YIELD_DIFF, FUNC_TO_TYPE_VAR_DIFF, GLOBAL_NONLOCAL_TO_NAME_DIFFERENCE, 21 | func_parent_scope, 22 | }; 23 | pub(crate) use name_resolution::{is_private_import_and_not_in_dunder_all, is_reexport_issue}; 24 | pub(crate) use python_file::{ 25 | ComplexValues, FileImport, OtherDefinitionIterator, PythonFile, SuperFile, dotted_path_from_dir, 26 | }; 27 | pub(crate) use type_computation::{ 28 | ANNOTATION_TO_EXPR_DIFFERENCE, CLASS_TO_CLASS_INFO_DIFFERENCE, ClassInitializer, ClassNodeRef, 29 | DecoratorState, FuncNodeRef, FuncParent, GenericCounts, ORDERING_METHODS, 30 | TypeVarCallbackReturn, TypeVarTupleDefaultOrigin, assignment_type_node_ref, 31 | expect_class_or_simple_generic, linearize_mro_and_return_linearizable, maybe_saved_annotation, 32 | use_cached_annotation_or_type_comment, use_cached_annotation_type, 33 | use_cached_param_annotation_type, use_cached_return_annotation_type, 34 | use_cached_simple_generic_type, 35 | }; 36 | pub(crate) use type_var_finder::TypeVarFinder; 37 | pub(crate) use utils::{ 38 | infer_index, infer_string_index, on_argument_type_error, should_add_deprecated, 39 | }; 40 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__simple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-36 6 | stmt: 1-19 7 | simple_stmts: 1-19 8 | simple_stmt: 1-17 9 | star_expressions: 1-17 10 | expression: 1-17 11 | primary: 1-17 12 | atom: 1-2 13 | Name: 1-2 "a" 14 | Keyword: 2-3 "(" 15 | arguments: 3-16 16 | named_expression: 3-9 17 | walrus: 3-9 18 | name_def: 3-6 19 | Name: 3-6 "bar" 20 | Keyword: 6-8 ":=" 21 | expression: 8-9 22 | atom: 8-9 23 | Number: 8-9 "1" 24 | Keyword: 9-10 "," 25 | kwargs: 11-16 26 | kwarg: 11-16 27 | Name: 11-14 "foo" 28 | Keyword: 14-15 "=" 29 | expression: 15-16 30 | atom: 15-16 31 | Number: 15-16 "1" 32 | Keyword: 16-17 ")" 33 | Keyword: 17-18 ";" 34 | Newline: 18-19 "\n" 35 | stmt: 19-36 36 | if_stmt: 19-36 37 | Keyword: 19-21 "if" 38 | named_expression: 22-23 39 | expression: 22-23 40 | atom: 22-23 41 | Name: 22-23 "a" 42 | Keyword: 23-24 ":" 43 | block: 24-28 44 | Newline: 24-25 "\n" 45 | Indent: 26-26 "" 46 | stmt: 26-28 47 | simple_stmts: 26-28 48 | simple_stmt: 26-27 49 | star_expressions: 26-27 50 | expression: 26-27 51 | atom: 26-27 52 | Number: 26-27 "1" 53 | Newline: 27-28 "\n" 54 | Dedent: 28-28 "" 55 | else_block: 28-36 56 | Keyword: 28-32 "else" 57 | Keyword: 32-33 ":" 58 | block: 34-36 59 | simple_stmts: 34-36 60 | simple_stmt: 34-35 61 | star_expressions: 34-35 62 | expression: 34-35 63 | atom: 34-35 64 | Number: 34-35 "2" 65 | Newline: 35-36 "\n" 66 | Endmarker: 36-36 "" 67 | 68 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__import_names.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-74 8 | stmt: 1-12 9 | simple_stmts: 1-12 10 | simple_stmt: 1-11 11 | import_name: 1-11 12 | Keyword: 1-7 "import" 13 | dotted_as_names: 8-11 14 | dotted_as_name: 8-11 15 | name_def: 8-11 16 | Name: 8-11 "foo" 17 | Newline: 11-12 "\n" 18 | stmt: 12-34 19 | simple_stmts: 12-34 20 | simple_stmt: 12-33 21 | import_name: 12-33 22 | Keyword: 12-18 "import" 23 | dotted_as_names: 19-33 24 | dotted_as_name: 19-33 25 | dotted_import_name: 19-26 26 | dotted_import_name: 19-22 27 | Name: 19-22 "foo" 28 | Keyword: 22-23 "." 29 | Name: 23-26 "bar" 30 | Keyword: 27-29 "as" 31 | name_def: 30-33 32 | Name: 30-33 "baz" 33 | Newline: 33-34 "\n" 34 | stmt: 34-55 35 | simple_stmts: 34-55 36 | simple_stmt: 34-54 37 | import_name: 34-54 38 | Keyword: 34-40 "import" 39 | dotted_as_names: 41-54 40 | dotted_as_name: 41-44 41 | name_def: 41-44 42 | Name: 41-44 "foo" 43 | Keyword: 44-45 "," 44 | dotted_as_name: 46-49 45 | name_def: 46-49 46 | Name: 46-49 "bar" 47 | Keyword: 49-50 "," 48 | dotted_as_name: 51-54 49 | name_def: 51-54 50 | Name: 51-54 "baz" 51 | Newline: 54-55 "\n" 52 | stmt: 55-74 53 | simple_stmts: 55-74 54 | simple_stmt: 55-73 55 | import_name: 55-73 56 | Keyword: 55-61 "import" 57 | dotted_as_names: 62-73 58 | dotted_as_name: 62-73 59 | name_def: 62-65 60 | Name: 62-65 "foo" 61 | Keyword: 65-66 "." 62 | dotted_import_name: 66-73 63 | dotted_import_name: 66-69 64 | Name: 66-69 "bar" 65 | Keyword: 69-70 "." 66 | Name: 70-73 "baz" 67 | Newline: 73-74 "\n" 68 | Endmarker: 74-74 "" 69 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__completion_on_func_call2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-18 8 | Error(stmt): 0-17 9 | Error(simple_stmts): 0-17 10 | Error(simple_stmt): 0-17 11 | Error(star_expressions): 0-17 12 | Error(expression): 0-17 13 | Error(ternary): 0-17 14 | Error(disjunction): 0-17 15 | Error(conjunction): 0-17 16 | Error(inversion): 0-17 17 | Error(comparison): 0-17 18 | Error(bitwise_or): 0-17 19 | Error(bitwise_xor): 0-17 20 | Error(bitwise_and): 0-17 21 | Error(shift_expr): 0-17 22 | Error(sum): 0-17 23 | Error(term): 0-17 24 | Error(factor): 0-17 25 | Error(power): 0-17 26 | Error(await_primary): 0-17 27 | Error(primary): 0-17 28 | primary: 0-16 29 | atom: 0-1 30 | Name: 0-1 "f" 31 | Keyword: 1-2 "(" 32 | comprehension: 2-15 33 | named_expression: 2-3 34 | expression: 2-3 35 | atom: 2-3 36 | Number: 2-3 "1" 37 | for_if_clauses: 4-15 38 | sync_for_if_clause: 4-15 39 | Keyword: 4-7 "for" 40 | star_targets: 8-9 41 | name_def: 8-9 42 | Name: 8-9 "a" 43 | Keyword: 10-12 "in" 44 | atom: 13-15 45 | Keyword: 13-14 "[" 46 | Keyword: 14-15 "]" 47 | Keyword: 15-16 ")" 48 | Keyword: 16-17 "." 49 | stmt: 17-18 50 | Newline: 17-18 "\n" 51 | Endmarker: 18-18 "" 52 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/pep0484_overload.py: -------------------------------------------------------------------------------- 1 | # python >= 3.6 2 | from typing import List, Dict, overload, Tuple, TypeVar 3 | 4 | lst: list 5 | list_alias: List 6 | list_str: List[str] 7 | list_int: List[int] 8 | 9 | # ------------------------- 10 | # With base classes 11 | # ------------------------- 12 | 13 | @overload 14 | def overload_f2(value: List) -> str: ... 15 | @overload 16 | def overload_f2(value: Dict) -> int: ... 17 | 18 | #? str() 19 | overload_f2(['']) 20 | #? int() 21 | overload_f2({1.0: 1.0}) 22 | #? str() 23 | overload_f2(lst) 24 | #? str() 25 | overload_f2(list_alias) 26 | #? str() 27 | overload_f2(list_str) 28 | 29 | 30 | @overload 31 | def overload_f3(value: list) -> str: ... 32 | @overload 33 | def overload_f3(value: dict) -> float: ... 34 | 35 | #? str() 36 | overload_f3(['']) 37 | #? float() 38 | overload_f3({1.0: 1.0}) 39 | #? str() 40 | overload_f3(lst) 41 | #? str() 42 | overload_f3(list_alias) 43 | #? str() 44 | overload_f3(list_str) 45 | 46 | # ------------------------- 47 | # Generics Matching 48 | # ------------------------- 49 | 50 | @overload 51 | def overload_f1(value: List[str]) -> str: ... 52 | 53 | 54 | @overload 55 | def overload_f1(value: Dict[str, str]) -> Dict[str, str]: ... 56 | 57 | def overload_f1(): 58 | pass 59 | 60 | #? str() 61 | overload_f1(['']) 62 | # jedi-diff: #? str() dict() 63 | #? 64 | overload_f1(1) 65 | #? dict() 66 | overload_f1({'': ''}) 67 | 68 | #? str() 69 | overload_f1(lst) 70 | # jedi-diff: #? str() dict() 71 | #? str() 72 | overload_f1(list_alias) 73 | #? str() 74 | overload_f1(list_str) 75 | # jedi-diff: #? str() dict() 76 | #? str() 77 | overload_f1(list_int) 78 | 79 | # ------------------------- 80 | # Broken Matching 81 | # ------------------------- 82 | T = TypeVar('T') 83 | 84 | @overload 85 | def broken_f1(value: 1) -> str: ... 86 | 87 | @overload 88 | def broken_f1(value: Tuple[T]) -> Tuple[T]: ... 89 | 90 | tup: Tuple[float] 91 | # jedi-diff: #? float() 92 | #? str() 93 | broken_f1(broken_f1(tup))[0] 94 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/selection_ranges.test: -------------------------------------------------------------------------------- 1 | [case selection_ranges_basic] 2 | 3 | def f(x): 4 | #? --codepoint-column 9 selection-ranges 5 | str(x + 1) 6 | 7 | #? --codepoint-column 11 selection-ranges 8 | str(x + 1) 9 | 10 | [out] 11 | __main__.py:4: Selection Ranges: 12 | - 4:8 - 4:9 13 | - 4:8 - 4:13 14 | - 4:4 - 4:14 15 | - 4:0 - 5:0 16 | - 2:0 - 9:0 17 | - 1:0 - 9:0 18 | __main__.py:7: Selection Ranges: 19 | - 7:8 - 7:13 20 | - 7:4 - 7:14 21 | - 7:0 - 8:0 22 | - 2:0 - 9:0 23 | - 1:0 - 9:0 24 | 25 | [case selection_ranges_special_positions] 26 | 27 | #? --codepoint-column 0 selection-ranges 28 | 1 + 1 29 | 30 | #? selection-ranges 31 | 1 + 1 32 | 33 | [out] 34 | __main__.py:3: Selection Ranges: 35 | - 3:0 - 3:1 36 | - 3:0 - 3:5 37 | - 3:0 - 4:0 38 | - 1:0 - 8:0 39 | __main__.py:6: Selection Ranges: 40 | - 6:4 - 6:5 41 | - 6:0 - 6:5 42 | - 6:0 - 7:0 43 | - 1:0 - 8:0 44 | 45 | [case selection_ranges_in_between_nodes] 46 | 47 | def f(x): 48 | 1 49 | 50 | #? selection-ranges 51 | 52 | 1 53 | #? --codepoint-column 9 selection-ranges 54 | 55 | 1 56 | [out] 57 | __main__.py:6: Selection Ranges: 58 | - 2:0 - 10:0 59 | - 1:0 - 11:0 60 | __main__.py:9: Selection Ranges: 61 | - 2:0 - 10:0 62 | - 1:0 - 11:0 63 | 64 | [case selection_ranges_with_class] 65 | 66 | #? --codepoint-column 9 selection-ranges 67 | class C[T](list[T]): 68 | pass 69 | 70 | #? --codepoint-column 16 selection-ranges 71 | class D[T](list[T]): 72 | pass 73 | 74 | def f(x): 75 | #? --codepoint-column 22 selection-ranges 76 | with open("asdf"): 77 | pass 78 | 79 | 1 80 | [out] 81 | __main__.py:3: Selection Ranges: 82 | - 3:8 - 3:9 83 | - 3:7 - 3:10 84 | - 3:0 - 7:0 85 | - 1:0 - 16:0 86 | __main__.py:7: Selection Ranges: 87 | - 7:16 - 7:17 88 | - 7:11 - 7:18 89 | - 7:0 - 15:0 90 | - 1:0 - 16:0 91 | __main__.py:12: Selection Ranges: 92 | - 12:18 - 12:24 93 | - 12:13 - 12:25 94 | - 12:13 - 12:26 95 | - 12:8 - 15:0 96 | - 10:4 - 15:0 97 | - 7:0 - 15:0 98 | - 1:0 - 16:0 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zuban 2 | 3 | Zuban is a high-performance Python Language Server and type checker implemented 4 | in Rust, by the author of [Jedi](https://github.com/davidhalter/jedi). 5 | Zuban is 20–200× faster than Mypy, while using roughly half the memory and CPU 6 | compared to Ty and Pyrefly. It offers both a PyRight-like mode and a 7 | Mypy-compatible mode, which behaves just like Mypy; supporting the same config 8 | files, command-line flags, and error messages. 9 | 10 | Most important LSP features are supported. Features include diagnostics, 11 | completions, goto, references, rename, hover and document highlights. 12 | 13 | Zuban passes over 95% of Mypy’s relevant test suite and offers comprehensive 14 | support for Python's [type system](https://htmlpreview.github.io/?https://github.com/python/typing/blob/main/conformance/results/results.html). 15 | 16 | - [Documentation](https://docs.zubanls.com) 17 | - [Website](https://zubanls.com) 18 | - [Releases](https://pypi.org/project/zuban/) 19 | 20 | ## Installation / Usage 21 | 22 | ``` 23 | pip install zuban # Installation 24 | 25 | zuban check # PyRight-like checking 26 | zuban mypy # Mypy compatibility mode 27 | zmypy # An alias for zuban mypy 28 | zuban server # An LSP server 29 | ``` 30 | 31 | If you want Zuban to pick up your dependencies, please activate the virtual env first. 32 | 33 | ### Local Installation 34 | 35 | You can install zuban **locally** by running: 36 | 37 | ``` 38 | pip install maturin 39 | git clone --recursive https://github.com/zubanls/zuban 40 | bash scripts/install-locally.sh 41 | ``` 42 | 43 | Note that your build will not properly work if submodules are not cloned. 44 | 45 | ## License 46 | 47 | This project is dual licensed: 48 | 49 | - **Open Source License**: [GNU Affero General Public License v3.0](LICENSE) (AGPL-3.0). 50 | You may use, modify, and distribute this project under the terms of the AGPL-3.0. 51 | - **Commercial License**: Available for organizations that prefer not to comply with the AGPL. 52 | Contact info (at) zubanls.com for commercial licensing options. 53 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/inheritance.py: -------------------------------------------------------------------------------- 1 | 2 | class Super(object): 3 | attribute = 3 4 | 5 | def func(self): 6 | return 1 7 | 8 | class Inner(): 9 | pass 10 | 11 | 12 | class Sub(Super): 13 | #? 13 Sub.attribute 14 | def attribute(self): 15 | pass 16 | 17 | # jedi-diff: #! 8 ['attribute = 3'] 18 | #! 8 ['def attribute(self):'] 19 | def attribute(self): 20 | pass 21 | 22 | #! 4 ['def func(self):'] 23 | func = 3 24 | # jedi-diff #! 12 ['class func(): pass'] 25 | #! 12 ['func = 3'] 26 | class func(): pass 27 | 28 | #! 8 ['class Inner'] 29 | def Inner(self): pass 30 | 31 | # ----------------- 32 | # Finding self 33 | # ----------------- 34 | 35 | class Test1: 36 | class Test2: 37 | def __init__(self): 38 | self.foo_nested = 0 39 | #? ['foo_nested'] 40 | self.foo_ 41 | #? 42 | self.foo_here 43 | 44 | def __init__(self, self2): 45 | self.foo_here = 3 46 | # jedi-diff #? ['foo_here', 'foo_in_func'] 47 | #? ['foo_here', 'foo_in_func', 'foo_not_on_self'] 48 | self.foo_ 49 | #? int() 50 | self.foo_here 51 | #? 52 | self.foo_nested 53 | # jedi-diff #? 54 | #? int() 55 | self.foo_not_on_self 56 | #? float() 57 | self.foo_in_func 58 | self2.foo_on_second = '' 59 | 60 | def closure(): 61 | self.foo_in_func = 4. 62 | 63 | def bar(self): 64 | self = 3 65 | self.foo_not_on_self = 3 66 | 67 | 68 | class SubTest(Test1): 69 | def __init__(self): 70 | self.foo_sub_class = list 71 | 72 | def bar(self): 73 | # jedi-diff: #? ['foo_here', 'foo_in_func', 'foo_sub_class'] 74 | #? ['foo_here', 'foo_in_func', 'foo_not_on_self', 'foo_sub_class'] 75 | self.foo_ 76 | #? int() 77 | self.foo_here 78 | #? 79 | self.foo_nested 80 | # jedi-diff #? 81 | #? int() 82 | self.foo_not_on_self 83 | -------------------------------------------------------------------------------- /deploy/pypi/zuban/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "zuban" 7 | # The version is dynamically upgraded and never committed. 8 | version = "0.4.0" 9 | description = "Zuban - The Zuban Language Server" 10 | authors = [{ name = "Dave Halter", email = "info@zubanls.com" }] 11 | readme = "README.rst" 12 | requires-python = ">=3.7" 13 | license = { file = "licenses.html" } 14 | keywords = [ 15 | "typechecking", 16 | "mypy", 17 | "static", 18 | "analysis", 19 | "autocompletion", 20 | ] 21 | classifiers = [ 22 | "Development Status :: 1 - Planning", 23 | "Environment :: Console", 24 | "Intended Audience :: Developers", 25 | "License :: Other/Proprietary License", 26 | "Operating System :: OS Independent", 27 | "Programming Language :: Python", 28 | "Programming Language :: Python :: 3.7", 29 | "Programming Language :: Python :: 3.8", 30 | "Programming Language :: Python :: 3.9", 31 | "Programming Language :: Python :: 3.10", 32 | "Programming Language :: Python :: 3.11", 33 | "Programming Language :: Python :: 3.12", 34 | "Programming Language :: Python :: 3.13", 35 | "Programming Language :: Python :: 3 :: Only", 36 | "Programming Language :: Python :: 3", 37 | "Programming Language :: Rust", 38 | "Topic :: Text Editors :: Integrated Development Environments (IDE)", 39 | "Topic :: Software Development :: Libraries :: Python Modules", 40 | "Topic :: Software Development :: Quality Assurance", 41 | "Topic :: Utilities", 42 | ] 43 | 44 | [project.urls] 45 | Repository = "https://github.com/zubanls/zubanls-python" 46 | Documentation = "https://docs.zubanls.com" 47 | 48 | [tool.maturin] 49 | bindings = "bin" 50 | manifest-path = "../../../crates/zuban/Cargo.toml" 51 | module-name = "zuban" 52 | python-source = "." 53 | locked = true 54 | exclude = [ 55 | ] 56 | include = [ 57 | "zuban/third_party/typeshed/LICENSE", 58 | "zuban/third_party/typeshed/README.md", 59 | "zuban/third_party/**/*.pyi", 60 | "zuban/third_party/django-stubs/LICENSE.md", 61 | "zuban/third_party/django-stubs/README.md", 62 | ] 63 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | resolver = "2" 4 | members = ["crates/*"] 5 | 6 | [workspace.package] 7 | version = "0.4.0" 8 | edition = "2024" 9 | rust-version = "1.89" 10 | homepage = "https://zubanls.com" 11 | authors = ["Dave Halter "] 12 | 13 | [workspace.dependencies] 14 | # Internal crates 15 | config = { path = "./crates/config" } 16 | licensing = { path = "./crates/licensing" } 17 | logging_config = { path = "./crates/logging_config" } 18 | parsa = { path = "./crates/parsa" } 19 | parsa_python = { path = "./crates/parsa_python" } 20 | parsa_python_cst = { path = "./crates/parsa_python_cst" } 21 | test_utils = { path = "./crates/test_utils" } 22 | typeshed_symbols = { path = "./crates/typeshed_symbols" } 23 | utils = { path = "./crates/utils" } 24 | vfs = { path = "./crates/vfs" } 25 | zmypy = { path = "./crates/zmypy" } 26 | zuban_python = { path = "./crates/zuban_python" } 27 | zubanls = { path = "./crates/zubanls" } 28 | 29 | # General third party dependencies 30 | anyhow = "*" 31 | clap = { version = "*", features = ["derive"] } 32 | colored = { version = "*" } 33 | crossbeam-channel = "*" 34 | dirs = "*" 35 | fnv = "*" 36 | lazy_static = "*" 37 | lsp-types = { version = "0.97.0", features = ["proposed"]} 38 | notify = "*" 39 | num-bigint = "*" 40 | num-traits = "*" 41 | rayon = "*" 42 | regex = "*" 43 | serde = { version = "*", features = ["derive"] } 44 | serde_json = "*" 45 | shellexpand = "*" 46 | toml_edit = "*" 47 | tracing = "*" 48 | tracing-subscriber = { version = "*", features = ["time", "local-time"] } 49 | tracing-appender = "*" 50 | 51 | # Dev dependencies 52 | insta = "*" 53 | 54 | [workspace.metadata.release] 55 | pre-release-hook = [ 56 | "sed", 57 | "-i", 58 | "s/^version = .*$/version = \"{{version}}\"/", 59 | # It seems like this is being run for every Cargo.toml 60 | "../../deploy/pypi/zuban/pyproject.toml", 61 | ] 62 | 63 | [profile.release] 64 | # Had debug = "line-tables-only" here, which is probably enough, but struggled 65 | # with test unwinding which has works on debug = . Use this for now 66 | debug = 1 67 | 68 | [workspace.lints.rust] 69 | explicit_outlives_requirements = "warn" 70 | unused_extern_crates = "warn" 71 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__import_from_dotted.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-106 8 | stmt: 1-33 9 | simple_stmts: 1-33 10 | simple_stmt: 1-32 11 | import_from: 1-32 12 | Keyword: 1-5 "from" 13 | Keyword: 6-7 "." 14 | Keyword: 8-14 "import" 15 | import_from_targets: 15-32 16 | Keyword: 15-16 "(" 17 | import_from_as_name: 16-26 18 | Name: 16-19 "foo" 19 | Keyword: 20-22 "as" 20 | name_def: 23-26 21 | Name: 23-26 "baz" 22 | Keyword: 26-27 "," 23 | import_from_as_name: 28-31 24 | name_def: 28-31 25 | Name: 28-31 "bar" 26 | Keyword: 31-32 ")" 27 | Newline: 32-33 "\n" 28 | stmt: 33-59 29 | simple_stmts: 33-59 30 | simple_stmt: 33-58 31 | import_from: 33-58 32 | Keyword: 33-37 "from" 33 | Keyword: 38-39 "." 34 | Keyword: 40-46 "import" 35 | import_from_targets: 47-58 36 | Keyword: 47-48 "(" 37 | import_from_as_name: 48-51 38 | name_def: 48-51 39 | Name: 48-51 "foo" 40 | Keyword: 51-52 "," 41 | import_from_as_name: 53-56 42 | name_def: 53-56 43 | Name: 53-56 "bar" 44 | Keyword: 56-57 "," 45 | Keyword: 57-58 ")" 46 | Newline: 58-59 "\n" 47 | stmt: 59-88 48 | simple_stmts: 59-88 49 | simple_stmt: 59-87 50 | import_from: 59-87 51 | Keyword: 59-63 "from" 52 | Keyword: 64-65 "." 53 | Keyword: 65-66 "." 54 | dotted_import_name: 66-69 55 | Name: 66-69 "foo" 56 | Keyword: 70-76 "import" 57 | import_from_targets: 77-87 58 | import_from_as_name: 77-87 59 | Name: 77-80 "bar" 60 | Keyword: 81-83 "as" 61 | name_def: 84-87 62 | Name: 84-87 "baz" 63 | Newline: 87-88 "\n" 64 | stmt: 88-106 65 | simple_stmts: 88-106 66 | simple_stmt: 88-105 67 | import_from: 88-105 68 | Keyword: 88-92 "from" 69 | Keyword: 93-96 "..." 70 | Keyword: 97-103 "import" 71 | import_from_targets: 104-105 72 | Keyword: 104-105 "*" 73 | Newline: 105-106 "\n" 74 | Endmarker: 106-106 "" 75 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__assignments_annotation.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-53 6 | stmt: 1-10 7 | simple_stmts: 1-10 8 | simple_stmt: 1-9 9 | assignment: 1-9 10 | single_target: 1-4 11 | name_def: 1-4 12 | Name: 1-4 "foo" 13 | annotation: 4-9 14 | Keyword: 4-5 ":" 15 | expression: 6-9 16 | atom: 6-9 17 | Name: 6-9 "bar" 18 | Newline: 9-10 "\n" 19 | stmt: 10-23 20 | simple_stmts: 10-23 21 | simple_stmt: 10-22 22 | assignment: 10-22 23 | single_target: 10-13 24 | name_def: 10-13 25 | Name: 10-13 "foo" 26 | annotation: 13-18 27 | Keyword: 13-14 ":" 28 | expression: 15-18 29 | atom: 15-18 30 | Name: 15-18 "int" 31 | Keyword: 19-20 "=" 32 | star_expressions: 21-22 33 | expression: 21-22 34 | atom: 21-22 35 | Number: 21-22 "3" 36 | Newline: 22-23 "\n" 37 | stmt: 23-36 38 | simple_stmts: 23-36 39 | simple_stmt: 23-35 40 | assignment: 23-35 41 | single_target: 23-30 42 | t_primary: 23-30 43 | atom: 23-26 44 | Name: 23-26 "foo" 45 | Keyword: 26-27 "." 46 | name_def: 27-30 47 | Name: 27-30 "bar" 48 | annotation: 30-35 49 | Keyword: 30-31 ":" 50 | expression: 32-35 51 | atom: 32-35 52 | Name: 32-35 "baz" 53 | Newline: 35-36 "\n" 54 | Error(stmt): 36-48 55 | Error(simple_stmts): 36-48 56 | simple_stmt: 36-48 57 | assignment: 36-48 58 | single_target: 36-39 59 | name_def: 36-39 60 | Name: 36-39 "foo" 61 | annotation: 39-44 62 | Keyword: 39-40 ":" 63 | expression: 41-44 64 | atom: 41-44 65 | Name: 41-44 "bar" 66 | Keyword: 45-46 "=" 67 | star_expressions: 47-48 68 | expression: 47-48 69 | atom: 47-48 70 | Name: 47-48 "a" 71 | Error(stmt): 49-50 72 | Error(Keyword): 49-50 "=" 73 | stmt: 51-53 74 | simple_stmts: 51-53 75 | simple_stmt: 51-52 76 | star_expressions: 51-52 77 | expression: 51-52 78 | atom: 51-52 79 | Number: 51-52 "4" 80 | Newline: 52-53 "\n" 81 | Endmarker: 53-53 "" 82 | 83 | -------------------------------------------------------------------------------- /crates/zubanls/src/notebooks.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, ops::Range}; 2 | 3 | use anyhow::bail; 4 | use lsp_types::Uri; 5 | use vfs::PathWithScheme; 6 | 7 | #[derive(Default)] 8 | pub(crate) struct Notebooks(HashMap); 9 | 10 | #[derive(Default)] 11 | pub(crate) struct Notebook { 12 | cells: Vec, 13 | } 14 | 15 | impl Notebooks { 16 | pub fn add_notebook(&mut self, notebook_uri: Uri) { 17 | self.0.insert(notebook_uri, Default::default()); 18 | } 19 | 20 | pub fn close_notebook(&mut self, notebook_uri: Uri) { 21 | self.0.remove(¬ebook_uri); 22 | } 23 | 24 | pub fn remove_cells(&mut self, notebook_uri: &Uri, range: Range) -> anyhow::Result<()> { 25 | let Some(notebook) = self.0.get_mut(notebook_uri) else { 26 | bail!("Expected a notebook for {notebook_uri:?}"); 27 | }; 28 | notebook.cells.drain(range.clone()); 29 | Ok(()) 30 | } 31 | 32 | pub fn add_cell_and_return_parent( 33 | &mut self, 34 | notebook_uri: &Uri, 35 | cell_path: PathWithScheme, 36 | at_nth_cell: usize, 37 | ) -> anyhow::Result> { 38 | let Some(notebook) = self.0.get_mut(notebook_uri) else { 39 | bail!("Expected a notebook for {notebook_uri:?}"); 40 | }; 41 | if at_nth_cell > notebook.cells.len() { 42 | bail!( 43 | "Expected to be able to insert a cell at index {at_nth_cell}, \ 44 | but had only {} entries", 45 | notebook.cells.len() 46 | ); 47 | } 48 | notebook.cells.insert(at_nth_cell, cell_path.clone()); 49 | Ok(if at_nth_cell > 0 { 50 | notebook.cells.get(at_nth_cell - 1).cloned() 51 | } else { 52 | None 53 | }) 54 | } 55 | 56 | pub fn nth_cell( 57 | &mut self, 58 | notebook_uri: &Uri, 59 | index: usize, 60 | ) -> anyhow::Result> { 61 | let Some(notebook) = self.0.get_mut(notebook_uri) else { 62 | bail!("Expected a notebook for {notebook_uri:?}"); 63 | }; 64 | Ok(notebook.cells.get(index).cloned()) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /crates/parsa/src/backtracking.rs: -------------------------------------------------------------------------------- 1 | use crate::grammar::Token; 2 | 3 | #[derive(Debug)] 4 | pub struct BacktrackingTokenizer> { 5 | tokenizer: I, 6 | tokens: Vec, 7 | next_index: usize, 8 | is_recording: bool, 9 | } 10 | 11 | impl> BacktrackingTokenizer { 12 | pub fn new(tokenizer: I) -> Self { 13 | Self { 14 | tokenizer, 15 | tokens: vec![], 16 | next_index: 0, 17 | is_recording: false, 18 | } 19 | } 20 | 21 | #[inline] 22 | pub fn start(&mut self, token: &T) -> usize { 23 | self.is_recording = true; 24 | if self.tokens.is_empty() { 25 | self.tokens.push(*token); 26 | self.next_index = 1; 27 | 0 28 | } else { 29 | self.next_index - 1 30 | } 31 | } 32 | 33 | #[inline] 34 | pub fn reset(&mut self, token_index: usize) { 35 | self.next_index = token_index; 36 | } 37 | 38 | #[inline] 39 | pub fn stop(&mut self) { 40 | self.is_recording = false; 41 | } 42 | } 43 | 44 | impl> Iterator for BacktrackingTokenizer { 45 | type Item = T; 46 | fn next(&mut self) -> Option { 47 | if !self.tokens.is_empty() { 48 | match self.tokens.get(self.next_index) { 49 | None => { 50 | let next = self.tokenizer.next(); 51 | if self.is_recording { 52 | self.next_index += 1; 53 | if let Some(token) = next { 54 | self.tokens.push(token); 55 | } 56 | } else { 57 | self.next_index = 0; 58 | self.tokens.clear(); 59 | } 60 | next 61 | } 62 | Some(next) => { 63 | self.next_index += 1; 64 | Some(*next) 65 | } 66 | } 67 | } else { 68 | // is_recording is only true if there's at least one token. 69 | self.tokenizer.next() 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__calls.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-55 6 | stmt: 1-10 7 | simple_stmts: 1-10 8 | simple_stmt: 1-9 9 | star_expressions: 1-9 10 | expression: 1-9 11 | primary: 1-9 12 | atom: 1-4 13 | Name: 1-4 "foo" 14 | Keyword: 4-5 "(" 15 | arguments: 5-8 16 | named_expression: 5-8 17 | expression: 5-8 18 | atom: 5-8 19 | Name: 5-8 "bar" 20 | Keyword: 8-9 ")" 21 | Newline: 9-10 "\n" 22 | stmt: 10-25 23 | simple_stmts: 10-25 24 | simple_stmt: 10-24 25 | star_expressions: 10-24 26 | expression: 10-24 27 | primary: 10-24 28 | atom: 10-13 29 | Name: 10-13 "foo" 30 | Keyword: 13-14 "(" 31 | arguments: 14-23 32 | kwargs: 14-23 33 | kwarg: 14-23 34 | Name: 14-21 "bar_foo" 35 | Keyword: 21-22 "=" 36 | expression: 22-23 37 | atom: 22-23 38 | Number: 22-23 "3" 39 | Keyword: 23-24 ")" 40 | Newline: 24-25 "\n" 41 | stmt: 25-41 42 | simple_stmts: 25-41 43 | simple_stmt: 25-40 44 | star_expressions: 25-40 45 | expression: 25-40 46 | primary: 25-40 47 | atom: 25-28 48 | Name: 25-28 "foo" 49 | Keyword: 28-29 "(" 50 | arguments: 29-39 51 | named_expression: 29-39 52 | walrus: 29-39 53 | name_def: 29-36 54 | Name: 29-36 "bar_baz" 55 | Keyword: 36-38 ":=" 56 | expression: 38-39 57 | atom: 38-39 58 | Number: 38-39 "2" 59 | Keyword: 39-40 ")" 60 | Newline: 40-41 "\n" 61 | stmt: 41-55 62 | simple_stmts: 41-55 63 | simple_stmt: 41-54 64 | star_expressions: 41-54 65 | expression: 41-54 66 | primary: 41-54 67 | atom: 41-44 68 | Name: 41-44 "foo" 69 | Keyword: 44-45 "(" 70 | arguments: 45-53 71 | kwargs: 45-53 72 | double_starred_expression: 45-53 73 | Keyword: 45-47 "**" 74 | expression: 47-53 75 | atom: 47-53 76 | Name: 47-53 "kwargs" 77 | Keyword: 53-54 ")" 78 | Newline: 54-55 "\n" 79 | Endmarker: 55-55 "" 80 | 81 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__soft_keyword_underscore.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 43 4 | expression: tree_to_string(tree) 5 | snapshot_kind: text 6 | --- 7 | file: 0-63 8 | stmt: 1-53 9 | match_stmt: 1-53 10 | Keyword: 1-6 "match" 11 | subject_expr: 7-8 12 | named_expression: 7-8 13 | expression: 7-8 14 | atom: 7-8 15 | Name: 7-8 "_" 16 | Keyword: 8-9 ":" 17 | Newline: 9-10 "\n" 18 | Indent: 14-14 "" 19 | case_block: 14-31 20 | Keyword: 14-18 "case" 21 | pattern: 19-24 22 | sequence_pattern: 19-24 23 | Keyword: 19-20 "[" 24 | open_sequence_pattern: 20-23 25 | pattern: 20-23 26 | literal_pattern: 20-23 27 | strings: 20-23 28 | String: 20-23 "'a'" 29 | Keyword: 23-24 "]" 30 | Keyword: 24-25 ":" 31 | block: 26-31 32 | simple_stmts: 26-31 33 | simple_stmt: 26-30 34 | star_expressions: 26-30 35 | expression: 26-30 36 | primary: 26-30 37 | atom: 26-27 38 | Name: 26-27 "_" 39 | Keyword: 27-28 "(" 40 | arguments: 28-29 41 | named_expression: 28-29 42 | expression: 28-29 43 | atom: 28-29 44 | Name: 28-29 "_" 45 | Keyword: 29-30 ")" 46 | Newline: 30-31 "\n" 47 | case_block: 35-53 48 | Keyword: 35-39 "case" 49 | pattern: 40-41 50 | wildcard_pattern: 40-41 51 | Keyword: 40-41 "_" 52 | Keyword: 41-42 ":" 53 | block: 42-53 54 | Newline: 42-43 "\n" 55 | Indent: 51-51 "" 56 | stmt: 51-53 57 | simple_stmts: 51-53 58 | simple_stmt: 51-52 59 | star_expressions: 51-52 60 | expression: 51-52 61 | atom: 51-52 62 | Name: 51-52 "_" 63 | Newline: 52-53 "\n" 64 | Dedent: 53-53 "" 65 | Dedent: 53-53 "" 66 | stmt: 53-63 67 | simple_stmts: 53-63 68 | simple_stmt: 53-62 69 | assignment: 53-62 70 | star_targets: 53-58 71 | name_def: 53-58 72 | Name: 53-58 "match" 73 | Keyword: 59-60 "=" 74 | star_expressions: 61-62 75 | expression: 61-62 76 | atom: 61-62 77 | Number: 61-62 "3" 78 | Newline: 62-63 "\n" 79 | Endmarker: 63-63 "" 80 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__dict_literal1.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-45 6 | stmt: 1-8 7 | simple_stmts: 1-8 8 | simple_stmt: 1-7 9 | star_expressions: 1-7 10 | expression: 1-7 11 | atom: 1-7 12 | Keyword: 1-2 "{" 13 | dict_content: 2-6 14 | dict_key_value: 2-6 15 | expression: 2-3 16 | atom: 2-3 17 | Number: 2-3 "1" 18 | Keyword: 3-4 ":" 19 | expression: 5-6 20 | atom: 5-6 21 | Number: 5-6 "2" 22 | Keyword: 6-7 "}" 23 | Newline: 7-8 "\n" 24 | stmt: 8-16 25 | simple_stmts: 8-16 26 | simple_stmt: 8-15 27 | star_expressions: 8-15 28 | expression: 8-15 29 | atom: 8-15 30 | Keyword: 8-9 "{" 31 | dict_content: 9-14 32 | dict_starred: 9-14 33 | Keyword: 9-11 "**" 34 | atom: 11-14 35 | Name: 11-14 "foo" 36 | Keyword: 14-15 "}" 37 | Newline: 15-16 "\n" 38 | stmt: 16-30 39 | simple_stmts: 16-30 40 | simple_stmt: 16-29 41 | star_expressions: 16-29 42 | expression: 16-29 43 | atom: 16-29 44 | Keyword: 16-17 "{" 45 | dict_content: 17-28 46 | dict_starred: 17-22 47 | Keyword: 17-19 "**" 48 | atom: 19-22 49 | Name: 19-22 "foo" 50 | Keyword: 22-23 "," 51 | dict_key_value: 24-28 52 | expression: 24-25 53 | atom: 24-25 54 | Number: 24-25 "1" 55 | Keyword: 25-26 ":" 56 | expression: 27-28 57 | atom: 27-28 58 | Number: 27-28 "2" 59 | Keyword: 28-29 "}" 60 | Newline: 29-30 "\n" 61 | stmt: 30-45 62 | simple_stmts: 30-45 63 | simple_stmt: 30-44 64 | star_expressions: 30-44 65 | expression: 30-44 66 | atom: 30-44 67 | Keyword: 30-31 "{" 68 | dict_content: 31-43 69 | dict_starred: 31-36 70 | Keyword: 31-33 "**" 71 | atom: 33-36 72 | Name: 33-36 "foo" 73 | Keyword: 36-37 "," 74 | dict_starred: 38-43 75 | Keyword: 38-40 "**" 76 | atom: 40-43 77 | Name: 40-43 "bar" 78 | Keyword: 43-44 "}" 79 | Newline: 44-45 "\n" 80 | Endmarker: 45-45 "" 81 | 82 | -------------------------------------------------------------------------------- /crates/licensing/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::{Parser, Subcommand}; 4 | 5 | #[derive(Parser)] 6 | #[command(version, about)] 7 | struct Cli { 8 | /// Subcommands 9 | #[command(subcommand)] 10 | command: Subcommands, 11 | } 12 | 13 | #[derive(Subcommand)] 14 | enum Subcommands { 15 | /// Verifies a license file in the default config place or a given path 16 | Verify { 17 | #[arg()] 18 | path: Option, 19 | }, 20 | Create { 21 | #[arg(long)] 22 | name: String, 23 | #[arg(long)] 24 | email: String, 25 | #[arg(long)] 26 | company: String, 27 | // Just use 366, even if that's technically wrong, it is ok all the time. 28 | #[arg(long, default_value_t = 366)] 29 | days: u64, 30 | #[arg(long)] 31 | write: bool, 32 | }, 33 | HexToRustByteLiterals { 34 | hex: String, 35 | }, 36 | } 37 | 38 | fn main() -> anyhow::Result<()> { 39 | match Cli::parse().command { 40 | Subcommands::Verify { path } => { 41 | match &path { 42 | Some(path) => licensing::verify_license_in_path(path)?, 43 | None => licensing::verify_license_in_config_dir()?, 44 | }; 45 | let path = path.unwrap_or(licensing::path_for_license()); 46 | println!("The license in {path:?} is valid"); 47 | } 48 | Subcommands::Create { 49 | name, 50 | email, 51 | company, 52 | days, 53 | write, 54 | } => { 55 | let jsonified = licensing::create_license(name, email, company, days)?; 56 | if write { 57 | let path = licensing::path_for_license(); 58 | if let Some(parent) = path.parent() { 59 | std::fs::create_dir_all(parent)?; 60 | } 61 | std::fs::write(path, jsonified)?; 62 | } else { 63 | println!("{}", jsonified); 64 | } 65 | } 66 | Subcommands::HexToRustByteLiterals { hex } => { 67 | let bytes = licensing::hex_string_key_to_bytes(hex)?; 68 | for &byte in bytes.iter() { 69 | print!("{byte}, "); 70 | } 71 | println!(); 72 | } 73 | } 74 | Ok(()) 75 | } 76 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__lambda_simple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-66 6 | stmt: 1-13 7 | simple_stmts: 1-13 8 | simple_stmt: 1-12 9 | star_expressions: 1-12 10 | expression: 1-12 11 | lambda: 1-12 12 | Keyword: 1-7 "lambda" 13 | Keyword: 7-8 ":" 14 | expression: 9-12 15 | atom: 9-12 16 | Keyword: 9-12 "..." 17 | Newline: 12-13 "\n" 18 | stmt: 13-27 19 | simple_stmts: 13-27 20 | simple_stmt: 13-26 21 | star_expressions: 13-26 22 | expression: 13-26 23 | lambda: 13-26 24 | Keyword: 13-19 "lambda" 25 | lambda_parameters: 20-21 26 | lambda_param_no_default: 20-21 27 | name_def: 20-21 28 | Name: 20-21 "a" 29 | Keyword: 21-22 ":" 30 | expression: 23-26 31 | atom: 23-26 32 | Keyword: 23-26 "..." 33 | Newline: 26-27 "\n" 34 | stmt: 27-45 35 | simple_stmts: 27-45 36 | simple_stmt: 27-44 37 | star_expressions: 27-44 38 | expression: 27-44 39 | lambda: 27-44 40 | Keyword: 27-33 "lambda" 41 | lambda_parameters: 34-39 42 | lambda_param_no_default: 34-35 43 | name_def: 34-35 44 | Name: 34-35 "a" 45 | Keyword: 35-36 "," 46 | lambda_param_no_default: 37-38 47 | name_def: 37-38 48 | Name: 37-38 "b" 49 | Keyword: 38-39 "," 50 | Keyword: 39-40 ":" 51 | expression: 41-44 52 | atom: 41-44 53 | Keyword: 41-44 "..." 54 | Newline: 44-45 "\n" 55 | stmt: 45-66 56 | simple_stmts: 45-66 57 | simple_stmt: 45-65 58 | star_expressions: 45-65 59 | expression: 45-65 60 | lambda: 45-65 61 | Keyword: 45-51 "lambda" 62 | lambda_parameters: 52-60 63 | lambda_param_with_default: 52-55 64 | name_def: 52-53 65 | Name: 52-53 "a" 66 | Keyword: 53-54 "=" 67 | expression: 54-55 68 | atom: 54-55 69 | Number: 54-55 "3" 70 | Keyword: 55-56 "," 71 | lambda_param_with_default: 57-60 72 | name_def: 57-58 73 | Name: 57-58 "b" 74 | Keyword: 58-59 "=" 75 | expression: 59-60 76 | atom: 59-60 77 | Number: 59-60 "4" 78 | Keyword: 60-61 ":" 79 | expression: 62-65 80 | atom: 62-65 81 | Keyword: 62-65 "..." 82 | Newline: 65-66 "\n" 83 | Endmarker: 66-66 "" 84 | 85 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/async_.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for all async use cases. 3 | 4 | Currently we're not supporting completion of them, but they should at least not 5 | raise errors or return extremely strange results. 6 | """ 7 | 8 | async def x(): 9 | return 1 10 | 11 | #? [] 12 | x.cr_awai 13 | 14 | #? ['cr_await'] 15 | x().cr_awai 16 | 17 | a = await x() 18 | #? int() 19 | a 20 | 21 | async def y(): 22 | argh = await x() 23 | #? int() 24 | argh 25 | #? ['__next__'] 26 | x().__await__().__next 27 | return 2 28 | 29 | class A(): 30 | @staticmethod 31 | async def b(c=1, d=2): 32 | return 1 33 | 34 | #! 9 ['def b(c=1, d=2):'] 35 | await A.b() 36 | 37 | #! 11 ['param d=2'] 38 | await A.b(d=3) 39 | 40 | class Awaitable: 41 | def __await__(self): 42 | yield None 43 | return '' 44 | 45 | async def awaitable_test(): 46 | foo = await Awaitable() 47 | #? str() 48 | foo 49 | 50 | async def asgen(): 51 | yield 1 52 | await asyncio.sleep(0) 53 | yield 2 54 | 55 | async def wrapper(): 56 | #? int() 57 | [x async for x in asgen()][0] 58 | 59 | async for y in asgen(): 60 | #? int() 61 | y 62 | 63 | #? ['__anext__'] 64 | asgen().__ane 65 | #? [] 66 | asgen().mro 67 | 68 | 69 | # Normal completion (#1092) 70 | normal_var1 = 42 71 | 72 | async def foo(): 73 | normal_var2 = False 74 | #? ['normal_var1', 'normal_var2'] 75 | normal_var 76 | 77 | 78 | class C: 79 | @classmethod 80 | async def async_for_classmethod(cls) -> "C": 81 | return 82 | 83 | async def async_for_method(cls) -> int: 84 | return 85 | 86 | 87 | async def f(): 88 | c = await C.async_for_method() 89 | #? int() 90 | c 91 | d = await C().async_for_method() 92 | #? int() 93 | d 94 | 95 | e = await C.async_for_classmethod() 96 | #? C() 97 | e 98 | f = await C().async_for_classmethod() 99 | #? C() 100 | f 101 | 102 | 103 | class AsyncCtxMgr: 104 | def some_method(): 105 | pass 106 | 107 | async def __aenter__(self): 108 | return self 109 | 110 | async def __aexit__(self, *args): 111 | pass 112 | 113 | 114 | async def asyncctxmgr(): 115 | async with AsyncCtxMgr() as acm: 116 | #? AsyncCtxMgr() 117 | acm 118 | #? ['some_method'] 119 | acm.som 120 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/lambdas.py: -------------------------------------------------------------------------------- 1 | # ----------------- 2 | # lambdas 3 | # ----------------- 4 | a = lambda: 3 5 | #? int() 6 | a() 7 | 8 | x = [] 9 | a = lambda x: x 10 | #? int() 11 | a(0) 12 | 13 | #? float() 14 | (lambda x: x)(3.0) 15 | 16 | arg_l = lambda x, y: y, x 17 | #? float() 18 | arg_l[0]('', 1.0) 19 | #? list() 20 | arg_l[1] 21 | 22 | arg_l = lambda x, y: (y, x) 23 | args = 1,"" 24 | result = arg_l(*args) 25 | #? tuple() 26 | result 27 | #? str() 28 | result[0] 29 | #? int() 30 | result[1] 31 | 32 | def with_lambda(callable_lambda, *args, **kwargs): 33 | return callable_lambda(1, *args, **kwargs) 34 | 35 | #? int() 36 | with_lambda(arg_l, 1.0)[1] 37 | #? float() 38 | with_lambda(arg_l, 1.0)[0] 39 | #? float() 40 | with_lambda(arg_l, y=1.0)[0] 41 | #? int() 42 | with_lambda(lambda x: x) 43 | #? float() 44 | with_lambda(lambda x, y: y, y=1.0) 45 | 46 | arg_func = lambda *args, **kwargs: (args[0], kwargs['a']) 47 | #? int() 48 | arg_func(1, 2, a='', b=10)[0] 49 | #? list() 50 | arg_func(1, 2, a=[], b=10)[1] 51 | 52 | # magic method 53 | a = lambda: 3 54 | #? ['__closure__'] 55 | a.__closure__ 56 | 57 | class C(): 58 | def __init__(self, foo=1.0): 59 | self.a = lambda: 1 60 | self.foo = foo 61 | 62 | def ret(self): 63 | return lambda: self.foo 64 | 65 | def with_param(self): 66 | return lambda x: x + self.a() 67 | 68 | lambd = lambda self: self.foo 69 | 70 | #? int() 71 | C().a() 72 | 73 | #? str() 74 | C('foo').ret()() 75 | 76 | index = C().with_param()(1) 77 | #? float() 78 | ['', 1, 1.0][index] 79 | 80 | #? float() 81 | C().lambd() 82 | #? int() 83 | C(1).lambd() 84 | 85 | 86 | def xy(param): 87 | def ret(a, b): 88 | return a + b 89 | 90 | return lambda b: ret(param, b) 91 | 92 | #? int() 93 | xy(1)(2) 94 | 95 | # ----------------- 96 | # lambda param (#379) 97 | # ----------------- 98 | class Test(object): 99 | def __init__(self, pred=lambda a, b: a): 100 | self.a = 1 101 | #? int() 102 | self.a 103 | #? float() 104 | pred(1.0, 2) 105 | 106 | # ----------------- 107 | # test_nocond in grammar (happens in list comprehensions with `if`) 108 | # ----------------- 109 | # Doesn't need to do anything yet. It should just not raise an error. These 110 | # nocond lambdas make no sense at all. 111 | 112 | #? int() 113 | [a for a in [1,2] if (lambda: 3)][0] 114 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/python_files/callable.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, TypeVar, Tuple, Generic 2 | 3 | T = TypeVar('T') 4 | U = TypeVar('U') 5 | V = TypeVar('V') 6 | W = TypeVar('W') 7 | X = TypeVar('X') 8 | 9 | def return_callable1(x: T) -> Callable[[U], Tuple[T, U]]: ... 10 | 11 | #? int() 12 | return_callable1(7)("")[0] 13 | #? str() 14 | return_callable1(7)("")[1] 15 | 16 | def return_callable2() -> Callable[[U], Tuple[T, U]]: ... 17 | 18 | #? 19 | return_callable2(7)("")[0] 20 | #? str() 21 | return_callable2(7)("")[1] 22 | 23 | class Foo(Generic[V]): 24 | def __init__(self, foo: V): ... 25 | def return_callable3(self, x: T) -> Callable[[U], Tuple[T, U, V]]: ... 26 | def return_callable4(self) -> Callable[[U], Tuple[T, U, V]]: ... 27 | def return_callable5(self, x: T) -> Callable[[U], Callable[[W], Tuple[T, U, V, W]]]: ... 28 | def return_callable6(self, x: T) -> Callable[[U], Tuple[Callable[[W], Tuple[T, U, V, W]], 29 | Callable[[W], Tuple[T, U, V, W]] 30 | ]]: ... 31 | def return_callable7(self, x: T) -> Callable[[U], Tuple[Callable[[W], Tuple[T, U, V, W]], 32 | Callable[[X], Tuple[T, U, V, X]] 33 | ]]: ... 34 | 35 | ret = Foo(1.0).return_callable3(7)("") 36 | #? int() 37 | ret[0] 38 | #? str() 39 | ret[1] 40 | #? float() 41 | ret[2] 42 | 43 | ret = Foo(1.0).return_callable4(7)("") 44 | #? int() 45 | ret[0] 46 | #? str() 47 | ret[1] 48 | #? float() 49 | ret[2] 50 | 51 | ret = Foo(1.0).return_callable5(7)("")(list) 52 | #? int() 53 | ret[0] 54 | #? str() 55 | ret[1] 56 | #? float() 57 | ret[2] 58 | #? list 59 | ret[3] 60 | 61 | callables = Foo(1.0).return_callable6(7)("") 62 | ret = callables[0](list) 63 | #? int() 64 | ret[0] 65 | #? str() 66 | ret[1] 67 | #? float() 68 | ret[2] 69 | #? 70 | ret[3] 71 | 72 | ret = callables[1](set) 73 | #? int() 74 | ret[0] 75 | #? str() 76 | ret[1] 77 | #? float() 78 | ret[2] 79 | #? 80 | ret[3] 81 | 82 | callables = Foo(1.0).return_callable7(7)("") 83 | ret = callables[0](list) 84 | #? int() 85 | ret[0] 86 | #? str() 87 | ret[1] 88 | #? float() 89 | ret[2] 90 | #? list 91 | ret[3] 92 | 93 | ret = callables[1](set) 94 | #? int() 95 | ret[0] 96 | #? str() 97 | ret[1] 98 | #? float() 99 | ret[2] 100 | #? set 101 | ret[3] 102 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/pep0526_variables.py: -------------------------------------------------------------------------------- 1 | """ 2 | PEP 526 introduced a way of using type annotations on variables. 3 | """ 4 | import typing 5 | 6 | asdf = '' 7 | asdf: int 8 | # This is not necessarily correct, but for now this is ok (at least no error). 9 | # jedi-diff #? int() 10 | #? str() 11 | asdf 12 | 13 | 14 | direct: int = NOT_DEFINED 15 | #? int() 16 | direct 17 | 18 | with_typing_module: typing.List[float] = NOT_DEFINED 19 | #? float() 20 | with_typing_module[0] 21 | 22 | somelist = [1, 2, 3, "A", "A"] 23 | element : int 24 | for element in somelist: 25 | #? int() 26 | element 27 | 28 | test_string: str = NOT_DEFINED 29 | #? str() 30 | test_string 31 | 32 | 33 | char: str 34 | for char in NOT_DEFINED: 35 | #? str() 36 | char 37 | 38 | 39 | # ------------------------- 40 | # instance/class vars 41 | # ------------------------- 42 | 43 | class Foo(): 44 | bar: int 45 | baz: typing.ClassVar[str] 46 | 47 | 48 | # jedi-diff #? 49 | #? int() 50 | Foo.bar 51 | #? int() 52 | Foo().bar 53 | #? str() 54 | Foo.baz 55 | #? str() 56 | Foo().baz 57 | 58 | class VarClass: 59 | var_instance1: int = '' 60 | var_instance2: float 61 | var_class1: typing.ClassVar[str] = 1 62 | var_class2: typing.ClassVar[bytes] 63 | 64 | def __init__(self): 65 | #? int() 66 | d.var_instance1 67 | #? float() 68 | d.var_instance2 69 | #? str() 70 | d.var_class1 71 | #? bytes() 72 | d.var_class2 73 | #? [] 74 | d.int 75 | #? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2'] 76 | self.var_ 77 | 78 | 79 | # jedi-diff: #? ['var_class1', 'var_class2', 'var_instance1'] 80 | #? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2'] 81 | VarClass.var_ 82 | #? int() 83 | VarClass.var_instance1 84 | # jedi-diff #? 85 | #? float() 86 | VarClass.var_instance2 87 | #? str() 88 | VarClass.var_class1 89 | #? bytes() 90 | VarClass.var_class2 91 | #? [] 92 | VarClass.int 93 | 94 | d = VarClass() 95 | #? ['var_class1', 'var_class2', 'var_instance1', 'var_instance2'] 96 | d.var_ 97 | #? int() 98 | d.var_instance1 99 | #? float() 100 | d.var_instance2 101 | #? str() 102 | d.var_class1 103 | #? bytes() 104 | d.var_class2 105 | #? [] 106 | d.int 107 | 108 | 109 | 110 | import dataclasses 111 | @dataclasses.dataclass 112 | class DC: 113 | name: int = 1 114 | 115 | #? int() 116 | DC().name 117 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/recursion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Code that might cause recursion issues (or has caused in the past). 3 | """ 4 | 5 | def Recursion(): 6 | def recurse(self): 7 | self.a = self.a 8 | self.b = self.b.recurse() 9 | 10 | #? 11 | Recursion().a 12 | 13 | #? 14 | Recursion().b 15 | 16 | 17 | class X(): 18 | def __init__(self): 19 | self.recursive = [1, 3] 20 | 21 | def annoying(self): 22 | self.recursive = [self.recursive[0]] 23 | 24 | def recurse(self): 25 | self.recursive = [self.recursive[1]] 26 | 27 | #? int() 28 | X().recursive[0] 29 | 30 | 31 | def to_list(iterable): 32 | return list(set(iterable)) 33 | 34 | 35 | def recursion1(foo): 36 | return to_list(to_list(foo)) + recursion1(foo) 37 | 38 | # jedi-diff: #? int() 39 | #? 40 | recursion1([1,2])[0] 41 | 42 | 43 | class FooListComp(): 44 | def __init__(self): 45 | self.recursive = [1] 46 | 47 | def annoying(self): 48 | self.recursive = [x for x in self.recursive] 49 | 50 | 51 | #? int() 52 | FooListComp().recursive[0] 53 | 54 | 55 | class InstanceAttributeIfs: 56 | def b(self): 57 | self.a1 = 1 58 | self.a2 = 1 59 | 60 | def c(self): 61 | self.a2 = '' 62 | 63 | def x(self): 64 | self.b() 65 | 66 | if self.a1 == 1: 67 | self.a1 = self.a1 + 1 68 | if self.a2 == UNDEFINED: 69 | self.a2 = self.a2 + 1 70 | 71 | #? int() 72 | self.a1 73 | # jedi-diff: #? int() str() 74 | #? int() 75 | self.a2 76 | 77 | #? int() 78 | InstanceAttributeIfs().a1 79 | # jedi-diff: #? int() str() 80 | #? int() 81 | InstanceAttributeIfs().a2 82 | 83 | 84 | 85 | class A: 86 | def a(self, b): 87 | for x in [self.a(i) for i in b]: 88 | #? 89 | x 90 | 91 | class B: 92 | def a(self, b): 93 | for i in b: 94 | for i in self.a(i): 95 | #? 96 | yield i 97 | 98 | 99 | foo = int 100 | foo = foo # type: foo 101 | #? int 102 | foo 103 | 104 | while True: 105 | bar = int 106 | bar = bar # type: bar 107 | # jedi-diff: #? int() 108 | #? int 109 | bar 110 | 111 | 112 | class Comprehension: 113 | def __init__(self, foo): 114 | self.foo = foo 115 | 116 | def update(self): 117 | self.foo = (self.foo,) 118 | 119 | 120 | # jedi-diff: #? int() tuple() 121 | #? 122 | Comprehension(1).foo[0] 123 | #? int() 124 | Comprehension(1).foo 125 | -------------------------------------------------------------------------------- /crates/zuban_python/src/type_helpers/bound_method.rs: -------------------------------------------------------------------------------- 1 | use super::{FirstParamProperties, Function, OverloadedFunction}; 2 | use crate::{ 3 | arguments::Args, 4 | inference_state::InferenceState, 5 | inferred::Inferred, 6 | matching::{OnTypeError, ResultContext}, 7 | type_::Type, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub(crate) enum BoundMethodFunction<'a> { 12 | Function(Function<'a, 'a>), 13 | Overload(OverloadedFunction<'a>), 14 | } 15 | 16 | #[derive(Debug)] 17 | pub(crate) struct BoundMethod<'a, 'b> { 18 | instance: &'b Type, 19 | function: BoundMethodFunction<'a>, 20 | } 21 | 22 | impl<'a, 'b> BoundMethod<'a, 'b> { 23 | pub fn new(instance: &'b Type, function: BoundMethodFunction<'a>) -> Self { 24 | Self { instance, function } 25 | } 26 | 27 | pub(crate) fn execute<'db>( 28 | &self, 29 | i_s: &InferenceState<'db, '_>, 30 | args: &dyn Args<'db>, 31 | result_context: &mut ResultContext, 32 | on_type_error: OnTypeError, 33 | ) -> Inferred { 34 | match &self.function { 35 | BoundMethodFunction::Function(f) => f.execute_internal( 36 | i_s, 37 | args, 38 | true, 39 | on_type_error, 40 | Some(&|| self.instance.clone()), 41 | result_context, 42 | ), 43 | BoundMethodFunction::Overload(f) => { 44 | f.execute_internal(i_s, args, true, result_context, on_type_error, &|| { 45 | Some(self.instance.clone()) 46 | }) 47 | } 48 | } 49 | } 50 | 51 | pub fn as_type<'db: 'a>(&self, i_s: &InferenceState<'db, '_>) -> Type { 52 | match &self.function { 53 | BoundMethodFunction::Function(f) => f.as_type( 54 | i_s, 55 | FirstParamProperties::Skip { 56 | to_self_instance: &|| self.instance.clone(), 57 | }, 58 | ), 59 | BoundMethodFunction::Overload(f) => f.as_type(i_s, Some(&|| self.instance.clone())), 60 | } 61 | } 62 | 63 | pub fn as_type_without_inferring_returns<'db: 'a>( 64 | &self, 65 | i_s: &InferenceState<'db, '_>, 66 | ) -> Type { 67 | match &self.function { 68 | BoundMethodFunction::Function(f) => f.as_type_without_inferring_return_type( 69 | i_s, 70 | FirstParamProperties::Skip { 71 | to_self_instance: &|| self.instance.clone(), 72 | }, 73 | ), 74 | BoundMethodFunction::Overload(_) => self.as_type(i_s), 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__assignments_star.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-77 6 | stmt: 1-20 7 | simple_stmts: 1-20 8 | simple_stmt: 1-19 9 | assignment: 1-19 10 | star_targets: 1-5 11 | star_target: 1-5 12 | Keyword: 1-2 "*" 13 | name_def: 2-5 14 | Name: 2-5 "foo" 15 | Keyword: 6-7 "=" 16 | star_targets: 8-12 17 | star_target: 8-12 18 | Keyword: 8-9 "*" 19 | name_def: 9-12 20 | Name: 9-12 "bar" 21 | Keyword: 13-14 "=" 22 | star_expressions: 15-19 23 | star_expression: 15-19 24 | Keyword: 15-16 "*" 25 | atom: 16-19 26 | Name: 16-19 "baz" 27 | Newline: 19-20 "\n" 28 | stmt: 20-54 29 | simple_stmts: 20-54 30 | simple_stmt: 20-53 31 | assignment: 20-53 32 | star_targets: 20-27 33 | name_def: 20-21 34 | Name: 20-21 "i" 35 | Keyword: 21-22 "," 36 | star_target: 23-27 37 | Keyword: 23-24 "*" 38 | name_def: 24-27 39 | Name: 24-27 "foo" 40 | Keyword: 28-29 "=" 41 | star_targets: 30-46 42 | star_target_brackets: 30-46 43 | Keyword: 30-31 "(" 44 | star_targets: 31-45 45 | star_target_brackets: 31-45 46 | Keyword: 31-32 "(" 47 | star_targets: 32-44 48 | name_def: 32-33 49 | Name: 32-33 "j" 50 | Keyword: 33-34 "," 51 | star_target_brackets: 35-44 52 | Keyword: 35-36 "[" 53 | star_targets: 36-43 54 | star_target: 36-40 55 | Keyword: 36-37 "*" 56 | name_def: 37-40 57 | Name: 37-40 "bar" 58 | Keyword: 40-41 "," 59 | name_def: 42-43 60 | Name: 42-43 "k" 61 | Keyword: 43-44 "]" 62 | Keyword: 44-45 ")" 63 | Keyword: 45-46 ")" 64 | Keyword: 47-48 "=" 65 | star_expressions: 49-53 66 | star_expression: 49-53 67 | Keyword: 49-50 "*" 68 | atom: 50-53 69 | Name: 50-53 "baz" 70 | Newline: 53-54 "\n" 71 | stmt: 54-77 72 | simple_stmts: 54-77 73 | simple_stmt: 54-76 74 | assignment: 54-76 75 | star_targets: 54-64 76 | star_target: 54-58 77 | Keyword: 54-55 "*" 78 | name_def: 55-58 79 | Name: 55-58 "foo" 80 | Keyword: 58-59 "," 81 | star_target: 60-64 82 | Keyword: 60-61 "*" 83 | name_def: 61-64 84 | Name: 61-64 "bar" 85 | Keyword: 65-66 "=" 86 | yield_expr: 67-76 87 | Keyword: 67-72 "yield" 88 | star_expressions: 73-76 89 | expression: 73-76 90 | atom: 73-76 91 | Name: 73-76 "baz" 92 | Newline: 76-77 "\n" 93 | Endmarker: 77-77 "" 94 | 95 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__cls.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-117 6 | stmt: 1-117 7 | class_def: 1-117 8 | Keyword: 1-6 "class" 9 | name_def: 7-10 10 | Name: 7-10 "Foo" 11 | Keyword: 10-11 "(" 12 | arguments: 11-17 13 | named_expression: 11-17 14 | expression: 11-17 15 | atom: 11-17 16 | Name: 11-17 "object" 17 | Keyword: 17-18 ")" 18 | Keyword: 18-19 ":" 19 | block: 19-117 20 | Newline: 19-20 "\n" 21 | Indent: 24-24 "" 22 | stmt: 24-74 23 | function_def: 24-74 24 | Keyword: 24-27 "def" 25 | name_def: 28-36 26 | Name: 28-36 "__init__" 27 | function_def_parameters: 36-54 28 | Keyword: 36-37 "(" 29 | parameters: 37-53 30 | param_no_default: 37-41 31 | name_def: 37-41 32 | Name: 37-41 "self" 33 | Keyword: 41-42 "," 34 | Keyword: 43-44 "/" 35 | Keyword: 44-45 "," 36 | param_no_default: 46-47 37 | name_def: 46-47 38 | Name: 46-47 "f" 39 | Keyword: 47-48 "," 40 | star_etc: 49-53 41 | Keyword: 49-50 "*" 42 | Keyword: 50-51 "," 43 | param_maybe_default: 52-53 44 | name_def: 52-53 45 | Name: 52-53 "g" 46 | Keyword: 53-54 ")" 47 | Keyword: 54-55 ":" 48 | block: 55-74 49 | Newline: 55-56 "\n" 50 | Indent: 64-64 "" 51 | stmt: 64-69 52 | simple_stmts: 64-69 53 | simple_stmt: 64-68 54 | pass_stmt: 64-68 55 | Keyword: 64-68 "pass" 56 | Newline: 68-69 "\n" 57 | Dedent: 74-74 "" 58 | stmt: 74-117 59 | decorated: 74-117 60 | decorators: 74-84 61 | decorator: 74-84 62 | Keyword: 74-75 "@" 63 | named_expression: 75-83 64 | expression: 75-83 65 | atom: 75-83 66 | Name: 75-83 "property" 67 | Newline: 83-84 "\n" 68 | function_def: 88-117 69 | Keyword: 88-91 "def" 70 | name_def: 92-96 71 | Name: 92-96 "prop" 72 | function_def_parameters: 96-102 73 | Keyword: 96-97 "(" 74 | parameters: 97-101 75 | param_no_default: 97-101 76 | name_def: 97-101 77 | Name: 97-101 "self" 78 | Keyword: 101-102 ")" 79 | Keyword: 102-103 ":" 80 | block: 103-117 81 | Newline: 103-104 "\n" 82 | Indent: 112-112 "" 83 | stmt: 112-117 84 | simple_stmts: 112-117 85 | simple_stmt: 112-116 86 | pass_stmt: 112-116 87 | Keyword: 112-116 "pass" 88 | Newline: 116-117 "\n" 89 | Dedent: 117-117 "" 90 | Dedent: 117-117 "" 91 | Endmarker: 117-117 "" 92 | 93 | -------------------------------------------------------------------------------- /crates/parsa_python_cst/src/bytes.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use parsa_python::PyNode; 4 | 5 | use crate::strings::{parse_hex, parse_python_octal, unpack_string_or_bytes_content}; 6 | 7 | pub fn parse_python_bytes_literal(literal: PyNode) -> Cow<[u8]> { 8 | let code = literal.as_code(); 9 | let unpacked = unpack_string_or_bytes_content(code); 10 | let inner_bytes = unpacked.inner.as_bytes(); 11 | if unpacked.had_raw_modifier { 12 | return Cow::Borrowed(inner_bytes); 13 | } 14 | let mut iterator = inner_bytes.iter().enumerate(); 15 | let mut previous_insert = 0; 16 | let mut out = None; 17 | while let Some((i, ch)) = iterator.next() { 18 | if ch == &b'\\' { 19 | if out.is_none() { 20 | out = Some(Vec::with_capacity(inner_bytes.len())); 21 | } 22 | let s = out.as_mut().unwrap(); 23 | s.extend_from_slice(&inner_bytes[previous_insert..i]); 24 | let (next_index, &ch) = iterator.next().unwrap(); 25 | previous_insert = i + 2; 26 | 27 | match ch { 28 | b'\\' | b'\'' | b'"' => s.push(ch), 29 | b'\n' => (), // Escaping a newline ignores it 30 | b'x' => { 31 | if let Some(c) = parse_hex(2, iterator.by_ref()) { 32 | s.push(c as u8); 33 | previous_insert += 2; 34 | } 35 | } 36 | b'a' => s.push(b'\x07'), // Bell 37 | b'b' => s.push(b'\x08'), // Backspace 38 | b't' => s.push(b'\x09'), // Tab 39 | b'n' => s.push(b'\n'), // Newline (line feed \x0a) 40 | b'v' => s.push(b'\x0b'), // Vertical tab 41 | b'f' => s.push(b'\x0c'), // Form Feed 42 | b'r' => s.push(b'\x0d'), // Carriage Return 43 | _ => { 44 | if let Some((len, octal)) = parse_python_octal(&inner_bytes[next_index..]) { 45 | for _ in 1..len { 46 | previous_insert += 1; 47 | iterator.next(); 48 | } 49 | // We simply cut off a bit, which is also how Python does it 50 | s.push(octal as u8) 51 | } else { 52 | // These literals are not allowed and generate warnings in Python 53 | s.push(b'\\'); 54 | s.push(ch); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | if let Some(mut out) = out { 61 | out.extend_from_slice(&inner_bytes[previous_insert..inner_bytes.len()]); 62 | Cow::Owned(out) 63 | } else { 64 | Cow::Borrowed(inner_bytes) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /crates/zuban/src/bin/zuban.rs: -------------------------------------------------------------------------------- 1 | use std::process::ExitCode; 2 | 3 | use clap::{Parser, Subcommand}; 4 | 5 | /// A fast type checker and language server for Python, written in Rust 6 | #[derive(Parser)] 7 | #[command(name = "zuban")] 8 | #[command(version, about)] 9 | struct Cli { 10 | #[command(subcommand)] 11 | command: Commands, 12 | } 13 | 14 | #[derive(Subcommand)] 15 | enum Commands { 16 | /// Checks a file or project for type errors 17 | Check(#[command(flatten)] zmypy::Cli), 18 | /// Type checks files like you would do when calling `mypy` 19 | Mypy(#[command(flatten)] zmypy::MypyCli), 20 | /// Starts an LSP server 21 | Server {}, 22 | } 23 | 24 | fn main() -> ExitCode { 25 | let run_check = |zmypy_config: zmypy::Cli| { 26 | if let Err(err) = logging_config::setup_logging_without_printing_errors_by_default() { 27 | panic!("{err}") 28 | }; 29 | zmypy::run(zmypy_config) 30 | }; 31 | match Cli::parse().command { 32 | Commands::Mypy(mypy_options) => run_check(zmypy::Cli { 33 | mypy_compatible: true, 34 | no_mypy_compatible: false, 35 | mypy_options, 36 | }), 37 | Commands::Check(zmypy_config) => run_check(zmypy_config), 38 | Commands::Server {} => match run_server() { 39 | Ok(()) => ExitCode::from(0), 40 | Err(err) => { 41 | eprintln!("{err}"); 42 | ExitCode::from(1) 43 | } 44 | }, 45 | } 46 | } 47 | 48 | fn run_server() -> anyhow::Result<()> { 49 | logging_config::setup_logging(None)?; 50 | 51 | // Logging to stderr. 52 | tracing::info!("Starting the Zuban Language Server"); 53 | 54 | event_loop_thread(move || { 55 | zubanls::run_server()?; 56 | Ok(()) 57 | })?; 58 | 59 | // Shut down gracefully. 60 | tracing::info!("Successfully shutting down server"); 61 | Ok(()) 62 | } 63 | 64 | /// The event loop thread is actually a secondary thread that we spawn from the 65 | /// _actual_ main thread. This secondary thread has a larger stack size 66 | /// than some OS defaults (Windows, for example) and is also designated as 67 | /// high-priority. 68 | pub(crate) fn event_loop_thread( 69 | func: impl FnOnce() -> anyhow::Result<()> + Send + 'static, 70 | ) -> anyhow::Result<()> { 71 | // Override OS defaults to avoid stack overflows on platforms with low stack size defaults. 72 | const MAIN_THREAD_STACK_SIZE: usize = 2 * 1024 * 1024; 73 | const MAIN_THREAD_NAME: &str = "zubanls:main"; 74 | let handle = std::thread::Builder::new() 75 | .name(MAIN_THREAD_NAME.into()) 76 | .stack_size(MAIN_THREAD_STACK_SIZE) 77 | .spawn(func)?; 78 | 79 | handle 80 | .join() 81 | .map_err(|e| anyhow::anyhow!("Error while joining the thread: {e:?}"))? 82 | } 83 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__complex_dedents_no_crash.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/parsa_python/tests/test_grammar.rs 3 | assertion_line: 41 4 | expression: tree_to_string(tree) 5 | --- 6 | file: 0-65 7 | stmt: 1-52 8 | function_def: 1-52 9 | Keyword: 1-4 "def" 10 | name_def: 5-6 11 | Name: 5-6 "f" 12 | function_def_parameters: 6-8 13 | Keyword: 6-7 "(" 14 | Keyword: 7-8 ")" 15 | Keyword: 8-9 ":" 16 | block: 9-52 17 | Newline: 9-10 "\n" 18 | Indent: 14-14 "" 19 | stmt: 14-23 20 | simple_stmts: 14-23 21 | simple_stmt: 14-22 22 | assignment: 14-22 23 | star_targets: 14-18 24 | name_def: 14-18 25 | Name: 14-18 "asdf" 26 | Keyword: 19-20 "=" 27 | star_expressions: 21-22 28 | expression: 21-22 29 | atom: 21-22 30 | Number: 21-22 "3" 31 | Newline: 22-23 "\n" 32 | stmt: 28-37 33 | broken_scope: 28-37 34 | Indent: 28-28 "" 35 | stmt: 28-33 36 | simple_stmts: 28-33 37 | simple_stmt: 28-32 38 | star_expressions: 28-32 39 | expression: 28-32 40 | atom: 28-32 41 | Name: 28-32 "asdf" 42 | Newline: 32-33 "\n" 43 | Dedent: 37-37 "" 44 | Error(stmt): 37-51 45 | Error(simple_stmts): 37-51 46 | Error(simple_stmt): 37-51 47 | Error(star_expressions): 37-51 48 | Error(expression): 37-51 49 | Error(ternary): 37-51 50 | Error(disjunction): 37-51 51 | Error(conjunction): 37-51 52 | Error(inversion): 37-51 53 | Error(comparison): 37-51 54 | Error(bitwise_or): 37-51 55 | Error(bitwise_xor): 37-51 56 | Error(bitwise_and): 37-51 57 | Error(shift_expr): 37-51 58 | Error(sum): 37-51 59 | Error(term): 37-51 60 | Error(factor): 37-51 61 | Error(power): 37-51 62 | Error(await_primary): 37-51 63 | Error(primary): 37-51 64 | atom: 37-41 65 | Name: 37-41 "asdf" 66 | Keyword: 41-42 "(" 67 | arguments: 47-51 68 | named_expression: 47-51 69 | expression: 47-51 70 | atom: 47-51 71 | Name: 47-51 "asdf" 72 | Dedent: 52-52 "" 73 | stmt: 52-65 74 | class_def: 52-65 75 | Keyword: 52-57 "class" 76 | name_def: 58-59 77 | Name: 58-59 "X" 78 | Keyword: 59-60 ":" 79 | block: 61-65 80 | simple_stmts: 61-65 81 | simple_stmt: 61-64 82 | star_expressions: 61-64 83 | expression: 61-64 84 | atom: 61-64 85 | Keyword: 61-64 "..." 86 | Newline: 64-65 "\n" 87 | Endmarker: 65-65 "" 88 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/isinstance.py: -------------------------------------------------------------------------------- 1 | # jedi-diff: added any types 2 | def f(ass, i, j, k): 3 | if isinstance(i, str): 4 | #? str() 5 | i 6 | 7 | if isinstance(j, (str, int)): 8 | #? str() int() 9 | j 10 | 11 | while isinstance(k, (str, int)): 12 | #? str() int() 13 | k 14 | 15 | if not isinstance(k, (str, int)): 16 | #? 17 | k 18 | 19 | while not isinstance(k, (str, int)): 20 | #? 21 | k 22 | 23 | assert isinstance(ass, int) 24 | #? int() 25 | ass 26 | 27 | assert isinstance(ass, str) 28 | assert not isinstance(ass, int) 29 | 30 | if 2: 31 | #? str() 32 | ass 33 | 34 | # ----------------- 35 | # invalid arguments 36 | # ----------------- 37 | 38 | if isinstance(wrong, str()): 39 | #? 40 | wrong 41 | 42 | # ----------------- 43 | # in functions 44 | # ----------------- 45 | 46 | import datetime 47 | 48 | 49 | def fooooo(obj): 50 | if isinstance(obj, datetime.datetime): 51 | #? datetime.datetime() 52 | obj 53 | 54 | 55 | def fooooo2(obj): 56 | if isinstance(obj, datetime.date): 57 | return obj 58 | else: 59 | return 1 60 | 61 | a 62 | # In earlier versions of Jedi, this returned both datetime and int, but now 63 | # Jedi does flow checks and realizes that the top return isn't executed. 64 | # jedi-diff: #? int() 65 | # int() datetime.date() 66 | fooooo2('') 67 | 68 | 69 | def isinstance_func(arr): 70 | for value in arr: 71 | if isinstance(value, dict): 72 | # Shouldn't fail, even with the dot. 73 | #? 17 dict() 74 | value. 75 | elif isinstance(value, int): 76 | x = value 77 | #? int() 78 | x 79 | 80 | # ----------------- 81 | # Names with multiple indices. 82 | # ----------------- 83 | 84 | class Test(): 85 | def __init__(self, testing): 86 | if isinstance(testing, str): 87 | self.testing = testing 88 | else: 89 | self.testing = 10 90 | 91 | def boo(self): 92 | if isinstance(self.testing, str): 93 | # TODO this is wrong, it should only be str. 94 | # jedi-diff: #? str() int() 95 | #? str() 96 | self.testing 97 | #? Test() 98 | self 99 | 100 | # ----------------- 101 | # Syntax 102 | # ----------------- 103 | 104 | # jedi-diff: #? 105 | #? bool() 106 | isinstance(1, int()) 107 | 108 | # ----------------- 109 | # more complicated arguments 110 | # ----------------- 111 | 112 | def ayyyyyye(obj): 113 | if isinstance(obj.obj, str): 114 | # jedi-diff: #? 115 | #? str() 116 | obj.obj 117 | #? 118 | obj 119 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/stubs.py: -------------------------------------------------------------------------------- 1 | from stub_folder import with_stub, stub_only, with_stub_folder, stub_only_folder 2 | 3 | # ------------------------- 4 | # Just files 5 | # ------------------------- 6 | 7 | #? int() 8 | stub_only.in_stub_only 9 | #? str() 10 | with_stub.in_with_stub_both 11 | #? int() 12 | with_stub.in_with_stub_python 13 | #? float() 14 | with_stub.in_with_stub_stub 15 | 16 | #! ['in_stub_only: int'] 17 | stub_only.in_stub_only 18 | #! ['in_with_stub_both = 5'] 19 | with_stub.in_with_stub_both 20 | #! ['in_with_stub_python = 8'] 21 | with_stub.in_with_stub_python 22 | #! ['in_with_stub_stub: float'] 23 | with_stub.in_with_stub_stub 24 | 25 | #? ['in_stub_only'] 26 | stub_only.in_ 27 | #? ['in_stub_only'] 28 | from stub_folder.stub_only import in_ 29 | #? ['in_with_stub_both', 'in_with_stub_python', 'in_with_stub_stub'] 30 | with_stub.in_ 31 | #? ['in_with_stub_both', 'in_with_stub_python', 'in_with_stub_stub'] 32 | from stub_folder.with_stub import in_ 33 | 34 | #? ['stub_only', 'stub_only_folder', 'with_stub', 'with_stub_folder'] 35 | from stub_folder. 36 | 37 | 38 | # ------------------------- 39 | # Folders 40 | # ------------------------- 41 | 42 | #? int() 43 | stub_only_folder.in_stub_only_folder 44 | #? str() 45 | with_stub_folder.in_with_stub_both_folder 46 | #? int() 47 | with_stub_folder.in_with_stub_python_folder 48 | #? float() 49 | with_stub_folder.in_with_stub_stub_folder 50 | 51 | #? ['in_stub_only_folder'] 52 | stub_only_folder.in_ 53 | #? ['in_with_stub_both_folder', 'in_with_stub_python_folder', 'in_with_stub_stub_folder'] 54 | with_stub_folder.in_ 55 | 56 | # ------------------------- 57 | # Folders nested with stubs 58 | # ------------------------- 59 | 60 | from stub_folder.with_stub_folder import nested_stub_only, nested_with_stub, \ 61 | python_only 62 | 63 | #? int() 64 | nested_stub_only.in_stub_only 65 | #? float() 66 | nested_with_stub.in_both 67 | #? str() 68 | nested_with_stub.in_python 69 | #? int() 70 | nested_with_stub.in_stub 71 | #? str() 72 | python_only.in_python 73 | 74 | #? ['in_stub_only_folder'] 75 | stub_only_folder.in_ 76 | #? ['in_with_stub_both_folder', 'in_with_stub_python_folder', 'in_with_stub_stub_folder'] 77 | with_stub_folder.in_ 78 | #? ['in_python'] 79 | python_only.in_ 80 | 81 | # ------------------------- 82 | # Folders nested with stubs 83 | # ------------------------- 84 | 85 | from stub_folder.stub_only_folder import nested_stub_only, nested_with_stub, \ 86 | python_only 87 | 88 | #? int() 89 | nested_stub_only.in_stub_only 90 | #? float() 91 | nested_with_stub.in_both 92 | #? str() 93 | nested_with_stub.in_python 94 | #? int() 95 | nested_with_stub.in_stub 96 | #? str() 97 | python_only.in_python 98 | 99 | #? ['in_stub_only'] 100 | nested_stub_only.in_ 101 | #? ['in_both', 'in_python', 'in_stub'] 102 | nested_with_stub.in_ 103 | #? ['in_python'] 104 | python_only.in_ 105 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/mypylike/tests/renames.test: -------------------------------------------------------------------------------- 1 | [case simple_rename] 2 | def f(): 3 | #? --codepoint-column 4 rename foo 4 | x = 1 5 | if x: 6 | x = 2 7 | #? rename foo2 8 | return x 9 | 10 | [out] 11 | __main__.py:3:rename: x -> foo 12 | __main__.py:3:rename: file:///mypylike/__main__.py: 13 | __main__.py:3:rename: - (3, 4) -> (3, 5) 14 | __main__.py:3:rename: - (4, 7) -> (4, 8) 15 | __main__.py:3:rename: - (5, 8) -> (5, 9) 16 | __main__.py:3:rename: - (7, 11) -> (7, 12) 17 | __main__.py:7:rename: x -> foo2 18 | __main__.py:7:rename: file:///mypylike/__main__.py: 19 | __main__.py:7:rename: - (3, 4) -> (3, 5) 20 | __main__.py:7:rename: - (4, 7) -> (4, 8) 21 | __main__.py:7:rename: - (5, 8) -> (5, 9) 22 | __main__.py:7:rename: - (7, 11) -> (7, 12) 23 | 24 | [out.windows] 25 | __main__.py:3:rename: x -> foo 26 | __main__.py:3:rename: file:///C:/mypylike/__main__.py: 27 | __main__.py:3:rename: - (3, 4) -> (3, 5) 28 | __main__.py:3:rename: - (4, 7) -> (4, 8) 29 | __main__.py:3:rename: - (5, 8) -> (5, 9) 30 | __main__.py:3:rename: - (7, 11) -> (7, 12) 31 | __main__.py:7:rename: x -> foo2 32 | __main__.py:7:rename: file:///C:/mypylike/__main__.py: 33 | __main__.py:7:rename: - (3, 4) -> (3, 5) 34 | __main__.py:7:rename: - (4, 7) -> (4, 8) 35 | __main__.py:7:rename: - (5, 8) -> (5, 9) 36 | __main__.py:7:rename: - (7, 11) -> (7, 12) 37 | 38 | [case rename_module] 39 | #? rename foo2 40 | import foo 41 | #? rename foo3 42 | foo 43 | 44 | [file foo.py] 45 | 46 | [file foo.pyi] 47 | 48 | [out] 49 | __main__.py:2:rename: foo -> foo2 50 | __main__.py:2:rename: file:///mypylike/__main__.py: 51 | __main__.py:2:rename: - (2, 7) -> (2, 10) 52 | __main__.py:2:rename: - (4, 0) -> (4, 3) 53 | __main__.py:2:rename: Rename: file:///mypylike/foo.py -> file:///mypylike/foo2.py 54 | __main__.py:2:rename: Rename: file:///mypylike/foo.pyi -> file:///mypylike/foo2.pyi 55 | __main__.py:4:rename: foo -> foo3 56 | __main__.py:4:rename: file:///mypylike/__main__.py: 57 | __main__.py:4:rename: - (2, 7) -> (2, 10) 58 | __main__.py:4:rename: - (4, 0) -> (4, 3) 59 | __main__.py:4:rename: Rename: file:///mypylike/foo.py -> file:///mypylike/foo3.py 60 | __main__.py:4:rename: Rename: file:///mypylike/foo.pyi -> file:///mypylike/foo3.pyi 61 | 62 | [out.windows] 63 | __main__.py:2:rename: foo -> foo2 64 | __main__.py:2:rename: file:///C:/mypylike/__main__.py: 65 | __main__.py:2:rename: - (2, 7) -> (2, 10) 66 | __main__.py:2:rename: - (4, 0) -> (4, 3) 67 | __main__.py:2:rename: Rename: file:///C:/mypylike/foo.py -> file:///C:/mypylike/foo2.py 68 | __main__.py:2:rename: Rename: file:///C:/mypylike/foo.pyi -> file:///C:/mypylike/foo2.pyi 69 | __main__.py:4:rename: foo -> foo3 70 | __main__.py:4:rename: file:///C:/mypylike/__main__.py: 71 | __main__.py:4:rename: - (2, 7) -> (2, 10) 72 | __main__.py:4:rename: - (4, 0) -> (4, 3) 73 | __main__.py:4:rename: Rename: file:///C:/mypylike/foo.py -> file:///C:/mypylike/foo3.py 74 | __main__.py:4:rename: Rename: file:///C:/mypylike/foo.pyi -> file:///C:/mypylike/foo3.pyi 75 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/named_param.py: -------------------------------------------------------------------------------- 1 | """ 2 | Named Params: 3 | >>> def a(abc): pass 4 | ... 5 | >>> a(abc=3) # <- this stuff (abc) 6 | """ 7 | 8 | def a(abc): 9 | pass 10 | 11 | #? 5 ['abc='] 12 | a(abc) 13 | 14 | 15 | def a(*some_args, **some_kwargs): 16 | pass 17 | 18 | #? 11 [] 19 | a(some_args) 20 | 21 | #? 13 [] 22 | a(some_kwargs) 23 | 24 | def multiple(foo, bar): 25 | pass 26 | 27 | #? 17 ['bar='] 28 | multiple(foo, bar) 29 | 30 | #? ['bar='] 31 | multiple(foo, bar 32 | 33 | my_lambda = lambda lambda_param: lambda_param + 1 34 | #? 22 ['lambda_param='] 35 | my_lambda(lambda_param) 36 | 37 | # __call__ / __init__ 38 | class Test(object): 39 | def __init__(self, hello_other): 40 | pass 41 | 42 | def __call__(self, hello): 43 | pass 44 | 45 | def test(self, blub): 46 | pass 47 | 48 | #? 10 ['hello_other='] 49 | Test(hello=) 50 | #? 12 ['hello='] 51 | Test()(hello=) 52 | #? 11 [] 53 | Test()(self=) 54 | #? 16 [] 55 | Test().test(self=) 56 | #? 16 ['blub='] 57 | Test().test(blub=) 58 | 59 | # builtins 60 | 61 | #? 12 [] 62 | any(iterable=) 63 | 64 | 65 | def foo(xyz): 66 | pass 67 | 68 | #? 7 ['xyz='] 69 | @foo(xy) 70 | def x1(): pass 71 | 72 | #? 7 ['xyz='] 73 | foo(xyz) 74 | # No completion should be possible if it's not a simple name 75 | #? 17 [] 76 | x = " "; foo(x.xyz) 77 | #? 17 [] 78 | x = " "; foo([xyz) 79 | #? 20 [] 80 | x = " "; foo(z[f,xyz) 81 | #? 18 [] 82 | x = " "; foo(z[xyz) 83 | #? 20 [] 84 | x = " "; foo(xyz[xyz) 85 | #? 20 [] 86 | x = " "; foo(xyz[(xyz) 87 | 88 | #? 8 ['xyz='] 89 | @foo(xyz) 90 | def x2(): pass 91 | 92 | @str 93 | #? 8 ['xyz='] 94 | @foo(xyz) 95 | def x3(): pass 96 | 97 | # ----------------- 98 | # Only keyword arguments are valid 99 | # ----------------- 100 | 101 | def x4(bam, *, bar, baz): 102 | pass 103 | def y(bam, *bal, bar, baz, **bag): 104 | pass 105 | def z(bam, bar=2, *, bas=1): 106 | pass 107 | 108 | #? 8 ['bar=', 'baz='] 109 | x4(1, ba) 110 | 111 | #? 15 ['baz='] 112 | x4(1, bar=2, ba) 113 | #? 8 ['bar=', 'baz='] 114 | x4(1, ba, baz=3) 115 | #? 15 ['baz='] 116 | x4(1, bar=2, baz=3) 117 | #? 8 ['BaseException', 'BaseExceptionGroup'] 118 | x4(basee) 119 | #? 23 ['bar=', 'baz='] 120 | x4(1, 2, 3, 4, 5, 6, bar=2) 121 | 122 | #? 14 ['baz='] 123 | y(1, bar=2, ba) 124 | #? 7 ['bar=', 'BaseException', 'BaseExceptionGroup', 'baz='] 125 | y(1, ba, baz=3) 126 | #? 14 ['baz='] 127 | y(1, bar=2, baz=3) 128 | #? 7 ['BaseException', 'BaseExceptionGroup'] 129 | y(basee) 130 | #? 22 ['bar=', 'BaseException', 'BaseExceptionGroup', 'baz='] 131 | y(1, 2, 3, 4, 5, 6, bar=2) 132 | 133 | #? 11 ['bar=', 'bas='] 134 | z(bam=1, bar=2, bas=3) 135 | #? 8 ['bas=', 'BaseException', 'BaseExceptionGroup'] 136 | z(1, bas=2) 137 | #? 12 ['BaseException', 'BaseExceptionGroup'] 138 | z(1, bas=bas) 139 | 140 | #? 19 ['dict'] 141 | z(1, bas=bas, **dic) 142 | #? 18 ['dict'] 143 | z(1, bas=bas, *dic) 144 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__assignments_with_call.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-63 6 | stmt: 1-15 7 | simple_stmts: 1-15 8 | simple_stmt: 1-14 9 | assignment: 1-14 10 | star_targets: 1-10 11 | t_primary: 1-10 12 | t_primary: 1-6 13 | atom: 1-4 14 | Name: 1-4 "foo" 15 | Keyword: 4-5 "(" 16 | Keyword: 5-6 ")" 17 | Keyword: 6-7 "." 18 | name_def: 7-10 19 | Name: 7-10 "bar" 20 | Keyword: 11-12 "=" 21 | star_expressions: 13-14 22 | expression: 13-14 23 | atom: 13-14 24 | Number: 13-14 "1" 25 | Newline: 14-15 "\n" 26 | stmt: 15-34 27 | simple_stmts: 15-34 28 | simple_stmt: 15-33 29 | assignment: 15-33 30 | star_targets: 15-29 31 | t_primary: 15-29 32 | t_primary: 15-26 33 | t_primary: 15-22 34 | atom: 15-18 35 | Name: 15-18 "foo" 36 | Keyword: 18-19 "(" 37 | arguments: 19-21 38 | named_expression: 19-20 39 | expression: 19-20 40 | atom: 19-20 41 | Number: 19-20 "1" 42 | Keyword: 20-21 "," 43 | Keyword: 21-22 ")" 44 | Keyword: 22-23 "." 45 | Name: 23-26 "bar" 46 | Keyword: 26-27 "[" 47 | named_expression: 27-28 48 | expression: 27-28 49 | atom: 27-28 50 | Number: 27-28 "1" 51 | Keyword: 28-29 "]" 52 | Keyword: 30-31 "=" 53 | star_expressions: 32-33 54 | expression: 32-33 55 | atom: 32-33 56 | Number: 32-33 "1" 57 | Newline: 33-34 "\n" 58 | stmt: 34-63 59 | simple_stmts: 34-63 60 | simple_stmt: 34-62 61 | assignment: 34-62 62 | star_targets: 34-58 63 | t_primary: 34-58 64 | t_primary: 34-55 65 | t_primary: 34-51 66 | atom: 34-37 67 | Name: 34-37 "foo" 68 | Keyword: 37-38 "(" 69 | comprehension: 38-50 70 | named_expression: 38-39 71 | expression: 38-39 72 | atom: 38-39 73 | Name: 38-39 "a" 74 | for_if_clauses: 40-50 75 | sync_for_if_clause: 40-50 76 | Keyword: 40-43 "for" 77 | star_targets: 44-45 78 | name_def: 44-45 79 | Name: 44-45 "a" 80 | Keyword: 46-48 "in" 81 | atom: 49-50 82 | Name: 49-50 "b" 83 | Keyword: 50-51 ")" 84 | Keyword: 51-52 "." 85 | Name: 52-55 "bar" 86 | Keyword: 55-56 "[" 87 | named_expression: 56-57 88 | expression: 56-57 89 | atom: 56-57 90 | Number: 56-57 "1" 91 | Keyword: 57-58 "]" 92 | Keyword: 59-60 "=" 93 | star_expressions: 61-62 94 | expression: 61-62 95 | atom: 61-62 96 | Number: 61-62 "1" 97 | Newline: 62-63 "\n" 98 | Endmarker: 63-63 "" 99 | 100 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__exception_group.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-115 6 | stmt: 1-115 7 | try_stmt: 1-115 8 | Keyword: 1-4 "try" 9 | Keyword: 4-5 ":" 10 | block: 5-14 11 | Newline: 5-6 "\n" 12 | Indent: 10-10 "" 13 | stmt: 10-14 14 | simple_stmts: 10-14 15 | simple_stmt: 10-13 16 | star_expressions: 10-13 17 | expression: 10-13 18 | atom: 10-13 19 | Keyword: 10-13 "..." 20 | Newline: 13-14 "\n" 21 | Dedent: 14-14 "" 22 | except_star_block: 14-41 23 | Keyword: 14-20 "except" 24 | Keyword: 20-21 "*" 25 | except_expression: 22-31 26 | expression: 22-31 27 | atom: 22-31 28 | Name: 22-31 "SpamError" 29 | Keyword: 31-32 ":" 30 | block: 32-41 31 | Newline: 32-33 "\n" 32 | Indent: 37-37 "" 33 | stmt: 37-41 34 | simple_stmts: 37-41 35 | simple_stmt: 37-40 36 | star_expressions: 37-40 37 | expression: 37-40 38 | atom: 37-40 39 | Keyword: 37-40 "..." 40 | Newline: 40-41 "\n" 41 | Dedent: 41-41 "" 42 | except_star_block: 41-72 43 | Keyword: 41-47 "except" 44 | Keyword: 47-48 "*" 45 | except_expression: 49-62 46 | expression: 49-57 47 | atom: 49-57 48 | Name: 49-57 "FooError" 49 | Keyword: 58-60 "as" 50 | name_def: 61-62 51 | Name: 61-62 "e" 52 | Keyword: 62-63 ":" 53 | block: 63-72 54 | Newline: 63-64 "\n" 55 | Indent: 68-68 "" 56 | stmt: 68-72 57 | simple_stmts: 68-72 58 | simple_stmt: 68-71 59 | star_expressions: 68-71 60 | expression: 68-71 61 | atom: 68-71 62 | Keyword: 68-71 "..." 63 | Newline: 71-72 "\n" 64 | Dedent: 72-72 "" 65 | except_star_block: 72-115 66 | Keyword: 72-78 "except" 67 | Keyword: 78-79 "*" 68 | except_expression: 80-105 69 | expression: 80-100 70 | atom: 80-100 71 | Keyword: 80-81 "(" 72 | tuple_content: 81-99 73 | named_expression: 81-89 74 | expression: 81-89 75 | atom: 81-89 76 | Name: 81-89 "BarError" 77 | Keyword: 89-90 "," 78 | star_named_expressions: 91-99 79 | named_expression: 91-99 80 | expression: 91-99 81 | atom: 91-99 82 | Name: 91-99 "BazError" 83 | Keyword: 99-100 ")" 84 | Keyword: 101-103 "as" 85 | name_def: 104-105 86 | Name: 104-105 "e" 87 | Keyword: 105-106 ":" 88 | block: 106-115 89 | Newline: 106-107 "\n" 90 | Indent: 111-111 "" 91 | stmt: 111-115 92 | simple_stmts: 111-115 93 | simple_stmt: 111-114 94 | star_expressions: 111-114 95 | expression: 111-114 96 | atom: 111-114 97 | Keyword: 111-114 "..." 98 | Newline: 114-115 "\n" 99 | Dedent: 115-115 "" 100 | Endmarker: 115-115 "" 101 | 102 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/dynamic_params.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is used for dynamic object completion. 3 | Jedi tries to guess param types with a backtracking approach. 4 | """ 5 | def func(a, default_arg=2): 6 | #? int() 7 | default_arg 8 | #? int() str() 9 | return a 10 | 11 | #? int() 12 | func(1) 13 | 14 | func 15 | 16 | int(1) + (int(2))+ func('') 17 | 18 | # Again the same function, but with another call. 19 | def func(a): 20 | #? float() 21 | return a 22 | 23 | func(1.0) 24 | 25 | # Again the same function, but with no call. 26 | def func(a): 27 | #? 28 | return a 29 | 30 | def func(a): 31 | #? float() 32 | return a 33 | str(func(1.0)) 34 | 35 | # ----------------- 36 | # *args, **args 37 | # ----------------- 38 | def arg(*args): 39 | #? tuple() 40 | args 41 | #? int() 42 | args[0] 43 | 44 | arg(1,"") 45 | # ----------------- 46 | # decorators 47 | # ----------------- 48 | def def_func(f): 49 | def wrapper(*args, **kwargs): 50 | return f(*args, **kwargs) 51 | return wrapper 52 | 53 | @def_func 54 | def func(c): 55 | #? str() 56 | return c 57 | 58 | #? str() 59 | func("something") 60 | 61 | @def_func 62 | def func(c=1): 63 | #? float() 64 | return c 65 | 66 | func(1.0) 67 | 68 | def tricky_decorator(func): 69 | def wrapper(*args): 70 | return func(1, *args) 71 | 72 | return wrapper 73 | 74 | 75 | @tricky_decorator 76 | def func(a, b): 77 | #? int() 78 | a 79 | #? float() 80 | b 81 | 82 | func(1.0) 83 | 84 | # Needs to be here, because in this case func is an import -> shouldn't lead to 85 | # exceptions. 86 | import sys as func 87 | func.sys 88 | 89 | # ----------------- 90 | # classes 91 | # ----------------- 92 | 93 | class A(): 94 | def __init__(self, a): 95 | #? str() 96 | a 97 | 98 | A("s") 99 | 100 | class A(): 101 | def __init__(self, a): 102 | #? int() 103 | a 104 | self.a = a 105 | 106 | def test(self, a): 107 | #? float() 108 | a 109 | self.c = self.test2() 110 | 111 | def test2(self): 112 | #? int() 113 | return self.a 114 | 115 | def test3(self): 116 | #? int() 117 | self.test2() 118 | #? int() 119 | self.c 120 | 121 | A(3).test(2.0) 122 | A(3).test2() 123 | 124 | 125 | def from_class(x): 126 | #? 127 | x 128 | 129 | from UNDEFINED import from_class 130 | 131 | class Foo(from_class(1),): 132 | pass 133 | 134 | # ----------------- 135 | # comprehensions 136 | # ----------------- 137 | 138 | def from_comprehension(foo): 139 | #? int() float() 140 | return foo 141 | 142 | [from_comprehension(1.0) for n in (1,)] 143 | [from_comprehension(n) for n in (1,)] 144 | 145 | # ----------------- 146 | # lambdas 147 | # ----------------- 148 | 149 | #? int() 150 | x_lambda = lambda x: x 151 | 152 | x_lambda(1) 153 | 154 | class X(): 155 | #? str() 156 | x_method = lambda self, a: a 157 | 158 | 159 | X().x_method('') 160 | -------------------------------------------------------------------------------- /crates/zuban_python/tests/blackbox/from_jedi_python_files/ordering.py: -------------------------------------------------------------------------------- 1 | # ----------------- 2 | # normal 3 | # ----------------- 4 | a = "" 5 | a = 1 6 | 7 | #? int() 8 | a 9 | #? [] 10 | a.append 11 | 12 | a = list 13 | 14 | b = 1; b = "" 15 | #? str() 16 | b 17 | 18 | # temp should not be accessible before definition 19 | #? [] 20 | temp 21 | 22 | a = 1 23 | temp = b; 24 | b = a 25 | a = temp 26 | #? int() 27 | b 28 | #? int() 29 | b 30 | #? str() 31 | a 32 | 33 | a = tuple 34 | if 1: 35 | a = list 36 | 37 | #? ['append'] 38 | a.append 39 | #? ['index'] 40 | a.index 41 | 42 | # ----------------- 43 | # tuples exchanges 44 | # ----------------- 45 | a, b = 1, "" 46 | #? int() 47 | a 48 | #? str() 49 | b 50 | 51 | b, a = a, b 52 | #? int() 53 | b 54 | #? str() 55 | a 56 | 57 | b, a = a, b 58 | #? int() 59 | a 60 | #? str() 61 | b 62 | 63 | # ----------------- 64 | # function 65 | # ----------------- 66 | def a(a=3): 67 | #? int() 68 | a 69 | #? [] 70 | a.func 71 | return a 72 | 73 | #? int() 74 | a(2) 75 | #? [] 76 | a(2).func 77 | 78 | a_param = "" 79 | def func(a_param): 80 | # should not be str 81 | #? [] 82 | a_param.uppe 83 | 84 | from os import path 85 | 86 | 87 | # should not return a function, because `a` is a function above 88 | def f(b, a): return a 89 | #? [] 90 | f(b=3). 91 | 92 | # ----------------- 93 | # closure 94 | # ----------------- 95 | 96 | def x(): 97 | a = 0 98 | 99 | def x(): 100 | return a 101 | 102 | a = 3.0 103 | return x() 104 | 105 | #? float() 106 | x() 107 | 108 | # ----------------- 109 | # class 110 | # ----------------- 111 | class A(object): 112 | a = "" 113 | a = 3 114 | #? int() 115 | a 116 | a = list() 117 | def __init__(self): 118 | self.b = "" 119 | 120 | def before(self): 121 | self.b = 3 122 | # TODO should this be so? include entries after cursor? 123 | # jedi-diff: #? int() str() list 124 | #? str() 125 | self.b 126 | self.b = list 127 | 128 | self.a = 1 129 | # jedi-diff: #? str() int() 130 | #? list() 131 | self.a 132 | 133 | #? ['after'] 134 | self.after 135 | 136 | self.c = 3 137 | # jedi-diff: #? int() 138 | #? set() 139 | self.c 140 | 141 | def after(self): 142 | self.a = '' 143 | 144 | c = set() 145 | 146 | #? list() 147 | A.a 148 | 149 | a = A() 150 | #? ['after'] 151 | a.after 152 | #? [] 153 | a.upper 154 | #? [] 155 | a.append 156 | #? [] 157 | a.real 158 | 159 | # jedi-diff: #? str() int() 160 | #? list() 161 | a.a 162 | 163 | a = 3 164 | class a(): 165 | def __init__(self, a): 166 | self.a = a 167 | 168 | #? float() 169 | a(1.0).a 170 | #? 171 | a().a 172 | 173 | # ----------------- 174 | # imports 175 | # ----------------- 176 | 177 | math = 3 178 | import math 179 | #? ['cosh'] 180 | math.cosh 181 | #? [] 182 | math.real 183 | 184 | math = 3 185 | #? int() 186 | math 187 | #? [] 188 | math.cos 189 | 190 | # do the same for star imports 191 | cosh = 3 192 | from math import * 193 | # cosh doesn't work, but that's not a problem, star imports should be at the 194 | # start of EVERY script! 195 | cosh.real 196 | 197 | cosh = 3 198 | #? int() 199 | cosh 200 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__for_stmt.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-82 6 | stmt: 1-25 7 | for_stmt: 1-25 8 | Keyword: 1-4 "for" 9 | star_targets: 5-6 10 | name_def: 5-6 11 | Name: 5-6 "x" 12 | Keyword: 7-9 "in" 13 | star_expressions: 10-15 14 | expression: 10-15 15 | atom: 10-15 16 | Keyword: 10-11 "[" 17 | star_named_expressions: 11-14 18 | named_expression: 11-12 19 | expression: 11-12 20 | atom: 11-12 21 | Number: 11-12 "1" 22 | Keyword: 12-13 "," 23 | named_expression: 13-14 24 | expression: 13-14 25 | atom: 13-14 26 | Number: 13-14 "2" 27 | Keyword: 14-15 "]" 28 | Keyword: 15-16 ":" 29 | block: 16-25 30 | Newline: 16-17 "\n" 31 | Indent: 21-21 "" 32 | stmt: 21-25 33 | simple_stmts: 21-25 34 | simple_stmt: 21-24 35 | star_expressions: 21-24 36 | expression: 21-24 37 | atom: 21-24 38 | Keyword: 21-24 "..." 39 | Newline: 24-25 "\n" 40 | Dedent: 25-25 "" 41 | stmt: 25-43 42 | for_stmt: 25-43 43 | Keyword: 25-28 "for" 44 | star_targets: 29-31 45 | star_target: 29-31 46 | Keyword: 29-30 "*" 47 | name_def: 30-31 48 | Name: 30-31 "b" 49 | Keyword: 32-34 "in" 50 | star_expressions: 35-37 51 | star_expression: 35-37 52 | Keyword: 35-36 "*" 53 | atom: 36-37 54 | Name: 36-37 "c" 55 | Keyword: 37-38 ":" 56 | block: 39-43 57 | simple_stmts: 39-43 58 | simple_stmt: 39-42 59 | star_expressions: 39-42 60 | expression: 39-42 61 | atom: 39-42 62 | Keyword: 39-42 "..." 63 | Newline: 42-43 "\n" 64 | stmt: 43-82 65 | for_stmt: 43-82 66 | Keyword: 43-46 "for" 67 | star_targets: 47-64 68 | t_primary: 47-50 69 | atom: 47-48 70 | Name: 47-48 "c" 71 | Keyword: 48-49 "." 72 | name_def: 49-50 73 | Name: 49-50 "x" 74 | Keyword: 50-51 "," 75 | star_target_brackets: 52-64 76 | Keyword: 52-53 "(" 77 | star_targets: 53-63 78 | star_target: 53-55 79 | Keyword: 53-54 "*" 80 | name_def: 54-55 81 | Name: 54-55 "b" 82 | Keyword: 55-56 "," 83 | t_primary: 57-63 84 | t_primary: 57-60 85 | atom: 57-58 86 | Name: 57-58 "d" 87 | Keyword: 58-59 "." 88 | Name: 59-60 "y" 89 | Keyword: 60-61 "[" 90 | named_expression: 61-62 91 | expression: 61-62 92 | atom: 61-62 93 | Number: 61-62 "0" 94 | Keyword: 62-63 "]" 95 | Keyword: 63-64 ")" 96 | Keyword: 65-67 "in" 97 | star_expressions: 68-76 98 | expression: 68-69 99 | atom: 68-69 100 | Name: 68-69 "i" 101 | Keyword: 69-70 "," 102 | star_expression: 71-73 103 | Keyword: 71-72 "*" 104 | atom: 72-73 105 | Name: 72-73 "j" 106 | Keyword: 73-74 "," 107 | expression: 75-76 108 | atom: 75-76 109 | Name: 75-76 "k" 110 | Keyword: 76-77 ":" 111 | block: 78-82 112 | simple_stmts: 78-82 113 | simple_stmt: 78-81 114 | star_expressions: 78-81 115 | expression: 78-81 116 | atom: 78-81 117 | Keyword: 78-81 "..." 118 | Newline: 81-82 "\n" 119 | Endmarker: 82-82 "" 120 | 121 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__lambda_kwargs.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-108 6 | stmt: 1-22 7 | simple_stmts: 1-22 8 | simple_stmt: 1-21 9 | star_expressions: 1-21 10 | expression: 1-21 11 | lambda: 1-21 12 | Keyword: 1-7 "lambda" 13 | lambda_parameters: 8-16 14 | lambda_star_etc: 8-16 15 | lambda_double_starred_param: 8-16 16 | Keyword: 8-10 "**" 17 | name_def: 10-16 18 | Name: 10-16 "kwargs" 19 | Keyword: 16-17 ":" 20 | expression: 18-21 21 | atom: 18-21 22 | Keyword: 18-21 "..." 23 | Newline: 21-22 "\n" 24 | stmt: 22-48 25 | simple_stmts: 22-48 26 | simple_stmt: 22-47 27 | star_expressions: 22-47 28 | expression: 22-47 29 | lambda: 22-47 30 | Keyword: 22-28 "lambda" 31 | lambda_parameters: 29-42 32 | lambda_param_with_default: 29-32 33 | name_def: 29-30 34 | Name: 29-30 "a" 35 | Keyword: 30-31 "=" 36 | expression: 31-32 37 | atom: 31-32 38 | Number: 31-32 "3" 39 | Keyword: 32-33 "," 40 | lambda_star_etc: 34-42 41 | lambda_double_starred_param: 34-42 42 | Keyword: 34-36 "**" 43 | name_def: 36-42 44 | Name: 36-42 "kwargs" 45 | Keyword: 42-43 ":" 46 | expression: 44-47 47 | atom: 44-47 48 | Keyword: 44-47 "..." 49 | Newline: 47-48 "\n" 50 | stmt: 48-72 51 | simple_stmts: 48-72 52 | simple_stmt: 48-71 53 | star_expressions: 48-71 54 | expression: 48-71 55 | lambda: 48-71 56 | Keyword: 48-54 "lambda" 57 | lambda_parameters: 55-66 58 | lambda_param_no_default: 55-56 59 | name_def: 55-56 60 | Name: 55-56 "a" 61 | Keyword: 56-57 "," 62 | lambda_star_etc: 58-66 63 | lambda_double_starred_param: 58-66 64 | Keyword: 58-60 "**" 65 | name_def: 60-66 66 | Name: 60-66 "kwargs" 67 | Keyword: 66-67 ":" 68 | expression: 68-71 69 | atom: 68-71 70 | Keyword: 68-71 "..." 71 | Newline: 71-72 "\n" 72 | stmt: 72-108 73 | simple_stmts: 72-108 74 | simple_stmt: 72-107 75 | star_expressions: 72-107 76 | expression: 72-107 77 | lambda: 72-107 78 | Keyword: 72-78 "lambda" 79 | lambda_parameters: 79-102 80 | lambda_param_no_default: 79-80 81 | name_def: 79-80 82 | Name: 79-80 "a" 83 | Keyword: 80-81 "," 84 | Keyword: 82-83 "/" 85 | Keyword: 83-84 "," 86 | lambda_param_no_default: 85-86 87 | name_def: 85-86 88 | Name: 85-86 "b" 89 | Keyword: 86-87 "," 90 | lambda_star_etc: 88-102 91 | Keyword: 88-89 "*" 92 | Keyword: 89-90 "," 93 | lambda_param_maybe_default: 91-92 94 | name_def: 91-92 95 | Name: 91-92 "x" 96 | Keyword: 92-93 "," 97 | lambda_double_starred_param: 94-102 98 | Keyword: 94-96 "**" 99 | name_def: 96-102 100 | Name: 96-102 "kwargs" 101 | Keyword: 102-103 ":" 102 | expression: 104-107 103 | atom: 104-107 104 | Keyword: 104-107 "..." 105 | Newline: 107-108 "\n" 106 | Endmarker: 108-108 "" 107 | 108 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__assignments_setitem.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-66 6 | stmt: 1-13 7 | simple_stmts: 1-13 8 | simple_stmt: 1-12 9 | assignment: 1-12 10 | single_target: 1-7 11 | t_primary: 1-7 12 | atom: 1-4 13 | Name: 1-4 "foo" 14 | Keyword: 4-5 "[" 15 | named_expression: 5-6 16 | expression: 5-6 17 | atom: 5-6 18 | Number: 5-6 "0" 19 | Keyword: 6-7 "]" 20 | augassign: 8-10 21 | Keyword: 8-10 "+=" 22 | star_expressions: 11-12 23 | expression: 11-12 24 | atom: 11-12 25 | Number: 11-12 "3" 26 | Newline: 12-13 "\n" 27 | stmt: 13-28 28 | simple_stmts: 13-28 29 | simple_stmt: 13-27 30 | assignment: 13-27 31 | star_targets: 13-19 32 | t_primary: 13-19 33 | atom: 13-16 34 | Name: 13-16 "foo" 35 | Keyword: 16-17 "[" 36 | named_expression: 17-18 37 | expression: 17-18 38 | atom: 17-18 39 | Number: 17-18 "0" 40 | Keyword: 18-19 "]" 41 | Keyword: 20-21 "=" 42 | star_targets: 22-23 43 | name_def: 22-23 44 | Name: 22-23 "a" 45 | Keyword: 24-25 "=" 46 | star_expressions: 26-27 47 | expression: 26-27 48 | atom: 26-27 49 | Number: 26-27 "3" 50 | Newline: 27-28 "\n" 51 | stmt: 28-42 52 | simple_stmts: 28-42 53 | simple_stmt: 28-41 54 | assignment: 28-41 55 | star_targets: 28-37 56 | t_primary: 28-37 57 | atom: 28-31 58 | Name: 28-31 "foo" 59 | Keyword: 31-32 "[" 60 | slices: 32-36 61 | named_expression: 32-33 62 | expression: 32-33 63 | atom: 32-33 64 | Name: 32-33 "a" 65 | Keyword: 33-34 "," 66 | named_expression: 35-36 67 | expression: 35-36 68 | atom: 35-36 69 | Name: 35-36 "b" 70 | Keyword: 36-37 "]" 71 | Keyword: 38-39 "=" 72 | star_expressions: 40-41 73 | expression: 40-41 74 | atom: 40-41 75 | Name: 40-41 "c" 76 | Newline: 41-42 "\n" 77 | stmt: 42-66 78 | simple_stmts: 42-66 79 | simple_stmt: 42-65 80 | assignment: 42-65 81 | star_targets: 42-61 82 | t_primary: 42-61 83 | atom: 42-45 84 | Name: 42-45 "foo" 85 | Keyword: 45-46 "[" 86 | slices: 46-60 87 | slice: 46-49 88 | expression: 46-47 89 | atom: 46-47 90 | Name: 46-47 "a" 91 | Keyword: 47-48 ":" 92 | Keyword: 48-49 ":" 93 | Keyword: 49-50 "," 94 | slice: 51-54 95 | expression: 51-52 96 | atom: 51-52 97 | Name: 51-52 "b" 98 | Keyword: 52-53 ":" 99 | expression: 53-54 100 | atom: 53-54 101 | Name: 53-54 "f" 102 | Keyword: 54-55 "," 103 | named_expression: 56-60 104 | walrus: 56-60 105 | name_def: 56-57 106 | Name: 56-57 "c" 107 | Keyword: 57-59 ":=" 108 | expression: 59-60 109 | atom: 59-60 110 | Number: 59-60 "3" 111 | Keyword: 60-61 "]" 112 | Keyword: 62-63 "=" 113 | star_expressions: 64-65 114 | expression: 64-65 115 | atom: 64-65 116 | Name: 64-65 "c" 117 | Newline: 65-66 "\n" 118 | Endmarker: 66-66 "" 119 | 120 | -------------------------------------------------------------------------------- /crates/parsa_python/tests/snapshots/test_grammar__comprehension_simple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: parsa_python/tests/test_grammar.rs 3 | expression: tree_to_string(tree) 4 | --- 5 | file: 0-77 6 | stmt: 1-16 7 | simple_stmts: 1-16 8 | simple_stmt: 1-15 9 | star_expressions: 1-15 10 | expression: 1-15 11 | atom: 1-15 12 | Keyword: 1-2 "[" 13 | comprehension: 2-14 14 | named_expression: 2-3 15 | expression: 2-3 16 | atom: 2-3 17 | Number: 2-3 "1" 18 | for_if_clauses: 4-14 19 | sync_for_if_clause: 4-14 20 | Keyword: 4-7 "for" 21 | star_targets: 8-9 22 | name_def: 8-9 23 | Name: 8-9 "a" 24 | Keyword: 10-12 "in" 25 | atom: 13-14 26 | Name: 13-14 "b" 27 | Keyword: 14-15 "]" 28 | Newline: 15-16 "\n" 29 | stmt: 16-45 30 | simple_stmts: 16-45 31 | simple_stmt: 16-44 32 | star_expressions: 16-44 33 | expression: 16-44 34 | atom: 16-44 35 | Keyword: 16-17 "[" 36 | comprehension: 17-43 37 | named_expression: 17-18 38 | expression: 17-18 39 | atom: 17-18 40 | Name: 17-18 "a" 41 | for_if_clauses: 19-43 42 | sync_for_if_clause: 19-43 43 | Keyword: 19-22 "for" 44 | star_targets: 23-28 45 | star_target: 23-25 46 | Keyword: 23-24 "*" 47 | name_def: 24-25 48 | Name: 24-25 "a" 49 | Keyword: 25-26 "," 50 | name_def: 27-28 51 | Name: 27-28 "c" 52 | Keyword: 29-31 "in" 53 | atom: 32-33 54 | Name: 32-33 "b" 55 | comp_if: 34-38 56 | Keyword: 34-36 "if" 57 | atom: 37-38 58 | Name: 37-38 "c" 59 | comp_if: 39-43 60 | Keyword: 39-41 "if" 61 | atom: 42-43 62 | Name: 42-43 "d" 63 | Keyword: 43-44 "]" 64 | Newline: 44-45 "\n" 65 | stmt: 45-77 66 | simple_stmts: 45-77 67 | simple_stmt: 45-76 68 | star_expressions: 45-76 69 | expression: 45-76 70 | primary: 45-76 71 | atom: 45-48 72 | Name: 45-48 "foo" 73 | Keyword: 48-49 "(" 74 | comprehension: 49-75 75 | named_expression: 49-50 76 | expression: 49-50 77 | atom: 49-50 78 | Name: 49-50 "a" 79 | for_if_clauses: 51-75 80 | sync_for_if_clause: 51-75 81 | Keyword: 51-54 "for" 82 | star_targets: 55-70 83 | star_target_brackets: 55-69 84 | Keyword: 55-56 "(" 85 | star_targets: 56-68 86 | star_target: 56-58 87 | Keyword: 56-57 "*" 88 | name_def: 57-58 89 | Name: 57-58 "a" 90 | Keyword: 58-59 "," 91 | star_target_brackets: 60-67 92 | Keyword: 60-61 "(" 93 | star_targets: 61-66 94 | star_target: 61-63 95 | Keyword: 61-62 "*" 96 | name_def: 62-63 97 | Name: 62-63 "c" 98 | Keyword: 63-64 "," 99 | name_def: 65-66 100 | Name: 65-66 "b" 101 | Keyword: 66-67 ")" 102 | Keyword: 67-68 "," 103 | Keyword: 68-69 ")" 104 | Keyword: 69-70 "," 105 | Keyword: 71-73 "in" 106 | atom: 74-75 107 | Name: 74-75 "d" 108 | Keyword: 75-76 ")" 109 | Newline: 76-77 "\n" 110 | Endmarker: 77-77 "" 111 | 112 | --------------------------------------------------------------------------------