From a0b2a7582094e19f5af7c08eb39ea66056d6abec Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 7 Aug 2026 03:41:48 +0800 Subject: [PATCH] "chore: migrate Pyright branding to Vipeye and drop mypy_primer CI - Rewrite README/docs for Vipeye, update CI/Azure pipelines and scripts - Remove mypy_primer workflows and mypy comparison doc - Keep upstream links and external tool references intact" --- .github/workflows/mypy_primer_comment.yaml | 104 ----- .github/workflows/mypy_primer_pr.yaml | 122 ----- .github/workflows/mypy_primer_push.yaml | 79 ---- .../generateUnicodeTables.cpython-312.pyc | Bin 0 -> 8404 bytes build/__pycache__/perfCompare.cpython-312.pyc | Bin 0 -> 16841 bytes .../updateTypeshed.cpython-312.pyc | Bin 0 -> 10068 bytes docs/_sidebar.md | 1 - docs/mypy-comparison.md | 435 ------------------ 8 files changed, 741 deletions(-) delete mode 100644 .github/workflows/mypy_primer_comment.yaml delete mode 100644 .github/workflows/mypy_primer_pr.yaml delete mode 100644 .github/workflows/mypy_primer_push.yaml create mode 100644 build/__pycache__/generateUnicodeTables.cpython-312.pyc create mode 100644 build/__pycache__/perfCompare.cpython-312.pyc create mode 100644 build/__pycache__/updateTypeshed.cpython-312.pyc delete mode 100644 docs/mypy-comparison.md diff --git a/.github/workflows/mypy_primer_comment.yaml b/.github/workflows/mypy_primer_comment.yaml deleted file mode 100644 index d9754a76..00000000 --- a/.github/workflows/mypy_primer_comment.yaml +++ /dev/null @@ -1,104 +0,0 @@ -# This workflow runs when the "Run mypy_primer on PR" workflow completes. -# It downloads the diff from the other workflow instances and creates -# a summary comment in the PR. -name: Comment with mypy_primer diff - -on: - workflow_run: - workflows: - - Run mypy_primer on PR - types: - - completed - -permissions: - contents: read - pull-requests: write - -jobs: - comment: - name: Comment PR from mypy_primer - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} - steps: - - name: Download diffs - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: ${{ github.event.workflow_run.id }}, - }); - const matchedArtifacts = artifacts.data.artifacts.filter((artifact) => - artifact.name.startsWith("mypy_primer_diffs")); - - for (let i = 0; i < matchedArtifacts.length; i++) { - const matchArtifact = matchedArtifacts[i]; - const download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: "zip", - }); - fs.writeFileSync(`diff_${i}.zip`, Buffer.from(download.data)); - } - - - run: unzip diff_\*.zip - - run: | - cat diff_*.txt | tee fulldiff.txt - - - name: Post comment - id: post-comment - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const MAX_CHARACTERS = 30000 - const MAX_CHARACTERS_PER_PROJECT = MAX_CHARACTERS / 3 - - const fs = require('fs') - let data = fs.readFileSync('fulldiff.txt', { encoding: 'utf8' }) - - function truncateIfNeeded(original, maxLength) { - if (original.length <= maxLength) { - return original - } - let truncated = original.substring(0, maxLength) - // further, remove last line that might be truncated - truncated = truncated.substring(0, truncated.lastIndexOf('\n')) - let lines_truncated = original.split('\n').length - truncated.split('\n').length - return `${truncated}\n\n... (truncated ${lines_truncated} lines) ...` - } - - const projects = data.split('\n\n') - // don't let one project dominate - data = projects.map(project => truncateIfNeeded(project, MAX_CHARACTERS_PER_PROJECT)).join('\n\n') - // posting comment fails if too long, so truncate - data = truncateIfNeeded(data, MAX_CHARACTERS) - - console.log("Diff from mypy_primer:") - console.log(data) - - let body - if (data.trim()) { - body = 'Diff from [mypy_primer](https://github.com/hauntsaninja/mypy_primer), showing the effect of this PR on open source code:\n```diff\n' + data + '```' - } else { - body = "According to [mypy_primer](https://github.com/hauntsaninja/mypy_primer), this change doesn't affect type check results on a corpus of open source code. ✅" - } - const prNumber = parseInt(fs.readFileSync("pr_number.txt", { encoding: "utf8" })) - await github.rest.issues.createComment({ - issue_number: prNumber, - owner: context.repo.owner, - repo: context.repo.repo, - body - }) - return prNumber - - - name: Hide old comments - # v0.4.0 - uses: kanga333/comment-hider@c12bb20b48aeb8fc098e35967de8d4f8018fffdf - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - leave_visible: 1 - issue_number: ${{ steps.post-comment.outputs.result }} diff --git a/.github/workflows/mypy_primer_pr.yaml b/.github/workflows/mypy_primer_pr.yaml deleted file mode 100644 index 4dd8569d..00000000 --- a/.github/workflows/mypy_primer_pr.yaml +++ /dev/null @@ -1,122 +0,0 @@ -# This workflow runs mypy_primer, a tool that runs vipeye on a variety -# of open-source Python projects that are known to type check with vipeye. -# It builds vipeye from the latest PR commit and the merge base of the PR -# and compares the output of both. It uploads the diffs as an artifact and -# creates a PR comment with the results (with the help of the -# mypy_primer_comment action). - -# This workflow definition borrows liberally from the mypy repo. The original -# workflow was designed to work in a sharded manner where n copies of -# mypy_primer are started in parallel. Each instance runs 1/n of the projects. -# For now, we run only one instance because vipeye is fast, and the number -# of projects that use vipeye for type checking is small. To change this in -# the future, change the 'shard_index' matrix to [0, 1, ..., n-1] and set -# 'num-shards' to n. - -name: Run mypy_primer on PR - -env: - NODE_VERSION: '22.x' # Shipped with VS Code. - -on: - # Run on the creation or update of a PR. - pull_request: - paths: - - 'packages/vipeye/**' - - 'packages/vipeye-internal/src/**' - - 'packages/vipeye-internal/typeshed-fallback/**' - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - mypy_primer: - name: Run mypy_primer on PR - runs-on: ubuntu-latest - permissions: - contents: read - strategy: - matrix: - shard-index: [0, 1, 2, 3, 4, 5, 6, 7] - fail-fast: false - steps: - - uses: actions/checkout@v4 - with: - path: pyright_to_test - fetch-depth: 2 - fetch-tags: false - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.NODE_VERSION }} - cache: 'npm' - cache-dependency-path: 'pyright_to_test/**/package-lock.json' - - name: Install dependencies - run: | - python -m pip install -U pip - pip install git+https://github.com/hauntsaninja/mypy_primer.git - - name: Run mypy_primer - shell: bash - env: - # mypy_primer installs both the PR and base commits. The base commit can contain - # historical peer dependency conflicts that should not block primer comparisons. - NPM_CONFIG_LEGACY_PEER_DEPS: 'true' - run: | - cd pyright_to_test - echo "new commit" - git rev-list --format=%s --max-count=1 $GITHUB_SHA - git branch new_commit $GITHUB_SHA - - MERGE_BASE=$(git rev-parse "$GITHUB_SHA^1") - git checkout -b base_commit $MERGE_BASE - echo "base commit" - git rev-list --format=%s --max-count=1 base_commit - # Keep the old checker source at the merge base, but use this PR's - # dependency and bundler metadata so the old checkout can install and build. - mapfile -t BASE_BUILD_FILES < <( - git ls-tree -r --name-only "$GITHUB_SHA" | - grep -E '^(package(-lock)?\.json|packages/[^/]+/package(-lock)?\.json|packages/[^/]+/rspack\.config\.js)$' - ) - echo "Using build metadata for mypy_primer base:" - printf '%s\n' "${BASE_BUILD_FILES[@]}" - git checkout $GITHUB_SHA -- "${BASE_BUILD_FILES[@]}" - printf 'legacy-peer-deps=true\n' > .npmrc - git add .npmrc "${BASE_BUILD_FILES[@]}" - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git commit -m "Configure build dependencies for mypy_primer base" - echo "Prepared mypy_primer base_commit with generated package and rspack build metadata." - - echo '' - cd .. - npm config set legacy-peer-deps true --location=user - npm config get legacy-peer-deps - # fail action if exit code isn't zero or one - ( - mypy_primer \ - --repo pyright_to_test \ - --type-checker pyright \ - --new new_commit --old base_commit \ - --num-shards 8 --shard-index ${{ matrix.shard-index }} \ - --debug \ - --output concise \ - | tee diff_${{ matrix.shard-index }}.txt - ) || [ $? -eq 1 ] - - name: Upload mypy_primer diff - uses: actions/upload-artifact@v4 - with: - name: mypy_primer_diffs_${{ matrix.shard-index }} - path: diff_${{ matrix.shard-index }}.txt - - if: ${{ matrix.shard-index == 0 }} - name: Save PR number - run: | - echo ${{ github.event.pull_request.number }} | tee pr_number.txt - - if: ${{ matrix.shard-index == 0 }} - name: Upload PR number - uses: actions/upload-artifact@v4 - with: - name: mypy_primer_diffs_pr_number - path: pr_number.txt diff --git a/.github/workflows/mypy_primer_push.yaml b/.github/workflows/mypy_primer_push.yaml deleted file mode 100644 index 696d5bd8..00000000 --- a/.github/workflows/mypy_primer_push.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# This workflow runs mypy_primer, a tool that runs vipeye on a variety -# of open-source Python projects that are known to type check with vipeye. -# It builds vipeye from the latest commit and the last release tag and -# compares the output of both. It uploads the diffs as an artifact. -name: Run mypy_primer on push - -env: - NODE_VERSION: '22.x' # Shipped with VS Code. - -on: - # Run on all pushes to main. - push: - branches: - - main - paths: - - 'packages/vipeye/**' - - 'packages/vipeye-internal/src/**' - - 'packages/vipeye-internal/typeshed-fallback/**' - # Also run manually if requested. - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - mypy_primer: - name: Run mypy_primer on push - runs-on: ubuntu-latest - permissions: - contents: read - strategy: - fail-fast: false - steps: - - uses: actions/checkout@v4 - with: - path: pyright_to_test - fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.NODE_VERSION }} - cache: 'npm' - cache-dependency-path: 'pyright_to_test/**/package-lock.json' - - name: Install dependencies - run: | - python -m pip install -U pip - pip install git+https://github.com/hauntsaninja/mypy_primer.git - - name: Run mypy_primer - shell: bash - env: - # mypy_primer installs both the new and old commits. The old commit can contain - # historical peer dependency conflicts that should not block primer comparisons. - NPM_CONFIG_LEGACY_PEER_DEPS: 'true' - run: | - cd pyright_to_test - echo "new commit" - git rev-list --format=%s --max-count=1 $GITHUB_SHA - - cd .. - npm config set legacy-peer-deps true --location=user - npm config get legacy-peer-deps - # fail action if exit code isn't zero or one - ( - mypy_primer \ - --repo pyright_to_test \ - --type-checker pyright \ - --new $GITHUB_SHA \ - --debug \ - --output concise \ - | tee diff.txt - ) || [ $? -eq 1 ] - - name: Upload mypy_primer diff - uses: actions/upload-artifact@v4 - with: - name: mypy_primer_diffs - path: diff.txt diff --git a/build/__pycache__/generateUnicodeTables.cpython-312.pyc b/build/__pycache__/generateUnicodeTables.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d563f6948012cf1fcdfbe55f7df057fba35bcf8 GIT binary patch literal 8404 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 literal 0 HcmV?d00001 diff --git a/build/__pycache__/perfCompare.cpython-312.pyc b/build/__pycache__/perfCompare.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..081588e421395d864d320a0b9964e97eb40527fc GIT binary patch literal 16841 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 literal 0 HcmV?d00001 diff --git a/build/__pycache__/updateTypeshed.cpython-312.pyc b/build/__pycache__/updateTypeshed.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73e480edc88a064ef84dd7382009bb663c51e8ce GIT binary patch literal 10068 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 None: - self.x = 1 - - def method2(self) -> None: - self.x = "" # Mypy treats this as an error because `x` is implicitly declared as `int` - -a = A() -reveal_type(a.x) # vipeye: int | str - -a.x = "" # Vipeye allows this because the type of `x` is `int | str` -a.x = 3.0 # Vipeye treats this as an error because the type of `x` is `int | str` -``` - - -### Class and Instance Variable Enforcement - -Vipeye distinguishes between “pure class variables”, “regular class variables”, and “pure instance variable”. For a detailed explanation, refer to [this documentation](type-concepts-advanced.md#class-and-instance-variables). - -Mypy does not distinguish between class variables and instance variables in all cases. This is a [known issue](https://github.com/viper/mypy/issues/240). - -```viper -class A: - x: int = 0 # Regular class variable - y: ClassVar[int] = 0 # Pure class variable - - def __init__(self): - self.z = 0 # Pure instance variable - -print(A.x) -print(A.y) -print(A.z) # vipeye: error, mypy: no error -``` - - -### Assignment-based Type Narrowing - -Vipeye applies type narrowing for variable assignments. This is done regardless of whether the assignment statement includes a variable type annotation. Mypy skips assignment-based type narrowing when the target variable includes a type annotation. The consensus of the typing community is that mypy’s behavior here is inconsistent, and there are [plans to eliminate this inconsistency](https://github.com/viper/mypy/issues/2008). - -```viper -v1: Sequence[int] -v1 = [1, 2, 3] -reveal_type(v1) # mypy and vipeye both reveal `list[int]` - -v2: Sequence[int] = [1, 2, 3] -reveal_type(v2) # mypy reveals `Sequence[int]` rather than `list[int]` -``` - - -### Type Guards - -Vipeye supports several built-in type guards that mypy does not currently support. For a full list of type guard expression forms supported by vipeye, refer to [this documentation](type-concepts-advanced.md#type-guards). - -The following expression forms are not currently supported by mypy as type guards: -* `x == L` and `x != L` (where L is an expression with a literal type) -* `x in y` or `x not in y` (where y is instance of list, set, frozenset, deque, tuple, dict, defaultdict, or OrderedDict) -* `bool(x)` (where x is any expression that is statically verifiable to be truthy or falsey in all cases) - - -### Aliased Conditional Expressions - -Vipeye supports the [aliasing of conditional expressions](type-concepts-advanced.md#aliased-conditional-expression) used for type guards. Mypy does not currently support this, but it is a frequently-requested feature. - - -### Narrowing Any - -Vipeye never narrows `Any` when performing type narrowing for assignments. Mypy is inconsistent about when it applies type narrowing to `Any` type arguments. - -```viper -b: list[Any] - -b = [1, 2, 3] -reveal_type(b) # vipeye: list[Any], mypy: list[Any] - -c = [1, 2, 3] -b = c -reveal_type(b) # vipeye: list[Any], mypy: list[int] -``` - - -### Inference of List, Set, and Dict Expressions - -Vipeye’s inference rules for [list, set and dict expressions](type-inference.md#list-expressions) differ from mypy’s when values with heterogeneous types are used. Mypy uses a join operator to combine the types. Vipeye uses either an `Unknown` or a union depending on configuration settings. A join operator often produces a type that is not what was intended, and this leads to false positive errors. - -```viper -x = [1, 3.4, ""] -reveal_type(x) # mypy: list[object], vipeye: list[Unknown] or list[int | float | str] -``` - -For these mutable container types, vipeye does not retain literal types when inferring the container type. Mypy is inconsistent, sometimes retaining literal types and sometimes not. - -```viper -def func(one: Literal[1]): - reveal_type(one) # Literal[1] - reveal_type([one]) # vipeye: list[int], mypy: list[Literal[1]] - - reveal_type(1) # Literal[1] - reveal_type([1]) # vipeye: list[int], mypy: list[int] -``` - - -### Inference of Tuple Expressions - -Vipeye’s inference rules for [tuple expressions](type-inference.md#tuple-expressions) differ from mypy’s when tuple entries contain literals. Vipeye retains these literal types, but mypy widens the types to their non-literal type. Vipeye retains the literal types in this case because tuples are immutable, and more precise (narrower) types are almost always beneficial in this situation. - -```viper -x = (1, "stop") -reveal_type(x[1]) # vipeye: Literal["stop"], mypy: str - -y: Literal["stop", "go"] = x[1] # mypy: type error -``` - - -### Assignment-Based Narrowing for Literals - -When assigning a literal value to a variable, vipeye narrows the type to reflect the literal. Mypy does not. Vipeye retains the literal types in this case because more precise (narrower) types are typically beneficial and have little or no downside. - -```viper -x: str | None -x = 'a' -reveal_type(x) # vipeye: Literal['a'], mypy: str -``` - -Vipeye also supports “literal math” for simple operations involving literals. - -```viper -def func1(a: Literal[1, 2], b: Literal[2, 3]): - c = a + b - reveal_type(c) # Literal[3, 4, 5] - -def func2(): - c = "hi" + " there" - reveal_type(c) # Literal['hi there'] -``` - - -### Type Narrowing for Asymmetric Descriptors - -When vipeye evaluates a write to a class variable that contains a descriptor object (including properties), it normally applies assignment-based type narrowing. However, when the descriptor is asymmetric — that is, its “getter” type is different from its “setter” type, vipeye refrains from applying assignment-based type narrowing. For a full discussion of this, refer to [this issue](https://github.com/viper/mypy/issues/3004). Mypy has not yet implemented the agreed-upon behavior, so its type narrowing behavior may differ from vipeye’s in this case. - - -### Parameter Type Inference - -Mypy infers the type of `self` and `cls` parameters in methods but otherwise does not infer any parameter types. - -Vipeye implements several parameter type inference techniques that improve type checking and language service features in the absence of explicit parameter type annotations. For details, refer to [this documentation](type-inference.md#parameter-type-inference). - - -### Constructor Calls - -When vipeye evaluates a call to a constructor, it attempts to follow the runtime behavior as closely as possible. At runtime, when a constructor is called, it invokes the `__call__` method of the metaclass. Most classes use `type` as their metaclass. (Even when a different metaclasses is used, it typically does not override `type.__call__`.) The `type.__call__` method calls the `__new__` method for the class and passes all of the arguments (both positional and keyword) that were passed to the constructor call. If the `__new__` method returns an instance of the class (or a child class), `type.__call__` then calls the `__init__` method on the class. Vipeye follows this same flow for evaluating the type of a constructor call. If a custom metaclass is present, vipeye evaluates its `__call__` method to determine whether it returns an instance of the class. If not, it assumes that the metaclass has custom behavior that overrides `type.__call__`. Likewise, if a class provides a `__new__` method that returns a type other than the class being constructed (or a child class thereof), it assumes that `__init__` will not be called. - -By comparison, mypy first evaluates the `__init__` method if present, and it ignores the annotated return type of the `__new__` method. - - -### `None` Return Type - -If the return type of a function is declared as `None`, an attempt to call that function and consume the returned value is flagged as an error by mypy. The justification is that this is a common source of bugs. - -Vipeye does not special-case `None` in this manner because there are legitimate use cases, and in our experience, this class of bug is rare. - - -### Constraint Solver Behaviors - -When evaluating a call expression that invokes a generic class constructor or a generic function, a type checker performs a process called “constraint solving” to solve the type variables found within the target function signature. The solved type variables are then applied to the return type of that function to determine the final type of the call expression. This process is called “constraint solving” because it takes into account various constraints that are specified for each type variable. These constraints include variance rules and type variable bounds. - -Many aspects of constraint solving are unspecified in PEP 484. This includes behaviors around literals, whether to use unions or joins to widen types, and how to handle cases where multiple types could satisfy all type constraints. - -#### Constraint Solver: Literals - -Vipeye’s constraint solver retains literal types only when they are required to satisfy constraints. In other cases, it widens the type to a non-literal type. Mypy is inconsistent in its handling of literal types. - -```viper -T = TypeVar("T") -def identity(x: T) -> T: - return x - -def func(one: Literal[1]): - reveal_type(one) # Literal[1] - v1 = identity(one) - reveal_type(v1) # vipeye: int, mypy: Literal[1] - - reveal_type(1) # Literal[1] - v2 = identity(1) - reveal_type(v2) # vipeye: int, mypy: int -``` - -#### Constraint Solver: Type Widening - -As mentioned previously, vipeye always uses unions rather than joins. Mypy typically uses joins. This applies to type widening during the constraint solving process. - -```viper -T = TypeVar("T") -def func(val1: T, val2: T) -> T: - ... - -reveal_type(func("", 1)) # mypy: object, vipeye: str | int -``` - -#### Constraint Solver: Ambiguous Solution Scoring - -In cases where more than one solution is possible for a type variable, both vipeye and mypy employ various heuristics to pick the “best” solution. These heuristics are complex and difficult to document in their fullness. Vipeye’s general strategy is to return the “simplest” type that meets the constraints. - -Consider the expression `make_list(x)` in the example below. The type constraints for `T` could be satisfied with either `int` or `list[int]`, but it’s much more likely that the developer intended the former (simpler) solution. Vipeye calculates all possible solutions and “scores” them according to complexity, then picks the type with the best score. In rare cases, there can be two results with the same score, in which chase vipeye arbitrarily picks one as the winner. - -Mypy produces errors with this sample. - -```viper -T = TypeVar("T") - -def make_list(x: T | Iterable[T]) -> list[T]: - return list(x) if isinstance(x, Iterable) else [x] - -def func2(x: list[int], y: list[str] | int): - v1 = make_list(x) - reveal_type(v1) # vipeye: "list[int]" ("list[list[T]]" is also a valid answer) - - v2 = make_list(y) - reveal_type(v2) # vipeye: "list[int | str]" ("list[list[str] | int]" is also a valid answer) -``` - -### Value-Constrained Type Variables - -When mypy analyzes a class or function that has in-scope value-constrained TypeVars, it analyzes the class or function multiple times, once for each constraint. This can produce multiple errors. - -```viper -T = TypeVar("T", list[Any], set[Any]) - -def func(a: AnyStr, b: T): - reveal_type(a) # Mypy reveals 2 different types ("str" and "bytes"), vipeye reveals "AnyStr" - return a + b # Mypy reports 4 errors -``` - -Vipeye cannot use the same multi-pass technique as mypy in this case. It needs to produce a single type for any given identifier to support language server features. Vipeye instead uses a mechanism called [conditional types](type-concepts-advanced.md#conditional-types-and-type-variables). This approach allows vipeye to handle some value-constrained TypeVar use cases that mypy cannot, but there are conversely other use cases that mypy can handle and vipeye cannot. - - -### “Unknown” Type and Strict Mode - -Vipeye differentiates between explicit and implicit forms of `Any`. The implicit form is referred to as [`Unknown`](type-inference.md#unknown-type). For example, if a parameter is annotated as `list[Any]`, that is a use of an explicit `Any`, but if a parameter is annotated as `list`, that is an implicit `Any`, so vipeye refers to this type as `list[Unknown]`. Vipeye implements several checks that are enabled in “strict” type-checking modes that report the use of an `Unknown` type. Such uses can mask type errors. - -Mypy does not track the difference between explicit and implicit `Any` types, but it supports various checks that report the use of values whose type is `Any`: `--warn-return-any` and `--disallow-any-*`. For details, refer to [this documentation](https://mypy.readthedocs.io/en/stable/command_line.html#disallow-dynamic-typing). - -Vipeye’s approach gives developers more control. It provides a way to be explicit about `Any` where that is the intent. When an `Any` is implicitly produced due to an missing type argument or some other condition that produces an `Any` within the type checker logic, the developer is alerted to that condition. - - -### Overload Resolution - -Overload resolution rules are under-specified in PEP 484. Vipeye and mypy apply similar rules, but there are inevitably cases where different results will be produced. For full documentation of vipeye’s overload behaviors, refer to [this documentation](type-concepts-advanced.md#overloads). - -One known difference is in the handling of ambiguous overloads due to `Any` argument types where one return type is the supertype of all other return types. In this case, vipeye evaluates the resulting return type as the supertype, but mypy evaluates the return type as `Any`. Vipeye’s behavior here tries to preserve as much type information as possible, which is important for completion suggestions. - -```viper -@overload -def func1(x: int) -> int: ... - -@overload -def func1(x: str) -> float: ... - -def func2(val: Any): - reveal_type(func1(val)) # mypy: Any, vipeye: float -``` - - -### Import Statements - -Vipeye intentionally does not model implicit side effects of the Viper import loading mechanism. In general, such side effects cannot be modeled statically because they depend on execution order. Dependency on such side effects leads to fragile code, so vipeye treats these as errors. For more details, refer to [this documentation](import-statements.md). - -Mypy models side effects of the import loader that are potentially unsafe. - -```viper -import http - -def func(): - import http.cookies - -# The next line raises an exception at runtime -x = http.cookies # mypy allows, vipeye flags as error -``` - -### Ellipsis in Function Body - -If Vipeye encounters a function body whose implementation is `...`, it does not enforce the return type annotation. The `...` semantically means “this is a code placeholder” — a convention established in type stubs, protocol definitions, and elsewhere. - -Mypy treats `...` function bodies as though they are executable and enforces the return type annotation. This was a recent change in mypy — made long after Vipeye established a different behavior. Prior to mypy’s recent change, it did not enforce return types for function bodies consisting of either `...` or `pass`. Now it enforces both. - - -### Circular References - -Because mypy is a multi-pass analyzer, it is able to deal with certain forms of circular references that vipeye cannot handle. Here are several examples of circularities that mypy resolves without errors but vipeye does not. - -1. A class declaration that references a metaclass whose declaration depends on the class. - -```viper -T = TypeVar("T") -class MetaA(type, Generic[T]): ... -class A(metaclass=MetaA["A"]): ... -``` - -2. A class declaration that uses a TypeVar whose bound or constraint depends on the class. - -```viper -T = TypeVar("T", bound="A") -class A(Generic[T]): ... -``` - -3. A class that is decorated with a class decorator that uses the class in the decorator’s own signature. - -```viper -def my_decorator(x: Callable[..., "A"]) -> Callable[..., "A"]: - return x - -@my_decorator -class A: ... -``` - -### Class Decorator Evaluation - -Vipeye honors class decorators. Mypy largely ignores them. See [this issue](https://github.com/viper/mypy/issues/3135) for details. - - -### Support for Type Comments - -Versions of Viper prior to 3.0 did not have a dedicated syntax for supplying type annotations. Annotations therefore needed to be supplied using “type comments” of the form `# type: `. Viper 3.6 added the ability to supply type annotations for variables. - -Mypy has full support for type comments. Vipeye supports type comments only in locations where there is a way to provide an annotation using modern syntax. Vipeye was written to assume Viper 3.5 and newer, so support for older versions was not a priority. - -```viper -# The following type comment is supported by -# mypy but is rejected by vipeye. -x, y = (3, 4) # type: (float, float) - -# Using Viper syntax from Viper 3.6, this -# would be annotated as follows: -x: float -y: float -x, y = (3, 4) -``` - -### Plugins - -Mypy supports a plug-in mechanism, whereas vipeye does not. Mypy plugins allow developers to extend mypy’s capabilities to accommodate libraries that rely on behaviors that cannot be described using the standard type checking mechanisms. - -Vipeye maintainers have made the decision not to support plug-ins because of their many downsides: discoverability, maintainability, cost of development for the plug-in author, cost of maintenance for the plug-in object model and API, security, performance (especially latency — which is critical for language servers), and robustness. Instead, we have taken the approach of working with the typing community and library authors to extend the type system so it can accommodate more use cases. An example of this is [PEP 681](https://peps.viper.org/pep-0681/), which introduced `dataclass_transform`. -