Skip to content

Conversation

ntBre
Copy link
Contributor

@ntBre ntBre commented Oct 6, 2025

Summary

Closes #19467 and also removes the warning about using Python 3.14 without
preview enabled.

I also bumped PythonVersion::default to 3.9 because it reaches EOL this month,
but we could also defer that for now if we wanted.

The first three commits are related to the latest bump to 3.14; the fourth commit
bumps the default to 3.10.

Note that this PR also bumps the default Python version for ty to 3.10 because
there was a test asserting that it stays in sync with ast::PythonVersion.

Test Plan

Existing tests

I spot-checked the ecosystem report, and I believe these are all expected. Inbits doesn't specify a target Python version, so I guess we're applying the default. UP007, UP035, and UP045 all use the new default value to emit new diagnostics.

ntBre added 4 commits October 6, 2025 10:52
Summary
--

Closes #19467 and also removes the warning about using Python 3.14 without
preview enabled.

I also bumped `PythonVersion::default` to 3.9 because it reaches EOL this month,
but we could also defer that for now if we wanted.

I'm actually a bit concerned about some of the snapshot differences I saw. It
looks like several rules that inspect annotations no longer work as expected,
including FAST001, FAST003, A003, and PTH210. Possibly this is intentional, but it means
that many, if not all, of these rules' diagnostics are eliminated. It looks like
this might affect all of our `TypeChecker`-based rules, but I haven't looked too
closely yet.

I've just pinned all of the affected tests to use 3.13 for now, but we should
follow up here if we don't have time to fix the underlying issue before the
release.

The test changes for 3.14 are in the second commit; reverting that seems like
the best way to see all of the affected cases.

Test Plan
--

Existing tests
Copy link
Contributor

github-actions bot commented Oct 6, 2025

Diagnostic diff on typing conformance tests

No changes detected when running ty on typing conformance tests ✅

@ntBre ntBre added breaking Breaking API change python314 Related to Python 3.14 labels Oct 6, 2025
Copy link
Contributor

github-actions bot commented Oct 6, 2025

mypy_primer results

No ecosystem changes detected ✅
No memory usage changes detected ✅

Copy link
Contributor

github-actions bot commented Oct 6, 2025

ruff-ecosystem results

Linter (stable)

ℹ️ ecosystem check detected linter changes. (+409 -0 violations, +0 -0 fixes in 1 projects; 54 projects unchanged)

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

+ lnbits/app.py:281:26: UP045 [*] Use `X | None` for type annotations
+ lnbits/app.py:9:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/commands.py:158:42: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:215:11: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:216:12: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:217:13: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:218:14: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:306:43: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:357:16: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:358:21: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:359:17: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:360:18: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:361:10: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:362:17: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:447:17: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:448:18: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:449:10: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:450:17: UP045 [*] Use `X | None` for type annotations
... 374 additional changes omitted for rule UP045
+ lnbits/core/models/misc.py:3:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/core/tasks.py:4:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/core/views/auth_api.py:6:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/core/views/generic.py:164:42: UP007 [*] Use `X | Y` for type annotations
+ lnbits/decorators.py:140:36: UP007 [*] Use `X | Y` for type annotations
+ lnbits/decorators.py:141:36: UP007 [*] Use `X | Y` for type annotations
+ lnbits/decorators.py:142:36: UP007 [*] Use `X | Y` for type annotations
+ lnbits/lnurl.py:2:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/middleware.py:65:10: UP007 [*] Use `X | Y` for type annotations
+ lnbits/tasks.py:6:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/utils/crypto.py:43:29: UP007 [*] Use `X | Y` for type annotations
+ lnbits/utils/logger.py:6:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/utils/nostr.py:152:22: UP007 [*] Use `X | Y` for type annotations
+ lnbits/wallets/nwc.py:362:38: UP007 [*] Use `X | Y` for type annotations
+ lnbits/wallets/nwc.py:516:49: UP007 [*] Use `X | Y` for type annotations
+ tests/wallets/fixtures/models.py:27:15: UP007 [*] Use `X | Y` for type annotations
+ tests/wallets/test_rest_wallets.py:62:29: UP007 [*] Use `X | Y` for type annotations
+ tests/wallets/test_rest_wallets.py:84:22: UP007 [*] Use `X | Y` for type annotations
... 373 additional changes omitted for project

