Merge upstream - #48
Merged
Merged
Conversation
These tests are currently split across `assignment/annotations.md` and `bidirectional.md` somewhat arbitrarily. I merged the two and reorganized some of the larger snippets which had devolved into catch-all sections.
…TZ002`) (#26658) ## Summary This updates the DTZ002 documentation to clarify the `datetime.today()` naming confusion raised in #23392. - Notes that `datetime.datetime.today()` is named like a date helper, but it returns a local `datetime`. - Keeps the existing guidance to use timezone-aware `datetime.now(...)` when you need an aware timestamp. ## Why this changed The code behavior is already correct; this is a docs-only improvement to make the rule intent easier to understand. ## Test Plan - `cargo dev generate-docs` - `cargo fmt --check --all` - `PATH="$PWD/target/debug:$PATH" uv run --only-group dev --locked python scripts/check_docs_formatted.py` --------- Co-authored-by: Mahadev Annabhimoju <219508079+Joosboy@users.noreply.github.com> Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
…105`) (#26423) Summary -- This is the first in a series of migration rules to help users move from `noqa` comments with rule codes to `ruff:ignore` comments with names. This rule simply replaces `noqa: codes` with `ruff:ignore[codes]` (and the file-level variants), deferring the transformation of codes to names to a follow-up rule. This rule may be useful on its own without the codes -> names rule, assuming we stabilize `ruff:ignore` in the next minor release without stabilizing human-readable names. The rule skips any comment consisting entirely of known `external` selectors, while still emitting diagnostics for totally unknown codes. We also skip file-level `flake8: noqa` directives assuming they are intended to be shared with flake8. I initially wrote a small, standalone implementation in the rule file itself, but it had to be combined with the `RUF100` implementation to leave unused `noqa` codes out of `RUF105` to be cleaned up by `RUF100` instead, as I note in the mdtest for this case. Test Plan -- New mdtests I also expect a huge number of ecosystem results on this PR, possibly so many that the check times out. --------- Co-authored-by: Micha Reiser <micha@reiser.io>
…2901`) (#25733) ## Summary Addresses #24986 The rule has been updated to ignore cases where a mutable object is updated in-place. This makes the behavior consistent with how method‑based updates are already ignored. ## Test Plan Added new test cases
…29`) (#26641) ## Summary This updates the RUF029 documentation to mention that an async function can sometimes be intentional even when it does not directly await anything. One example is an async test or callback that needs to run inside an async execution context, such as code that uses a ContextVar. The docs now point users toward either adding a real await when appropriate or disabling the rule for that specific function when the async boundary is intentional. Refs #23196. ## Test Plan - `cargo dev generate-docs` - `cargo fmt --check --all` - `PATH="$PWD/target/debug:$PATH" uv run --only-group dev --locked python scripts/check_docs_formatted.py` - `PATH="$PWD/target/debug:$PATH" uv run --only-group dev --locked prek run --files crates/ruff_linter/src/rules/ruff/rules/unused_async.rs` - `cargo test -p ruff_linter rules::ruff::tests::rules::rule_unusedasync_path_new_ruf029_py_expects` Co-authored-by: Mahadev Annabhimoju <219508079+Joosboy@users.noreply.github.com>
## Summary
Prior to this change, we treated a protocol member produced by a
descriptor decorator as the descriptor object itself. This caused a
class using `cached_property` to fail an otherwise matching protocol
because we compared the instance read type, `str`, with
`cached_property[str]`:
```python
from functools import cached_property
from typing import Protocol
class HasName(Protocol):
@cached_property
def name(self) -> str: ...
class WithName:
@cached_property
def name(self) -> str:
return "example"
has_name: HasName = WithName()
```
We now resolve descriptor-decorated protocol methods through their
instance interface. The result of `__get__` determines the readable
member type, and the value parameter of a single ordinary `__set__`
signature determines the writable member type. This lets
`cached_property` implementations satisfy the protocol and preserves the
expected type when reading from or assigning through a protocol
instance.
This PR is focused on the simple case and leaves others as TODOs (with
our existing, conservative behavior).
Closes astral-sh/ty#3953.
## Summary
Prior to this change, we rejected an unhashable class even though its
`__hash__` matched the protocol:
```python
from typing import ClassVar, Protocol
class NotHashableProto(Protocol):
__hash__: ClassVar[None]
class NotHashable:
__hash__: ClassVar[None] = None
def accepts(value: NotHashableProto) -> None: ...
# Before: rejected
# After: accepted
accepts(NotHashable())
```
The problem is that we were checking the inherited `type.__hash__`
instead of the class's own `__hash__ = None`.
For this case, class-object reads already used the correct precedence:
metaclass data descriptor, then class attribute, then metaclass non-data
descriptor. For writes, though, we treated _any_ metaclass member as the
primary and only consulted the class attribute as a fallback. We now let
an always-defined class member take precedence over an ordinary
metaclass member that is known not to be a data descriptor, while
preserving uncertainty for gradual metaclass members.
Closes astral-sh/ty#3951.
<!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? - Does this PR follow our AI policy (https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)? --> ## Summary <!-- What's the purpose of the change? What does it do, and why? --> This improves our tests by making significant whitespace visible in snapshot output. Two trailing spaces at the end of a Markdown paragraph preserve a hard break (rendered as `<HB>` in snapshots) at the end of that line. ## Test Plan See included tests. <!-- How was it tested? -->
…e in structural rendering. (#26691) <!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? - Does this PR follow our AI policy (https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)? --> ## Summary <!-- What's the purpose of the change? What does it do, and why? --> This just picks a new name for a type that makes more sense with [upcoming markdown rendering for Google-style docstrings](astral-sh/ruff#26599). ## Test Plan Relies on existing test coverage. <!-- How was it tested? -->
## Summary
Our data-descriptor predicates recursively inspect outer unions,
intersections, and type aliases. An unguarded recursive alias can
therefore revisit the same classification:
```python
class Descriptor:
def __set__(self, instance: object, value: object) -> None: ...
type NonData = int | NonDataTail
type NonDataTail = NonData
type Data = Descriptor | DataTail
type DataTail = Data
```
We now evaluate both descriptor classifiers as Salsa queries and route
alias expansion back through those queries. Definite classifications use
`true`, the identity for their all-of union folds, as cycle recovery;
possible-data classification uses `false`, the identity for its any-of
fold.
The regression test exercises `is_data_descriptor`,
`may_be_data_descriptor`, and `is_definitely_non_data_descriptor` for
recursive aliases with and without a reachable data descriptor.
## Summary Add support for `Annotated[T, Field(...)]`, `Strict(..)` and (transitively) also support for aliases like `StrictInt`. One challenge here is that aliases like `StrictInt` are defined as `StrictInt = Annotated[int, Strict()]`. When we see `StrictInt` in a field annotation, we have already lost the `Annotated` metadata. For now, we simply resolve this by following the alias back to it's original definition, since it seems wrong to store `Annotated` metadata (or a link back to its definition) inside the type for this specific use-case only. closes astral-sh/ty#2130 closes astral-sh/ty#3948 towards astral-sh/ty#2403 ## Test Plan New and updated Markdown tests.
## Summary `Name` represents a Python identifier, but ty's IDE paths also used it for arbitrary text such as dotted module and qualified names, synthesized completion insertions, and dotted base-class references. Use `CompactString` for completion matching, display, insertion, and fully qualified symbol text, and construct `ModuleName` directly for module references used in `__all__` resolution. This keeps dotted and generated text out of the AST identifier type, avoids intermediate `String` allocations when formatting completion text and module paths, and preserves the existing completion and symbol behavior.
## Summary We allocated a new `Box<dyn Write>` for every `Write` call (unless the compiler managed to remove it). This is rather expensive. This PR replaces the `handle() -> Box<dyn Write>` function with `with_handle` that takes a callback instead. This avoids the allocation entirely. > On 50k diagnostics, concise improved from 497.1ms to 475.2ms—about 13.5% faster for rendering-only time. Related to astral-sh/ty#3958. ## Test Plan Testing: Passed the ty test suite and repository hooks.
## Summary A Pydantic model with a base class with a custom `__init__` method previously prevented us from adding `extra: **Unknown` to the models constructor. ## Test Plan Added Markdown tests
<!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? - Does this PR follow our AI policy (https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)? --> ## Summary This corrects how we expand tabs as part of PEP-257 normalization of docstrings. Previously, we incorrectly replaced each tab in the docstring with 8 spaces. However, as per [the reference implementation](https://peps.python.org/pep-0257/#handling-docstring-indentation) (and the [`str.expandtabs` docs](https://docs.python.org/3/library/stdtypes.html#str.expandtabs)), we should instead treat a tab as a directive to advance to the next tab stop (the next column at a multiple of 8). This matters for Google-style parameter extraction when a tab follows spaces. For example, consider an `Args:` section whose first item is indented with two spaces followed by a tab, while the second uses eight spaces: ```python def example(first: str, second: str): """Summary. Args: \tfirst: First parameter. second: Second parameter. """ ``` The previous normalization produced this docstring. Because the parameter items have different indentation, `second` is treated as continuation text for `first` rather than as a separate parameter: ```text Summary. Args: first: First parameter. second: Second parameter. ``` Whereas the correct interpretation according to PEP 257 aligns the parameter items, allowing documentation to be extracted for both `first` and `second`: ```text Summary. Args: first: First parameter. second: Second parameter. ``` ## Test Plan See included tests. <!-- How was it tested? -->
## Summary
This PR allows `str`, `int`, and `bytes` types to narrow to literal
values when an equality check succeeds by default.
The behavior is unsound because a subclass could override `__eq__`, but
it is convenient for users and matches other type checkers. Users who
prefer conservative behavior can enable `strict-literal-narrowing`; the
option defaults to `false`.
```toml
[tool.ty.analysis]
strict-literal-narrowing = true
```
For example, the default behavior allows:
```python
from typing import Literal
type Choice = Literal["a", "b"]
def parse(value: str, choices: list[Choice]) -> Choice | None:
if value in choices:
reveal_type(value) # Literal["a", "b"]
return value
return None
```
Closes astral-sh/ty#1566.
Closes astral-sh/ty#1659.
Closes astral-sh/ty#2178.
Closes astral-sh/ty#2211.
Closes astral-sh/ty#3233.
Closes astral-sh/ty#3852.
…(#26696)
## Summary
We generally treat `Callable`-typed dunder class members as
function-like descriptors. This is deliberately a name-based heuristic:
a `Callable` annotation can describe either a function descriptor or an
arbitrary callable object, so the type alone does not tell us whether
instance access should bind the first parameter. The heuristic
nevertheless supports common patterns where special methods are
installed dynamically and explicitly include the instance in their
signature:
```python
def pow_impl(tensor: Tensor, exponent: int) -> Tensor: ...
class Tensor:
__pow__: Callable[[Tensor, int], Tensor] = pow_impl
Tensor() ** 2
```
There are two narrow cases where we should not apply this heuristic. A
callable with no parameters cannot accept a receiver, and a callable
parameterized directly by a `ParamSpec` does not identify a receiver
parameter:
```python
class Task[**P](Protocol):
__call__: Callable[P, int]
def check(task: Task[[str]]) -> int:
return task("value")
```
Once `P` is specialized to `(str,)`, converting the member to a
function-like callable would bind away the `str` parameter and expose
`() -> int` instead. We now record that a dunder callable was declared
with a bare `ParamSpec` before generic specialization and preserve it as
a regular callable during instance and protocol binding. Parameterless
callable dunders likewise remain regular.
All other concrete and gradual `Callable` dunders retain the existing
descriptor heuristic. In particular, we deliberately do not inspect
whether the first parameter annotation can accept the owning instance.
Closes astral-sh/ty#3957.
## Summary This follows up on #26414 by documenting that the standard library's `StrEnum` and `IntEnum` types are subclasses of `str` and `int`, respectively, and are therefore affected by unsafe builtin literal narrowing. The new `StrEnum` example shows how a call can be statically typed as returning `Literal["a"] | None` while returning a `Choice` enum member at runtime. The generated configuration documentation and schema now include the clarification.
## Summary
In #26696, we stopped treating `Callable[P, R]`-typed dunder members as
function-like descriptors because binding the first parameter after
specializing `P` would incorrectly remove a real argument. However,
implicit dunder lookup uses a no-instance-fallback path that returned
before the normal eager `Self` binding step. As a result, a ParamSpec
dunder that returned `Self` preserved the declaring class's `Self` type
instead of resolving it to the concrete receiver:
```python
class Base[**P]:
__getitem__: Callable[P, Self]
class Child(Base[[int]]): ...
def check(value: Child) -> None:
reveal_type(value[0]) # Self@Base, expected Child
```
We now apply the existing receiver-aware `Self` mapping to the result of
no-instance-fallback descriptor lookup. Function-like callables continue
to defer `Self` binding to their signature-binding path, while regular
ParamSpec callables resolve `Self` without dropping any parameters.
The regression coverage verifies that explicit `value.__getitem__(0)`
and implicit `value[0]` access both return `Child`.
…ts (`RUF106`) (#26682) Summary -- This is the second of our migration rules for moving from `noqa` comments to `ruff:ignore` comments with human-readable names. `RUF106` performs the second part of this transformation, converting `ruff:ignore` comments that use rule codes to `ruff:ignore` comments with human-readable names. The rule skips unknown codes, external and otherwise. Unlike `RUF105` I don't think there's anything sensible we could do with those here. Test Plan -- New mdtests and a CLI test for the combination of `RUF105` and `RUF106`. I did a local grep of our ecosystem projects and didn't find any `ruff:ignore`-style suppressions, so I don't expect any ecosystem hits for this.
… observations from popular projects (#26673) <!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? - Does this PR follow our AI policy (https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)? --> ## Summary This changes how we resolve an ambiguity in extracting parameter documentation from Google-style docstrings that being with a reStructuredText literal block. Here is an [example of such a docstring that occurs in the wild](https://github.com/pytorch/pytorch/blob/e3f5bf0b18585511e6cd7d7a574ebf82f465e5ae/torch/_native/instrumentation.py#L360-L384) (paraphrased below): ```py def instrument_triton_kernel(op: str): """Instrument a Triton kernel, with the instrumentation decorator above the jit:: @instrument_triton_kernel("aten::bmm") @triton.jit def kernel(...): ... The kernel compiles lazily and caches variants on the kernel object. Args: op: Operator symbol being compiled. """ ``` Previously, the parser used the raw indentation of the docstring and incorrectly treated the entire remainder of the docstring as part of the literal block. As a result, we did not recognize any parameter documentation from the above block. After the fix, we instead parse the PEP-257-normalized docstring, and thus correct recognize the documentation for `op`. The tradeoff is that if the entire remainder of a docstring beginning with a literal block were intended as literal content, and that content looked like a Google-style param section, we would instead interpret it as real parameter documentation: ```py def example(): """Example:: Args: value: Literal text, not parameter documentation. """ ``` Whereas the former example was actually observed in the wild in quick survey of popular Python repos, the latter was not. As an added benefit, this simplifies our Google-style docstring parsing and aligns it with our reStructuredText parsing, thereby streamlining follow-up [document-model](astral-sh/ruff#26670) and [Markdown-rendering](astral-sh/ruff#26599) changes. ## Test Plan Please see included tests. <!-- How was it tested? -->
…(#26714) Use the `remove_inferable_typevar_artifacts_from_solution` helper from astral-sh/ruff#26099, instead of the current naive union filtering during collection inference, to eliminate unnecessary inferable type variables that prevent the constraint solver from determining bidirectional inference type mappings. I also reorganized the relevant tests slightly. Resolves astral-sh/ty#3956 (comment). Note that the diagnostics there were a little misleading because the callable types and concrete function types are rendered identically if they have the same signature, despite being distinct types.
## Summary Check only the current function definition when validating `TypeIs` and `TypeGuard` definitions. `check_type_guard_definition` runs for every `FunctionDef`, which includes each individual overload, so it shouldn't iterate previous overloads each time also. Fixes astral-sh/ty#3968 ## Validation Added mdtests.
## Summary
Prior to this change, we could incorrectly reject a call to a stored
bound method obtained from a class-based protocol. Member lookup has
already specialized the method for its receiver, but calling through a
callable union reconstructed the original signature and prepended the
receiver as a synthetic argument. Generic call inference then
structurally checked the inferred `self` annotation again:
```python
from collections.abc import Iterator
from typing import Any
class PeekIterator(Iterator[Any]):
def __init__(self, iterator: Iterator[Any]) -> None:
self._next = iterator.__next__
def __next__(self) -> Any:
# Before: Expected `Self@__next__`, found `Iterator[Any]`
return self._next()
def use_fallback(self) -> None:
self._next = lambda: None
```
The receiver is already bound, so `Iterator.__next__` genuinely takes no
call-site arguments and should not be checked against `Self` again. We
now consume an implicit positional receiver before call inference, apply
the `typing.Self` substitution to the remaining signature, and refresh
its return type. This removes the false positive and avoids re-entering
recursive protocol interfaces during binding, which could otherwise
cause overloaded recursive members to grow the deferred constraint graph
combinatorially.
This also corrects constructor diagnostics for class-based protocols.
Previously we counted the implicit receiver in both the expected and
supplied arity:
```python
from collections.abc import Hashable
def construct(value: Hashable) -> None:
# Before: expected 1, got 4
# After: expected 0, got 3
type(value)(1970, 1, 1)
```
Because binding removes the leading parameter, we preserve its
source-parameter offset so invalid-argument and missing-argument
diagnostics still point at the correct declaration. Explicit, variadic,
keyword-only, and other nonstandard receivers retain the existing
call-inference path; the protocol relation itself is unchanged.
The independent recursive traversal in `redundant-cast` checks is
handled separately in #26708.
Related: <astral-sh/ty#3954>
## Summary
Prior to this change, the common-protocol-constraint optimization for
unions of `TypedDict`s did not apply after an `isinstance(value, dict)`
check. Narrowing represents each surviving arm as an intersection with
`Top[dict[Unknown, Unknown]]`, so a call like `dict(value)` fell back to
combining equivalent generic protocol constraints independently across
every union member:
```python
from typing import Literal, TypedDict
class A(TypedDict):
tag: Literal["a"]
class B(TypedDict):
tag: Literal["b"]
def copy(value: A | B | str) -> None:
if isinstance(value, dict):
reveal_type(dict(value)) # dict[str, object]
```
With a sufficiently wide union, that fallback exhibited pathological
runtime. On the issue's 21-arm reproduction, the pre-fix build did not
complete within ten seconds; the optimized path completes in under 0.2
seconds.
We now recognize intersections containing a positive `TypedDict` as
`TypedDict` alternatives, while retaining the complete intersection when
comparing its protocol constraints with `Mapping[str, object]`.
Closes astral-sh/ty#3974.
Co-authored-by: Nate Bracy <nate@bracy.dev>
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Micha Reiser <micha@reiser.io>
## Summary Pull in [astral-sh/ecosystem-analyzer#130](astral-sh/ecosystem-analyzer#130), which updates its mypy-primer pin to include [hauntsaninja/mypy_primer#256](hauntsaninja/mypy_primer#256). `svcs` moved its typing tests from `tests/typing` to `typing_tests` in [hynek/svcs#177](hynek/svcs#177). The previous analyzer pin still used a mypy-primer entry with `paths=["src", "tests/typing"]`, so ty reported the missing path as an I/O error and ecosystem-analyzer recorded a persistent exit code 2 on both sides of PR comparisons.
## Summary Prior to this change, every system-file lookup normalized and allocated an absolute path before checking the file cache, even when the supplied path was already absolute and cached. Module resolution repeatedly takes this path while discovering packages and directory listings. We now check the cache with the supplied absolute path first in both `Files::system` and `Files::try_system`. Camino's path equality and hashing already normalize repeated separators and `.` components; paths containing `..` or relative paths safely miss and continue through the existing normalization path. --------- Co-authored-by: Charlie Marsh <charliemarsh@openai.com>
## Summary
Prior to this change, equality and membership narrowing preserved
otherwise-incompatible union members because a subclass could override
`__eq__`. By default, we now assume that subclasses of a non-final class
do not override the inherited comparison method:
```py
class Foo: ...
def f(value: Foo | None, other: Foo) -> None:
reveal_type(None == other) # Literal[False]
if value == other:
reveal_type(value) # Foo
if value in [other]:
reveal_type(value) # Foo
```
The behavior is intentionally unsound. Users who want conservative
equality behavior can enable the single `strict-equality-narrowing`
setting, which also preserves broad builtin types instead of narrowing
them to literals:
```toml
[tool.ty.analysis]
strict-equality-narrowing = true
```
For compatibility, `strict-literal-narrowing` remains supported as an
alias for `strict-equality-narrowing`.
Closes astral-sh/ty#3419.
As a prereq for implementing [scoped quantifiers](https://gist.github.com/dcreager/679132607be4c7cfb08ddc8ad1076982), this PR updates all of our BDD path walking code to use a new visitor trait. Instead of each higher level method implementing its own copy of the path walking code, there is now a `PathAssignments::visit` method that handles the path walking. It takes in an impl of the new `PathVisitor` trait, which lets each higher level method define what overall result is being produced, and to provide opportunites to abort the walk once we've found an answer. As an interesting wrinkle, the `is_always_satisfied` method needs its own variant, `visit_negated`, which visits the negation of a BDD. Importantly, it constructs that negation lazily, so that we can still return early. (An early draft of this PR just called `visit` on `node.negated`, which quickly exposed this perf regression.)
…#27100) ## Summary Prior to this change, narrowing an `Unknown | None` match subject across a sequence of value patterns accumulated constraints of the form `(Unknown & ~Literal[EventType.A00]) | None`. Intersecting all of those constraints before normalization distributed the shared `None` arm across every combination, yielding exponential time and memory. We now normalize each intersection as it is accumulated, allowing equivalent union arms to collapse before the next constraint is applied. A 24-arm `StrEnum` match that previously exhausted memory now finishes in about 40 ms using 38 MiB, and an mdtest covers the original nullable dynamic-subject shape. Closes astral-sh/ty#4068. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
## Summary Fix disjointness checking between `type[T]` and `TypeForm[S]` by comparing the represented instance types. PEP 747 defines `type[T]` as a subtype of `TypeForm[T]`, but the generic-subclass fallback could incorrectly report the pair as disjoint; intersection reduction then became order-dependent and generic `TypeIs[type[T]]` narrowing produced `Never`. ## Test plan Added mdtest coverage for subtype and disjointness invariants, both intersection orders, overlapping and genuinely disjoint represented types, and generic `TypeIs` narrowing from `TypeForm[T]` to `type[T]`.
## Summary
Fixes some issues with generic stringified PEP 613 type aliases:
```py
from typing import TypeAlias, TypeVar
T = TypeVar("T")
ListAlias: TypeAlias = "list[T]"
def takes_list(value: ListAlias) -> None:
reveal_type(value) # `list[Unknown]`, previously `list[T]`
takes_list([1]) # accepted, previously a cryptic error
```
closes astral-sh/ty#4064
## Test Plan
Added regression test
## Ecosystem
Codex's summary: "favorable and fully explained by the intended fix"
Move Ruff's macOS test and binary-build jobs to the `namespace-profile-macos-15` runner already used by uv. Keep the existing Rust-cache behavior and GitHub-hosted fallbacks for forks, and leave the infrequent typeshed-sync job on GitHub-hosted macOS since it requires `contents: write`. Three sequential Namespace samples were compared with the three most recent `main` test runs on `depot-macos-15` and the three most recent production releases on `macos-15`: | Job / metric | Baseline mean, n=3 | Namespace mean, n=3 | Improvement | | --- | ---: | ---: | ---: | | macOS tests, whole job | 5:26 | 2:31 | 54% faster | | macOS tests, test step | 4:34 | 2:13 | 51% faster | | x86_64 release, whole job | 9:54 | 3:05 | 69% faster | | x86_64 release, build step | 9:13 | 2:44 | 70% faster | | aarch64 release, whole job | 9:13 | 3:14 | 65% faster | | aarch64 release, build step | 8:52 | 2:54 | 67% faster | All nine Namespace jobs were admitted on distinct runners within 19-29 seconds, with all three macOS jobs running concurrently in each sample: | Namespace job | Queue median (range), n=3 | | --- | ---: | | macOS tests | 0:21 (0:20-0:29) | | x86_64 release | 0:22 (0:22-0:22) | | aarch64 release | 0:21 (0:19-0:22) | The Namespace test runs were cold Rust-cache misses: the image's preinstalled stable toolchain differs from Depot's and produces a different `Swatinem/rust-cache` key. `main` will populate the new key after the transition. This is an effective configured-runner comparison, not an apples-to-apples provider comparison: the Namespace profile runs on M4 Pro with 12 vCPU and 28 GB RAM, `depot-macos-15` runs on M2 with 8 CPU and 24 GB RAM, and `macos-15` uses a smaller GitHub-hosted runner. --------- Co-authored-by: Zanie Blue <contact@zanie.dev>
…` models (#27098)
## Summary
Copy the underscore-prefixed parameters like `_secrets_dir` from
`BaseSettings.__init__` when synthesizing constructors Pydantic models
derived from for `BaseSettings`. This allows us to type check valid code
like this without any errors:
```py
class Settings(BaseSettings):
host: str
port: int
Settings(_secrets_dir="./secrets")
```
closes astral-sh/ty#4067
## Test plan
New Markdown test
Addresses #15584 for D400. Adds fix safety documentation to the `D400` rule. Co-authored-by: Mahadev Annabhimoju <219508079+Joosboy@users.noreply.github.com>
…27091)
## Summary
For lax-mode models, we try to model Pydantic's coercion behavior by
promoting some types like `int` to `LaxInt = int | str | ...`. When a
field referred to a non-builtin type, we previously widened the
corresponding constructor parameter type to `Any`, since we didn't know
what that field would accept. However, there is one case where we can be
more strict: if the field refers to another Pydantic model, we can widen
`Child` to `Child | Mapping[str, Any]`, since that represents all
possible values that Pydantic can accept for a field like this:
```py
class Child(BaseModel):
value: int
class Stranger(BaseModel): ...
class Parent(BaseModel):
child: Child
Parent(child=Child(value=1))
Parent(child=Stranger()) # previously accepted, now an error
```
Adding support for this also required proper modeling of a few other
behaviors, or otherwise this would have caused false positives:
- Detecting the [`from_attributes`
setting](https://pydantic.dev/docs/validation/dev/api/pydantic/config/#pydantic.config.ConfigDict.from_attributes).
Models that use this setting still use `Any` as the input type.
- Detecting ["before"
validators](https://pydantic.dev/docs/validation/latest/concepts/validators/#field-before-validator)
which can change the input type. For now, affected fields also still use
`Any` as the input type.
- Handling generic models. `Box[int]` can accept a `Box[str]`, so we
currently use the `Unknown` specialization `Box[Unknown]` for the input
type.
closes astral-sh/ty#3947
closes astral-sh/ty#3510
## Test Plan
- New Markdown tests
- validated all new test assertions against Pydantic runtime behavior
## Ecosystem
The new hits on Prefect are all expected.
## Summary A small refactor for `invalid_index_type` rule code, no functional changes introduced. The code has an `is_literal()` check that isn't actually needed - even when we matched literal, we still then match `Int | Bool` or `Int | Bool | None` explicitly, so we might as well drop `is_literal` completely. Other literals besides `Int | Bool | None` don't carry any meaning here, they're just other unexpected types from `CheckableExprType`, just as `List`, `Set`, etc. https://github.com/astral-sh/ruff/blob/4fc9653b505dd7784f56afa574ccb11b602d675e/crates/ruff_linter/src/rules/ruff/rules/invalid_index_type.rs#L239-L242 https://github.com/astral-sh/ruff/blob/4fc9653b505dd7784f56afa574ccb11b602d675e/crates/ruff_linter/src/rules/ruff/rules/invalid_index_type.rs#L91-L104 Just a small leftover from the refactor in https://github.com/astral-sh/ruff/pull/8064/changes#diff-0a227bd56e5ecade9d52a2a29e98b681d03951897372742e42a55800481cf97f, when `is_literal` was introduced to replace the old `Expr::Constant`. ## Test Plan No new tests needed, existing tests pass.
## Summary Use ty's normal intersection-member lookup when inferring class-pattern captures instead of widening attributes from overlapping nominal classes into a union. This lets a class pattern over a non-final union correctly narrow a captured attribute. ## Test plan - Covers a non-final `A | B` class-pattern capture where both classes define `params`, verifying that the `A` arm narrows both the subject and captured value and permits access to the `A`-specific attribute. - Covers incompatible overlapping attributes, compatible overlapping attributes with a reachable nested `str()` pattern, and generic overlapping attributes that retain `Unknown`. - Ecosystem changes are all desirable (removal of false positives from overly-broad types).
We currently infer interpolated string literals as `LiteralString`, but
do not allow them to be later promoted to `str` if used in invariant
position. Interpolated string literals should be promotable like any
other string literal form:
```py
from typing import Literal
def _(source: Literal["foo", "bar"]):
x = f"hello:{source}"
reveal_type(x) # revealed: LiteralString
reveal_type([x]) # revealed: list[str]
```
…`) (#26972) Closes #14353. Clarifies that `os.environ` converts environment variables to uppercase on Windows, which can lead to cross-platform bugs if code assumes variables preserve their original case. --------- Co-authored-by: Mahadev Annabhimoju <219508079+Joosboy@users.noreply.github.com> Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
merges `upstream/main` at 47d7184 into the fork. 45 conflicts resolved; typeshed regenerated from scratch; full suite green (10355 tests), ci insta gate clean, clippy and prek clean. notable ripples from upstream refactors: - salsa `Update` derive renamed to `SalsaValue`; tracked functions and interned fields now return references unless marked `returns(copy)` - `Type::to_instance` returns an `InstanceProjection`; fork call sites that only need the type use `to_instance_approximation` - the parser collects into `expr_scratch`/`stmt_scratch` buffers, so the fork's subscript and simple-statement hooks push there instead. the `context`-parameter validation had to move after the buffers are drained, or it inspected empty parameter lists and never fired - upstream's two-tier expression cache replaces the fork's `(expression, tcx) -> Type` map; the fluid fields are threaded through `FullExpressionCacheEntry` - `check_file` became a salsa query; `apply_overrides` became `apply_override_options` - `ProtocolMemberWrite` wraps the write capability, so the reified-member lookup goes through `domain()` - pydantic field metadata moved behind `pydantic::field_metadata`, which gains the fork's `frozen` flag fluid specializations needed a real fix: upstream now specializes a generic call's parameter type from the arguments themselves, so every generic call recorded a self-derived "adoption" and locked the specialization. argument contexts built that way are marked `inferred_from_argument` and no longer count as external observers. the typeshed patch pipeline is reproducible from scratch again: `container_overlapping` moved to the post-conversion pass (it matches the converted `Element`/`Key` names and collided with `mapping` in pass 1), and the sync script now runs the patches to a fixed point, since `private type _X` only becomes visible after `TypeAliasStatements` writes the `type _X = …` statement. fork divergences newly exposed by upstream tests are annotated in place: `isinstance(x, (List,))` through the recursive pep 695 `_ClassInfo` alias, covariant `Mapping` keys not widening to the declared key type, `in` on a provably-disjoint container erroring under `Overlapping`, and a `reveal_type` in the arguments of a fluid-receiver call reporting an intermediate pass.
KotlinIsland
temporarily deployed
to
github-pages
July 23, 2026 12:30 — with
GitHub Actions
Inactive
KotlinIsland
temporarily deployed
to
release-playground
July 23, 2026 12:30 — with
GitHub Actions
Inactive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.