Skip to content

Merge upstream - #216

Merged
KotlinIsland merged 715 commits into
mainfrom
worktree-merge-upstream-oct2026
Oct 25, 2026
Merged

KotlinIsland merged 715 commits into
mainfrom
worktree-merge-upstream-oct2026

Conversation

@KotlinIsland

@KotlinIsland KotlinIsland commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Merges astral-sh/ruff through e330485: 714 upstream commits from Sep 4 to Oct 23 2026, as one merge commit. The vendored typeshed is regenerated from upstream's .pyi rather than merged.

Where the fork and upstream now overlap

  • redundant-condition: upstream's lint replaces the fork's. The fork keeps overlapping-condition and its boolean-comparison checks. A truthiness test of T | None is upstream's truthiness-test-of-none-union.
  • Return-type inference: upstream's return-type-inference option gates the fork's engine. The strict preset turns it on; ty-compatible keeps upstream's default. Upstream's own engine was unreachable behind the fork's and is deleted. redundant-return-annotation is gated on the option.
  • Ambiguous overload results: these stay the fork's UnsafeUnion.
  • Local classes: a class a function returns is type[C], and an instance of one is C.
  • Closed documents: LSP requests on a closed file are answered from disk, as upstream now does. textHash is only needed when a client names a specific text.
  • Inlay hints: the return-type setting is upstream's inlayHints.returnTypes.

Fixed along the way

  • Bound-method variance and pep695_generic_context on an enum variant recursed forever.
  • if let clauses were swept by the condition lints.
  • An imported implicit receiver stopped resolving, including after a later rebinding.
  • A capturing bare case q: exempted the rest of its scope from redundant-condition.
  • type(name, bases, {}) rejected {}, which predates the merge.
  • A return type that never settles was hinted as -> Divergent.
  • Django tag libraries beside a .pyi stub were not found.
  • ty_project tests that assert a query did not run passed vacuously; the test database now refuses to hand back events it never recorded.
  • A long "a" | "b" | ... type expression recursed once per member and overflowed the unoptimised test stack.
  • Two upstream uv e2e tests assumed a refresh after every file change, or queried a script while uv was still installing its dependencies. They now follow the fork's server, which refreshes only when a change reaches the database.
  • The typeshed conversion mixed an imported legacy TypeVar into PEP 695 signatures, so max(xs, default=0) was rejected.

CI changes

The scripts dogfood step is upstream's new one, which checks every PEP 723 script:

  • The upstream scripts it flagged are fixed.
  • The fork's scripts are locked or pin their build dependencies by hash.
  • The step fails on errors only, because the fork makes every warning an error by default.

Upstream now requires uv 0.13 or later, so the fork's own setup-uv steps (cargo test, zensical, the docs deploy and the ecosystem round-trip) pin uv 0.13.5, as upstream's steps do.

Upstream's zizmor config also forbids taiki-e/install-action, so the fork's Python-matrix test job installs nextest from the dev dependencies instead.

The pygments lexer builds with flit-core, since upstream now pins build backends by hash.

Checked locally

  • Full workspace suite: passes with all features, external mdtests and opt-level 0, as CI runs it.
  • Corpus tests at opt-level 0: pass, matching CI's build.
  • Static checks: clippy, cargo hawk with no errors, and both cargo doc passes with doc tests.
  • Other CI checks: cargo deny and all three dogfood steps.
  • Hooks: prek run --all-files.

Known follow-ups (not in this PR)

  • Upstream's new attribute override check reports every drf serializer's class Meta:, which replaces the stubs' ModelSerializer.Meta without subclassing it (astral-sh/ty#4711). external/drf.md expects the error for now.
  • With a Python 3.7 target, the checker never finishes on upstream's new corpus file recursive_annotated_narrowing.py. CI does not check it at 3.7.
  • A class decorator that returns a subclass it defines makes the decorated class unusable as an annotation or base.
  • A generic function's local class leaks its type variable.
  • An unannotated override inherits its base's inferred Never or None, and its own body is never checked against it.
  • A valid classmethod[Owner, [int], str] = classmethod(make) is rejected.
  • The API lockfile stamps ty_python_semantic's crate version rather than basedpython's.
  • Upstream's sign-release-binaries now gates release.yml, so releases will not publish as merged.

carljm and others added 30 commits October 7, 2026 10:31
Concrete subclasses of generic dataclass-transform classes retained the
base class's type variables in inherited converter inputs, rejecting
valid constructor calls, attribute assignments, and `__replace__` calls.
Specialize the types stored in field metadata so these operations accept
the subclass's concrete type arguments.

Apply type mappings to field defaults and converter input/output types,
and avoid applying the specialization twice when collecting Pydantic
right-hand-side defaults.

Fixes astral-sh/ty#4677.

## Test plan

Manually compared constructor signatures for equivalent right-hand-side
and `Annotated` Pydantic field defaults using minimal local stubs before
and after the change. Both forms retained matching defaults under
generic specialization.
Comparisons on narrowed operands can return objects other than `bool`,
but intersection comparison inference intersected those results with
`bool`. This could turn a valid comparison into `Never` and hide errors
in code that remains reachable.

Resolve rich-comparison methods with the full intersection receiver so
non-boolean results and `Self` constraints are preserved. Keep boolean
simplifications for boolean results, respect reflected-method priority,
and use inherited `object` comparison methods only as fallbacks.
Preserve union alternatives within each intersection component's return
type, with a conservative union fallback when distributing them would
exceed the complexity limit. Retain definite equality and inequality
results for gradual operands narrowed to `None`.

Fixes astral-sh/ty#4380.

## Ecosystem report