Changes by rule (3 rules affected)

code total + violation - violation + fix - fix
UP045 390 390 0 0 0
UP007 12 12 0 0 0
UP035 7 7 0 0 0

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+409 -0 violations, +0 -0 fixes in 1 projects; 54 projects unchanged)

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

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

+ lnbits/app.py:281:26: UP045 [*] Use `X | None` for type annotations
+ lnbits/app.py:9:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/commands.py:158:42: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:215:11: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:216:12: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:217:13: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:218:14: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:306:43: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:357:16: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:358:21: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:359:17: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:360:18: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:361:10: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:362:17: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:447:17: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:448:18: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:449:10: UP045 [*] Use `X | None` for type annotations
+ lnbits/commands.py:450:17: UP045 [*] Use `X | None` for type annotations
... 374 additional changes omitted for rule UP045
+ lnbits/core/models/misc.py:3:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/core/tasks.py:4:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/core/views/auth_api.py:6:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/core/views/generic.py:164:42: UP007 [*] Use `X | Y` for type annotations
+ lnbits/decorators.py:140:36: UP007 [*] Use `X | Y` for type annotations
+ lnbits/decorators.py:141:36: UP007 [*] Use `X | Y` for type annotations
+ lnbits/decorators.py:142:36: UP007 [*] Use `X | Y` for type annotations
+ lnbits/lnurl.py:2:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/middleware.py:65:10: UP007 [*] Use `X | Y` for type annotations
+ lnbits/tasks.py:6:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/utils/crypto.py:43:29: UP007 [*] Use `X | Y` for type annotations
+ lnbits/utils/logger.py:6:1: UP035 [*] Import from `collections.abc` instead: `Callable`
+ lnbits/utils/nostr.py:152:22: UP007 [*] Use `X | Y` for type annotations
+ lnbits/wallets/nwc.py:362:38: UP007 [*] Use `X | Y` for type annotations
+ lnbits/wallets/nwc.py:516:49: UP007 [*] Use `X | Y` for type annotations
+ tests/wallets/fixtures/models.py:27:15: UP007 [*] Use `X | Y` for type annotations
+ tests/wallets/test_rest_wallets.py:62:29: UP007 [*] Use `X | Y` for type annotations
+ tests/wallets/test_rest_wallets.py:84:22: UP007 [*] Use `X | Y` for type annotations
... 373 additional changes omitted for project

Changes by rule (3 rules affected)

code total + violation - violation + fix - fix
UP045 390 390 0 0 0
UP007 12 12 0 0 0
UP035 7 7 0 0 0

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

Copy link

codspeed-hq bot commented Oct 6, 2025

CodSpeed Performance Report

Merging #20725 will improve performances by 7.28%

Comparing brent/314 (e77312d) with main (8fb29ea)

Summary

⚡ 2 improvements
✅ 49 untouched

Benchmarks breakdown

Mode Benchmark BASE HEAD Change
Instrumentation linter/all-rules[pydantic/types.py] 8.7 ms 8.1 ms +7.28%
Instrumentation linter/all-with-preview-rules[pydantic/types.py] 10.3 ms 9.7 ms +6.15%

Copy link
Contributor Author

@ntBre ntBre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I talked with @dylwil3 on Discord, and we think the removed diagnostics are related to pre-existing issues. He tested at least some of them and found that the diagnostics are also suppressed on earlier Python versions with from __future__ import annotations, so I'm planning to open a follow-up issue and not block the release, if that sounds good to everyone. The rules that look suspicious to me (and I'll include in the follow-up issue) are:

  • FAST001
  • FAST003
  • A003
  • PTH210
  • RUF001

