Skip to content

Merge upstream - #7

Closed
KotlinIsland wants to merge 694 commits into
mainfrom
merge-upstream
Closed

Merge upstream#7
KotlinIsland wants to merge 694 commits into
mainfrom
merge-upstream

Conversation

@KotlinIsland

Copy link
Copy Markdown
Owner

No description provided.

renovate Bot and others added 30 commits June 23, 2026 21:37
## Summary

We record a reachability predicate for every statement-level call
because the call may return `Never`. Predicate IDs follow source order,
but reachability decision diagrams put later predicates before earlier
ones. In a large generated method, analyzing a late call could infer its
receiver, re-enter reachability for the preceding call, and continue
backward through thousands of calls until the worker stack overflowed.

For example, given:

```python
call_a()  # predicate 0
call_b()  # predicate 1
call_c()  # predicate 2
```

Reachability previously discovered the dependencies backward:

```text
analyze call_c
└─ analyze call_b
   └─ analyze call_a
```

Before evaluating a reachability constraint, we now force analysis to
process the relevant statement-level calls one by one in source order:

```text
analyze call_a → cached
analyze call_b → call_a is already cached
analyze call_c → call_b is already cached
```

Non-terminal-call analysis is a tracked Salsa query, so each result is
cached. When inferring a later call asks about an earlier call, it gets
that cached result instead of adding another nested inference and
reachability frame.

Inferring a call can itself re-enter reachability, so a scope-keyed
guard prevents the same source-order pass from starting again. A nested
scope is still allowed to perform its own pass. The ordinary
decision-diagram evaluation and `Never`-based narrowing behavior remain
unchanged.

The exact PySide6 reproduction now completes successfully instead of
aborting with a stack overflow.

Closes astral-sh/ty#3822.
Co-authored-by: Micha Reiser <micha@reiser.io>
## Summary

This PR updates the parser README to:
1. Link the parser README's AST reference to the published
`ruff_python_ast` crate
2. Remove the obsolete "Python version support" section now that version
specific syntax errors are implemented

Additionally, I've also updated the README generator to add a
"Versioning" section while preserving the rest of the handwritten README
and updated the generator to refresh marker-delimited versioning
sections. Maybe this is not worth doing, let me know if that's the case.

### Review

-
astral-sh/ruff@5a4145a
- updates the README manually as mentioned in (1) and (2) above
-
astral-sh/ruff@dcb5925
- updates the README generator to add the versioning section

## Test plan