Analyzed head: `9ea38ae03484b349136e8fb102c21fcb1938ef72`. Most changes
are expected off-by-default unsoundness warnings for gradual comparison
results. Preserving non-boolean results also removes false
unreachable-branch diagnostics and exposes existing downstream
annotation mismatches; reflected arithmetic becomes more precise. No
remaining ecosystem regressions were identified. [Full ecosystem
analysis](astral-sh/ruff#28045 (comment)).

The current-head memory report and [CodSpeed
comparison](https://app.codspeed.io/astral-sh/ruff/runs/compare/6abc0e147d32f5eb9f94ffd2..6abc1f087d32f5eb9f95019d)
show no material performance regression.
## Summary

We now avoid a stack overflow when checking recursive protocol methods
with intersection types:

```python
from __future__ import annotations
from typing import Protocol, reveal_type
from ty_extensions import Intersection, Not

class A[T](Protocol):
    def make_invariant(self, value: T) -> T: ...
    def cause_problems(self) -> A[Intersection[Not[A[T]], A[str]]]: ...

def foo[T](x: A[T]):
    reveal_type(x.cause_problems().cause_problems())
```

Specifically, we now specialize protocol members only when they are
accessed or compared, avoiding eager expansion of recursive return
types.

Closes astral-sh/ty#4099.
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [icu_normalizer](https://icu4x.unicode.org)
([source](https://redirect.github.com/unicode-org/icu4x)) |
workspace.dependencies | minor | `2.2.0` → `2.3.0` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "before 4am on Wednesday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMzQuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjEzNC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
Constraint set relations now take a `ConstraintProvenance` parameter,
allowing the provenance of the constraint set to be chosen upon
construction, instead of a forcing a second pass to create a set of
validity constraints. See
astral-sh/ruff#28909 (comment).
Add eight- and sixteen-case benchmarks for tuple class patterns that
capture attributes. Successive cases create correlated tuple
alternatives and wide element unions, reproducing the expensive checking
reported in astral-sh/ty#4676. The smaller workload also measures the
cost of shorter match statements.

## Test plan

Parent: `5ce273da22e1`. Head: `7e0103df61e7`.

Measured on macOS-26.7.1-arm64-arm-64bit with optimized debug builds
(opt-level 1, LTO off). Criterion values are medians of three run means;
CLI values are medians of 15 warmed runs, with variants interleaved.
Reductions use geometric means of matched head/parent ratios (which
differ from ratios of the displayed medians); small Criterion
differences remain uncertain with only three rounds. One 32-case CLI
comparison round interrupted by system sleep was repeated in full.

| Workload | Benchmark baseline median |
| --- | --- |
| 8 cases, Criterion | 0.250 s |
| 16 cases, Criterion | 2.554 s |
| 8 cases, CLI | 0.226 s |
| 32 cases, CLI | 14.836 s |

The 32-case CLI input uses the original issue’s tuple/class-pattern
workload scaled to 32 cases; it is a separate fixture from the 16-case
Criterion workload.
When checking whether an intersection is disjoint from a type it
explicitly excludes, test that exclusion before visiting unrelated
positive components. This avoids unnecessary relation checks while
normalizing the intersections produced by tuple pattern narrowing.
Gradual types retain the full reflexive subtyping check.

Related to astral-sh/ty#4676.

## Test plan

Parent: `7e0103df61e7`. Head: `339c848e2e43`.

Measured on Apple Silicon with macOS 26.7.1 using optimized debug builds
(opt-level 1, LTO off). Criterion values are medians of three run means;
CLI values are medians of 15 warmed runs, with variants interleaved.
Reductions use geometric means of matched head/parent ratios (which
differ from ratios of the displayed medians); small Criterion
differences remain uncertain with only three rounds.

| Workload | Parent median | Head median | Paired time reduction |
| --- | --- | --- | --- |
| 8 cases, Criterion | 0.232 s | 0.190 s | +18.4% |
| 16 cases, Criterion | 2.343 s | 1.853 s | +20.0% |
| 8 cases, CLI | 0.223 s | 0.189 s | +15.4% |
| 32 cases, CLI | 14.664 s | 12.261 s | +15.9% |

The 32-case CLI input uses the original issue’s tuple/class-pattern
workload scaled to 32 cases; it is a separate fixture from the 16-case
Criterion workload.
For `["x"] if flag else []`, ty currently infers `list[Unknown]`.
Similarly, `{"x": 1} if flag else {}` is inferred as `dict[Unknown,
Unknown]`.

Infer the nonempty branch first when the other branch is an empty
literal of the same kind. This lets the empty branch use its peer’s type
as context, so both branch orders infer `list[str]` and `dict[str, int]`
for these examples.

Adapted from @tamird's [original
change](tamird/ruff@7914a95).

---------

Co-authored-by: Tamir Duberstein <tamird@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary

Reverts #29059, which caused a new stack overflow when inferring nested
generic protocol specializations (astral-sh/ty#4694).

This also removes the original fix for astral-sh/ty#4099, which will
need a different approach.

Closes astral-sh/ty#4694.
Follow-up to #29151: upgrade the benchmark dependency from hyperfine
2.0.0-alpha.2 to 2.0.0.

Use `--env RAYON_NUM_THREADS=1` instead of an `env` command prefix for
the single-threaded formatter benchmarks. This avoids measuring the
overhead of `env` itself (~5 ms).
Ruff's release pipeline publishes unsigned macOS and Windows binaries.
Add shared actions to sign Windows binaries and sign and notarize macOS
binaries, then reassemble wheels and GitHub archives with updated
`RECORD` entries and checksums before publication. Use 16 KiB
code-signature pages for ARM64 macOS binaries to reduce
signature-verification work. Signing uses the protected `release`
environment and the repository's `main` workflow, including release dry
runs.
Release signing fails while collecting `powerpc64-unknown-linux-gnu`
wheels because the target remains in `dist-workspace.toml` after #22766
removed its builds. Remove the stale entry so the signing inventory
matches the release build matrix.

Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary

We now check that attribute and property overrides preserve the types
that can be read and written through the superclass. The checks extend
`invalid-attribute-override` and introduce two separately configurable
rules, following the distinction proposed in
[ty#2158](astral-sh/ty#2158).

### `invalid-attribute-override`

We reject ordinary attributes whose readable type is incompatible with
the inherited declaration. This rule is enabled by default:

```python
class Base:
    value: int

class Child(Base):
    value: str  # Now rejected: invalid-attribute-override.
```

The rule also rejects removing write access, such as replacing a
writable attribute with a `Final` attribute, and retains the existing
checks for incompatible `ClassVar` qualifiers.

### `invalid-property-type-override`

We reject incompatible property result types, setters that accept fewer
values than the inherited setter, and getter-only properties that
replace writable attributes. This rule is also enabled by default:

```python
class Base:
    @Property
    def value(self) -> int:
        return 0

class Child(Base):
    @Property
    def value(self) -> str:  # Now rejected: invalid-property-type-override.
        return ""
```

A read-only property returning `bool` would be a valid override of this
`int` property. Reads can become more specific; writes must continue to
accept every value permitted by the superclass.

### `invalid-mutable-override`

Narrowing a writable attribute's type preserves reads but restricts
writes. We report this separately, and leave the rule disabled by
default:

```python
class Base:
    value: int

class Child(Base):
    value: bool  # invalid-mutable-override, when enabled.

def reset(obj: Base) -> None:
    obj.value = 42

child = Child()
reset(child)  # Allowed through Base, but child.value is no longer a bool.
```

This distinction lets users opt into mutable invariance without changing
the other override checks.

## Case analysis

### Cached properties in Home Assistant

The [ecosystem report](https://fed09ff3.ty-ecosystem-ext.pages.dev/diff)
adds 6,986 `invalid-property-type-override` diagnostics.

Home Assistant accounts for 6,863 of those, including 6,861 cases where
a read-only property replaces a writable attribute.

A common pattern is
[BlueCurrentEntity.available](https://github.com/home-assistant/core/blob/8f4c394961405acec82bc01f24620359736617b3/homeassistant/components/blue_current/entity.py#L43),
which replaces the inherited [Entity.available cached
property](https://github.com/home-assistant/core/blob/8f4c394961405acec82bc01f24620359736617b3/homeassistant/helpers/entity.py#L870)
with a getter-only property:

```python
from functools import cached_property

class Entity:
    @cached_property
    def available(self) -> bool:
        return True

class IntegrationEntity(Entity):
    @Property
    def available(self) -> bool:  # Now rejected: invalid-property-type-override.
        return True

def mark_unavailable(entity: Entity) -> None:
    entity.available = False

mark_unavailable(Entity())  # Succeeds.
mark_unavailable(IntegrationEntity())  # Raises AttributeError.
```

Both getters return `bool`, but the override removes a supported write
(a `cached_property` stores its value in the instance dictionary and
permits assignment to replace it, even before the first read, so it's
writable).

This PR retains the diagnostic (it's correct, after all): a function
accepting `Entity` can assign to `entity.available`. Replacing the
cached property with a getter-only property makes that valid operation
fail on subclass instances.

(pyright 1.1.411 in standard mode rejects this override, but mypy 2.3.1
and pyrefly 1.2.0 with default checks accept it.)

Part of astral-sh/ty#2158.
Comparisons between nominal instances with a non-generic target only
inspect MRO class identities. Skip the recursion guard for these
comparisons, avoiding its bookkeeping. Tuple targets and specialized
generic targets retain the guard because their element or type-argument
comparisons can recurse.

Related to astral-sh/ty#4676.

## Test plan

Parent: `4830211afdd4`. Head: `10168bb1e5dc`.

Measured on Apple Silicon with macOS 26.7.1 using optimized debug builds
(opt-level 1, LTO off). Criterion values are medians of three run means;
CLI values are medians of 15 warmed runs, with variants interleaved.
Reductions use geometric means of matched head/parent ratios (which
differ from ratios of the displayed medians); small Criterion
differences remain uncertain with only three rounds.

| Workload | Parent median | Head median | Paired time reduction |
| --- | --- | --- | --- |
| 8 cases, Criterion | 0.195 s | 0.175 s | +9.1% |
| 16 cases, Criterion | 1.874 s | 1.687 s | +11.6% |
| 8 cases, CLI | 0.193 s | 0.177 s | +6.6% |
| 32 cases, CLI | 12.139 s | 10.666 s | +11.8% |

The eight-case CLI comparison is inconclusive in this run; the
sixteen-case Criterion benchmark and 32-case CLI example show clearer
gains.

The 32-case CLI input uses the original issue’s tuple/class-pattern
workload scaled to 32 cases; it is a separate fixture from the 16-case
Criterion workload.

CodSpeed results on the PR look like a smaller improvement than measured
locally, but still significant.

The [automatic CodSpeed
report](astral-sh/ruff#29202 (comment))
compares `10168bb` with `4830211`. It identifies a 4.05% efficiency
improvement in the eight-case CPU simulation benchmark, with 179 metrics
classified as untouched and 60 skipped.
The Intel macOS wheel smoke test runs on an ARM runner. Although
`setup-python` selects `x64`, its universal interpreter defaults to ARM,
so `pip` rejects the signed Intel wheel as unsupported. Run the Python
commands through the Intel slice for `x86_64-apple-darwin`, matching the
architecture selection in astral-sh/uv#21532.

Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary

We now mark both branches of a condition with type `Never` as
unreachable:

```python
from typing import Never

def f(value: Never):
    if value:
        result = 1  # Unreachable.
    else:
        result = 2  # Also unreachable.
```

Previously, we treated `Never` as having ambiguous truthiness, allowing
either assignment to affect inference. In the reported loop, the
variables depend on each other, so including these impossible
assignments made their inferred types depend on which file we checked
first.

We add `Truthiness::Uninhabited` to represent the absence of either
boolean outcome. `Type::bool` and `Type::try_bool` return this variant
for `Never`, replacing the opt-in `bool_if_inhabited` API and making
callers handle the distinction explicitly.

Closes astral-sh/ty#4577.
Codex was overfitting its optimizations for tuple match narrowing to
non-generic classes, when generic classes display similar or worse
performance. Update the benchmark to also include the generic-class
case, so we don't fail to optimize that as well.

Add 8- and 16-case variants of the tuple class-pattern capture
benchmark, using writable `T | None` attributes and class instances
specialized with `int`.

The existing non-generic benchmark names and generated inputs are
unchanged.

Related to astral-sh/ty#4676.
ty can allow assignment to or deletion of a `Final` instance attribute
when the receiver is a union or has been narrowed through a protocol.
Another member of the composite type can make the attribute appear
writable.

In this contrived example, ty previously accepted the assignment because
`Mutable.value` is writable. With this change, ty reports
`invalid-assignment` because the receiver could be a `Fixed` instance:

```python
from typing import Final

class Fixed:
    def __init__(self) -> None:
        self.value: Final[int] = 1

class Mutable:
    value: int = 1

def update(either: Fixed | Mutable) -> None:
    # previously this was fine, but with this PR:
    either.value = 2  # error: [invalid-assignment]
```

Check the members of unions and positive intersection components for
`Final` restrictions. Assignment checks also follow type aliases, so an
aliased union member retains the restriction.

A class can still assign to its own `Final` attribute through `self` in
`__init__`, even when `self` is narrowed through a protocol. If several
union members prohibit the same assignment, ty reports one error and
stops checking rather than producing duplicate errors.

Adapted from @tamird’s [original
change](tamird/ruff@cf8a56c).

---------

Co-authored-by: Tamir Duberstein <tamird@gmail.com>
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[unicode-normalization](https://redirect.github.com/unicode-rs/unicode-normalization)
| workspace.dependencies | patch | `0.1.24` → `0.1.25` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "before 4am on Wednesday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Summary
--

This is the CLI accompaniment to #27807. The main change here is showing
display-only fix diffs in
the CLI. However, display-only fixes don't have a corresponding flag
like `--unsafe-fixes`, and I
thought it would be weird to show them unconditionally when we don't do
the same for unsafe fixes,
so this PR also unconditionally renders unsafe fix diffs, even when that
flag isn't passed. We
already emit a note about the fix being unsafe, so I think it makes
sense always to show you the
fix, and make `--unsafe-fixes` enable actually applying the fix. We also
don't want to apply
display-only fixes, so enabling rendering of the fixes with
`--display-only-fixes` or similar would
also disagree with the semantics of the unsafe flag. I'm open to other
ideas here, though.

The sub-diagnostic for display-only fixes uses a higher severity than
the note for unsafe fixes and
renders in red, as shown below.

The change here also affects ty, which now has both unsafe and
display-only fixes.

I again checked what clippy and rustc do in these cases, and they don't
include any additional note or warning about the fix being unsafe:

<details><summary>Rust diagnostics</summary>
<p>

Here's a
[`clippy::needless_pass_by_value`](https://rust-lang.github.io/rust-clippy/rust-1.97.0/index.html#needless_pass_by_value)
diagnostic, which is `MaybeIncorrect` ( similar to our `Unsafe`):

```
warning: this argument is passed by value, but not consumed in the function body
 --> src/main.rs:3:15
  |
3 | fn size(data: Data) -> usize {
  |               ^^^^
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.97.0/index.html#needless_pass_by_value
  = note: requested on the command line with `-W clippy::needless-pass-by-value`
help: consider taking a reference instead
  |
3 | fn size(data: &Data) -> usize {
  |               +
```

and here's a rustc
[`LetLike`](https://github.com/rust-lang/rust/blob/8bab26f4f68e0e26f0bb7960be334d5b520ea452/compiler/rustc_trait_selection/src/errors.rs#L268-L288)
diagnostic, which is `has-placeholders` (similar to our `DisplayOnly`
but definitely produces invalid code in this case):

```
error[E0282]: type annotations needed for `Vec<_>`
 --> placeholder.rs:2:9
  |
2 |     let values = Vec::new();
  |         ^^^^^^   ---------- type must be known at this point
  |
help: consider giving `values` an explicit type, where the type for type parameter `T` is specified
  |
2 |     let values: Vec<T> = Vec::new();
  |               ++++++++
```

</p>
</details>

Fixes astral-sh/ty#1679

Test Plan
--

Updated existing snapshots, and some manual testing in the CLI:

<img width="965" height="400" alt="image"
src="https://github.com/user-attachments/assets/888ad79a-6853-48d4-8344-48da4af47d74"
/>

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This PR updates quick-junit to
[v0.8.0](https://github.com/nextest-rs/quick-junit/releases/tag/quick-junit-0.8.0).
The original renovate description of the bump to v0.7.0 is below.

Note that I also had to update `fuzz/Cargo.lock` to get the fuzz build to run:

```console
cargo update --manifest-path fuzz/Cargo.toml thiserror@2.0.18 --precise 2.0.20
```

<hr>

This PR contains the following updates:

| Package | Type | Update | Change | Pending |
|---|---|---|---|---|
| [quick-junit](https://redirect.github.com/nextest-rs/quick-junit) |
workspace.dependencies | minor | `0.6.0` → `0.7.0` | `0.8.0` |

---

<details>
<summary>nextest-rs/quick-junit (quick-junit)</summary>

[`v0.7.0`](https://redirect.github.com/nextest-rs/quick-junit/blob/HEAD/CHANGELOG.md#070---2026-07-20)

[Compare
Source](https://redirect.github.com/nextest-rs/quick-junit/compare/quick-junit-0.6.1...quick-junit-0.7.0)

Skipped test counts are now reported via the `skipped` attribute rather
than `disabled`. For `<testsuite>`, `skipped` matches the [llg
schema](https://llg.cubic.org/docs/junit/) and the Jenkins [junit-10
schema](https://redirect.github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd),
as well as pytest and Maven Surefire.

- Each `<testsuite>` now emits `skipped="N"`, and the root
`<testsuites>` element now emits an aggregated `skipped="N"` count.
Previously the root didn't carry a skip count at all.
- Root-level `skipped` is a quick-junit extension. It follows the modern
JUnit convention (as documented, for example, by
[testmoapp/junitxml](https://redirect.github.com/testmoapp/junitxml))
and is similar to quick-junit's existing extensions such as `uuid`.
- `TestSuite`'s `disabled` field has been renamed to `skipped`.
- `Report` gains new public `skipped` and `disabled` fields, and
`Report::add_test_suite` now aggregates skip counts into
`Report::skipped`.

`disabled` is now modeled as a separate optional count on both `Report`
and `TestSuite`, distinct from `skipped`. (`disabled` is intended to
count tests that are disabled by design, such as googletest's
`DISABLED_`-prefixed tests. Note, however, that quick-junit doesn't have
a notion of disabled tests at the moment.)

- `Report` is now `#[non_exhaustive]`, consistent with `TestSuite` and
`TestCase`. Construct it via `Report::new` and then assign fields, so
that future field additions are non-breaking.
- Childless `<testsuite>` and `<testcase>` elements are now serialized
as self-closing tags (for example, `<testcase name="my-test"/>` rather
than `<testcase name="my-test"></testcase>`). The output is semantically
identical XML, only more compact and idiomatic. The deserializer already
accepted these self-closing forms, so serializing them means round-trip
tests now cover those parse paths.

</details>

---

📅 **Schedule**: (UTC)

- Branch creation
  - "before 4am on Wednesday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQ0Ljc5LjEiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbImludGVybmFsIl19-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
Remove the deprecated `ruff-lsp` compatibility CI job, its dependency
group and Python constraint, and the associated `pygls` Renovate rule.
Prune ten unused packages from the root lockfile while preserving all
remaining package versions and Git revisions.

Related: astral-sh/ruff#28545,
astral-sh/ruff-vscode#1170
Summary
--

This makes a few modifications to our default rule set for v0.17. As the
snapshots show, these had
already been incorporated into our preview defaults via rule
recategorization. This just ports them
back to the stable default rules.

This closes #27197, which brought the problems with the DTZ rules to our
attention. F406 being a
syntax error was raised in [this comment].

[this comment]:
astral-sh/ruff#27199 (comment)

Test Plan
--

Updated snapshots
Summary
--

I wasn't really planning to do a full stabilization pass, but I quickly skimmed through `preview.rs`
and remembered that the isort pragma change was kind a bug fix but technically breaking and thought
we might want to stabilize it.

This PR stabilizes the isort pragma comment width handling introduced in #27313, along with the
related pragma handling for `E501` and `W505` from #24071. There was a comment about also
stabilizing the related formatter preview feature on the function from #24071, so that is stabilized
here too.

I'm fine holding off on this, especially if the formatter change is a better fit for the 2027 style
guide. Like I said above, I mostly wanted to stabilize the isort change, but that snowballed a bit
since the helpers were shared.

Test Plan
--

Updated existing snapshots
Update the editor documentation for the VS Code extension's transition
to always using Ruff's native language server.

Remove the retired `ruff-lsp` settings entries, links to those entries,
and obsolete setup notes while retaining migration examples and
replacement guidance. Document `ruff.nativeServer` as deprecated in Ruff
0.17 and describe `ruff.interpreter` solely in terms of executable
discovery.

Related: astral-sh/ruff#28545,
astral-sh/ruff-vscode#1170

<!-- codex-thread: 01a0c3c7-f498-7682-9193-6e0e5307066b -->
zaniebot and others added 22 commits October 22, 2026 12:54
## Summary

We make redundancy comparisons converge when they participate in a Salsa
inference cycle. Comparing inferred callable signatures can depend on
the union being simplified: removing an alternative changes the inferred
signatures, which can make the next comparison return a different
answer.

The query still starts with `true`, but combines successive results with
`previous && current`. Once a comparison returns `false`, it remains
false for that cycle. Retaining the union alternative prevents the
comparison from oscillating; the tradeoff is that a redundant
alternative may remain in the union.

This is a prerequisite for #29553, whose stable lambda identities expose
this cycle. The regression examples belong to that PR because they
already terminate on `main`.
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Linux CI installs `mold` by replacing the system linker, but the current
Rust toolchain and MSRV already select bundled `lld` for
`x86_64-unknown-linux-gnu`. Remove the installation steps to avoid
downloading `mold` and changing the system linker for these jobs.

[Eight pairs of `ruff` debug
builds](https://github.com/astral-sh/ruff/actions/runs/39841713358) on
16-vCPU CI runners averaged 28.6 s with `mold` installation and 27.5 s
without. Each pair built both variants on the same runner from empty
target directories, with build order alternated. Times include
installation when applicable and exclude dependency downloads. The mean
change was −1.1 s (two-sided 90% paired t confidence interval: −8.8 to
+6.7 s); the interval includes zero. The second build was faster in
every pair, indicating a substantial order effect.

Co-authored-by: zaniebot <242828183+zaniebot@users.noreply.github.com>
## Summary

This PR adds basic tests for Django support in ty, and adds a first
customization point where we now synthesize `.id` and `.pk` attributes
for primary keys on Django models. Django support currently relies on
`django-types`, a fork of `django-stubs` which requires less
special-casing in type checkers.

Example
```py
from django.db import models

class Book(models.Model):
    isbn = models.CharField(max_length=13, primary_key=True)
    title = models.CharField()
    pages = models.IntegerField(null=True)

def _(book: Book):
    reveal_type(book.isbn)   # str
    reveal_type(book.title)  # str
    reveal_type(book.pages)  # int | None  (because null=True)

    reveal_type(book.pk)     # str  (because `isbn` is the primary key)

    book.id  # error: no automatic `id` with a custom primary key
```

## Test Plan

I wrote new Markdown test and also verified all of our assertions
against Django's runtime behavior.
ty ecosystem comments currently require downloading a ZIP to view the
full reports. Add an optional `upload-html` mode to the shared report
publisher and enable it for ty, linking directly to the diagnostic diff
and timing results while retaining the downloadable artifact.

The publisher runs from `main`, uses STS for GitHub credentials, and
obtains temporary AWS upload credentials through GitHub OIDC when
hosting is enabled. Each caller supplies filenames and content types,
with optional link labels. The publisher reads only those files from a
bounded archive without extracting or executing them during publication,
and stores reports under separate paths for each PR, report, run, and
attempt. If an upload fails, the publisher still attempts to post the
summary and, when available, the artifact link.

The `automations` environment requires `REPORTS_BUCKET`, `REPORTS_URL`,
`REPORTS_ROLE_ARN`, and `REPORTS_REGION` from the report hosting
infrastructure.

---------

Co-authored-by: Zanie Blue <2586601+zanieb@users.noreply.github.com>
Co-authored-by: zaniebot <242828183+zaniebot@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
Report comments include a redundant ZIP download link after the hosted
HTML links. Show the ZIP link only when the HTML report is unavailable,
including when its upload fails.

Co-authored-by: zaniebot <242828183+zaniebot@users.noreply.github.com>
## Summary

This PR adds support for the `get_<field>_display()` methods that Django
generates for model fields with `choices`.

Example:

```py
from django.db import models

class Article(models.Model):
    status = models.CharField(
        max_length=1,
        choices=[("D", "Draft"), ("P", "Published")],
    )

article = Article(status="P")

reveal_type(article.status)                # str
reveal_type(article.get_status_display())  # str ("Published" at runtime)
```

Towards astral-sh/ty#1018.

## Test Plan

New Markdown test.
Summary
--

This addresses the suggestion from [#29635]. `DisplayDiagnosticConfig`
previously determined the
`DiagnosticFormat` for diagnostics coming from both Ruff and ty, which
meant that it had to have a
shared enum between the two, despite each program supporting a different
subset of the available
formats. This PR eliminates the shared `DiagnosticFormat` and dispatch
through `DisplayDiagnostics`,
leaving Ruff and ty to construct `Renderer`s directly and do their own
dispatch on the output
formats they actually support. This will be especially helpful in
#29635, where we want to provide
slightly different arguments to each new `SarifRenderer`.

The `Renderer` trait itself isn't strictly necessary, but it made the
calls on the Ruff side a lot
neater since the trait provides a default `write` method based on the
required `render` method.
Codex also threw in a default `display` method that helps in a couple of
places too.

Test Plan
--

Existing tests

[#29635]:
astral-sh/ruff#29635 (comment)
…29653)

## Summary

This PR adds support for the `get_next_by_<field>()` and
`get_previous_by_<field>()` methods that Django generates for
(non-`null=True`) `DateField` and `DateTimeField` fields.

Example:

```py
from django.db import models

class Article(models.Model):
    published = models.DateField()

def _(article: Article):
    reveal_type(article.get_next_by_published())      # Article
    reveal_type(article.get_previous_by_published())  # Article
```

Towards astral-sh/ty#1018.

## Test Plan

New Markdown tests.
…664)

## Summary

This PR adds support for the `<field>_id` attributes that Django
generates for foreign keys. Their types follow the related model's
primary key, or the field specified by `to_field`.

Example:

```py
from django.db import models

class Author(models.Model):
    pass

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

def _(book: Book):
    reveal_type(book.author)  # Author
    reveal_type(book.author_id)  # int
```

Towards astral-sh/ty#1018.

## Test Plan

New Markdown tests. I checked against Django runtime behavior.
The bottom signature is trivially assignable _to_, and the top signature
is trivially assignable _from_, all other signatures. We can add fast
path checks for those before performing more complex signature
comparisons.
Handle `bot:ai-policy-comment` and `bot:ai-policy-close` on issues and
pull requests with the shared `ai-policy` action. The automation
dispatcher runs `ai-policy.yml` on `main`, where an STS-issued
`astral-automations-bot` token posts the policy reminder and optionally
closes the target.

Draft and fork PRs are supported. The workflow checks the current label
and state without checking out PR code.

Co-authored-by: zaniebot <242828183+zaniebot@users.noreply.github.com>
## Summary

We scan ASCII identifier continuations in sixteen-byte batches using a
byte lookup table, avoiding per-character decoding. Keyword lookup
filters by length and initial byte, then uses a compact hash table with
a full-word comparison to reject collisions. Non-ASCII suffixes retain
the existing Unicode path.
## Summary

We give ASCII identifiers a dedicated path, avoiding Unicode bookkeeping
and checking string prefixes only when a one- or two-letter name reaches
a quote. Both paths share the existing batched ASCII scanner, preserving
fast scanning for Unicode names with long ASCII suffixes.

Stacked on #29463 to measure the incremental improvement. [CodSpeed
reports](https://github.com/astral-sh/ruff/pull/29503/checks?check_run_id=118300735426)
6.6–9.8% greater efficiency across all five lexer benchmarks, with no
reported regressions. We preserve the existing scanner and add no
inlining attributes or unsafe code.
@KotlinIsland
KotlinIsland force-pushed the worktree-merge-upstream-oct2026 branch from 6db7be1 to e0b10f1 Compare October 24, 2026 22:00
@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

ecosystem check

Linter (stable)

ℹ️ ecosystem check detected linter changes. (+320 -231 violations, +0 -8 fixes in 27 projects; 1 project error; 30 projects unchanged)

DisnakeDev/disnake (+34 -0 violations, +0 -0 fixes)

+ disnake/activity.py:5:8: ICN001 `datetime` should be imported as `dt`
+ disnake/audit_logs.py:35:12: ICN001 `datetime` should be imported as `dt`
+ disnake/automod.py:5:8: ICN001 `datetime` should be imported as `dt`
+ disnake/channel.py:6:8: ICN001 `datetime` should be imported as `dt`
+ disnake/embeds.py:5:8: ICN001 `datetime` should be imported as `dt`
+ disnake/entitlement.py:5:8: ICN001 `datetime` should be imported as `dt`
+ disnake/ext/commands/base_core.py:6:8: ICN001 `datetime` should be imported as `dt`
+ disnake/ext/commands/core.py:6:8: ICN001 `datetime` should be imported as `dt`
+ disnake/ext/tasks/__init__.py:6:8: ICN001 `datetime` should be imported as `dt`
+ disnake/guild.py:6:8: ICN001 `datetime` should be imported as `dt`
... 24 additional changes omitted for project

RasaHQ/rasa (+9 -0 violations, +0 -0 fixes)

+ rasa/core/brokers/kafka.py:95:111: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ rasa/core/channels/hangouts.py:219:113: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ rasa/core/channels/hangouts.py:274:98: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ rasa/core/channels/hangouts.py:275:92: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ rasa/core/channels/mattermost.py:94:95: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ rasa/shared/nlu/training_data/formats/luis.py:39:92: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ tests/core/channels/test_hangouts.py:37:104: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ tests/core/channels/test_hangouts.py:54:108: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ tests/core/channels/test_hangouts.py:67:97: RUF100 [*] Unused `noqa` directive (unused: `E501`)

apache/superset (+17 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --no-preview --select ALL

+ superset/common/utils/dataframe_utils.py:19:8: ICN001 `datetime` should be imported as `dt`
+ superset/config.py:1261:88: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ superset/config.py:2425:115: RUF100 [*] Unused `noqa` directive (unused: `E501`)
+ superset/extensions/metadb.py:41:8: ICN001 `datetime` should be imported as `dt`
+ superset/mcp_service/system/tool/generate_bug_report.py:29:8: ICN001 `datetime` should be imported as `dt`
+ superset/mcp_service/system/tool/health_check.py:20:8: ICN001 `datetime` should be imported as `dt`
+ superset/migrations/versions/2018-07-05_15-19_3dda56f1c4c6_migrate_num_period_compare_and_period_.py:27:8: ICN001 `datetime` should be imported as `dt`
+ superset/result_set.py:19:8: ICN001 `datetime` should be imported as `dt`
... 10 additional changes omitted for rule ICN001
... 9 additional changes omitted for project

binary-husky/gpt_academic (+2 -34 violations, +0 -0 fixes)

- crazy_functions/Academic_Conversation.py:162:28: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- crazy_functions/Paper_Reading.py:239:17: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- crazy_functions/doc_fns/AI_review_doc.py:170:47: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- crazy_functions/doc_fns/batch_file_query_doc.py:170:47: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- crazy_functions/doc_fns/conversation_doc/excel_doc.py:102:42: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- crazy_functions/doc_fns/conversation_doc/txt_doc.py:54:20: DTZ005 `datetime.datetime.now()` called without a `tz` argument
... 26 additional changes omitted for rule DTZ005
- crazy_functions/doc_fns/text_content_loader.py:235:31: DTZ006 `datetime.datetime.fromtimestamp()` called without a `tz` argument
- crazy_functions/高级功能函数模板.py:51:25: DTZ011 `datetime.date.today()` used
- crazy_functions/高级功能函数模板.py:52:23: DTZ011 `datetime.date.today()` used
+ shared_utils/fastapi_stream_server.py:327:32: UP041 [*] Replace aliased errors with `TimeoutError`
... 26 additional changes omitted for project

bokeh/bokeh (+15 -5 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --no-preview --select ALL

+ examples/server/app/weather/main.py:10:8: ICN001 `datetime` should be imported as `dt`
+ scripts/milestone.py:4:8: ICN001 `datetime` should be imported as `dt`
+ src/bokeh/_types.pyi:9:8: ICN001 `datetime` should be imported as `dt`
+ src/bokeh/core/property/datetime.py:24:8: ICN001 `datetime` should be imported as `dt`
+ src/bokeh/core/property/visual.py:25:8: ICN001 `datetime` should be imported as `dt`
+ src/bokeh/core/property/visual.pyi:19:4: PYI002 `if` test in a stub file must be a simple comparison against `sys.platform` or `sys.version_info`
- src/bokeh/core/property/visual.pyi:19:4: PYI002 `if` test must be a simple comparison against `sys.platform` or `sys.version_info`
+ src/bokeh/models/plots.pyi:52:4: PYI002 `if` test in a stub file must be a simple comparison against `sys.platform` or `sys.version_info`
- src/bokeh/models/plots.pyi:52:4: PYI002 `if` test must be a simple comparison against `sys.platform` or `sys.version_info`
+ src/bokeh/models/sources.pyi:22:4: PYI002 `if` test in a stub file must be a simple comparison against `sys.platform` or `sys.version_info`
... 10 additional changes omitted for project

docker/docker-py (+0 -5 violations, +0 -0 fixes)

- docs/conf.py:60:8: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- tests/integration/api_container_test.py:975:54: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- tests/integration/client_test.py:38:17: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- tests/integration/client_test.py:48:25: DTZ005 `datetime.datetime.now()` called without a `tz` argument
- tests/unit/client_test.py:29:17: DTZ001 `datetime.datetime()` called without a `tzinfo` argument

fronzbot/blinkpy (+0 -0 violations, +0 -0 fixes)


ibis-project/ibis (+35 -0 violations, +0 -0 fixes)

+ ibis/backends/bigquery/tests/system/test_client.py:4:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/bigquery/tests/unit/test_compiler.py:3:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/clickhouse/converter.py:4:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/exasol/__init__.py:4:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/exasol/converter.py:3:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/flink/utils.py:3:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/impala/tests/test_client.py:3:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/mssql/__init__.py:6:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/polars/compiler.py:4:8: ICN001 `datetime` should be imported as `dt`
+ ibis/backends/pyspark/converter.py:3:8: ICN001 `datetime` should be imported as `dt`
... 25 additional changes omitted for project

jrnl-org/jrnl (+6 -0 violations, +0 -0 fixes)

+ jrnl/journals/DayOneJournal.py:5:8: ICN001 `datetime` should be imported as `dt`
+ jrnl/journals/Entry.py:4:8: ICN001 `datetime` should be imported as `dt`
+ jrnl/journals/Journal.py:4:8: ICN001 `datetime` should be imported as `dt`
+ jrnl/time.py:4:8: ICN001 `datetime` should be imported as `dt`
+ tests/unit/test_journals_folder_journal.py:4:8: ICN001 `datetime` should be imported as `dt`
+ tests/unit/test_time.py:4:8: ICN001 `datetime` should be imported as `dt`

langchain-ai/langchain (+13 -1 violations, +0 -0 fixes)

+ .github/scripts/check_diff.py:16:1: I001 [*] Import block is un-sorted or un-formatted
- .github/scripts/check_extras_sync.py:9:1: I001 [*] Import block is un-sorted or un-formatted
+ .github/scripts/check_prerelease_dependencies.py:3:1: I001 [*] Import block is un-sorted or un-formatted
+ .github/scripts/get_min_versions.py:6:4: UP036 Version block is outdated for minimum Python version
+ libs/core/langchain_core/document_loaders/langsmith.py:3:8: ICN001 `datetime` should be imported as `dt`
+ libs/core/langchain_core/tracers/evaluation.py:59:46: RUF012 Mutable default value for class attribute
+ libs/core/langchain_core/utils/utils.py:4:8: ICN001 `datetime` should be imported as `dt`
+ libs/core/tests/unit_tests/document_loaders/test_langsmith.py:1:8: ICN001 `datetime` should be imported as `dt`
+ libs/langchain/langchain_classic/chains/query_constructor/parser.py:1:8: ICN001 `datetime` should be imported as `dt`
+ libs/langchain/langchain_classic/retrievers/time_weighted_retriever.py:1:8: ICN001 `datetime` should be imported as `dt`
... 4 additional changes omitted for project

latchbio/latch (+3 -0 violations, +0 -0 fixes)

+ docs/source/conf.py:9:8: ICN001 `datetime` should be imported as `dt`
+ src/latch/registry/record.py:3:8: ICN001 `datetime` should be imported as `dt`
+ src/latch/resources/tasks.py:27:8: ICN001 `datetime` should be imported as `dt`

lnbits/lnbits (+0 -0 violations, +0 -0 fixes)


milvus-io/pymilvus (+14 -0 violations, +0 -0 fixes)

+ examples/bulk_import/bulk_writer_all_types.py:1:8: ICN001 `datetime` should be imported as `dt`
+ examples/timestamptz.py:3:8: ICN001 `datetime` should be imported as `dt`
+ examples/timestamptz_insert.py:3:8: ICN001 `datetime` should be imported as `dt`
+ examples/timestamptz_meta.py:3:8: ICN001 `datetime` should be imported as `dt`
+ examples/timestamptz_search_query.py:3:8: ICN001 `datetime` should be imported as `dt`
+ pymilvus/client/check.py:1:8: ICN001 `datetime` should be imported as `dt`
+ pymilvus/client/iterator/query_iterator.py:3:8: ICN001 `datetime` should be imported as `dt`
+ pymilvus/client/prepare.py:2:8: ICN001 `datetime` should be imported as `dt`
+ pymilvus/client/ts_utils.py:1:8: ICN001 `datetime` should be imported as `dt`
+ pymilvus/client/utils.py:1:8: ICN001 `datetime` should be imported as `dt`
... 4 additional changes omitted for project

... Truncated remaining completed project reports due to GitHub comment length restrictions

apache/airflow (error)

ruff check --no-cache --exit-zero --no-fix --output-format concise --no-preview --select ALL

warning: `incorrect-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. Ignoring `incorrect-blank-line-before-class`.
warning: `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible. Ignoring `multi-line-summary-second-line`.
ruff failed
  Cause: Failed to parse /home/runner/work/_temp/ruff-ecosystem-checkouts/apache:airflow/airflow-core/pyproject.toml
  Cause: TOML parse error at line 352, column 37
    |
352 | [tool.ruff.lint.flake8-tidy-imports.extend-banned-api]
    |                                     ^^^^^^^^^^^^^^^^^
unknown field `extend-banned-api`, expected one of `ban-relative-imports`, `banned-api`, `banned-module-level-imports`, `require-lazy`, `ban-lazy`

Changes by rule (20 rules affected)

code total + violation - violation + fix - fix
ICN001 247 247 0 0 0
DTZ001 92 0 92 0 0
I001 63 7 56 0 0
DTZ005 58 0 58 0 0
UP017 29 29 0 0 0
RUF100 15 15 0 0 0
PYI002 8 4 4 0 0
UP035 8 1 1 0 6
DTZ011 7 0 7 0 0
SIM117 6 6 0 0 0
invalid-syntax 6 3 3 0 0
DTZ007 5 0 5 0 0
RUF071 3 3 0 0 0
UP041 2 2 0 0 0
E501 2 1 1 0 0
DTZ006 2 0 2 0 0
SLF001 2 0 2 0 0
PLW0120 2 0 0 0 2
UP036 1 1 0 0 0
RUF012 1 1 0 0 0

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+878 -189 violations, +0 -0 fixes in 33 projects; 1 project error; 24 projects unchanged)

DisnakeDev/disnake (+34 -15 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ disnake/activity.py:5:8: unconventional-import-alias `datetime` should be imported as `dt`
+ disnake/audit_logs.py:35:12: unconventional-import-alias `datetime` should be imported as `dt`
+ disnake/automod.py:5:8: unconventional-import-alias `datetime` should be imported as `dt`
+ disnake/channel.py:6:8: unconventional-import-alias `datetime` should be imported as `dt`
+ disnake/embeds.py:5:8: unconventional-import-alias `datetime` should be imported as `dt`
+ disnake/entitlement.py:5:8: unconventional-import-alias `datetime` should be imported as `dt`
... 29 additional changes omitted for rule unconventional-import-alias
- disnake/enums.py:751:33: multiple-spaces-before-keyword [*] Multiple spaces before keyword
- disnake/enums.py:752:41: multiple-spaces-before-keyword [*] Multiple spaces before keyword
- disnake/enums.py:753:32: multiple-spaces-before-keyword [*] Multiple spaces before keyword
- disnake/enums.py:754:34: multiple-spaces-before-keyword [*] Multiple spaces before keyword
... 39 additional changes omitted for project

PlasmaPy/PlasmaPy (+3 -12 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ .github/scripts/citation_updater.py:66:29: unnecessary-regular-expression-compile Compiled regular expression is used only once
- src/plasmapy/formulary/quantum.py:123:10: float-equality-comparison Unreliable floating point equality comparison `0 * u.m / u.s == V`
- tests/formulary/test_fusion.py:326:16: float-equality-comparison Unreliable floating point equality comparison `sigma.unit == u.m**3 / u.s`
- tests/formulary/test_fusion.py:430:16: float-equality-comparison Unreliable floating point equality comparison `sv.unit == u.m**3 / u.s`
- tests/formulary/test_fusion.py:451:16: float-equality-comparison Unreliable floating point equality comparison `sv.unit == u.m**3 / u.s`
- tests/formulary/test_laser.py:89:12: float-equality-comparison Unreliable floating point equality comparison `result.unit == u.Watt / u.m**2`
+ tests/formulary/test_lengths.py:104:15: math-constant Replace `3.14159` with `math.pi`
- tests/formulary/test_lengths.py:104:15: math-constant [*] Replace `3.14159` with `math.pi`
- tests/formulary/test_misc.py:131:12: float-equality-comparison Unreliable floating point equality comparison `(Bohm_diffusion(T_e, B)).unit == u.m**2 / u.s`
... 5 additional changes omitted for rule float-equality-comparison
+ tests/particles/test_exceptions.py:304:13: math-constant Replace `3.14159` with `math.pi`
... 5 additional changes omitted for project

apache/superset (+103 -15 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ superset/commands/deletion_retention/purge_cascade.py:73:62: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
+ superset/commands/importers/v1/utils.py:394:66: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
+ superset/common/utils/dataframe_utils.py:19:8: unconventional-import-alias `datetime` should be imported as `dt`
+ superset/config.py:2453:6: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
+ superset/distributed_lock/__init__.py:33:6: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
+ superset/extensions/context.py:103:51: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
+ superset/extensions/context.py:122:46: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
... 68 additional changes omitted for rule context-manager-iterator
+ superset/extensions/metadb.py:41:8: unconventional-import-alias `datetime` should be imported as `dt`
+ superset/mcp_service/chart/tool/get_chart_preview.py:1127:44: math-constant Replace `6.28` with `math.tau`
- superset/mcp_service/chart/tool/get_chart_preview.py:1127:44: math-constant [*] Replace `6.28` with `math.tau`
... 108 additional changes omitted for project

binary-husky/gpt_academic (+11 -9 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

- crazy_functions/Audio_Assistant.py:24:32: strip-with-multi-characters Using `.strip()` with multi-character strings is misleading
+ crazy_functions/Audio_Assistant.py:24:34: strip-with-multi-characters Using `.strip()` with multi-character strings is misleading
- crazy_functions/Latex_Function.py:465:18: strip-with-multi-characters Using `.strip()` with multi-character strings is misleading
+ crazy_functions/Latex_Function.py:465:27: strip-with-multi-characters Using `.lstrip()` with multi-character strings is misleading
- request_llms/bridge_moss.py:236:32: strip-with-multi-characters Using `.strip()` with multi-character strings is misleading
+ request_llms/bridge_moss.py:236:41: strip-with-multi-characters Using `.strip()` with multi-character strings is misleading
... 13 additional changes omitted for rule strip-with-multi-characters
+ shared_utils/fastapi_stream_server.py:327:32: timeout-error-alias [*] Replace aliased errors with `TimeoutError`
+ shared_utils/fastapi_stream_server.py:371:32: timeout-error-alias [*] Replace aliased errors with `TimeoutError`
... 12 additional changes omitted for project

bokeh/bokeh (+25 -10 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

- examples/server/app/surface3d/surface3d.py:13:16: multiple-spaces-before-keyword [*] Multiple spaces before keyword
+ examples/server/app/weather/main.py:10:8: unconventional-import-alias `datetime` should be imported as `dt`
+ scripts/milestone.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ src/bokeh/_types.pyi:9:8: unconventional-import-alias `datetime` should be imported as `dt`
+ src/bokeh/core/property/datetime.py:24:8: unconventional-import-alias `datetime` should be imported as `dt`
+ src/bokeh/core/property/visual.py:25:8: unconventional-import-alias `datetime` should be imported as `dt`
+ src/bokeh/core/property/visual.pyi:19:4: complex-if-statement-in-stub `if` test in a stub file must be a simple comparison against `sys.platform` or `sys.version_info`
- src/bokeh/core/property/visual.pyi:19:4: complex-if-statement-in-stub `if` test must be a simple comparison against `sys.platform` or `sys.version_info`
+ src/bokeh/io/util.py:137:19: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
+ src/bokeh/io/util.py:216:67: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
... 25 additional changes omitted for project

facebookresearch/chameleon (+2 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ chameleon/viewer/backend/data_types.py:17:7: replace-str-enum Class WSMessageType inherits from both `str` and `enum.Enum`
+ chameleon/viewer/backend/data_types.py:28:7: replace-str-enum Class ContentType inherits from both `str` and `enum.Enum`

ibis-project/ibis (+73 -36 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ ibis/backends/_adbc.py:15:51: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
+ ibis/backends/bigquery/tests/system/test_client.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ ibis/backends/bigquery/tests/unit/test_compiler.py:3:8: unconventional-import-alias `datetime` should be imported as `dt`
+ ibis/backends/clickhouse/converter.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ ibis/backends/clickhouse/tests/test_functions.py:120:50: math-constant Replace `3.14` with `math.pi`
- ibis/backends/clickhouse/tests/test_functions.py:120:50: math-constant [*] Replace `3.14` with `math.pi`
+ ibis/backends/clickhouse/tests/test_functions.py:120:57: math-constant Replace `3.14` with `math.pi`
- ibis/backends/clickhouse/tests/test_functions.py:120:57: math-constant [*] Replace `3.14` with `math.pi`
+ ibis/backends/exasol/__init__.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ ibis/backends/exasol/converter.py:3:8: unconventional-import-alias `datetime` should be imported as `dt`
... 99 additional changes omitted for project

jrnl-org/jrnl (+6 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ jrnl/journals/DayOneJournal.py:5:8: unconventional-import-alias `datetime` should be imported as `dt`
+ jrnl/journals/Entry.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ jrnl/journals/Journal.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ jrnl/time.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ tests/unit/test_journals_folder_journal.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ tests/unit/test_time.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`

langchain-ai/langchain (+42 -7 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ .github/scripts/check_diff.py:16:1: unsorted-imports [*] Import block is un-sorted or un-formatted
- .github/scripts/check_extras_sync.py:9:1: unsorted-imports [*] Import block is un-sorted or un-formatted
+ .github/scripts/check_prerelease_dependencies.py:3:1: unsorted-imports [*] Import block is un-sorted or un-formatted
+ .github/scripts/get_min_versions.py:6:4: outdated-version-block Version block is outdated for minimum Python version
+ libs/core/langchain_core/document_loaders/langsmith.py:3:8: unconventional-import-alias `datetime` should be imported as `dt`
+ libs/core/langchain_core/tracers/evaluation.py:59:46: mutable-class-default Mutable default value for class attribute
+ libs/core/langchain_core/utils/utils.py:4:8: unconventional-import-alias `datetime` should be imported as `dt`
+ libs/core/langchain_core/utils/utils.py:74:46: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
+ libs/core/tests/unit_tests/document_loaders/test_langsmith.py:1:8: unconventional-import-alias `datetime` should be imported as `dt`
+ libs/core/tests/unit_tests/language_models/chat_models/test_base.py:384:35: context-manager-iterator Use `Generator` instead of `Iterator` for a context manager
... 39 additional changes omitted for project

... Truncated remaining completed project reports due to GitHub comment length restrictions

apache/airflow (error)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

warning: `incorrect-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. Ignoring `incorrect-blank-line-before-class`.
warning: `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible. Ignoring `multi-line-summary-second-line`.
ruff failed
  Cause: Failed to parse /home/runner/work/_temp/ruff-ecosystem-checkouts/apache:airflow/airflow-core/pyproject.toml
  Cause: TOML parse error at line 352, column 37
    |
352 | [tool.ruff.lint.flake8-tidy-imports.extend-banned-api]
    |                                     ^^^^^^^^^^^^^^^^^
unknown field `extend-banned-api`, expected one of `ban-relative-imports`, `banned-api`, `banned-module-level-imports`, `require-lazy`, `ban-lazy`

Changes by rule (21 rules affected)

code total + violation - violation + fix - fix
context-manager-iterator 387 387 0 0 0
unconventional-import-alias 247 247 0 0 0
math-constant 200 100 100 0 0
replace-str-enum 73 73 0 0 0
float-equality-comparison 55 4 51 0 0
datetime-timezone-utc 29 29 0 0 0
strip-with-multi-characters 22 11 11 0 0
multiple-spaces-before-keyword 16 0 16 0 0
complex-if-statement-in-stub 8 4 4 0 0
multiple-with-statements 6 6 0 0 0
invalid-syntax 6 3 3 0 0
unnecessary-regular-expression-compile 3 3 0 0 0
unsorted-imports 3 2 1 0 0
timeout-error-alias 2 2 0 0 0
useless-else-on-loop 2 1 1 0 0
RUF069 2 2 0 0 0
private-member-access 2 0 2 0 0
outdated-version-block 1 1 0 0 0
mutable-class-default 1 1 0 0 0
RUF100 1 1 0 0 0
UP052 1 1 0 0 0

Formatter (stable)

ℹ️ ecosystem check detected format changes. (+152 -136 lines in 29 files in 3 projects; 1 project error; 54 projects unchanged)

PlasmaPy/PlasmaPy (+1 -3 lines across 1 file)

src/plasmapy/formulary/braginskii.py~L371

             self.m_i = m_i
         self.Z = _grab_charge(ion, Z) * u.dimensionless_unscaled
         if self.Z < 0:
-            raise ValueError(
-                "Z is not allowed to be negative!"
-            )  # TODO: remove?  # noqa: FIX002
+            raise ValueError("Z is not allowed to be negative!")  # TODO: remove?  # noqa: FIX002
 
         # decide on the particle string for the electrons
         self.e_particle = "e-"

demisto/content (+3 -9 lines across 1 file)

Packs/MicrosoftGraphIdentityandAccess/Integrations/MicrosoftGraphIdentityandAccess/MicrosoftGraphIdentityandAccess.py~L928

     return CommandResults(readable_output="Could not create IP named location")
 
 
-def azure_ad_identity_protection_confirm_compromised_command(
-    ms_client: Client, args: dict
-) -> CommandResults:  # pragma: no cover  # noqa
+def azure_ad_identity_protection_confirm_compromised_command(ms_client: Client, args: dict) -> CommandResults:  # pragma: no cover  # noqa
     user_ids = str(args.get("user_ids")).split(",")
     data     = {"userIds": user_ids}
     try:

Packs/MicrosoftGraphIdentityandAccess/Integrations/MicrosoftGraphIdentityandAccess/MicrosoftGraphIdentityandAccess.py~L940

         return CommandResults(readable_output=f"Could not compromised {user_ids!s}:\n{e}")
 
 
-def azure_ad_identity_protection_risky_users_dismiss_command(
-    ms_client: Client, args: dict
-) -> CommandResults:  # pragma: no cover  # noqa
+def azure_ad_identity_protection_risky_users_dismiss_command(ms_client: Client, args: dict) -> CommandResults:  # pragma: no cover  # noqa
     user_ids = str(args.get("user_ids")).split(",")
     data     = {"userIds": user_ids}
     try:

Packs/MicrosoftGraphIdentityandAccess/Integrations/MicrosoftGraphIdentityandAccess/MicrosoftGraphIdentityandAccess.py~L1032

         return CommandResults(readable_output="could not list IP named locations")
 
 
-def azure_ad_identity_protection_risky_users_history_list(
-    ms_client: Client, args: dict
-) -> CommandResults:  # pragma: no cover  # noqa
+def azure_ad_identity_protection_risky_users_history_list(ms_client: Client, args: dict) -> CommandResults:  # pragma: no cover  # noqa
     limit   = args.get("limit")
     odata   = args.get("odata_query")
     user_id = args.get("user_id")

rotki/rotki (+148 -124 lines across 27 files)

rotkehlchen/accounting/export/csv.py~L177

 
         index               = event.index + CSV_INDEX_OFFSET
         value_formula       = f"{amount_column}{index}*H{index}"
-        total_value_formula = f"(F{index}*H{index}+G{index}*H{index})"  # formula of both free and taxable  # noqa: E501
+        total_value_formula = (
+            f"(F{index}*H{index}+G{index}*H{index})"  # formula of both free and taxable  # noqa: E501
+        )
         cost_basis_column   = "K" if name == "taxable" else "L"
         cost_basis          = f"{cost_basis_column}{index}"
 

rotkehlchen/chain/ethereum/airdrops.py~L157

     """
 
     api_url: str
-    amount_path: str  # json keys separated by `/` that point to the amount that needs to be extracted  # noqa: E501
+    amount_path: (
+        str  # json keys separated by `/` that point to the amount that needs to be extracted  # noqa: E501
+    )
 
 
 class AirdropClaimEventQueryParams(NamedTuple):

rotkehlchen/chain/ethereum/modules/eigenlayer/decoder.py~L476

                     extra_data={"matched": True},
                 )
             else:
-                new_event = self.base.make_event_from_transaction(  # so let's just keep minimal info  # noqa: E501
-                    transaction=context.transaction,
-                    tx_log=context.tx_log,
-                    event_type=HistoryEventType.INFORMATIONAL,
-                    event_subtype=HistoryEventSubType.NONE,
-                    asset=A_ETH,
-                    amount=ZERO,
-                    location_label=context.transaction.from_address,
-                    notes=f"Complete eigenlayer withdrawal {withdrawal_root}",
-                    counterparty=CPT_EIGENLAYER,
-                    address=context.tx_log.address,
+                new_event = (
+                    self.base.make_event_from_transaction(  # so let's just keep minimal info  # noqa: E501
+                        transaction=context.transaction,
+                        tx_log=context.tx_log,
+                        event_type=HistoryEventType.INFORMATIONAL,
+                        event_subtype=HistoryEventSubType.NONE,
+                        asset=A_ETH,
+                        amount=ZERO,
+                        location_label=context.transaction.from_address,
+                        notes=f"Complete eigenlayer withdrawal {withdrawal_root}",
+                        counterparty=CPT_EIGENLAYER,
+                        address=context.tx_log.address,
+                    )
                 )
 
         return EvmDecodingOutput(events=[new_event])

rotkehlchen/chain/ethereum/modules/eigenlayer/decoder.py~L593

                     token=token,
                 )
                 notes  = f"Withdraw {amount} {token.symbol} from Eigenlayer"
-                for event in context.decoded_events:  # pre-slashing contracts transfer before emitting the completion event  # noqa: E501
+                for event in (
+                    context.decoded_events
+                ):  # pre-slashing contracts transfer before emitting the completion event  # noqa: E501
                     if (
                         event.event_type == HistoryEventType.RECEIVE
                         and event.event_subtype == HistoryEventSubType.NONE

rotkehlchen/chain/ethereum/modules/gwei_names/decoder.py~L177

         """
         label, expires = decode_abi(["string", "uint256"], context.tx_log.data)
         if expires == 0:
-            return DEFAULT_EVM_DECODING_OUTPUT  # subdomain. Handled in _decode_subdomain_registered  # noqa: E501
+            return (
+                DEFAULT_EVM_DECODING_OUTPUT  # subdomain. Handled in _decode_subdomain_registered  # noqa: E501
+            )
 
         fullname = f"{label}.gwei"
         with GlobalDBHandler().conn.write_ctx() as write_cursor:

rotkehlchen/chain/evm/decoding/decoder.py~L1314

         if not self.base.any_tracked([owner_address, spender_address]):
             return DEFAULT_EVM_DECODING_OUTPUT
 
-        event = self.base.make_event_from_transaction(  # notes are generated, see EvmEvent.auto_notes  # noqa: E501
-            transaction=transaction,
-            tx_log=tx_log,
-            event_type=HistoryEventType.INFORMATIONAL,
-            event_subtype=HistoryEventSubType.APPROVE,
-            asset=token,
-            amount=token_normalized_value(token_amount=amount_raw, token=token),
-            location_label=owner_address,
-            address=spender_address,
+        event = (
+            self.base.make_event_from_transaction(  # notes are generated, see EvmEvent.auto_notes  # noqa: E501
+                transaction=transaction,
+                tx_log=tx_log,
+                event_type=HistoryEventType.INFORMATIONAL,
+                event_subtype=HistoryEventSubType.APPROVE,
+                asset=token,
+                amount=token_normalized_value(token_amount=amount_raw, token=token),
+                location_label=owner_address,
+                address=spender_address,
+            )
         )
         return EvmDecodingOutput(events=[event])
 

rotkehlchen/chain/evm/decoding/ens/decoder.py~L94

         if transfer_event is None:  # Can happen if neither from/to is tracked
             return DEFAULT_EVM_DECODING_OUTPUT
 
-        label_hash = f"0x{collectible_id:064x}"  # Transform the int token id to a 32 bytes hex label hash  # noqa: E501
+        label_hash = (
+            f"0x{collectible_id:064x}"  # Transform the int token id to a 32 bytes hex label hash  # noqa: E501
+        )
         found_name = self._maybe_get_labelhash_name(context=context, label_hash=label_hash)
         if found_name is None:
             name_to_show = ""

rotkehlchen/chain/evm/decoding/safe/decoder.py~L117

         have too many events spamming if owners are added, removed etc."""
         for event in context.decoded_events:
             if event.counterparty == CPT_SAFE_MULTISIG and event.address == context.tx_log.address:
-                return DEFAULT_EVM_DECODING_OUTPUT  # only add if no other safe-specific events exist  # noqa: E501
+                return (
+                    DEFAULT_EVM_DECODING_OUTPUT  # only add if no other safe-specific events exist  # noqa: E501
+                )
 
         if not self.base.any_tracked([context.transaction.from_address, context.tx_log.address]):
             return DEFAULT_EVM_DECODING_OUTPUT

rotkehlchen/chain/evm/decoding/superchain_bridge/l2/decoder.py~L206

                 )
                 break
         else:
-            return decoded_events  # no tracked ETH transfer was decoded for this deposit transaction  # noqa: E501
+            return (
+                decoded_events  # no tracked ETH transfer was decoded for this deposit transaction  # noqa: E501
+            )
 
         return [
             event

rotkehlchen/chain/solana/modules/stake/decoder.py~L114

         is newly created (a transformed transfer is already in the decoded events), or
         None when no funding by a tracked address is found.
         """
-        for event in context.decoded_events:  # a plain transfer funding the stake account has already been decoded as a spend  # noqa: E501
+        for event in (
+            context.decoded_events
+        ):  # a plain transfer funding the stake account has already been decoded as a spend  # noqa: E501
             if (
                 event.address == stake_account
                 and event.event_type == HistoryEventType.SPEND

rotkehlchen/chain/solana/utils.py~L94

 
 
 # Minimum size for an initialized stake account (state + Meta)
-STAKE_ACCOUNT_META_SIZE: Final      = 124  # 4 (state) + 8 (rent_exempt) + 32 (staker) + 32 (withdrawer) + 8 + 8 + 32 (lockup)  # noqa: E501
+STAKE_ACCOUNT_META_SIZE: Final      = (
+    124  # 4 (state) + 8 (rent_exempt) + 32 (staker) + 32 (withdrawer) + 8 + 8 + 32 (lockup)  # noqa: E501
+)
 # Size including delegation data
 STAKE_ACCOUNT_DELEGATED_SIZE: Final = (
     196  # Meta(124) + Delegation(32 + 8 + 8 + 8 + 8) + credits_observed(8)  # noqa: E501

rotkehlchen/data_migrations/migrations/migration_20.py~L268

             for row in cursor:
                 spend, receive = get_swap_spend_receive_v47_48(
                     is_buy=row[4]
-                    in {
-                        "A",
-                        "C",
-                    },  # A/C = buy/settlement buy; B,D = sell/settlement sell  # noqa: E501
+                    in {"A", "C"},  # A/C = buy/settlement buy; B,D = sell/settlement sell  # noqa: E501
                     base_asset=Asset(row[2]),
                     quote_asset=Asset(row[3]),
                     amount=FVal(row[5]),

rotkehlchen/db/drivers/sqlite.py~L52

 type UnderlyingCursor = rsqlite.Cursor | sqlcipher.Cursor  # pylint: disable=no-member
 type UnderlyingConnection = rsqlite.Connection | sqlcipher.Connection  # pylint: disable=no-member
 
-CONTEXT_SWITCH_WAIT                       = 0.025  # seconds between cancellation checks while waiting for the transaction slot  # noqa: E501
+CONTEXT_SWITCH_WAIT                       = (
+    0.025  # seconds between cancellation checks while waiting for the transaction slot  # noqa: E501
+)
 # Rows a cursor prefetches per statement_lock acquisition when iterated. Iteration
 # is the hot row-consumption path (~1650 ca

_report truncated to fit GitHub's comment size limit; see the `ecosystem-result` artifact of the run for the full text._

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

by ecosystem round-trip

base: 8471642a71d77d646a19b64d010f7bbe4fd7df64 (merge base) → head: 216/merge

regressions: 3, changed: 1254, improvements: 3, error changes: 0 (across 31551 files in 149 projects)

⚠️ 5 project(s) fail to round-trip on both base and head, so this check says nothing about them.

❌ regressions (built on base, now fails)

jax —
build: killed: timed out after 900s
kopf —
reverse: thread '<unknown>' (4290) has overflowed its stack
fatal runtime error: stack overflow, aborting
scipy —
reverse: killed: exceeded 11.7365GB memory budget

ℹ️ changed round-trip output

Expression — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -174,5 +174,5 @@
     "/tmp/tmptr_66v43/Expression/build/tests/test_option_builder.py": {"by": "sha256:7840a4f57784d2ef311a2ef18c79b209a034eac6bfb5df8337f90d250e373987", "py": "sha256:0de7554008625d8560ae4eb0682547226ac1091be046962e4e39108e1411c484"},
     "/tmp/tmptr_66v43/Expression/build/tests/test_parser.py": {"by": "sha256:39577ecb356a7f3b4158c49f8c060dfc9cd0142128c6af15f95b7f422dfc605b", "py": "sha256:8e4aefd4d1ee28dd4d0da4df7e832d03522ac3a44a8406ff866f091c528ab97a"},
-    "/tmp/tmptr_66v43/Expression/build/tests/test_pipe.py": {"by": "sha256:dccc0720a422704d70137f01c446c7271f1e9c6b616020c0d4ba6030f4121120", "py": "sha256:2dbef997f2ecd1ebe6f21f8fa435feefb5b8f0fb5934e16fe779468adc98554c"},
+    "/tmp/tmptr_66v43/Expression/build/tests/test_pipe.py": {"by": "sha256:dccc0720a422704d70137f01c446c7271f1e9c6b616020c0d4ba6030f4121120", "py": "sha256:813a73ac91621947d88dc450bf0f173d257c36eb6d29066bc8220140b9aff5bc"},
     "/tmp/tmptr_66v43/Expression/build/tests/test_result.py": {"by": "sha256:6e5e956b69fde7ce2117abd740cd468e49d379c604b2acd100c61d75a240c610", "py": "sha256:1d34e2859d6d75364eaa62659f3b195628d4461c54248e203a7f5f02631ff7d7"},
     "/tmp/tmptr_66v43/Expression/build/tests/test_result_builder.py": {"by": "sha256:4079a8bdf4aac52cc8667e3bbc363c65367597b1c54b49ffd0fabf287a91c519", "py": "sha256:31ce4b1874b967f46a9926bb1b2a6c8f8ed149fcf3d130cfc8ceda89e12b325c"},
Expression — tests/test_pipe.py
--- base/tests/test_pipe.py
+++ head/tests/test_pipe.py
@@ -139,5 +139,5 @@
 
 def test_starpipe_simple():
-    assert _soundness_check(starpipe((1, 2), fn), tuple) == _soundness_check(fn(1, 2), tuple)
+    assert starpipe((1, 2), fn) == _soundness_check(fn(1, 2), tuple)
PyGithub — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -515,5 +515,5 @@
     "/tmp/tmphxkhcjpc/PyGithub/build/openapi/conftest.py": {"by": "sha256:b7b3ce9254deaa83ee4787b0ee0511658b3a0d1aec5321906f0c6e3dc323dc38", "py": "sha256:d3a19b3f9de87d52196b54d6b4ab1467a3601a020a1e46ddbc1ce708e18dbee4"},
     "/tmp/tmphxkhcjpc/PyGithub/build/scripts/fix_headers.py": {"by": "sha256:105094085c5acc16071f1de2451593a71500faa1ed75d5af681744f7732b0a38", "py": "sha256:a5ea5c1cde206ab6e3c70137dc404a0f57f3f195da8ed3d616884dafd2d90915"},
-    "/tmp/tmphxkhcjpc/PyGithub/build/scripts/openapi.py": {"by": "sha256:6b7d3d9508920c13f91e4dd5f6cff52b5293f5eb927e1dbe22ca8243a48df925", "py": "sha256:cd1b4404f1a88641ff2a530a40d1762ee22481f189711953c54679ef11765c99"},
+    "/tmp/tmphxkhcjpc/PyGithub/build/scripts/openapi.py": {"by": "sha256:6b7d3d9508920c13f91e4dd5f6cff52b5293f5eb927e1dbe22ca8243a48df925", "py": "sha256:ebfccf9b54622e889f90d778b946e5e17eb20ebd5f52795570615d59a99cd0df"},
     "/tmp/tmphxkhcjpc/PyGithub/build/scripts/prepare-for-update-assertions.py": {"by": "sha256:dd1f8c58c01073da2d1c925f9ba265e4909ac9b3e6a2541b593af5b9cde3c906", "py": "sha256:08f68b18dd974244f15cb23c240fbb847e2bd6ada1140deb67a5c093b9fb9837"},
     "/tmp/tmphxkhcjpc/PyGithub/build/scripts/sort_class.py": {"by": "sha256:0b97be6db4fc1e7ec55d925d5ddb6e5f686522b4b43360ab51ce1808153ddec4", "py": "sha256:dbc4b082c54d1f7f237218ae45b1afd3c88294db47bc67b9139d77d504dcb433"},
PyGithub — scripts/openapi.py
--- base/scripts/openapi.py
+++ head/scripts/openapi.py
@@ -190,5 +190,5 @@
                 return GithubClass(**classes.get(class_name))
             if verbose:
-                for class_name in _soundness_iter(classes, str):
+                for class_name in classes:
                     if class_name not in classes:
                         print(f"Class not found in index: {class_name}")
Tanjun — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -136,5 +136,5 @@
     "/tmp/tmpt84dtb2m/Tanjun/build/tanjun/context/message.py": {"by": "sha256:be1728bd9288183fde8e43248bbd5c7a2d04309caa3a0bb46a20873fcb20316e", "py": "sha256:b7d747a3be937ca356315920833e6f0b90b582b242828b388f05eea8e35f8813"},
     "/tmp/tmpt84dtb2m/Tanjun/build/tanjun/context/slash.py": {"by": "sha256:550220ec90ff87af79bf4dd8f0e65461d5069994a9e850f83542895d70e491c0", "py": "sha256:040ede5e92eed7df3e616519fd6159b8256b6df42f1300f5db17fc311dd74758"},
-    "/tmp/tmpt84dtb2m/Tanjun/build/tanjun/conversion.py": {"by": "sha256:872b6ab8fe53724088dcd06b15129f65494c859552997829eaa7dce7152ceef2", "py": "sha256:67bf5196e32ce645600a30b7607ba94d0930c68981fda11712f7e58411aed1b8"},
+    "/tmp/tmpt84dtb2m/Tanjun/build/tanjun/conversion.py": {"by": "sha256:872b6ab8fe53724088dcd06b15129f65494c859552997829eaa7dce7152ceef2", "py": "sha256:77aaa56f619e4e380923531367b23aeb21f05d1a90a4958d2bc5b56fcba95fd3"},
... 2788 characters elided ...
     "/tmp/tmpt84dtb2m/Tanjun/build/tests/test_utilities.py": {"by": "sha256:77bbddada1803c358aa6a20c89e455a374df73e8621ee90315c3ab077cca62dd", "py": "sha256:90b7de842db371c4f16353419b40466ab81749193701d9e26edb6b48a3eb194e"},
 }
Tanjun — tanjun/conversion.py
--- base/tanjun/conversion.py
+++ head/tanjun/conversion.py
@@ -1302,6 +1302,6 @@
         capture = next(_MESSAGE_LINK_REGEX.finditer(value), None)
         if capture:
-            channel_id = hikari.Snowflake(capture[2])
-            message_id = hikari.Snowflake(capture[3])
+            channel_id = hikari.Snowflake(_soundness_check(capture[2], str))
+            message_id = hikari.Snowflake(_soundness_check(capture[3], str))
 
     # We should also range check the provided ID.
Tanjun — tanjun/dependencies/reloaders.py
--- base/tanjun/dependencies/reloaders.py
+++ head/tanjun/dependencies/reloaders.py
@@ -515,5 +515,5 @@
         raise FileNotFoundError(error_message)
 
-    return directory.resolve(), (namespace, set()) if namespace is None else (namespace, set())
+    return directory.resolve(), (namespace, set()) if namespace is None else (namespace, set[str]())
Tanjun — tests/test_schedules.py
--- base/tests/test_schedules.py
+++ head/tests/test_schedules.py
@@ -568,7 +568,7 @@
         assert interval._task is None
         assert interval._tasks == []
-        assert _soundness_check(mock_task_1.cancelled(), bool) is True
-        assert _soundness_check(mock_task_2.cancelled(), bool) is True
-        assert _soundness_check(mock_task_3.cancelled(), bool) is True
+        assert mock_task_1.cancelled() is True
+        assert mock_task_2.cancelled() is True
+        assert mock_task_3.cancelled() is True
         mock_client.call_with_async_di.assert_not_called()
 
@@ -726,7 +726,7 @@
         assert interval._tasks == []
         assert mock_task_1.result() is None
-        assert _soundness_check(mock_task_2.cancelled(), bool) is True
+        assert mock_task_2.cancelled() is True
         assert mock_task_3.result() is None
-        assert _soundness_check(mock_task_4.cancelled(), bool) is True
+        assert mock_task_4.cancelled() is True
         mock_client.call_with_async_di.assert_not_called()
 
@@ -1151,7 +1151,7 @@
         assert interval.is_alive is False
... 555 characters elided ...
-        assert _soundness_check(mock_task_1.cancelled(), bool) is True
+        assert mock_task_1.cancelled() is True
         assert mock_task_2.result() is None
-        assert _soundness_check(mock_task_3.cancelled(), bool) is True
+        assert mock_task_3.cancelled() is True
         assert mock_task_4.result() is None
aiohttp — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -180,7 +180,7 @@
 DIGESTS = {
     "/tmp/tmptb1k9t6q/aiohttp/build/aiohttp/__init__.py": {"by": "sha256:d29745d55a831a6a2731f10386a32710b24232ab24e95b389f6f4f6a0df2b242", "py": "sha256:a1c2bb9b08e719e7054662e147a2712f80c5633955b5ac8da574d8ac4ebbd33c"},
-    "/tmp/tmptb1k9t6q/aiohttp/build/aiohttp/_cookie_helpers.py": {"by": "sha256:ce04d777a848bea4dfd15c515dbb4da02bb06870f354328c29f6cb33acf70a40", "py": "sha256:76883ff72a0113a4c4642a292da833e18568fd9123a5a28434675e2f623ff337"},
+    "/tmp/tmptb1k9t6q/aiohttp/build/aiohttp/_cookie_helpers.py": {"by": "sha256:ce04d777a848bea4dfd15c515dbb4da02bb06870f354328c29f6cb33acf70a40", "py": "sha256:ce4be4abe3201ac794868fcd33101429911750b485209a943a58c4cdd1de9cda"},
     "/tmp/tmptb1k9t6q/aiohttp/build/aiohttp/_websocket/__init__.py": {"by": "sha256:31aaf747dfef04dfc46b896c5bb8930155c3ece2acc0a3c6a85e71812cadefb9", "py": "sha256:31aaf747dfef04dfc46b896c5bb8930155c3ece2acc0a3c6a85e71812cadefb9"},
... 10586 characters elided ...
     "/tmp/tmptb1k9t6q/aiohttp/build/tests/test_web_exceptions.py": {"by": "sha256:3e4e2971ad7c5dbeec4de619934ab2a52939ac839a16d060341765ba8c7d7471", "py": "sha256:cc1419cb995ab85a7fef4cbaa266bbb5323c7a704e4099e2720db4ae23f814cd"},
aiohttp — aiohttp/_cookie_helpers.py
--- base/aiohttp/_cookie_helpers.py
+++ head/aiohttp/_cookie_helpers.py
@@ -244,7 +244,7 @@
             continue
 
-        key = match.group("key")
-        value = match.group("val") or ""
-        i = match.end(0)
+        key = _soundness_check(match.group("key"), str)
+        value = _soundness_check(match.group("val"), (str, type(None))) or ""
+        i = _soundness_check(match.end(0), int)
 
         # Validate the name
aiohttp — aiohttp/_websocket/helpers.py
--- base/aiohttp/_websocket/helpers.py
+++ head/aiohttp/_websocket/helpers.py
@@ -95,6 +95,6 @@
                 # Server never fail to detect compress handshake.
                 # Server does not need to send max wbit to client
-                if match.group(4):
-                    compress = int(match.group(4))
+                if _soundness_check(match.group(4), (str, type(None))):
+                    compress = int(_soundness_check(match.group(4), (str, type(None))))
                     # Group3 must match if group4 matches
                     # Compress wbit 8 does not support in zlib
@@ -104,11 +104,11 @@
                         compress = 0
                         continue
-                if match.group(1):
+                if _soundness_check(match.group(1), (str, type(None))):
                     notakeover = True
                 # Ignore regex group 5 & 6 for client_max_window_bits
                 break
             else:
-                if match.group(6):
-                    compress = int(match.group(6))
+                if _soundness_check(match.group(6), (str, type(None))):
... 288 characters elided ...
                         raise WSHandshakeError("Invalid window size")
-                if match.group(2):
+                if _soundness_check(match.group(2), (str, type(None))):
                     notakeover = True
                 # Ignore regex group 5 & 6 for client_max_window_bits
aiohttp — aiohttp/client_middleware_digest_auth.py
--- base/aiohttp/client_middleware_digest_auth.py
+++ head/aiohttp/client_middleware_digest_auth.py
@@ -258,9 +258,9 @@
             )
 
-        qop_raw = challenge.get("qop", "")
+        qop_raw = _soundness_check(challenge.get("qop", ""), str)
         # Preserve original algorithm case for response while using uppercase for processing
-        algorithm_original = challenge.get("algorithm", "MD5")
+        algorithm_original = _soundness_check(challenge.get("algorithm", "MD5"), str)
         algorithm = algorithm_original.upper()
-        opaque = challenge.get("opaque", "")
+        opaque = _soundness_check(challenge.get("opaque", ""), str)
 
         # Convert string values to bytes once
@@ -446,5 +446,5 @@
         self._protection_space = []
 
-        if domain := self._challenge.get("domain"):
+        if domain := _soundness_check(self._challenge.get("domain"), (str, type(None))):
             # Parse space-separated list of URIs
             for uri in _soundness_iter(domain.split(), str):
aiohttp — aiohttp/cookiejar.py
--- base/aiohttp/cookiejar.py
+++ head/aiohttp/cookiejar.py
@@ -506,5 +506,5 @@
             elif copy_morsels:
                 # TODO(https://github.com/python/typeshed/pull/16346): Remove cast
-                cookie = _soundness_parametric(cast(Morsel[str], _soundness_check(cookie.copy(), dict)), Morsel[str], (0,))
+                cookie = _soundness_parametric(cast(Morsel[str], _soundness_check(cookie.copy(), Morsel)), Morsel[str], (0,))
 
             domain = cookie["domain"]
@@ -835,5 +835,5 @@
                 if day_match:
                     found_day = True
-                    day = int(day_match.group())
+                    day = int(_soundness_check(day_match.group(), str))
                     continue
 
@@ -850,5 +850,5 @@
                 if year_match:
                     found_year = True
-                    year = int(year_match.group())
+                    year = int(_soundness_check(year_match.group(), str))
 
         if 70 <= year <= 99:
aiohttp — aiohttp/web_protocol.py
--- base/aiohttp/web_protocol.py
+++ head/aiohttp/web_protocol.py
@@ -281,5 +281,5 @@
         self._lingering_time = float(lingering_time)
 
-        self._messages: deque[_MsgType] = deque()
+        self._messages: deque[_MsgType] = deque[tuple[RawRequestMessage | _ErrInfo, StreamReader]]()
         self._message_tail = b""
         self._data_received_cb: Callable2[[], None] | None = None
aiohttp — tests/test_http_parser.py
--- base/tests/test_http_parser.py
+++ head/tests/test_http_parser.py
@@ -265,5 +265,5 @@
     messages, upgrade, tail = parser.feed_data(text)
     assert len(messages) == 1
-    msg = messages[0][0]
+    msg = _soundness_check(messages[0], tuple)[0]
 
     assert list[tuple[str, str]](msg.headers.items()) == [
@@ -424,5 +424,5 @@
     messages, _, _ = parser.feed_data(b"st: a\r\n\r\n")
     assert len(messages) == 1
-    assert messages[0][0].path == "/split"
+    assert _soundness_check(messages[0], tuple)[0].path == "/split"
 
 
@@ -537,5 +537,5 @@
     text = f"POST / HTTP/1.1\r\nHost: a\r\n{headers}\r\n\r\n".encode()
     messages, upgrade, tail = parser.feed_data(text)
-    msg = messages[0][0]
+    msg = _soundness_check(messages[0], tuple)[0]
 
     assert msg.headers.getall("Foo") == expected
@@ -753,5 +753,5 @@
     text = b"GET / HTTP/1.1\r\nHost: a\r\ntest: \t value \t \r\n\r\n"
     messages, upgrade, tail = parser.feed_data(text)
-    msg = messages[0][0]
+    msg = _soundness_check(messages[0], tuple)[0]
 
     assert msg.headers["test"] == "value"
@@ -776,5 +776,5 @@
... 30730 characters elided ...
 
     assert msg.path == quote("/путь?ключ=знач#фраг", safe="/?=#")
@@ -3049,5 +3049,5 @@
     text = b"GET /path?#frag HTTP/1.1\r\nHost: a\r\n\r\n"
     messages, upgrade, tail = parser.feed_data(text)
-    msg = messages[0][0]
+    msg = _soundness_check(messages[0], tuple)[0]
 
     assert msg.url.path == "/path"
aiohttp — tests/test_multipart.py
--- base/tests/test_multipart.py
+++ head/tests/test_multipart.py
@@ -1760,7 +1760,7 @@
                 if isinstance(part, aiohttp.BodyPartReader):
                     if part.headers[CONTENT_TYPE] == "application/json":
-                        assert _soundness_check(next(idata), (dict, int, bytes)) == (await part.json())
+                        assert next(idata) == (await part.json())
                     else:
-                        assert _soundness_check(next(idata), (dict, int, bytes)) == await part.read(decode=True)
+                        assert next(idata) == await part.read(decode=True)
                 else:
                     await check(part)
aiohttp — tests/test_run_app.py
--- base/tests/test_run_app.py
+++ head/tests/test_run_app.py
@@ -837,5 +837,5 @@
         assert proc.stdout.readline().startswith(b"======== Running on")  # type: ignore[union-attr]
         proc.send_signal(signal.SIGINT)
-        assert _soundness_check(proc.wait(), int) == 0
+        assert proc.wait() == 0
 
 
@@ -852,5 +852,5 @@
         assert proc.stdout.readline().startswith(b"======== Running on")  # type: ignore[union-attr]
         proc.terminate()
-        assert _soundness_check(proc.wait(), int) == 0
+        assert proc.wait() == 0
aiohttp — tests/test_streams.py
--- base/tests/test_streams.py
+++ head/tests/test_streams.py
@@ -1738,5 +1738,5 @@
     line_iter = iter(DATA.splitlines(keepends=True))
     async for line in await create_stream():
-        assert line == _soundness_check(next(line_iter, None), (bytes, type(None)))
+        assert line == next(line_iter, None)
     pytest.raises(StopIteration, next, line_iter)
 
@@ -1747,5 +1747,5 @@
     chunk_iter = chunkify(DATA, 9)
     async for data in _soundness_aiter((await create_stream()).iter_chunked(9), bytes):
-        assert data == _soundness_check(next(chunk_iter, None), (Sequence, type(None)))
+        assert data == next(chunk_iter, None)
     pytest.raises(StopIteration, next, chunk_iter)
 
@@ -1755,5 +1755,5 @@
     chunk_iter = chunkify(DATA, 8)
     async for data in _soundness_aiter((await create_stream()).iter_chunked(8), bytes):
-        assert data == _soundness_check(next(chunk_iter, None), (Sequence, type(None)))
+        assert data == next(chunk_iter, None)
     pytest.raises(StopIteration, next, chunk_iter)
 
@@ -1781,5 +1781,5 @@
     item_iter = iter(items)
     async for item in buffer:
-        assert item is _soundness_check(next(item_iter, None), (str, type(None)))
+        assert item is next(item_iter, None)
     pytest.raises(StopIteration, next, item_iter)
aiohttp — tests/test_web_app.py
--- base/tests/test_web_app.py
+++ head/tests/test_web_app.py
@@ -188,7 +188,7 @@
     key = web.AppKey[int]("key", int)
     app = web.Application()
-    assert _soundness_check(app.get(key, "foo"), (int, str)) == "foo"
+    assert app.get(key, "foo") == "foo"
     app[key] = 5
-    assert _soundness_check(app.get(key, "foo"), (int, str)) == 5
+    assert app.get(key, "foo") == 5
aiohttp-devtools — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -35,5 +35,5 @@
     "/tmp/tmpjv9u8e67/aiohttp-devtools/build/aiohttp_devtools/cli.py": {"by": "sha256:4344a08cb73fb6e8d539031e6e51fd060e38ee0b996351540bda2c8e19e48e4f", "py": "sha256:f158fa573674f74fbc906fa5ac87c87ac2e492024699f11b3644793225ecd469"},
     "/tmp/tmpjv9u8e67/aiohttp-devtools/build/aiohttp_devtools/exceptions.py": {"by": "sha256:7a18b495b8286f306530bb430f5c574d9e11e8af6e23979adac9046076a075a2", "py": "sha256:7a18b495b8286f306530bb430f5c574d9e11e8af6e23979adac9046076a075a2"},
-    "/tmp/tmpjv9u8e67/aiohttp-devtools/build/aiohttp_devtools/logs.py": {"by": "sha256:1ee64aff1ad7f71466e395e40df5a86c2586752fcc5005f81ca4a548d58dddbb", "py": "sha256:8e01743e68c97701c5a0e1123824be68d5e0c021fd92ee43a9b88a04d9f4db96"},
+    "/tmp/tmpjv9u8e67/aiohttp-devtools/build/aiohttp_devtools/logs.py": {"by": "sha256:1ee64aff1ad7f71466e395e40df5a86c2586752fcc5005f81ca4a548d58dddbb", "py": "sha256:a75f51901d1a7f1cc8d91dbfabd62d53c6ee517094a79c6c02d1713f35e3bd4e"},
... 252 characters elided ...
     "/tmp/tmpjv9u8e67/aiohttp-devtools/build/aiohttp_devtools/runserver/config.py": {"by": "sha256:c44008ce961b71c0ad818076124279d8cf28df4abd2a2b024a765ebd4f105cb8", "py": "sha256:b1d568fb9b0be5ee832a97065a4d22a640cd899f6c59370096aa68145b7e92a9"},
aiohttp-devtools — aiohttp_devtools/logs.py
--- base/aiohttp_devtools/logs.py
+++ head/aiohttp_devtools/logs.py
@@ -48,5 +48,5 @@
         if m:
             time = sformat(_soundness_check(_soundness_check(m.groups(), tuple)[0], str), sformat.magenta)
-            return time + sformat(msg[m.end():], log_color)
+            return time + sformat(msg[_soundness_check(m.end(), int):], log_color)
 
         return sformat(msg, log_color)
aiortc — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -93,5 +93,5 @@
     "/tmp/tmpg_01e4dt/aiortc/build/aiortc/codecs/g711.py": {"by": "sha256:db8e03b010baa67d88455577bb8bdae4257e5141f9e77cf23d1459d7a3e17b1d", "py": "sha256:caa02e468fe3dfb9297854d292aa2e3346335abc53717b156b1db2f5f68b5668"},
     "/tmp/tmpg_01e4dt/aiortc/build/aiortc/codecs/g722.py": {"by": "sha256:148718999908305ad1add2222432c2eadb91417e625c7285b443e8968569d9d3", "py": "sha256:3dba07fb8a9233088d48ca07b6ae852420553778fbdf5420cbf89edb840f9e4c"},
-    "/tmp/tmpg_01e4dt/aiortc/build/aiortc/codecs/h264.py": {"by": "sha256:20436a39e3f817de1e00ac7afe19f489bdc58d2af89f1301d2098855d1ddd98d", "py": "sha256:0eb48684cd38d780a0a918806b8c81af62350c425a470b473ee8efc6e0b6cccc"},
+    "/tmp/tmpg_01e4dt/aiortc/build/aiortc/codecs/h264.py": {"by": "sha256:20436a39e3f817de1e00ac7afe19f489bdc58d2af89f1301d2098855d1ddd98d", "py": "sha256:2b424ab8cb7614aa484e152878ab6af6a8e99622740e07a84878ed47b68797f1"},
... 1568 characters elided ...
     "/tmp/tmpg_01e4dt/aiortc/build/aiortc/utils.py": {"by": "sha256:dd1e1b5678f972bcfb4ac2a6cf3f8eb223594aeab2d5df3cf37c49d52a244b80", "py": "sha256:d685d95fffcc76c8ad6f5f5c9266d4772122e11722edd27a45669a4bf8186f1e"},
aiortc — aiortc/codecs/h264.py
--- base/aiortc/codecs/h264.py
+++ head/aiortc/codecs/h264.py
@@ -236,9 +236,9 @@
 
         packages_iterator = iter(packages)
-        package = _soundness_check(next(packages_iterator, None), (bytes, type(None)))
+        package = next(packages_iterator, None)
         while package is not None:
             if len(package) > PACKET_MAX:
                 packetized_packages.extend(cls._packetize_fu_a(package))
-                package = _soundness_check(next(packages_iterator, None), (bytes, type(None)))
+                package = next(packages_iterator, None)
             else:
                 packetized, package = cls._packetize_stap_a(package, packages_iterator)
aiortc — aiortc/sdp.py
--- base/aiortc/sdp.py
+++ head/aiortc/sdp.py
@@ -155,5 +155,5 @@
     m = re.match("^IN (IP4|IP6) ([^ ]+)$", sdp)
     assert m
-    return m.group(2)
+    return _soundness_check(m.group(2), str)
 
 
@@ -436,6 +436,6 @@
 
             # check payload types are valid
-            kind = m.group(1)
-            fmt = m.group(4).split()
+            kind = _soundness_check(m.group(1), str)
+            fmt = _soundness_check(m.group(4), str).split()
             fmt_int: Optional[list[int]] = None
             if kind in ["audio", "video"]:
@@ -446,5 +446,5 @@
 
             current_media = MediaDescription(
-                kind=kind, port=int(m.group(2)), profile=m.group(3), fmt=fmt_int or fmt
+                kind=kind, port=int(_soundness_check(m.group(2), str)), profile=_soundness_check(m.group(3), str), fmt=fmt_int or fmt
             )
             current_media.dtls = RTCDtlsParameters(
alectryon — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -56,7 +56,7 @@
     "/tmp/tmptr_66v43/alectryon/build/alectryon/coqc_time.py": {"by": "sha256:7c602d50dcae85794e8ebe6eea452ba122d9a86a147c8db1cf71e938c6351767", "py": "sha256:48557a20b6e6a3194a2f8d104d01800b130f4571f86d13192de25ecdd0efd55b"},
     "/tmp/tmptr_66v43/alectryon/build/alectryon/coqlsp.py": {"by": "sha256:1e6f77ad8a3ad07a68a36aa42eeda31a4283a3f1acc98ca81cf3a38b675b8aeb", "py": "sha256:185f14c04c64f62164126cefc0fa7750cd2f6f65adaf4c69bafc75131519b979"},
-    "/tmp/tmptr_66v43/alectryon/build/alectryon/core.py": {"by": "sha256:3455231481c7be1768159f0b6a3f59a65756a2b1fb09b1bed388290caa3385a9", "py": "sha256:2ceed7436d3e62622e0852530a66d71cec13304b3a3442be2b843bc178679e25"},
+    "/tmp/tmptr_66v43/alectryon/build/alectryon/core.py": {"by": "sha256:3455231481c7be1768159f0b6a3f59a65756a2b1fb09b1bed388290caa3385a9", "py": "sha256:1a3b38b2fc27c10c4b0b718fbec4c162c12562b1e5b13b701d76dc6a378c4822"},
... 5893 characters elided ...
     "/tmp/tmptr_66v43/alectryon/build/alectryon.py": {"by": "sha256:680ba7e0bde5cb9c6013a55fae72d531ca4ffde71b728fff0144b1c02fac1d92", "py": "sha256:f434bc20127819411bc5b534f91eeab563baf816ba3723664a0ce355bdcd0dc8"},
alectryon — alectryon/core.py
--- base/alectryon/core.py
+++ head/alectryon/core.py
@@ -76,9 +76,9 @@
 def measure_indentation(line):
     m = INDENTATION_RE.match(line)
-    return m.end() - m.start() if m else None
+    return _soundness_check(m.end(), int) - _soundness_check(m.start(), int) if m else None
 
 def measure_min_indentation(lines):
     indents = (measure_indentation(ln) for ln in lines)
-    return min((i for i in _soundness_iter(indents, (int, type(None))) if i is not None), default=0)
+    return _soundness_check(min((i for i in _soundness_iter(indents, (int, type(None))) if i is not None), default=0), int)
 
 def dedent(lines, amount):
@@ -1162,5 +1162,5 @@
         for block in blocks:
             if isinstance(block, CodeSnippet):
-                block = _soundness_once_bound(("CodeSnippet",), must(_soundness_once_bound(("CodeSnippet",), next(it, None), _soundness_check, lambda: (CodeSnippet, type(None)))), _soundness_check, lambda: CodeSnippet)
+                block = must(next(it, None))
             yield block
-        assert _soundness_once_bound(("CodeSnippet",), next(it, None), _soundness_check, lambda: (CodeSnippet, type(None))) is None
+        assert next(it, None) is None
alectryon — alectryon/docutils.py
--- base/alectryon/docutils.py
+++ head/alectryon/docutils.py
@@ -1049,5 +1049,5 @@
     mid = COQ_ID_RE.match(text)
     assert mid
-    title, target = mid.group("title"), mid.group("target")
+    title, target = _soundness_check(mid.group("title"), str), _soundness_check(mid.group("target"), (str, type(None)))
     return title, target
alectryon — alectryon/literate.py
--- base/alectryon/literate.py
+++ head/alectryon/literate.py
@@ -365,5 +365,5 @@
                 expected = _soundness_check(self.TRANSITIONS[state], tuple)
                 raise ParsingError(self.doc, state, expected, start, token_end)
-            tok, mstart, self.pos = Token(m.lastgroup), m.start(), m.end()
+            tok, mstart, self.pos = Token(m.lastgroup), _soundness_check(m.start(), int), _soundness_check(m.end(), int)
             if not self.step(state, start, tok, mstart):
                 break
@@ -476,5 +476,5 @@
 
 class CoqParser(BlockParser):
-    TRANSITIONS: dict[State, tuple[Token, Token, Token] | tuple[Token, Token]] = {
+    TRANSITIONS: dict[State, tuple[Token, ...]] = {
         State.CODE: (Token.COMMENT_OPEN,
                      Token.STRING_DELIM,
@@ -566,5 +566,5 @@
     # TODO: Add support for char (``'"'``) syntax
     # TODO: Technically doc comments don't support nesting
-    TRANSITIONS: dict[State, tuple[Token, Token, Token, Token] | tuple[Token, Token, Token] | tuple[Token] | tuple[Token, Token]] = {
+    TRANSITIONS: dict[State, tuple[Token, ...]] = {
         State.CODE: (Token.LINE_COMMENT_OPEN,
                      Token.COMMENT_OPEN,
alectryon — alectryon/lsp.py
--- base/alectryon/lsp.py
+++ head/alectryon/lsp.py
@@ -65,5 +65,5 @@
             header = LSPServerMessage.JRPC_HEADER_RE.match(line)
             if header:
-                length = int(header.group("len"))
+                length = int(_soundness_check(header.group("len"), str))
 
         if line == "":
alectryon — alectryon/markers.py
--- base/alectryon/markers.py
+++ head/alectryon/markers.py
@@ -193,14 +193,14 @@
             raise ParseError(path[start:])
         for matcher_name, matcher in QUERY_MATCHERS.items():
-            needle = m.group(matcher_name)
+            needle = _soundness_check(m.group(matcher_name), (str, type(None)))
             if needle is not None:
-                kind = m.group("kind")
+                kind = _soundness_check(m.group("kind"), (str, type(None)))
                 allowed_matchers = _soundness_check(QUERY_KINDS[kind], tuple)
                 if matcher_name not in allowed_matchers:
                     if matcher_name == "nil":
                         raise MissingPattern(kind, path[start:])
-                    raise UnsupportedPattern(m.group("search"), kind, path[start:])
+                    raise UnsupportedPattern(_soundness_check(m.group("search"), (str, type(None))), kind, path[start:])
                 parsed[kind] = matcher(needle)
-        start = m.end()
+        start = _soundness_check(m.end(), int)
     return parsed
alectryon — alectryon/sexp.py
--- base/alectryon/sexp.py
+++ head/alectryon/sexp.py
@@ -99,5 +99,5 @@
     stack = [sexp]
     while stack:
-        top = stack.pop()
+        top = _soundness_check(stack.pop(), int)
         if isinstance(top, list):
             buf.append(OPEN)
alectryon — alectryon/transforms.py
--- base/alectryon/transforms.py
+++ head/alectryon/transforms.py
@@ -867,6 +867,6 @@
             m = LEAN_TRAILING_BLANKS_RE.search(contents)
             if m:
-                yield _replace_contents(fr, contents[:m.start()])
-                fr = Text(contents[m.start():])
+                yield _replace_contents(fr, contents[:_soundness_check(m.start(), int)])
+                fr = Text(contents[_soundness_check(m.start(), int):])
         yield fr
 
@@ -886,5 +886,5 @@
             if m and not isinstance(grouped[idx - 1], Text):
                 prev = grouped[idx-1]
-                comma, rest = fr.contents[:m.end()], fr.contents[m.end():]
+                comma, rest = fr.contents[:_soundness_check(m.end(), int)], fr.contents[_soundness_check(m.end(), int):]
                 grouped[idx-1] = _replace_contents(prev, _contents(prev) + comma)
                 grouped[idx] = Text(rest) if rest else None
alectryon — alectryon/typst.py
--- base/alectryon/typst.py
+++ head/alectryon/typst.py
@@ -271,5 +271,5 @@
         # Typst labels come wrapped as ``<name>``
         label = re.sub(r"\A<(.*)>\Z", r"\1", lbl) if (lbl := item.get("label")) else None
-        return _soundness_check(TypstCodeSnippet.of_text(m.group(1), "", _soundness_check(text, str), src=text, label=label), TypstCodeSnippet)
+        return _soundness_check(TypstCodeSnippet.of_text(_soundness_check(m.group(1), str), "", _soundness_check(text, str), src=text, label=label), TypstCodeSnippet)
 
     def _parse_query_output(self, items: Iterable[JSON], snippets: list[TypstCodeSnippet]) -> None:
altair — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -54,5 +54,5 @@
     "/tmp/tmpy177xswn/altair/build/altair/expr/__init__.py": {"by": "sha256:c0756990a9d8202b055eb3eb01dd3d4cdb6e3fbb227b4670c90d2efefcf34d64", "py": "sha256:20409b7ebbfd04c77ab58e829989ef8c76dfc2b65d77c258467f493afeede37a"},
     "/tmp/tmpy177xswn/altair/build/altair/expr/consts.py": {"by": "sha256:4166f78ef96c438372a05b020b592a22c86f43b2895b99dd3c91e5d2aee5a1e0", "py": "sha256:4166f78ef96c438372a05b020b592a22c86f43b2895b99dd3c91e5d2aee5a1e0"},
-    "/tmp/tmpy177xswn/altair/build/altair/expr/core.py": {"by": "sha256:fb1d69f063bd5b05e8a236f9ea7c8e5982bae6d040fc6b3d2cf6d1cededc4eee", "py": "sha256:6191784795c738e3df34361cb5db696c9d3dcb374a5d4afc5359be58e5d16c53"},
+    "/tmp/tmpy177xswn/altair/build/altair/expr/core.py": {"by": "sha256:fb1d69f063bd5b05e8a236f9ea7c8e5982bae6d040fc6b3d2cf6d1cededc4eee", "py": "sha256:a7b19edef139eedd4a11ab5cff815ec39961ca9c17a4fe011e3ecd7526e9b445"},
... 4612 characters elided ...
     "/tmp/tmpy177xswn/altair/build/tools/versioning.py": {"by": "sha256:3a3e090ca3e25a1dcae6831047f836ea8c122584e068ed96834465aa450ad908", "py": "sha256:427eef115e540c970afe6873ca9a7a9e03ead80835a684b90ae675351c0b7967"},
 }
altair — altair/expr/core.py
--- base/altair/expr/core.py
+++ head/altair/expr/core.py
@@ -221,5 +221,5 @@
 """
 
-    _schema: dict[str, str] = {"type": "string"}
+    _schema = {"type": "string"}
 
     def to_dict(self, *args, **kwargs):
altair — altair/vegalite/v6/schema/core.py
--- base/altair/vegalite/v6/schema/core.py
+++ head/altair/vegalite/v6/schema/core.py
@@ -3035,5 +3035,5 @@
     """Aggregate schema wrapper."""
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/Aggregate"}
+    _schema = {"$ref": "#/definitions/Aggregate"}
 
     def __init__(self, *args, **kwds):
@@ -3044,5 +3044,5 @@
     """AggregateOp schema wrapper."""
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/AggregateOp"}
+    _schema = {"$ref": "#/definitions/AggregateOp"}
 
     def __init__(self, *args):
@@ -3067,5 +3067,5 @@
 """
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/AggregatedFieldDef"}
+    _schema = {"$ref": "#/definitions/AggregatedFieldDef"}
 
     def __init__(
@@ -3085,5 +3085,5 @@
     """Align schema wrapper."""
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/Align"}
+    _schema = {"$ref": "#/definitions/Align"}
 
     def __init__(self, *args):
@@ -3094,5 +3094,5 @@
     """AnyMark schema wrapper."""
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/AnyMark"}
+    _schema = {"$ref": "#/definitions/AnyMark"}
 
... 97191 characters elided ...
+    _schema = {"$ref": "#/definitions/WindowOnlyOp"}
 
     def __init__(self, *args):
@@ -35528,5 +35528,5 @@
 """
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/WindowTransform"}
+    _schema = {"$ref": "#/definitions/WindowTransform"}
 
     def __init__(
altair — altair/vegalite/v6/schema/mixins.py
--- base/altair/vegalite/v6/schema/mixins.py
+++ head/altair/vegalite/v6/schema/mixins.py
@@ -607,5 +607,5 @@
 """
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/MarkDef"}
+    _schema = {"$ref": "#/definitions/MarkDef"}
 
     def __init__(
@@ -1071,5 +1071,5 @@
 """
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/BoxPlotDef"}
+    _schema = {"$ref": "#/definitions/BoxPlotDef"}
 
     def __init__(
@@ -1175,5 +1175,5 @@
 """
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/ErrorBarDef"}
+    _schema = {"$ref": "#/definitions/ErrorBarDef"}
 
     def __init__(
@@ -1287,5 +1287,5 @@
 """
 
-    _schema: dict[str, str] = {"$ref": "#/definitions/ErrorBandDef"}
+    _schema = {"$ref": "#/definitions/ErrorBandDef"}
 
     def __init__(
altair — tools/datasets/datapackage.py
--- base/tools/datasets/datapackage.py
+++ head/tools/datasets/datapackage.py
@@ -104,5 +104,5 @@
             _soundness_check(rsrc["name"], str): {_soundness_check(f["name"], str): _soundness_check(f["type"], str) for f in _soundness_iter(s["fields"], dict)}
             for rsrc in _soundness_iter(self._resources, dict)
-            if (s := rsrc.get("schema"))
+            if (s := _soundness_check(rsrc.get("schema"), (dict, type(None))))
         }
         return m
altair — tools/vega_expr.py
--- base/tools/vega_expr.py
+++ head/tools/vega_expr.py
@@ -823,5 +823,5 @@
                 and (child := next(iter(children)).get(RAW) is not None)
                 and (match := FUNCTION_DEF_LINE.match(child))
-                and (node := cls(match["name"], children)).is_callable()
+                and (node := cls(_soundness_check(match["name"], (str, type(None))), children)).is_callable()
             ):
                 yield node.with_parameters().with_signature()
antidote — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -134,5 +134,5 @@
     "/tmp/tmpt84dtb2m/antidote/build/antidote/lib/lazy_ext/__init__.py": {"by": "sha256:42d59e0fed76ba6ce17ded2affd9fef1ee926e443fbb2a8c61e8783fff565e8c", "py": "sha256:2c267fa0ec3807c453223a6fcb91ee7d28afae73d9c33c7bc1254ac9e8d5494a"},
     "/tmp/tmpt84dtb2m/antidote/build/antidote/lib/lazy_ext/_const.py": {"by": "sha256:2d56c8b857a107f5eca18bbc12358ad067b81a90fc2fa77c5b84dbdb55bdb583", "py": "sha256:36b249222b3abc09de8325326debd816738d7239aea31b6d90e2b495caa9488b"},
-    "/tmp/tmpt84dtb2m/antidote/build/antidote/lib/lazy_ext/_lazy.py": {"by": "sha256:7478fa9c191f38bcff021fec098dd0365e7cce770821043a9453a2145197e14c", "py": "sha256:04dad5ff55d53d56debfe52224794b146fb7b61bede13685f1b842d254b03e2c"},
+    "/tmp/tmpt84dtb2m/antidote/build/antidote/lib/lazy_ext/_lazy.py": {"by": "sha256:7478fa9c191f38bcff021fec098dd0365e7cce770821043a9453a2145197e14c", "py": "sha256:3ba661e34365d375b57ecd0be3f8ad01729c572976f0bc13aa56f344dd99190e"},
     "/tmp/tmpt84dtb2m/antidote/build/antidote/lib/lazy_ext/_provider.py": {"by": "sha256:04af321174bee59902ba9fe3c1768c098a79bb9fd60af8f47746e2345bb1386c", "py": "sha256:2b1f9d80e9b35307e4916173134b0c674064b7a6c7479c149460d2b1693106a1"},
     "/tmp/tmpt84dtb2m/antidote/build/tests/__init__.py": {"by": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "py": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
antidote — antidote/lib/lazy_ext/_lazy.py
--- base/antidote/lib/lazy_ext/_lazy.py
+++ head/antidote/lib/lazy_ext/_lazy.py
@@ -205,5 +205,5 @@
             else:
                 assert _kind is FunctionKind.VALUE
-                return LazyValue(
+                return LazyValue[object](
                     wrapped=wrapped,
                     injected=injected,
anyio — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -25,5 +25,5 @@
     "/tmp/tmptb1k9t6q/anyio/build/anyio/abc/_resources.py": ("/tmp/tmptb1k9t6q/anyio/src/anyio/abc/_resources.by", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, 15, 15, 15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 35, 36]),
     "/tmp/tmptb1k9t6q/anyio/build/anyio/abc/_subprocesses.py": ("/tmp/tmptb1k9t6q/anyio/src/anyio/abc/_subprocesses.by", [None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 14, 14, 14, 14, 14, 19, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 29, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 39, 40, 40, 41, 41, 41, 41, 41, 41, 41, 48, 49, 50, 50, 51, 52, 53, 54, 54, 55, 55, 55, 55, 59, 60, 61, 61, 62, 63, 64, 65, 65, 66, 67, 68, 69, 69, 70]),
... 11985 characters elided ...
     "/tmp/tmptb1k9t6q/anyio/build/tests/test_to_process.py": {"by": "sha256:1643b91289e468965d77eb4788681e960688a807d2e9db576f12fb6358e9a84c", "py": "sha256:75843a72830a9f49d841c90eaaa838e9e6c2d3316552565d326881ab3b3e5947"},
anyio — anyio/_core/_concurrency_utils.py
--- base/anyio/_core/_concurrency_utils.py
+++ head/anyio/_core/_concurrency_utils.py
@@ -90,5 +90,5 @@
     handles: list[TaskHandle[Any]] = []
     async with create_task_group() as tg:
-        handles.extend(_soundness_check(tg.create_task(coro), TaskHandle) for coro in _soundness_iter(coros, Coroutine))
+        handles.extend(_soundness_check(tg.create_task(coro), TaskHandle) for coro in coros)
 
     return tuple(h.return_value for h in _soundness_iter(handles, TaskHandle))
anyio — anyio/_core/_eventloop.py
--- base/anyio/_core/_eventloop.py
+++ head/anyio/_core/_eventloop.py
@@ -226,5 +226,5 @@
     else:
         try:
-            return sniffio.current_async_library()
+            return _soundness_check(sniffio.current_async_library(), (str, type(None)))
         except sniffio.AsyncLibraryNotFoundError:
             pass
@@ -238,5 +238,5 @@
         return None
 
-    return sniffio.current_async_library_cvar.set(asynclib_name)
+    return _soundness_check(sniffio.current_async_library_cvar.set(asynclib_name), (Token, type(None)))
anyio — anyio/abc/_tasks.py
--- base/anyio/abc/_tasks.py
+++ head/anyio/abc/_tasks.py
@@ -1,3 +1,2 @@
-from anyio._by_runtime import _soundness_check
 
 lazy import sys
@@ -157,5 +156,5 @@
 """
         final_name = get_callable_name(func, name)
-        return self.create_task(_soundness_check(call_for_coroutine(func, args), Coroutine), name=final_name)
+        return self.create_task(call_for_coroutine(func, args), name=final_name)
 
     @overload
anyio — tests/test_from_thread.py
--- base/tests/test_from_thread.py
+++ head/tests/test_from_thread.py
@@ -175,5 +175,5 @@
             assert result == 3
         finally:
-            await _soundness_check(gen.aclose(), Coroutine)
+            await gen.aclose()
 
     async def test_run_sync_from_thread(self) -> None:
@@ -387,5 +387,5 @@
                 assert result == 3
         finally:
-            await _soundness_check(gen.aclose(), Coroutine)
+            await gen.aclose()
 
     async def test_aexit_with_exception(self) -> None:
anyio — tests/test_taskgroups.py
--- base/tests/test_taskgroups.py
+++ head/tests/test_taskgroups.py
@@ -1085,7 +1085,7 @@
     try:
         loop = asyncio.get_running_loop()
-        await loop.create_task(_soundness_check(host_agen.__anext__(), Coroutine))
+        await loop.create_task(host_agen.__anext__())
     finally:
-        await _soundness_check(host_agen.aclose(), Coroutine)
+        await host_agen.aclose()
 
     assert done
anyio — tests/test_tempfile.py
--- base/tests/test_tempfile.py
+++ head/tests/test_tempfile.py
@@ -175,5 +175,5 @@
     async def test_cancellation_during_creation(self) -> None:
         original = tempfile.TemporaryDirectory
-        manager = TemporaryDirectory()
+        manager = TemporaryDirectory[str]()
         paths: list[pathlib.Path] = []
 
@@ -252,5 +252,5 @@
 cancellation is still delivered to the caller afterwards.\
 """
-        td = TemporaryDirectory()
+        td = TemporaryDirectory[str]()
         async with td as td_str:
             td_path = pathlib.Path(td_str)
apprise — _by_sourcemap.py
--- base/_by_sourcemap.py
+++ head/_by_sourcemap.py
@@ -9,5 +9,5 @@
     "/tmp/tmpjv9u8e67/apprise/build/apprise/attachment/__init__.py": ("/tmp/tmpjv9u8e67/apprise/apprise/attachment/__init__.by", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38]),
... 153172 characters elided ...
     "/tmp/tmpjv9u8e67/apprise/build/tests/test_plugin_zalo.py": {"by": "sha256:7bd4aa54026e400758fbce9b43b6716b70f030a1ca63342911ca85a86e64df90", "py": "sha256:6db3ee8863ffec5805bcc71e6e5299275720cb286f18fb41cec40f02fda09332"},
apprise — apprise/attachment/file.py
--- base/apprise/attachment/file.py
+++ head/apprise/attachment/file.py
@@ -1,2 +1,3 @@
+from apprise._by_runtime import _soundness_check
 # BSD 2-Clause License
 #
@@ -141,4 +142,4 @@
             return None
 
-        results["path"] = AttachFile.unquote(match.group("path"))
+        results["path"] = AttachFile.unquote(_soundness_check(match.group("path"), str))
         return results
apprise — apprise/attachment/http.py
--- base/apprise/attachment/http.py
+++ head/apprise/attachment/http.py
@@ -226,5 +226,5 @@
                     )
                     if result:
-                        self.detected_name = result.group("name").strip()
+                        self.detected_name = _soundness_check(result.group("name"), str).strip()
 
                     # Keep the file on Windows; __del__ cleans it up.
apprise — apprise/attachment/memory.py
--- base/apprise/attachment/memory.py
+++ head/apprise/attachment/memory.py
@@ -199,5 +199,5 @@
                 # Store our filename only (ignore any defined paths)
                 results["name"] = _soundness_check(os.path.basename(
-                    AttachMemory.unquote(match.group("path"))
+                    AttachMemory.unquote(_soundness_check(match.group("path"), str))
                 ), str)
         return results
apprise — apprise/config/base.py
--- base/apprise/config/base.py
+++ head/apprise/config/base.py
@@ -573,5 +573,5 @@
 
             # Attempt to detect configuration
-            if result.group("yaml"):
+            if _soundness_check(result.group("yaml"), (str, type(None))):
                 config_format = common.ConfigFormat.YAML
                 ConfigBase.logger.debug(
@@ -580,5 +580,5 @@
                 break
 
-            elif result.group("text") or result.group("assign"):
+            elif _soundness_check(result.group("text"), (str, type(None))) or _soundness_check(result.group("assign"), (str, type(None))):
                 config_format = common.ConfigFormat.TEXT
                 ConfigBase.logger.debug(
@@ -724,7 +724,7 @@
             # Retrieve our line
             url, assign, config = (
-                result.group("url"),
-                result.group("assign"),
-                result.group("config"),
+                _soundness_check(result.group("url"), (str, type(None))),
+                _soundness_check(result.group("assign"), (str, type(None))),
... 813 characters elided ...
@@ -1711,5 +1711,5 @@
         # URL Processing
         #
-        for entry in _soundness_iter(preloaded, dict):
+        for entry in preloaded:
             # Point to our results entry for easier reference below
             results = entry["results"]
apprise — apprise/config/file.py
--- base/apprise/config/file.py
+++ head/apprise/config/file.py
@@ -1,2 +1,3 @@
+from apprise._by_runtime import _soundness_check
 # BSD 2-Clause License
 #
@@ -169,4 +170,4 @@
             return None
 
-        results["path"] = ConfigFile.unquote(match.group("path"))
+        results["path"] = ConfigFile.unquote(_soundness_check(match.group("path"), str))
         return results
apprise — apprise/conversion/commonmark.py
--- base/apprise/conversion/commonmark.py
+++ head/apprise/conversion/commonmark.py
@@ -129,5 +129,5 @@
     """
     m = _HTML_BLOCK_START_RE.match(line)
-    if m and m.group("cdata") and m.group("cdata") != "<![CDATA[":
+    if m and _soundness_check(m.group("cdata"), (str, type(None))) and _soundness_check(m.group("cdata"), (str, type(None))) != "<![CDATA[":
         return None
     return m
@@ -396,6 +396,6 @@
     if not m:
         return None
-    marker_end = pos + m.end()
-    marker_col = col + _text_width(m.group(0), col)
+    marker_end = pos + _soundness_check(m.end(), int)
+    marker_col = col + _text_width(_soundness_check(m.group(0), str), col)
 
     rest = line[marker_end:]
@@ -450,10 +450,10 @@
     """
     if not quote_depth:
-        if _leading_width(line) >= list_width:
+        if _soundness_check(_leading_width(line), int) >= list_width:
             return _chars_for_width(line, list_width)
         return None
 
     # List-then-quote.
-    if _leading_width(line) >= list_width:
+    if _soundness_check(_leading_width(line), int) >= list_width:
... 3886 characters elided ...
             for marker in payload["consumed_markers"]:
-                working_pending[marker] = _soundness_check(working_pending.get(marker, 0), int) - 1
+                working_pending[marker] = working_pending.get(marker, 0) - 1
         else:
             out.append(payload)
apprise — apprise/conversion/html.py
--- base/apprise/conversion/html.py
+++ head/apprise/conversion/html.py
@@ -1089,5 +1089,5 @@
         if ordered:
             # Escape just the '.', not the digits themselves
-            dot = ordered.end() - 1
+            dot = _soundness_check(ordered.end(), int) - 1
             return content[:dot] + "\\" + content[dot:]
 
@@ -1197,5 +1197,5 @@
             if tag == "ol":
                 # Read an optional starting number from ordered lists.
-                start_attr = _soundness_check(next((v for k, v in attrs if k == "start"), None), (str, type(None)))
+                start_attr = next((v for k, v in attrs if k == "start"), None)
                 if start_attr is not None:
                     with contextlib.suppress(TypeError, ValueError):
@@ -1235,5 +1235,5 @@
             if _soundness_check(ctx["list_type"], (type(None), int, str)) == "ol" and _soundness_check(ctx["counter"], (type(None), int, str)) is not None:
                 # Read an optional per-item numbering override.
... 724 characters elided ...
                 if (
                     scheme
-                    and scheme.group(1).lower() in self._UNSAFE_HREF_SCHEMES
+                    and _soundness_check(scheme.group(1), str).lower() in self._UNSAFE_HREF_SCHEMES
                 ):
                     # Preserve link shape while neutralizing unsafe targets.
apprise — apprise/decorators/base.py
--- base/apprise/decorators/base.py
+++ head/apprise/decorators/base.py
@@ -115,7 +115,7 @@
 
         # Acquire our schema
-        schema = re_match.group("schema").lower()
-
-        if not re_match.group("base"):
+        schema = _soundness_check(re_match.group("schema"), str).lower()
+
+        if not _soundness_check(re_match.group("base"), (str, type(None))):
             url = f"{schema}://"
 
@@ -159,5 +159,5 @@
             )
 
-            requirements: dict[str, str] = {
+            requirements = {
                 # Define our required packaging in order to work
                 "details": f"Source: {inspect.getfile(send_func)}"
@@ -168,5 +168,5 @@
 
             # Update our default arguments
-            _base_args: None | dict[str, None | str | dict[Never, Never]] | dict[Never, Never] = base_args
+            _base_args: None | dict[str, None | str | dict[Never, Never]] = base_args
 
             def __init__(self, **kwargs):
apprise — apprise/locale.py
--- base/apprise/locale.py
+++ head/apprise/locale.py
@@ -257,11 +257,11 @@
 
         result = AppriseLocale._local_re.match(entry)
-        if not result or not result.group("lang"):
+        if not result or not _soundness_check(result.group("lang"), (str, type(None))):
             # The value is not a supported language format.
             return None
 
         # Language codes are lower case, regions are upper case (en_CA)
-        language = result.group("lang").lower()
-        country = result.group("country")
+        language = _soundness_check(result.group("lang"), (str, type(None))).lower()
+        country = _soundness_check(result.group("country"), (str, type(None)))
 
         # Drop unsupported regions or script names such as Hans in zh-Hans.
@@ -279,5 +279,5 @@
         # C, POSIX, and C.UTF-8 all mean the same thing here
         result = AppriseLocale._local_re.match(value)
-        return bool(result and result.group("ansii"))
+        return bool(result and _soundness_check(result.group("ansii"), (str, type(None))))
 
     @staticmethod
apprise — apprise/manager.py
--- base/apprise/manager.py
+++ head/apprise/manager.py
@@ -206,5 +206,5 @@
 
                 # Store our notification/plugin name:
-                module_name = match.group("name")
+                module_name = _soundness_check(match.group("name"), str)
                 module_pyname = f"{module_name_prefix}.{module_name}"
 
@@ -464,5 +464,5 @@
                 re_match = module_re.match(os.path.basename(path))
                 # must be a match and must have a .py extension
-                if not re_match or not re_match.group(1):
+                if not re_match or not _soundness_check(re_match.group(1), (str, type(None))):
                     # keep going
                     logger.trace("Plugin Scan: Ignoring %s", path)
apprise — apprise/persistent_store.py
--- base/apprise/persistent_store.py
+++ head/apprise/persistent_store.py
@@ -301,5 +301,5 @@
             return str(self) == str(other)
 
-        return self.__value == other
+        return _soundness_check(self.__value == other, bool)
 
     def __str__(self) -> str:
@@ -1767,6 +1767,6 @@
                     if not result
                     else (
-                        result["key"]
-                        if self.__valid_key.fullmatch(result["key"])
+                        _soundness_check(result["key"], (str, type(None)))
+                        if self.__valid_key.fullmatch(_soundness_check(result["key"], (str, type(None))))
                         else None
                     )
@@ -1791,6 +1791,6 @@
                     if not result
                     else (
-                        result["key"]
-                        if self.__valid_key.fullmatch(result["key"])
+                        _soundness_check(result["key"], (str, type(None)))
+                        if self.__valid_key.fullmatch(_soundness_check(result["key"], (str, type(None))))
                         else None
                     )
apprise — apprise/plugins/apprise_api.py
--- base/apprise/plugins/apprise_api.py
+++ head/apprise/plugins/apprise_api.py
@@ -568,22 +568,22 @@
                     schema=(
                         NotifyAppriseAPI.secure_protocol
-                        if result.group("secure")
+                        if _soundness_check(result.group("secure"), str)
                         else NotifyAppriseAPI.protocol
                     ),
-                    hostname=result.group("hostname"),
+                    hostname=_soundness_check(result.group("hostname"), str),
                     port=(
                         ""
-                        if not result.group("port")
-                        else ":{}".format(result.group("port"))
+                        if not _soundness_check(result.group("port"), (str, type(None)))
+                        else ":{}".format(_soundness_check(result.group("port"), (str, type(None))))
                     ),
                     path=(
                         ""
-                        if not result.group("path")
-                        else result.group("path")
... 401 characters elided ...
                     # "?" (it's captured by the regex), so it's used as-is.
-                    params=result.group("params") or "",
+                    params=_soundness_check(result.group("params"), (str, type(None))) or "",
                 )
             )
apprise — apprise/plugins/bark.py
--- base/apprise/plugins/bark.py
+++ head/apprise/plugins/bark.py
@@ -121,5 +121,5 @@
     service_name: str = "Bark"
 
-    requirements: dict[str, str] = {
+    requirements = {
         "packages_recommended": "cryptography",
     }
apprise — apprise/plugins/base.py
--- base/apprise/plugins/base.py
+++ head/apprise/plugins/base.py
@@ -535,13 +535,13 @@
                     _retry_raw,
                 )
-                self.retry = min(
-                    max(0, self.asset.default_service_retry),
+                self.retry = _soundness_check(min(
+                    _soundness_check(max(0, self.asset.default_service_retry), int),
                     APPRISE_MAX_SERVICE_RETRY,
-                )
+                ), int)
         else:
-            self.retry = min(
-                max(0, self.asset.default_service_retry),
+            self.retry = _soundness_check(min(
+                _soundness_check(max(0, self.asset.default_service_retry), int),
                 APPRISE_MAX_SERVICE_RETRY,
-            )
+            ), int)
 
         # Initialize wait (inter-retry delay) from (in priority order):
apprise — apprise/plugins/blink1.py
--- base/apprise/plugins/blink1.py
+++ head/apprise/plugins/blink1.py
@@ -148,5 +148,5 @@
     enabled: bool = NOTIFY_BLINK1_ENABLED
 
-    requirements: dict[str, str] = {
+    requirements = {
         "packages_required": "hidapi",
     }
apprise — apprise/plugins/bluesky.py
--- base/apprise/plugins/bluesky.py
+++ head/apprise/plugins/bluesky.py
@@ -164,6 +164,6 @@
         results = HANDLE_HOST_PARSE_RE.match(self.user)
         if results:
-            self.user = results.group("handle").strip()
-            self.host = results.group("host").strip()
+            self.user = _soundness_check(results.group("handle"), str).strip()
+            self.host = _soundness_check(results.group("host"), str).strip()
 
         return
apprise — apprise/plugins/bulksms.py
--- base/apprise/plugins/bulksms.py
+++ head/apprise/plugins/bulksms.py
@@ -249,5 +249,5 @@
                 # If the target specified is all digits, it MUST have a @
                 # in front of it to eliminate any ambiguity
-                self.groups.append(group_re.group("group"))
+                self.groups.append(_soundness_check(group_re.group("group"), str))
                 continue
apprise — apprise/plugins/fcm/__init__.py
--- base/apprise/plugins/fcm/__init__.py
+++ head/apprise/plugins/fcm/__init__.py
@@ -97,5 +97,5 @@
     enabled: bool = NOTIFY_FCM_SUPPORT_ENABLED
 
-    requirements: dict[str, str] = {
+    requirements = {
         # Define our required packaging in order to work
         "packages_required": "cryptography"

1206 finding(s) omitted to fit GitHub's 65536-character comment limit. Every finding, with nothing elided, is in the roundtrip-report.md artifact of this run.

714 commits from astral-sh/ruff (sep 4 → oct 23 2026). the typeshed is
regenerated from upstream's `.pyi` rather than merged.

where the two sides now overlap, upstream's version is kept and the fork's
behaviour sits on top of it:

`redundant-condition` is upstream's lint now. the fork's declaration is gone,
and its conditions module keeps only `overlapping-condition` and the boolean
comparison checks. a truthiness test of `T | None` is reported by upstream's
`truthiness-test-of-none-union`, so `overlapping-condition` leaves `None` to it.

return types come from the fork's inference, gated by upstream's
`return-type-inference` option. the strict preset turns it on and the
ty-compatible preset keeps upstream's default. upstream's own body-inference
engine was unreachable behind the fork's and is deleted, and
`redundant-return-annotation` is gated on that option.

an ambiguous overload result stays the fork's `UnsafeUnion`.

requests on a closed file are answered from disk, as upstream now does. a
`textHash` is only needed when a client wants a specific text.

the return-type inlay hint setting takes upstream's name, `returnTypes`, in
place of the fork's `inferredReturnTypes`.

fixed along the way:

- a class or function returned from a body is generalized (`type[Point]`,
  a callable) rather than handed back as that exact object; an instance of
  a local class is returned as itself
- variance of a bound method no longer recurses forever, and neither does
  `pep695_generic_context` for an enum variant
- `if let` clauses are not swept by the condition lints
- an implicit receiver imported from another module resolves again, even
  after a later rebinding in the same scope
- a bare `case q:` that captures no longer exempts the rest of the scope from
  `redundant-condition`
- `Overlapping[...]` is unwrapped before the lazy type variable step
- `type(name, bases, {})` infers the namespace against `dict[str, Any]`
- the inlay hint for a return type that never settles (`-> Divergent`) is gone
- django template tags are found beside a `.pyi` stub again
- the pygments lexer builds with `flit-core`, since build backends are now
  pinned by hash
- a long `"a" | "b" | ...` type expression is walked in a loop rather than
  recursively, which overflowed the test stack unoptimised (`dbm._TFlags`)
- the pep 695 typeshed conversion leaves a function legacy when it uses a
  typevar imported from another module, so `max(xs, default=0)` and the keyed
  `bisect` functions solve again

ci's scripts step is upstream's new one, over every PEP 723 script: the
upstream scripts it flagged are fixed, the fork's scripts are locked or pin
their build dependencies by hash, and the step fails on errors only, since the
fork makes every warning an error by default.

upstream now sets `required-version = ">=0.13.0"` for uv, so the fork's own
setup-uv steps (cargo test, zensical, the docs deploy and the ecosystem
round-trip) pin uv 0.13.5 as upstream's do.
upstream's zizmor config also forbids `taiki-e/install-action`, so the fork's
python-matrix test job takes nextest from the dev dependencies instead.

upstream's new attribute override check reports every drf serializer's
`class Meta:`, which replaces the stubs' `ModelSerializer.Meta` without
subclassing it (astral-sh/ty#4711). `external/drf.md` expects that error for
now, and a view whose `queryset` is another model's now reports where it is
assigned.

two of upstream's uv e2e tests are adjusted to the fork's server. after
corrected script metadata restores an environment, the rescan has already read
the installed package, so reporting it changes nothing and the server sends no
refresh. and the closed-script test waits for the synchronization to finish
rather than querying between refreshes, which could remember a module as
missing while uv was still installing it.

`ty_project` tests that assert a query did not run used a database that
recorded no events, so they passed whatever ran. `take_salsa_events` now
refuses a non-recording database.
@KotlinIsland
KotlinIsland force-pushed the worktree-merge-upstream-oct2026 branch from e0b10f1 to 57db3e4 Compare October 24, 2026 23:57
@KotlinIsland
KotlinIsland merged commit 57db3e4 into main Oct 25, 2026
103 of 105 checks passed
@KotlinIsland
KotlinIsland deployed to release-playground October 25, 2026 07:55 — with GitHub Actions Active

This branch was successfully deployed

2 active deployments
release-playground — 57db3e43 Deployed Oct 25, 2026 by KotlinIsland via publish #244
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.