In general, these look like cases where we no longer visit annotations because they aren't evaluated at runtime, but it seems like a worse result not to visit these annotations since it prevents us from emitting helpful diagnostics.

On the other side, the rules with changes that look correct to me are:

  • PYI055
  • TC001-TC004
  • F821
  • UP037
  • UP006
  • UP007
  • UP036
  • RUF058

Somewhere in between I'd put TC010. String union members are allowed on 3.14, but we probably still want to emit some diagnostic for "int" | str; this doesn't seem to trigger any of the TC rules unless I'm missing some additional configuration.

One remaining question is whether it's better to accept these snapshots or pin them to 3.13. I think it's probably easier to pin for now and then remove the pins while addressing the follow-up issue, but I'm curious what others think.

|
2 | from typing import Union
3 |
4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are correct. The check disabling these on earlier Python versions was added in #6457:

// The `|` operator isn't always safe to allow to runtime-evaluated annotations.
if semantic.execution_context().is_runtime() {
return;
}

@ntBre ntBre added this to the v0.14 milestone Oct 6, 2025
@ntBre ntBre marked this pull request as ready for review October 6, 2025 18:16
Copy link
Contributor

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ty changes look good to me, thank you!

@AlexWaygood Are there any interactions with typeshed here that we should worry about? Going to a higher default version should be fine, I think?

.set_python_version_with_source(&mut db)
.to(PythonVersionWithSource {
version: PythonVersion::latest_preview(),
version: PythonVersion::latest(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the change here necessary? It seems to me like we would want to run this test on 3.15 preview soon?

@AlexWaygood
Copy link
Member

@AlexWaygood Are there any interactions with typeshed here that we should worry about? Going to a higher default version should be fine, I think?

Yes, should be fine!

Copy link
Member

@AlexWaygood AlexWaygood Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we not add a Py314 variant to the PythonVersion enum in this file? We should definitely let ty users specify --python-version=3.14!

@MichaReiser
Copy link
Member

MichaReiser commented Oct 7, 2025

I agree that we shouldn't block the release if those are pre-existing issues, and creating follow-up issues sounds great. I do think that it's somewhat important to look into what's happening. From a quick investigation, it seems that

return dbg!(semantic.resolve_name(response_mode_name_expr))
            == dbg!(semantic.resolve_name(return_value_name_expr));

now returns false because resolve_name for the return value returns None. I haven't investigated further. @dylwil3 is this something you could take a look at. Unless @ntBre want's to fix it before the release ;)

Regarding the tests. For tests that regress, I'd prefer to use test_path_with_settings_diff so that it clearly demonstrates the undesired difference. The bugfix PR should then remove all the diffs.

The stringified union example is a bit funny (but also pre-existing).

x: "int" | str

is reported by TC010 but the following isn't, because the rule isn't in a runtime context:

from __future__ import annotations

x: "int" | str

I'd say this is also a pre-existing issue that doesn't need to block the 3.14 release

#[default]
#[value(name = "3.9")]
Py39,
#[default]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to update the default value in the ruff settings documentation here

let source = fs::read_to_string(input_path).expect("Expected test file to exist");
let options = extract_options(&source).unwrap_or_else(|| {
ParseOptions::from(Mode::Module).with_target_version(PythonVersion::latest_preview())
ParseOptions::from(Mode::Module).with_target_version(PythonVersion::latest())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to keep latest_preview and use it here as we, very intentionally, want to use the latest preview version for parser tests.

@ntBre
Copy link
Contributor Author

ntBre commented Oct 7, 2025

Thanks all for the reviews!

I think I've addressed all the feedback and will draft the follow-up issue while CI runs.

@ntBre ntBre merged commit 88c0ce3 into main Oct 7, 2025
39 checks passed
@ntBre ntBre deleted the brent/314 branch October 7, 2025 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaking API change python314 Related to Python 3.14

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Change latest Python version to Python 3.14

4 participants