Here's the [Rendered
README](https://github.com/astral-sh/ruff/blob/dcb59253c41ecbebacd919defcbb725461a95478/crates/ruff_python_parser/README.md)
for preview.

Ran `uv run --script scripts/generate-crate-readmes.py` twice and
verified idempotent output
## Summary

Adds generated versioning sections to three handwritten crate READMEs.

Follow-up to [ruff#26315](astral-sh/ruff#26315).

I've skipped `ruff_annotate_snippets` for now as it's a fork of the
original crate.
This document explores the combination of generics and gradual
set-theoretic types. In particular, we derive the following results that
might be useful for implementing some union and intersection
simplifications in ty, which in turn might help implement some features
around `isinstance` and `TypeIs` narrowing (especially the last one):

```py
Co[P] | Co[Any] = Co[P | Any]
Co[P] & Co[Any] = Co[P & Any]

Contra[P] | Contra[Any] = Contra[P & Any]
Contra[P] & Contra[Any] = Contra[P | Any]

Invariant[P] & Invariant[Any] = Invariant[P]
```
## Summary

We already infer equality results for literal values of the same kind,
but mismatched literal kinds fell through to dunder lookup and produced
`bool`. As a result, direct comparisons such as `1 == ""` remained
ambiguous, as did tuple comparisons whose corresponding elements had
different literal kinds.

This adds a narrow expression-inference case for mismatched built-in
`int`, `bool`, `str`, and `bytes` literals. It also handles
`LiteralString` compared with non-string literals while preserving
`bool` for `LiteralString` compared with a particular string literal,
since those values may be equal.

```python
reveal_type(1 == "")  # Literal[False]
reveal_type(True != "")  # Literal[True]

left = (1, 2)
right = (1, "two")
reveal_type(left == right)  # Literal[False]
```

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
## Summary

Avoid replacing the recursive occurrence when the current type is
already a variadic tuple, which prevents unbounded growth in cases like
`x = (*x, x)`.

Closes astral-sh/ty#3838

## Testing

Added mdtest.
## Summary

- Make `ty_python_semantic`, `ty_python_core`, and `ty_vendored`
publishable; `ty_module_resolver` is already in the publishing set and
remains unchanged.
- Publish `ty_combine` as the required runtime dependency of
`ty_python_core`. All other ty crates remain excluded from crates.io
publishing.
- Add the generated crate READMEs and make `ty_vendored`'s source-commit
include package-relative so the packaged crate verifies outside the Ruff
workspace.

The complete new dependency chain passes Cargo's publish dry run, all
774 affected crate tests pass, and the repository hooks and dependency
checks pass.
Co-authored-by: Micha Reiser <micha@reiser.io>
## Summary

We currently treat the members declared on every enum as its complete
runtime domain. That assumption does not hold for `Flag` and `IntFlag`,
which also have zero and unnamed combinations, or for enums whose custom
`_missing_` method or metaclass can create additional members. As a
result, we can incorrectly treat a one-member enum as a singleton, widen
all declared members to the nominal enum, erase a non-empty complement,
or consider a `match` over the declared members exhaustive.

This adds a `members_are_exhaustive` property to `EnumClassLiteral` and
uses it wherever we rely on an enum being a finite set: complement
construction, union simplification, singleton detection,
finite-alternative narrowing, and match exhaustiveness. Open enums
retain the nominal remainder after known members are excluded.

Explicit membership tests remain precise when the enum uses identity
comparison. For example, a metaclass may add `INJECTED`, but testing
against `ONLY` still establishes that exact member on the positive
branch and excludes only that member on the negative branch:

```python
class OpenEnum(Enum, metaclass=InjectingEnumMeta):
    ONLY = 1

def check(value: OpenEnum):
    if value in (OpenEnum.ONLY,):
        reveal_type(value)  # Literal[OpenEnum.ONLY]
    else:
        reveal_type(value)  # OpenEnum & ~Literal[OpenEnum.ONLY]
```

This is the semantic foundation split out of #26270; that PR adds the
compact same-enum comparison path on top.
## Summary

`@Todo` is a dynamic type that represents a known missing feature or
incomplete implementation in ty, but it previously had no user-facing
definition. As a result, go-to-type-definition on an inferred `@Todo`
type returned no target, and users had no editor-visible explanation of
what the type meant.

This adds a documented `Todo` symbol to `ty_extensions` and routes all
internal `@Todo` variants to that definition, following the existing
`Divergent` pattern. Users can now command-click an inferred `@Todo`
type to reach its documentation. `ty_extensions.Todo` remains an
internal navigation target and is rejected in annotations.

This complements astral-sh/ty#3847, which adds
an FAQ entry.

Closes astral-sh/ty#3209.
Summary
--

This addresses another discrepancy Codex noticed between `noqa` and
`ruff:ignore` while reviewing my
`--add-ignore` PR. `noqa` had special handling for shebang lines, which
allowed it to suppress a
diagnostic with a 0..0 range, like `D100`, even when a shebang was
present that prevented putting
the suppression on the first line:

```py
 #!/usr/bin/python
 # noqa: D100
```

[Playground](https://play.ruff.rs/5381b897-06d1-4d41-900b-cc85a40b2740)

This was previously not the case for `ruff:ignore`.

```py
 #!/usr/bin/python
 # ruff:ignore[D100]
```

[Playground](https://play.ruff.rs/1d3dde25-2692-43f3-a6fb-54b2e156ba66)

Test Plan
--

A new mdtest covering this scenario
## Summary

Record `ty_combine`, `ty_python_core`, `ty_python_semantic`, and
`ty_vendored` as configured for crates.io trusted publishing after
#26323 made them publishable.

The bootstrap script uses `.known-crates` as a persistent checkpoint and
skips entries on later runs. Checking in the generated result makes
subsequent bootstrap runs a no-op until another workspace package
becomes publishable.

The generated file now matches all 36 publishable workspace packages.
sharkdp and others added 28 commits July 8, 2026 07:18
## Summary

When a Pydantic model uses a `RootModel` as a field, that field can
either be passed as an instance of the class (`IntList`), or as the
underlying type directly (`list[int]`):

```py
class IntList(RootModel[list[int]]): ...

class Model(BaseModel):
    int_list: IntList

Model(int_list=IntList([1, 2, 3]))  # okay
Model(int_list=[1, 2, 3])  # also okay

Model(int_list=1)  # error
```

Prior to this PR, we allowed `Any` type to be passed for `RootModel`
fields.

towards astral-sh/ty#2403

## Ecosystem

The new diagnostic on prefect looks like a true positive.

## Test Plan

Updated Markdown tests
…637)

## Summary

A Pydantic field which uses `...` (ellipsis) for the `default` argument
is actually required and provides no default value.

```py
class Model(BaseModel):
    value: Any = Field(...)

Model()  # error
```

towards astral-sh/ty#2403

## Ecosystem

The two new diagnostics are false positives due to missing support for
`AliasChoices`. I think they are acceptable. They were missing
previously because we were modeling something completely wrong, so it's
hardly a regression (we only recently removed those diagnostics when
adding support for `extra`).

## Test Plan

Updated Markdown tests
## Summary

Report the actual and required read types for incompatible protocol
attributes, distinguishing missing writable capabilities from members
that reject the required write type.

For the motivating case, the diagnostic now explains:

```text
info: type `C` is not assignable to protocol `WithValue`
info: └── protocol member `value` is incompatible
info:     └── the member does not accept writes of type `str | None`
```

Related to astral-sh/ty#3940.

## Testing

Added mdtests with inline diagnostic snapshots.
## Summary

Attach existing assignability error context when an explicit type
argument fails a TypeVar upper bound.

Closes astral-sh/ty#3940.

For the motivating case in that issue, this now clarifies why the
concrete type is not assignable to the protocol.

## Validation

Added mdtest with inline diagnostic snapshot.
## Summary

Recognize frozen models that have `frozen=True` in their model config:

```py
class Person(BaseModel):
    model_config = ConfigDict(frozen=True)

    name: str

person = Person(name="Alice")
person.name = "Bob"  # error: [invalid-assignment]
```

With this change, we can now also get rid of the
`DataclassTransformerParams` that were still stored in every Pydantic
model's metadata.

towards astral-sh/ty#2403

## Test Plan

Updated Markdown tests
Co-authored-by: Carl Meyer <carl@astral.sh>
<!--
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 refactors how we extract parameter documentation from Google-style
docstrings. It replaces the existing regex-based scanner in
`ty_ide/src/docstring.rs` with a parser that should strictly improve
parameter documentation extraction along the following dimensions:

- We will now recognize parameters from `Keyword Args` and `Other Args`
sections (as well their other common spellings), where previously we
only recognized parameters from `Args` and `Parameters` sections.
- We now respect PEP 257 indentation and container boundaries, thereby
ignoring parameter-like text that appears inside Markdown fences/lists,
doctests, and reStructuredText directives, field lists, and literal
blocks.
- We now recognize continuation prose in parameter documentation more
consistently.
- We now extract docs for all parameters in a comma-separated list.

In addition, to those immediate improvements, this new section visitor
provides the source text range tracking that will allow us to render
Google-style docstring as markdown in an [upcoming
change](astral-sh/ruff#26599).

Please note that I have intentionally biased this parser towards
successfully parsing shapes that were actually found in the wild during
a corpus review of popular public repositories that use Google-style
docstrings. As such, there are theoretically possible shapes that we do
not bother to support because they are very unlikely to occur in real
docstrings. I think this is an acceptable compromise in favour of
maintainability.

## Test Plan

Please see included tests.
<!-- How was it tested? -->
## Summary

Closes #20729.

This marks `PYI061` fixes as unsafe in non-stub Python files.

The fix can rewrite `Literal["foo", "bar", None]` into `Literal["foo",
"bar"] | None`. That is equivalent for type checkers, but it is not
fully equivalent at runtime: code that introspects annotations with
`typing.get_args()` observes a different structure after the rewrite.

Stub files keep the previous behavior, since they are not runtime Python
files: fixes are still safe unless comments are removed.

I also added a regression case to the existing `PYI061.py` fixture.
While updating the snapshots, this also removes trailing whitespace from
the affected diagnostic messages.

## Test Plan

Reproduced the issue before the change:

```sh
cargo run -p ruff -- check /tmp/pyi061_repro.py --select PYI061 --fix --diff --no-cache --isolated
```

Verified that after the change, the fix is no longer applied by default
and is only available with `--unsafe-fixes`.

Verified that the fix is still available with unsafe fixes enabled:

```sh
cargo run -p ruff -- check /tmp/pyi061_repro.py --select PYI061 --fix --diff --unsafe-fixes --no-cache --isolated
```

Ran the targeted snapshot tests:

```sh
CARGO_PROFILE_DEV_OPT_LEVEL=1 INSTA_FORCE_PASS=1 INSTA_UPDATE=always CARGO_PROFILE_DEV_DEBUG="line-tables-only" MDTEST_UPDATE_SNAPSHOTS=1 cargo test -p ruff_linter rule_redundantnoneliteral_path_new_pyi061 -- --nocapture
```

Also ran:

```sh
cargo dev generate-all
uv run --only-group dev --locked prek run --files crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap
git diff --check
```

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
This showed a massive performance win for #25559; pulling it out into a
separate PR.

We have several operations that existentially quantify away a set of
typevars. Before, we would do this one typevar at a time, producing an
intermediate BDD after each. This was causing a big performance hit on
#25559, where we are about to start deferring quantification until we
start extracting solutions from a constraint set. That means that (a) we
have a larger set of typevars to quantify at once, and (b) we've built
up a larger, more complex BDD by the time we're ready to quantify.
Quantifying the first typevar is fine, but that produces an even larger
BDD, which then feeds into quantifying the second typevar, which
produces an even larger...and so on. There _are_ opportunities for the
quantification to _simplify_ things and produce a smaller BDD, but
there's no guarantee where in the sequence of typevars that will happen.

The main contribution of this PR is to quantify all of the typevars in a
single pass through the original BDD. That change is actually pretty
straightforward. But it identified an issue (that already had a TODO
comment describing it) in our "nested subsitution" logic. That logic
tries to identify runaway expansions in a constraint whose typevar
appears recursively in its lower/upper bound. The detection logic was
too simple, and could incorrectly flag unrelated expansions as being
runaways. The new logic tracks a separate chain of expansions (called
"histories" in the code) for each assignment in the path.
<!--
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 is a minor refactor to remove some unnecessary duplication in
tests.

## Test Plan

See modified tests.
<!-- How was it tested? -->
<!--
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 adds additional Google-style section types to the ones that we
recognize in a docstring. For the moment this is inert, but it will soon
allow us to [render those additional section types as
Markdown](astral-sh/ruff#26599).

## Test Plan

Since this new code is inert, we are relying on existing regression
coverage.
<!-- How was it tested? -->
In the legacy solver, when a nominal instance is matched against a
(formal) `Callable`, use the `(Type::Callable(formal_callable), _)`
match arm which handles the transformation of a nominal instance into a
callable object. This allows us to solve cases like

```py
from typing import Callable

def call[R](callable: Callable[[], R]) -> R:
    return callable()

class MyCallable:
    def __call__(self) -> int:
        return 1

my_callable = MyCallable()
reveal_type(call(my_callable))  # previously: Unknown, now: int
```

closes astral-sh/ty#3763

The ecosystem impact here shows the downstream fallout of type inference
becoming more precise. I didn't spot anything that would indicate a
problem with this particular change.
## Summary

`float`s that were embedded in unions were not recognized by our lax
mode mapping (since we were only looking for the expanded `float |
int`), so they were mapped to `Any` instead of `LaxFloat`, which lead to
overly permissive lax types.

closes astral-sh/ty#3946

## Test Plan

Added a regression test
## Summary

Minor cleanup to avoid all of the `matches!(...)`.
## Summary

In #25955, we started to reuse our equality logic to narrow membership
tests (`in`, `not in`). This PR then defines when the right-hand side of
an `in` or `not in` test has behavior that is precise enough for us to
use that reasoning.

For nominal types, we walk the statically visible MRO. `list`, `set`,
`frozenset`, `dict`, `tuple`, and `range` use the "containment domain"
of their specialized built-in base when we reach that base before any
custom `__contains__` implementation. Final classes that only define
`__iter__` can also use their iteration domain, while open iterable
classes remain conservative because a subclass could add `__contains__`.

A visible `__contains__` override on the class or an intermediate base
disables narrowing:

```python
from typing import Literal, TypedDict

class Payload(TypedDict):
    value: int

class MissingList(list[Literal["missing"]]):
    pass

class ContainsEverything(list[Literal["missing"]]):
    def __contains__(self, value: object) -> bool:
        return True

def inherited(value: Payload | Literal["missing"], values: MissingList) -> None:
    if value in values:
        reveal_type(value)  # Literal["missing"]

def overridden(value: Payload | Literal["missing"], values: ContainsEverything) -> None:
    if value in values:
        reveal_type(value)  # Payload | Literal["missing"]
```

The rule is intentionally unsound for open subclasses of built-ins. At
runtime, a `list[T]` could be a subclass that overrides `__contains__`,
but we assume it isn't. This matches our existing policy for tuple
dunder methods, where we assume unsafe overrides will eventually be
reported at the definition site.
## Summary

Previously, rendering edits in preview mode involved taking a diff of
the entire file (for non-notebooks) for each separate violation/fix.
Instead, we now take a small context window around the edit, and diff
that instead. This gives a **1.75-3.6x** speed up on real world cases.

No AI was used in generating the code or PR text.

## Test Plan

Correctness tested with `cargo nextest run`.

Performance tested using the `profiling` profile on the python code in
the `ruff` repo:

- current `main`:
```console
$ ./ruff_main version
ruff 0.15.20+90 (40a62bc 2026-07-06)

$ hyperfine -w 3 -i "./ruff_main check --preview --select=ALL"
Benchmark 1: ./ruff_main check --preview --select=ALL
  Time (mean ± σ):     355.7 ms ±   2.0 ms    [User: 559.8 ms, System: 45.0 ms]
  Range (min … max):   352.2 ms … 358.4 ms    10 runs
```
- this PR:
```console
$ hyperfine -w 3 -i "target/profiling/ruff check --preview --select=ALL"
Benchmark 1: target/profiling/ruff check --preview --select=ALL
  Time (mean ± σ):     205.0 ms ±   4.6 ms    [User: 401.8 ms, System: 48.2 ms]
  Range (min … max):   200.2 ms … 213.9 ms    14 runs
```

Taking the min time for each, this is a 1.75x speedup. I tested other
(real) projects, and this change was even faster on them.

I think this is a solution for #24548 -- although this PR is not able to
complete that extreme example within the timeout of 3 minutes, it does
manage to render about 75% of the violations. With a timeout of 10s,
current `main` gets to about `func_0_32` on my machine, whereas this PR
gets to `func_0_415`.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
## Summary

This represents parameter display names as the original `Name` plus a
small display prefix instead of constructing synthetic names such as
`*args` and `**kwargs`.

Signature formatting now writes the prefix only when rendering, and
diagnostic contexts retain an owned wrapper that clones or shares the
underlying identifier without allocating a decorated `Name`.
## Summary

This replaces the AST `Name` used by `ProjectMetadata` with a private
`ProjectName` backed by `CompactString`.

Project names can come from distribution metadata or directory names and
are not necessarily Python identifiers. Keeping them in a dedicated type
makes that distinction explicit while preserving compact storage and the
existing string accessor.
## Summary

Metaclasses can declare instance variables that their construction hooks
populate in every class they create. We already exposed these attributes
on the class object, but instance lookup only searched the constructed
class's ordinary MRO. As a result, custom enum classes could fail
protocol checks even though their metaclass populated the required
attributes at runtime.

Treat an annotation-only instance declaration directly on the concrete
metaclass as a contract for an attribute stored in each constructed
class namespace:

```python
class Meta(type):
    generated: int

    def __new__(mcls, name: str, bases: tuple[type, ...], namespace: dict[str, object]):
        namespace["generated"] = 1
        return super().__new__(mcls, name, bases, namespace)

class C(metaclass=Meta): ...

reveal_type(C().generated)  # int
```

The new lookup preserves normal class and instance precedence, including
data descriptors, dynamic bases, and implicit special-method lookup.
Bound metaclass attributes and inferred method writes remain excluded
from this contract. The mdtests cover the motivating protocol case along
with the relevant class, instance, and descriptor interactions.

Closes astral-sh/ty#3535.
…ions

- sort pep695 typevar-removal iteration and merge rename frames via extend
  (new iter-over-hash-type lint)
- regenerate uv.lock for the merged [dependency-groups]
- accept ci-gate inline snapshots (.pyi->.byi paths, ty->by binary name,
  fork doc urls, 0.0.3 crate versions); rustfmt pass

- pack class-header bools into ClassLiteralFlags (salsa 12-field limit), add
  IS_ENUM_VARIANT so upstream's has_type_params fast path doesn't skip
  variant generic-context inheritance
- expand a payload-enum-bounded Self to its variant union in expand_type so
  match self stays exhaustive under upstream's new coverage analysis
- gate upstream's ~T negation-type syntax and BitAnd runtime probe to
  standard python files
- port covariant-projection write check into attribute_assignment.rs
- rewire based payload-enum split from deleted narrow.rs helpers into
  type_expansion.rs
- update mdtest expectations for fork divergences (covariant Mapping key,
  typeshed typevar names, is_subtype_of moved to ty_extensions._internal)
- dedupe [dependency-groups] in pyproject.toml
- regenerate ty docs (cargo dev generate-all)
- windows cli snapshot: the merged filter regex still matched `ty` while the
  binary is `by`, so `by.exe` wasn't normalized to `by` — retarget to `by`
- restore primer/good.txt (upstream deleted it moving to all-projects); the
  fork's ecosystem-roundtrip harness reads it as its default project list
the standalone `crates/basedpython` wheel crate (its own workspace, built by
maturin with --locked) had a stale Cargo.lock after the merge bumped workspace
dependency versions; `maturin build --locked` refused to update it, failing
every wheel-build platform. regenerate with `cargo update --workspace`
- remove the cargo-publish-dry-run job (upstream re-added it in a
  non-conflicting merge region; the fork had deleted it). the fork keeps
  ruff/ty crate versions in lock-step with astral's crates.io versions but
  modifies their apis, so `cargo publish --dry-run` resolves a published
  crate for a transitive dep and fails on api skew. the fork publishes only
  the basedpython wheel to pypi, never these crates
- bump cargo-test-linux and cargo-test-(windows/macos) timeouts 20 -> 45 min:
  the merged test count overruns the depot-tuned 20-minute budget on the
  fork's slower ubuntu-latest/windows-latest runners (both timed out mid-suite)
the formatter ecosystem check intermittently failed with a single project
error — `ruff format --preview` emitting many pathless `No such file or
directory (os error 2)` from the parallel file walk on one large project (a
different one each run: ibis, then airflow).

root cause is load: ruff-ecosystem defaults to 50 concurrent projects, each
running a 12-thread `buff format` walk plus git clone/reset. on the fork's
small `ubuntu-latest` runner this massively oversubscribes cpu and fs and the
parallel walk transiently races; astral's 32-core depot runner absorbs it, so
upstream stays green. the walker code itself is byte-identical to upstream.

add a `--max-parallelism` flag to the vendored ruff-ecosystem cli (default
unchanged at 50) and pass `--max-parallelism 2` for the two `format` runs.
the read-only `check` runs never errored, so they keep the default.

not reproducible locally on macos (linux/load-specific); validated the flag
plumbing end-to-end. the 360-minute job budget absorbs the slower run.
@KotlinIsland
KotlinIsland deleted the merge-upstream branch July 12, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.