Compare commits

..

38 Commits

Author SHA1 Message Date
David Peter
49c077d5d4 [ty] Emit unresolved-attribute diagnostics in annotated assignments 2025-05-26 19:41:33 +02:00
David Peter
4e68dd96a6 [ty] Infer types for ty_extensions.Intersection[A, B] tuple expressions (#18321)
## Summary

fixes astral-sh/ty#366

## Test Plan

* Added panic corpus regression tests
* I also wrote a hover regression test (see below), but decided not to
include it. The corpus tests are much more "effective" at finding these
types of errors, since they exhaustively check all expressions for
types.

<details>

```rs
#[test]
fn hover_regression_test_366() {
    let test = cursor_test(
        r#"
    from ty_extensions import Intersection

    class A: ...
    class B: ...

    def _(x: Intersection[A,<CURSOR> B]):
        pass
    "#,
    );

    assert_snapshot!(test.hover(), @r"
    A & B
    ---------------------------------------------
    ```text
    A & B
    ```
    ---------------------------------------------
    info[hover]: Hovered content is
     --> main.py:7:31
      |
    5 |         class B: ...
    6 |
    7 |         def _(x: Intersection[A, B]):
      |                               ^^-^
      |                               | |
      |                               | Cursor offset
      |                               source
    8 |             pass
      |
    ");
}
```

</details>
2025-05-26 17:08:52 +02:00
Maddy Guthridge
b25b642371 Improve readability of rule status icons in documentation (#18297)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-05-26 14:35:20 +00:00
Micha Reiser
175402aa75 [ty] Remove unnecessary lifetimes for Task (#18261) 2025-05-26 12:44:43 +00:00
Micha Reiser
d8216fa328 [ty] Gracefully handle salsa cancellations and panics in background request handlers (#18254) 2025-05-26 13:37:49 +01:00
David Peter
d51f6940fe [ty] Playground: Better default settings (#18316)
## Summary

The playground default settings set the `division-by-zero` rule severity
to `error`. This slightly confusing because `division-by-zero` is now
disabled by default. I am assuming that we have a `rules` section in
there to make it easier for users to customize those settings (in
addition to what the JSON schema gives us).

Here, I'm proposing a different default rule-set (`"undefined-reveal":
"ignore"`) that I would personally find more helpful for the playground,
since we're using it so frequently for MREs that often involve some
`reveal_type` calls.
2025-05-26 14:14:23 +02:00
Micha Reiser
66b082ff71 [ty] Abort process if worker thread panics (#18211) 2025-05-26 13:09:06 +01:00
Micha Reiser
5d93d619f3 Use git-commit as ty playground version instead of 0.0.0 (#18314) 2025-05-26 11:55:11 +00:00
David Peter
e1b662bf5d [ty] Always pass NO_INSTANCE_FALLBACK in try_call_dunder_with_policy (#18315)
## Summary

The previous `try_call_dunder_with_policy` API was a bit of a footgun
since you needed to pass `NO_INSTANCE_FALLBACK` in *addition* to other
policies that you wanted for the member lookup. Implicit calls to dunder
methods never access instance members though, so we can do this
implicitly in `try_call_dunder_with_policy`.

No functional changes.
2025-05-26 13:20:27 +02:00
Felix Scherz
f885cb8a2f [ty] use __getattribute__ to lookup unknown members on a type (#18280)
## Summary

`Type::member_lookup_with_policy` now falls back to calling
`__getattribute__` when a member cannot be found as a second fallback
after `__getattr__`.


closes https://github.com/astral-sh/ty/issues/441

## Test Plan

Added markdown tests.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: David Peter <mail@david-peter.de>
2025-05-26 12:59:45 +02:00
David Peter
4ef2c223c9 [ty] Respect MRO_NO_OBJECT_FALLBACK policy when looking up symbols on type instances (#18312)
## Summary

This should address a problem that came up while working on
https://github.com/astral-sh/ruff/pull/18280. When looking up an
attribute (typically a dunder method) with the `MRO_NO_OBJECT_FALLBACK`
policy, the attribute is first looked up on the meta type. If the meta
type happens to be `type`, we go through the following branch in
`find_name_in_mro_with_policy`:


97ff015c88/crates/ty_python_semantic/src/types.rs (L2565-L2573)

The problem is that we now look up the attribute on `object` *directly*
(instead of just having `object` in the MRO). In this case,
`MRO_NO_OBJECT_FALLBACK` has no effect in `class_member_from_mro`:


c3feb8ce27/crates/ty_python_semantic/src/types/class.rs (L1081-L1082)

So instead, we need to explicitly respect the `MRO_NO_OBJECT_FALLBACK`
policy here by returning `Symbol::Unbound`.

## Test Plan

Added new Markdown tests that explain the ecosystem changes that we
observe.
2025-05-26 12:03:29 +02:00
Vasanth
d078ecff37 [flake8_async] Refactor argument name resolution for async sleep func… (#18262)
Co-authored-by: Vasanth-96 <ramavath.naik@itilite.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-05-26 09:53:03 +00:00
David Peter
7eca6f96e3 [ty] Fix attribute writes to unions/intersections including modules (#18313)
## Summary

Fix a bug that involved writes to attributes on union/intersection types
that included modules as elements.

This is a prerequisite to avoid some ecosystem false positives in
https://github.com/astral-sh/ruff/pull/18312

## Test Plan

Added regression test
2025-05-26 11:41:03 +02:00
David Sherret
fbaf826a9d Only enable js feature of uuid crate for wasm crates (#18152) 2025-05-26 10:33:51 +01:00
Wei Lee
d8a5b9de17 [airflow] Revise fix title AIR3 (#18215) 2025-05-26 10:31:48 +01:00
otakutyrant
c3feb8ce27 Update editor integrations link in README (#17977)
Co-authored-by: Oscar Gustafsson <oscar.gustafsson@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-05-26 09:50:09 +01:00
Jo
97ff015c88 [ty] Add tests to src.root if it exists and is not a package (#18286) 2025-05-26 09:08:57 +01:00
renovate[bot]
1f7134f727 Update rui314/setup-mold digest to 67424c1 (#18300) 2025-05-26 07:43:52 +02:00
renovate[bot]
6a0b93170e Update pre-commit dependencies (#18302) 2025-05-26 07:43:31 +02:00
renovate[bot]
cc59ff8aad Update dependency ruff to v0.11.11 (#18301) 2025-05-26 07:41:54 +02:00
renovate[bot]
2b90e7fcd7 Update NPM Development dependencies (#18305) 2025-05-26 07:41:37 +02:00
renovate[bot]
a43f5b2129 Update taiki-e/install-action action to v2.52.1 (#18307) 2025-05-26 07:41:18 +02:00
renovate[bot]
f3fb7429ca Update astral-sh/setup-uv action to v6.1.0 (#18304) 2025-05-26 07:40:51 +02:00
renovate[bot]
83498b95fb Update Rust crate uuid to v1.17.0 (#18306) 2025-05-26 07:40:01 +02:00
renovate[bot]
03d7be3747 Update Rust crate jiff to v0.2.14 (#18303) 2025-05-26 07:38:37 +02:00
Dhruv Manilawala
d95b029862 [ty] Move diagnostics API for the server (#18308)
## Summary

This PR moves the diagnostics API for the language server out from the
request handler module to the diagnostics API module.

This is in preparation to add support for publishing diagnostics.
2025-05-26 04:16:38 +00:00
Charlie Marsh
14c3755445 Fix YTT201 for '!=' comparisons (#18293)
## Summary

Closes #18292.
2025-05-25 13:16:19 -04:00
Jo
83a036960b [ty] Add long help for --config argument (#18285) 2025-05-25 13:09:02 +02:00
chiri
be76fadb05 [pyupgrade] make fix unsafe if it deletes comments (UP010, unnecessary-future-import) (#18291) 2025-05-25 12:44:21 +02:00
Alex Waygood
e293411679 [ty] get_protocol_members returns a frozenset, not a tuple (#18284) 2025-05-23 23:20:34 +00:00
lipefree
53d19f8368 [ty] Resolving Python path using CONDA_PREFIX variable to support Conda and Pixi (#18267) 2025-05-23 20:00:42 +02:00
InSync
a1399656c9 [ty] Fix binary intersection comparison inference logic (#18266)
## Summary

Resolves https://github.com/astral-sh/ty/issues/485.

`infer_binary_intersection_type_comparison()` now checks for all
positive members before concluding that an operation is unsupported for
a given intersection type.

## Test Plan

Markdown tests.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-05-23 12:55:17 +02:00
David Peter
6392dccd24 [ty] Add warning that docs are autogenerated (#18270)
## Summary

This is a practice I followed on previous projects. Should hopefully
further help developers who want to update the documentation.

The big downside is that it's annoying to see this *as a user of the
documentation* if you don't open the Markdown file in the browser. But
I'd argue that those files don't really follow the original Markdown
spirit anyway with all the inline HTML.
2025-05-23 09:58:16 +00:00
David Peter
93ac0934dd [ty] Type compendium (#18263)
## Summary

This is something I wrote a few months ago, and continued to update from
time to time. It was mostly written for my own education. I found a few
bugs while writing it at the time (there are still one or two TODOs in
the test assertions that are probably bugs). Our other tests are fairly
comprehensive, but they are usually structured around a certain
functionality or operation (subtyping, assignability, narrowing). The
idea here was to focus on individual *types and their properties*.

closes #197 (added `JustFloat` and `JustComplex` to `ty_extensions`).
2025-05-23 11:41:31 +02:00
David Peter
aae4482c55 [ty] Replace remaining knot.toml reference (#18269)
## Summary

Fix remaining `knot.toml` reference and replace it with `ty.toml`. This
change was probably still in flight while we renamed things.

## Test Plan

Added a second assertion which ensures that the config file has any
effect.
2025-05-23 10:44:46 +02:00
Alex Waygood
d02c9ada5d [ty] Do not carry the generic context of Protocol or Generic in the ClassBase enum (#17989)
## Summary

It doesn't seem to be necessary for our generics implementation to carry
the `GenericContext` in the `ClassBase` variants. Removing it simplifies
the code, fixes many TODOs about `Generic` or `Protocol` appearing
multiple times in MROs when each should only appear at most once, and
allows us to more accurately detect runtime errors that occur due to
`Generic` or `Protocol` appearing multiple times in a class's bases.

In order to remove the `GenericContext` from the `ClassBase` variant, it
turns out to be necessary to emulate
`typing._GenericAlias.__mro_entries__`, or we end up with a large number
of false-positive `inconsistent-mro` errors. This PR therefore also does
that.

Lastly, this PR fixes the inferred MROs of PEP-695 generic classes,
which implicitly inherit from `Generic` even if they have no explicit
bases.

## Test Plan

mdtests
2025-05-22 21:37:03 -04:00
Dylan
6c0a59ea78 Fix insider docs requirement syntax (#18265)
Attempting to fix the `mkdocs` workflow (maybe `uv` is more forgiving
than `pip` for the syntax in `requirements.txt`?)
2025-05-22 16:21:51 -05:00
Carl Meyer
0b181bc2ad Fix instance vs callable subtyping/assignability (#18260)
## Summary

Fix some issues with subtying/assignability for instances vs callables.
We need to look up dunders on the class, not the instance, and we should
limit our logic here to delegating to the type of `__call__`, so it
doesn't get out of sync with the calls we allow.

Also, we were just entirely missing assignability handling for
`__call__` implemented as anything other than a normal bound method
(though we had it for subtyping.)

A first step towards considering what else we want to change in
https://github.com/astral-sh/ty/issues/491

## Test Plan

mdtests

---------

Co-authored-by: med <medioqrity@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-05-22 19:47:05 +00:00
126 changed files with 3312 additions and 1201 deletions

View File

@@ -237,13 +237,13 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@941e8a4d9d7cdb696bd4f017cf54aca281f8ffff # v2.51.2
uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2.52.1
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@941e8a4d9d7cdb696bd4f017cf54aca281f8ffff # v2.51.2
uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2.52.1
with:
tool: cargo-insta
- name: ty mdtests (GitHub annotations)
@@ -295,13 +295,13 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@941e8a4d9d7cdb696bd4f017cf54aca281f8ffff # v2.51.2
uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2.52.1
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@941e8a4d9d7cdb696bd4f017cf54aca281f8ffff # v2.51.2
uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2.52.1
with:
tool: cargo-insta
- name: "Run tests"
@@ -324,7 +324,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: "Install cargo nextest"
uses: taiki-e/install-action@941e8a4d9d7cdb696bd4f017cf54aca281f8ffff # v2.51.2
uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2.52.1
with:
tool: cargo-nextest
- name: "Run tests"
@@ -380,7 +380,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
- name: "Build"
run: cargo build --release --locked
@@ -405,13 +405,13 @@ jobs:
MSRV: ${{ steps.msrv.outputs.value }}
run: rustup default "${MSRV}"
- name: "Install mold"
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
- name: "Install cargo nextest"
uses: taiki-e/install-action@941e8a4d9d7cdb696bd4f017cf54aca281f8ffff # v2.51.2
uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2.52.1
with:
tool: cargo-nextest
- name: "Install cargo insta"
uses: taiki-e/install-action@941e8a4d9d7cdb696bd4f017cf54aca281f8ffff # v2.51.2
uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2.52.1
with:
tool: cargo-insta
- name: "Run tests"
@@ -459,7 +459,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
name: Download Ruff binary to test
id: download-cached-binary
@@ -660,7 +660,7 @@ jobs:
branch: ${{ github.event.pull_request.base.ref }}
workflow: "ci.yaml"
check_artifacts: true
- uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
- name: Fuzz
env:
FORCE_COLOR: 1
@@ -730,7 +730,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
@@ -773,7 +773,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: Install uv
uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: uv pip install -r docs/requirements-insiders.txt --system
@@ -910,7 +910,7 @@ jobs:
run: rustup show
- name: "Install codspeed"
uses: taiki-e/install-action@941e8a4d9d7cdb696bd4f017cf54aca281f8ffff # v2.51.2
uses: taiki-e/install-action@6c6479b49816fcc0975a31af977bdc1f847c2920 # v2.52.1
with:
tool: cargo-codspeed

View File

@@ -34,11 +34,11 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
- name: Build ruff
# A debug build means the script runs slower once it gets started,

View File

@@ -37,7 +37,7 @@ jobs:
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
with:

View File

@@ -22,7 +22,7 @@ jobs:
id-token: write
steps:
- name: "Install uv"
uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: wheels-*

View File

@@ -80,7 +80,7 @@ repos:
pass_filenames: false # This makes it a lot faster
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.10
rev: v0.11.11
hooks:
- id: ruff-format
- id: ruff
@@ -98,7 +98,7 @@ repos:
# zizmor detects security vulnerabilities in GitHub Actions workflows.
# Additional configuration for the tool is found in `.github/zizmor.yml`
- repo: https://github.com/woodruffw/zizmor-pre-commit
rev: v1.7.0
rev: v1.8.0
hooks:
- id: zizmor

35
Cargo.lock generated
View File

@@ -486,7 +486,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -495,7 +495,7 @@ version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -909,7 +909,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -1444,7 +1444,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
"hermit-abi 0.5.1",
"libc",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -1498,9 +1498,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93"
dependencies = [
"jiff-static",
"jiff-tzdb-platform",
@@ -1508,14 +1508,14 @@ dependencies = [
"portable-atomic",
"portable-atomic-util",
"serde",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
name = "jiff-static"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
dependencies = [
"proc-macro2",
"quote",
@@ -3081,6 +3081,7 @@ dependencies = [
"ruff_workspace",
"serde",
"serde-wasm-bindgen",
"uuid",
"wasm-bindgen",
"wasm-bindgen-test",
]
@@ -3162,7 +3163,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -3531,7 +3532,7 @@ dependencies = [
"getrandom 0.3.3",
"once_cell",
"rustix",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -4003,6 +4004,7 @@ dependencies = [
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.1",
"salsa",
"serde",
"serde_json",
"shellexpand",
@@ -4071,6 +4073,7 @@ dependencies = [
"ty_ide",
"ty_project",
"ty_python_semantic",
"uuid",
"wasm-bindgen",
"wasm-bindgen-test",
]
@@ -4240,9 +4243,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [
"getrandom 0.3.3",
"js-sys",
@@ -4253,9 +4256,9 @@ dependencies = [
[[package]]
name = "uuid-macro-internal"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72dcd78c4f979627a754f5522cea6e6a25e55139056535fe6e69c506cd64a862"
checksum = "26b682e8c381995ea03130e381928e0e005b7c9eb483c6c8682f50e07b33c2b7"
dependencies = [
"proc-macro2",
"quote",
@@ -4523,7 +4526,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]

View File

@@ -179,7 +179,6 @@ uuid = { version = "1.6.1", features = [
"v4",
"fast-rng",
"macro-diagnostics",
"js",
] }
walkdir = { version = "2.3.2" }
wasm-bindgen = { version = "0.2.92" }
@@ -188,7 +187,7 @@ wild = { version = "2" }
zip = { version = "0.6.6", default-features = false }
[workspace.metadata.cargo-shear]
ignored = ["getrandom", "ruff_options_metadata"]
ignored = ["getrandom", "ruff_options_metadata", "uuid"]
[workspace.lints.rust]

View File

@@ -34,8 +34,7 @@ An extremely fast Python linter and code formatter, written in Rust.
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
- 📏 Over [800 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
of popular Flake8 plugins, like flake8-bugbear
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/integrations/) for
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://docs.astral.sh/ruff/editors/setup)
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/editors) for [VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://docs.astral.sh/ruff/editors/setup)
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://docs.astral.sh/ruff/configuration/#config-file-discovery)
Ruff aims to be orders of magnitude faster than alternative tools while integrating more

View File

@@ -131,7 +131,7 @@ fn benchmark_incremental(criterion: &mut Criterion) {
fn setup() -> Case {
let case = setup_tomllib_case();
let result: Vec<_> = case.db.check().unwrap();
let result: Vec<_> = case.db.check();
assert_diagnostics(&case.db, &result, EXPECTED_TOMLLIB_DIAGNOSTICS);
@@ -159,7 +159,7 @@ fn benchmark_incremental(criterion: &mut Criterion) {
None,
);
let result = db.check().unwrap();
let result = db.check();
assert_eq!(result.len(), EXPECTED_TOMLLIB_DIAGNOSTICS.len());
}
@@ -179,7 +179,7 @@ fn benchmark_cold(criterion: &mut Criterion) {
setup_tomllib_case,
|case| {
let Case { db, .. } = case;
let result: Vec<_> = db.check().unwrap();
let result: Vec<_> = db.check();
assert_diagnostics(db, &result, EXPECTED_TOMLLIB_DIAGNOSTICS);
},
@@ -293,7 +293,7 @@ fn benchmark_many_string_assignments(criterion: &mut Criterion) {
},
|case| {
let Case { db, .. } = case;
let result = db.check().unwrap();
let result = db.check();
assert_eq!(result.len(), 0);
},
BatchSize::SmallInput,
@@ -339,7 +339,7 @@ fn benchmark_many_tuple_assignments(criterion: &mut Criterion) {
},
|case| {
let Case { db, .. } = case;
let result = db.check().unwrap();
let result = db.check();
assert_eq!(result.len(), 0);
},
BatchSize::SmallInput,

View File

@@ -1,3 +1,4 @@
use std::any::Any;
use std::backtrace::BacktraceStatus;
use std::cell::Cell;
use std::panic::Location;
@@ -24,17 +25,25 @@ impl Payload {
None
}
}
pub fn downcast_ref<R: Any>(&self) -> Option<&R> {
self.0.downcast_ref::<R>()
}
}
impl std::fmt::Display for PanicError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "panicked at")?;
write!(f, "panicked at")?;
if let Some(location) = &self.location {
write!(f, " {location}")?;
}
if let Some(payload) = self.payload.as_str() {
write!(f, ":\n{payload}")?;
}
if let Some(query_trace) = self.salsa_backtrace.as_ref() {
let _ = writeln!(f, "{query_trace}");
}
if let Some(backtrace) = &self.backtrace {
match backtrace.status() {
BacktraceStatus::Disabled => {
@@ -49,6 +58,7 @@ impl std::fmt::Display for PanicError {
_ => {}
}
}
Ok(())
}
}

View File

@@ -18,44 +18,43 @@ const FIX_SYMBOL: &str = "🛠️";
const PREVIEW_SYMBOL: &str = "🧪";
const REMOVED_SYMBOL: &str = "";
const WARNING_SYMBOL: &str = "⚠️";
const STABLE_SYMBOL: &str = "✔️";
const SPACER: &str = "&nbsp;&nbsp;&nbsp;&nbsp;";
/// Style for the rule's fixability and status icons.
const SYMBOL_STYLE: &str = "style='width: 1em; display: inline-block;'";
/// Style for the container wrapping the fixability and status icons.
const SYMBOLS_CONTAINER: &str = "style='display: flex; gap: 0.5rem; justify-content: end;'";
fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>, linter: &Linter) {
table_out.push_str("| Code | Name | Message | |");
table_out.push_str("| Code | Name | Message | |");
table_out.push('\n');
table_out.push_str("| ---- | ---- | ------- | ------: |");
table_out.push_str("| ---- | ---- | ------- | -: |");
table_out.push('\n');
for rule in rules {
let status_token = match rule.group() {
RuleGroup::Removed => {
format!("<span title='Rule has been removed'>{REMOVED_SYMBOL}</span>")
format!(
"<span {SYMBOL_STYLE} title='Rule has been removed'>{REMOVED_SYMBOL}</span>"
)
}
RuleGroup::Deprecated => {
format!("<span title='Rule has been deprecated'>{WARNING_SYMBOL}</span>")
format!(
"<span {SYMBOL_STYLE} title='Rule has been deprecated'>{WARNING_SYMBOL}</span>"
)
}
RuleGroup::Preview => {
format!("<span title='Rule is in preview'>{PREVIEW_SYMBOL}</span>")
}
RuleGroup::Stable => {
// A full opacity checkmark is a bit aggressive for indicating stable
format!("<span title='Rule is stable' style='opacity: 0.6'>{STABLE_SYMBOL}</span>")
format!("<span {SYMBOL_STYLE} title='Rule is in preview'>{PREVIEW_SYMBOL}</span>")
}
RuleGroup::Stable => format!("<span {SYMBOL_STYLE}></span>"),
};
let fix_token = match rule.fixable() {
FixAvailability::Always | FixAvailability::Sometimes => {
format!("<span title='Automatic fix available'>{FIX_SYMBOL}</span>")
}
FixAvailability::None => {
format!(
"<span title='Automatic fix not available' style='opacity: 0.1' aria-hidden='true'>{FIX_SYMBOL}</span>"
)
format!("<span {SYMBOL_STYLE} title='Automatic fix available'>{FIX_SYMBOL}</span>")
}
FixAvailability::None => format!("<span {SYMBOL_STYLE}></span>"),
};
let tokens = format!("{status_token} {fix_token}");
let rule_name = rule.as_ref();
// If the message ends in a bracketed expression (like: "Use {replacement}"), escape the
@@ -82,15 +81,14 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>,
#[expect(clippy::or_fun_call)]
let _ = write!(
table_out,
"| {ss}{0}{1}{se} {{ #{0}{1} }} | {ss}{2}{se} | {ss}{3}{se} | {ss}{4}{se} |",
linter.common_prefix(),
linter.code_for_rule(rule).unwrap(),
rule.explanation()
"| {ss}{prefix}{code}{se} {{ #{prefix}{code} }} | {ss}{explanation}{se} | {ss}{message}{se} | <div {SYMBOLS_CONTAINER}>{status_token}{fix_token}</div>|",
prefix = linter.common_prefix(),
code = linter.code_for_rule(rule).unwrap(),
explanation = rule
.explanation()
.is_some()
.then_some(format_args!("[{rule_name}](rules/{rule_name}.md)"))
.unwrap_or(format_args!("{rule_name}")),
message,
tokens,
);
table_out.push('\n');
}
@@ -104,12 +102,6 @@ pub(crate) fn generate() -> String {
table_out.push_str("### Legend");
table_out.push('\n');
let _ = write!(
&mut table_out,
"{SPACER}{STABLE_SYMBOL}{SPACER} The rule is stable."
);
table_out.push_str("<br />");
let _ = write!(
&mut table_out,
"{SPACER}{PREVIEW_SYMBOL}{SPACER} The rule is unstable and is in [\"preview\"](faq.md#what-is-preview)."
@@ -132,7 +124,8 @@ pub(crate) fn generate() -> String {
&mut table_out,
"{SPACER}{FIX_SYMBOL}{SPACER} The rule is automatically fixable by the `--fix` command-line option."
);
table_out.push_str("<br />");
table_out.push_str("\n\n");
table_out.push_str("All rules not marked as preview, deprecated or removed are stable.");
table_out.push('\n');
for linter in Linter::iter() {

View File

@@ -80,6 +80,7 @@ fn generate() -> String {
let mut parents = Vec::new();
output.push_str("<!-- WARNING: This file is auto-generated (cargo dev generate-all). Edit the doc comments in 'crates/ty/src/args.rs' if you want to change anything here. -->\n\n");
output.push_str("# CLI Reference\n\n");
generate_command(&mut output, &ty, &mut parents);

View File

@@ -25,6 +25,10 @@ pub(crate) fn main(args: &Args) -> anyhow::Result<()> {
let file_name = "crates/ty/docs/configuration.md";
let markdown_path = PathBuf::from(ROOT_DIR).join(file_name);
output.push_str(
"<!-- WARNING: This file is auto-generated (cargo dev generate-all). Update the doc comments on the 'Options' struct in 'crates/ty_project/src/metadata/options.rs' if you want to change anything here. -->\n\n",
);
generate_set(
&mut output,
Set::Toplevel(Options::metadata()),

View File

@@ -56,6 +56,10 @@ fn generate_markdown() -> String {
let mut output = String::new();
let _ = writeln!(
&mut output,
"<!-- WARNING: This file is auto-generated (cargo dev generate-all). Edit the lint-declarations in 'crates/ty_python_semantic/src/types/diagnostic.rs' if you want to change anything here. -->\n"
);
let _ = writeln!(&mut output, "# Rules\n");
let mut lints: Vec<_> = registry.lints().iter().collect();

View File

@@ -114,16 +114,11 @@ from airflow.sensors.sql_sensor import SqlSensor
SqlSensor()
from airflow.operators.jdbc_operator import JdbcOperator
from airflow.operators.mssql_operator import MsSqlOperator
from airflow.operators.mysql_operator import MySqlOperator
from airflow.operators.oracle_operator import OracleOperator
from airflow.operators.postgres_operator import PostgresOperator
from airflow.operators.sqlite_operator import SqliteOperator
from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
JdbcOperator()
MsSqlOperator()
MySqlOperator()
OracleOperator()
PostgresOperator()
SqliteOperator()
SQLExecuteQueryOperator()
SQLExecuteQueryOperator()
SQLExecuteQueryOperator()
SQLExecuteQueryOperator()
SQLExecuteQueryOperator()
SQLExecuteQueryOperator()

View File

@@ -145,3 +145,23 @@ def func():
sleep = 10
anyio.run(main)
async def test_anyio_async115_helpers():
import anyio
await anyio.sleep(delay=1) # OK
await anyio.sleep(seconds=1) # OK
await anyio.sleep(delay=0) # ASYNC115
await anyio.sleep(seconds=0) # OK
async def test_trio_async115_helpers():
import trio
await trio.sleep(seconds=1) # OK
await trio.sleep(delay=1) # OK
await trio.sleep(seconds=0) # ASYNC115
await trio.sleep(delay=0) # OK

View File

@@ -108,3 +108,23 @@ async def import_from_anyio():
# catch from import
await sleep(86401) # error: 116, "async"
async def test_anyio_async116_helpers():
import anyio
await anyio.sleep(delay=1) # OK
await anyio.sleep(seconds=1) # OK
await anyio.sleep(delay=86401) # ASYNC116
await anyio.sleep(seconds=86401) # OK
async def test_trio_async116_helpers():
import trio
await trio.sleep(seconds=1) # OK
await trio.sleep(delay=1) # OK
await trio.sleep(seconds=86401) # ASYNC116
await trio.sleep(delay=86401) # OK

View File

@@ -12,3 +12,4 @@ if True:
if True:
from __future__ import generator_stop
from __future__ import invalid_module, generators
from __future__ import generators # comment

View File

@@ -65,24 +65,26 @@ impl Violation for Airflow3MovedToProvider {
fn fix_title(&self) -> Option<String> {
let Airflow3MovedToProvider { replacement, .. } = self;
match replacement {
ProviderReplacement::None => None,
if let Some((module, name, provider, version)) = match &replacement {
ProviderReplacement::AutoImport {
name,
module,
name,
provider,
version,
} => Some(format!(
"Install `apache-airflow-providers-{provider}>={version}` and use `{module}.{name}` instead."
)),
} => Some((module, *name, provider, version)),
ProviderReplacement::SourceModuleMovedToProvider {
name,
module,
name,
provider,
version,
} => Some(format!(
"Install `apache-airflow-providers-{provider}>={version}` and use `{module}.{name}` instead."
)),
} => Some((module, name.as_str(), provider, version)),
ProviderReplacement::None => None,
} {
Some(format!(
"Install `apache-airflow-providers-{provider}>={version}` and use `{name}` from `{module}` instead."
))
} else {
None
}
}
}

View File

@@ -73,10 +73,10 @@ impl Violation for Airflow3Removal {
Replacement::AttrName(name) => Some(format!("Use `{name}` instead")),
Replacement::Message(message) => Some((*message).to_string()),
Replacement::AutoImport { module, name } => {
Some(format!("Use `{module}.{name}` instead"))
Some(format!("Use `{name}` from `{module}` instead."))
}
Replacement::SourceModuleMoved { module, name } => {
Some(format!("Use `{module}.{name}` instead"))
Some(format!("Use `{name}` from `{module}` instead."))
}
}
}

View File

@@ -77,7 +77,7 @@ impl Violation for Airflow3SuggestedToMoveToProvider {
provider,
version,
} => Some(format!(
"Install `apache-airflow-providers-{provider}>={version}` and use `{module}.{name}` instead."
"Install `apache-airflow-providers-{provider}>={version}` and use `{name}` from `{module}` instead."
)),
ProviderReplacement::SourceModuleMovedToProvider {
module,
@@ -85,7 +85,7 @@ impl Violation for Airflow3SuggestedToMoveToProvider {
provider,
version,
} => Some(format!(
"Install `apache-airflow-providers-{provider}>={version}` and use `{module}.{name}` instead."
"Install `apache-airflow-providers-{provider}>={version}` and use `{name}` from `{module}` instead."
)),
}
}

View File

@@ -72,10 +72,10 @@ impl Violation for Airflow3SuggestedUpdate {
Replacement::AttrName(name) => Some(format!("Use `{name}` instead")),
Replacement::Message(message) => Some((*message).to_string()),
Replacement::AutoImport { module, name } => {
Some(format!("Use `{module}.{name}` instead"))
Some(format!("Use `{name}` from `{module}` instead."))
}
Replacement::SourceModuleMoved { module, name } => {
Some(format!("Use `{module}.{name}` instead"))
Some(format!("Use `{name}` from `{module}` instead."))
}
}
}

View File

@@ -171,7 +171,7 @@ AIR301_class_attribute.py:42:6: AIR301 [*] `airflow.datasets.manager.DatasetMana
43 | dm.register_dataset_change()
44 | dm.create_datasets()
|
= help: Use `airflow.assets.manager.AssetManager` instead
= help: Use `AssetManager` from `airflow.assets.manager` instead.
Safe fix
19 19 | from airflow.providers_manager import ProvidersManager
@@ -302,7 +302,7 @@ AIR301_class_attribute.py:50:11: AIR301 [*] `airflow.lineage.hook.DatasetLineage
| ^^^^^^^^^^^^^^^^^^ AIR301
51 | dl_info.dataset
|
= help: Use `airflow.lineage.hook.AssetLineageInfo` instead
= help: Use `AssetLineageInfo` from `airflow.lineage.hook` instead.
Safe fix
9 9 | DatasetAny,

View File

@@ -85,7 +85,7 @@ AIR301_names.py:60:1: AIR301 [*] `airflow.configuration.get` is removed in Airfl
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
| ^^^ AIR301
|
= help: Use `airflow.configuration.conf.get` instead
= help: Use `conf.get` from `airflow.configuration` instead.
Safe fix
19 19 | has_option,
@@ -111,7 +111,7 @@ AIR301_names.py:60:6: AIR301 [*] `airflow.configuration.getboolean` is removed i
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
| ^^^^^^^^^^ AIR301
|
= help: Use `airflow.configuration.conf.getboolean` instead
= help: Use `conf.getboolean` from `airflow.configuration` instead.
Safe fix
19 19 | has_option,
@@ -137,7 +137,7 @@ AIR301_names.py:60:18: AIR301 [*] `airflow.configuration.getfloat` is removed in
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
| ^^^^^^^^ AIR301
|
= help: Use `airflow.configuration.conf.getfloat` instead
= help: Use `conf.getfloat` from `airflow.configuration` instead.
Safe fix
19 19 | has_option,
@@ -163,7 +163,7 @@ AIR301_names.py:60:28: AIR301 [*] `airflow.configuration.getint` is removed in A
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
| ^^^^^^ AIR301
|
= help: Use `airflow.configuration.conf.getint` instead
= help: Use `conf.getint` from `airflow.configuration` instead.
Safe fix
19 19 | has_option,
@@ -189,7 +189,7 @@ AIR301_names.py:60:36: AIR301 [*] `airflow.configuration.has_option` is removed
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
| ^^^^^^^^^^ AIR301
|
= help: Use `airflow.configuration.conf.has_option` instead
= help: Use `conf.has_option` from `airflow.configuration` instead.
Safe fix
19 19 | has_option,
@@ -215,7 +215,7 @@ AIR301_names.py:60:48: AIR301 [*] `airflow.configuration.remove_option` is remov
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
| ^^^^^^^^^^^^^ AIR301
|
= help: Use `airflow.configuration.conf.remove_option` instead
= help: Use `conf.remove_option` from `airflow.configuration` instead.
Safe fix
19 19 | has_option,
@@ -241,7 +241,7 @@ AIR301_names.py:60:63: AIR301 [*] `airflow.configuration.as_dict` is removed in
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
| ^^^^^^^ AIR301
|
= help: Use `airflow.configuration.conf.as_dict` instead
= help: Use `conf.as_dict` from `airflow.configuration` instead.
Safe fix
19 19 | has_option,
@@ -267,7 +267,7 @@ AIR301_names.py:60:72: AIR301 [*] `airflow.configuration.set` is removed in Airf
60 | get, getboolean, getfloat, getint, has_option, remove_option, as_dict, set
| ^^^ AIR301
|
= help: Use `airflow.configuration.conf.set` instead
= help: Use `conf.set` from `airflow.configuration` instead.
Safe fix
19 19 | has_option,
@@ -308,7 +308,7 @@ AIR301_names.py:72:1: AIR301 `airflow.hooks.base_hook.BaseHook` is removed in Ai
72 | BaseHook()
| ^^^^^^^^ AIR301
|
= help: Use `airflow.hooks.base.BaseHook` instead
= help: Use `BaseHook` from `airflow.hooks.base` instead.
AIR301_names.py:76:1: AIR301 `airflow.operators.subdag.SubDagOperator` is removed in Airflow 3.0
|
@@ -324,7 +324,7 @@ AIR301_names.py:85:1: AIR301 `airflow.sensors.base_sensor_operator.BaseSensorOpe
85 | BaseSensorOperator()
| ^^^^^^^^^^^^^^^^^^ AIR301
|
= help: Use `airflow.sdk.bases.sensor.BaseSensorOperator` instead
= help: Use `BaseSensorOperator` from `airflow.sdk.bases.sensor` instead.
AIR301_names.py:89:1: AIR301 `airflow.triggers.external_task.TaskStateTrigger` is removed in Airflow 3.0
|
@@ -446,7 +446,7 @@ AIR301_names.py:117:1: AIR301 `airflow.utils.file.TemporaryDirectory` is removed
| ^^^^^^^^^^^^^^^^^^ AIR301
118 | mkdirs
|
= help: Use `tempfile.TemporaryDirectory` instead
= help: Use `TemporaryDirectory` from `tempfile` instead.
AIR301_names.py:118:1: AIR301 `airflow.utils.file.mkdirs` is removed in Airflow 3.0
|
@@ -466,7 +466,7 @@ AIR301_names.py:121:1: AIR301 [*] `airflow.utils.helpers.chain` is removed in Ai
| ^^^^^^^^^^^^ AIR301
122 | helper_cross_downstream
|
= help: Use `airflow.sdk.chain` instead
= help: Use `chain` from `airflow.sdk` instead.
Safe fix
48 48 | from airflow.utils.trigger_rule import TriggerRule
@@ -495,7 +495,7 @@ AIR301_names.py:122:1: AIR301 [*] `airflow.utils.helpers.cross_downstream` is re
123 |
124 | # airflow.utils.log
|
= help: Use `airflow.sdk.cross_downstream` instead
= help: Use `cross_downstream` from `airflow.sdk` instead.
Safe fix
48 48 | from airflow.utils.trigger_rule import TriggerRule
@@ -523,7 +523,7 @@ AIR301_names.py:125:1: AIR301 `airflow.utils.log.secrets_masker` is removed in A
126 |
127 | # airflow.utils.state
|
= help: Use `airflow.sdk.execution_time.secrets_masker` instead
= help: Use `secrets_masker` from `airflow.sdk.execution_time` instead.
AIR301_names.py:128:1: AIR301 `airflow.utils.state.SHUTDOWN` is removed in Airflow 3.0
|
@@ -595,7 +595,7 @@ AIR301_names.py:146:1: AIR301 `airflow.operators.python.get_current_context` is
147 |
148 | # airflow.providers.mysql
|
= help: Use `airflow.sdk.get_current_context` instead
= help: Use `get_current_context` from `airflow.sdk` instead.
AIR301_names.py:151:1: AIR301 `airflow.providers.mysql.datasets.mysql.sanitize_uri` is removed in Airflow 3.0
|
@@ -606,7 +606,7 @@ AIR301_names.py:151:1: AIR301 `airflow.providers.mysql.datasets.mysql.sanitize_u
152 |
153 | # airflow.providers.postgres
|
= help: Use `airflow.providers.mysql.assets.mysql.sanitize_uri` instead
= help: Use `sanitize_uri` from `airflow.providers.mysql.assets.mysql` instead.
AIR301_names.py:156:1: AIR301 `airflow.providers.postgres.datasets.postgres.sanitize_uri` is removed in Airflow 3.0
|
@@ -617,7 +617,7 @@ AIR301_names.py:156:1: AIR301 `airflow.providers.postgres.datasets.postgres.sani
157 |
158 | # airflow.providers.trino
|
= help: Use `airflow.providers.postgres.assets.postgres.sanitize_uri` instead
= help: Use `sanitize_uri` from `airflow.providers.postgres.assets.postgres` instead.
AIR301_names.py:161:1: AIR301 `airflow.providers.trino.datasets.trino.sanitize_uri` is removed in Airflow 3.0
|
@@ -628,7 +628,7 @@ AIR301_names.py:161:1: AIR301 `airflow.providers.trino.datasets.trino.sanitize_u
162 |
163 | # airflow.notifications.basenotifier
|
= help: Use `airflow.providers.trino.assets.trino.sanitize_uri` instead
= help: Use `sanitize_uri` from `airflow.providers.trino.assets.trino` instead.
AIR301_names.py:166:1: AIR301 `airflow.notifications.basenotifier.BaseNotifier` is removed in Airflow 3.0
|
@@ -639,7 +639,7 @@ AIR301_names.py:166:1: AIR301 `airflow.notifications.basenotifier.BaseNotifier`
167 |
168 | # airflow.auth.manager
|
= help: Use `airflow.sdk.bases.notifier.BaseNotifier` instead
= help: Use `BaseNotifier` from `airflow.sdk.bases.notifier` instead.
AIR301_names.py:171:1: AIR301 `airflow.auth.managers.base_auth_manager.BaseAuthManager` is removed in Airflow 3.0
|
@@ -648,4 +648,4 @@ AIR301_names.py:171:1: AIR301 `airflow.auth.managers.base_auth_manager.BaseAuthM
171 | BaseAuthManager()
| ^^^^^^^^^^^^^^^ AIR301
|
= help: Use `airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager` instead
= help: Use `BaseAuthManager` from `airflow.api_fastapi.auth.managers.base_auth_manager` instead.

View File

@@ -10,7 +10,7 @@ AIR301_names_fix.py:19:1: AIR301 [*] `airflow.api_connexion.security.requires_ac
20 |
21 | DatasetDetails()
|
= help: Use `airflow.api_fastapi.core_api.security.requires_access_asset` instead
= help: Use `requires_access_asset` from `airflow.api_fastapi.core_api.security` instead.
Safe fix
15 15 | from airflow.secrets.local_filesystm import load_connections
@@ -31,7 +31,7 @@ AIR301_names_fix.py:21:1: AIR301 [*] `airflow.auth.managers.models.resource_deta
21 | DatasetDetails()
| ^^^^^^^^^^^^^^ AIR301
|
= help: Use `airflow.api_fastapi.auth.managers.models.resource_details.AssetDetails` instead
= help: Use `AssetDetails` from `airflow.api_fastapi.auth.managers.models.resource_details` instead.
Safe fix
15 15 | from airflow.secrets.local_filesystm import load_connections
@@ -54,7 +54,7 @@ AIR301_names_fix.py:24:1: AIR301 [*] `airflow.datasets.manager.DatasetManager` i
25 | dataset_manager()
26 | resolve_dataset_manager()
|
= help: Use `airflow.assets.manager.AssetManager` instead
= help: Use `AssetManager` from `airflow.assets.manager` instead.
Safe fix
15 15 | from airflow.secrets.local_filesystm import load_connections
@@ -80,7 +80,7 @@ AIR301_names_fix.py:25:1: AIR301 [*] `airflow.datasets.manager.dataset_manager`
| ^^^^^^^^^^^^^^^ AIR301
26 | resolve_dataset_manager()
|
= help: Use `airflow.assets.manager.asset_manager` instead
= help: Use `asset_manager` from `airflow.assets.manager` instead.
Safe fix
15 15 | from airflow.secrets.local_filesystm import load_connections
@@ -109,7 +109,7 @@ AIR301_names_fix.py:26:1: AIR301 [*] `airflow.datasets.manager.resolve_dataset_m
27 |
28 | DatasetLineageInfo()
|
= help: Use `airflow.assets.manager.resolve_asset_manager` instead
= help: Use `resolve_asset_manager` from `airflow.assets.manager` instead.
Safe fix
15 15 | from airflow.secrets.local_filesystm import load_connections
@@ -138,7 +138,7 @@ AIR301_names_fix.py:28:1: AIR301 [*] `airflow.lineage.hook.DatasetLineageInfo` i
29 |
30 | AllowListValidator()
|
= help: Use `airflow.lineage.hook.AssetLineageInfo` instead
= help: Use `AssetLineageInfo` from `airflow.lineage.hook` instead.
Safe fix
10 10 | dataset_manager,
@@ -167,7 +167,7 @@ AIR301_names_fix.py:30:1: AIR301 [*] `airflow.metrics.validators.AllowListValida
| ^^^^^^^^^^^^^^^^^^ AIR301
31 | BlockListValidator()
|
= help: Use `airflow.metrics.validators.PatternAllowListValidator` instead
= help: Use `PatternAllowListValidator` from `airflow.metrics.validators` instead.
Safe fix
11 11 | resolve_dataset_manager,
@@ -196,7 +196,7 @@ AIR301_names_fix.py:31:1: AIR301 [*] `airflow.metrics.validators.BlockListValida
32 |
33 | load_connections()
|
= help: Use `airflow.metrics.validators.PatternBlockListValidator` instead
= help: Use `PatternBlockListValidator` from `airflow.metrics.validators` instead.
Safe fix
11 11 | resolve_dataset_manager,
@@ -226,7 +226,7 @@ AIR301_names_fix.py:35:1: AIR301 [*] `airflow.security.permissions.RESOURCE_DATA
36 |
37 | has_access_dataset()
|
= help: Use `airflow.security.permissions.RESOURCE_ASSET` instead
= help: Use `RESOURCE_ASSET` from `airflow.security.permissions` instead.
Safe fix
13 13 | from airflow.lineage.hook import DatasetLineageInfo
@@ -265,7 +265,7 @@ AIR301_names_fix.py:44:1: AIR301 [*] `airflow.listeners.spec.dataset.on_dataset_
| ^^^^^^^^^^^^^^^^^^ AIR301
45 | on_dataset_changed()
|
= help: Use `airflow.listeners.spec.asset.on_asset_created` instead
= help: Use `on_asset_created` from `airflow.listeners.spec.asset` instead.
Safe fix
40 40 | on_dataset_changed,
@@ -283,7 +283,7 @@ AIR301_names_fix.py:45:1: AIR301 [*] `airflow.listeners.spec.dataset.on_dataset_
45 | on_dataset_changed()
| ^^^^^^^^^^^^^^^^^^ AIR301
|
= help: Use `airflow.listeners.spec.asset.on_asset_changed` instead
= help: Use `on_asset_changed` from `airflow.listeners.spec.asset` instead.
Safe fix
40 40 | on_dataset_changed,

View File

@@ -10,7 +10,7 @@ AIR301_provider_names_fix.py:25:1: AIR301 [*] `airflow.providers.amazon.aws.auth
26 |
27 | s3_create_dataset()
|
= help: Use `airflow.providers.amazon.aws.auth_manager.avp.entities.AvpEntities.ASSET` instead
= help: Use `AvpEntities.ASSET` from `airflow.providers.amazon.aws.auth_manager.avp.entities` instead.
Safe fix
22 22 | translate_airflow_dataset,
@@ -30,7 +30,7 @@ AIR301_provider_names_fix.py:27:1: AIR301 [*] `airflow.providers.amazon.aws.data
| ^^^^^^^^^^^^^^^^^ AIR301
28 | s3_convert_dataset_to_openlineage()
|
= help: Use `airflow.providers.amazon.aws.assets.s3.create_asset` instead
= help: Use `create_asset` from `airflow.providers.amazon.aws.assets.s3` instead.
Safe fix
21 21 | DatasetInfo,
@@ -54,7 +54,7 @@ AIR301_provider_names_fix.py:28:1: AIR301 [*] `airflow.providers.amazon.aws.data
29 |
30 | io_create_dataset()
|
= help: Use `airflow.providers.amazon.aws.assets.s3.convert_asset_to_openlineage` instead
= help: Use `convert_asset_to_openlineage` from `airflow.providers.amazon.aws.assets.s3` instead.
Safe fix
21 21 | DatasetInfo,
@@ -79,7 +79,7 @@ AIR301_provider_names_fix.py:36:1: AIR301 [*] `airflow.providers.google.datasets
37 | # airflow.providers.google.datasets.gcs
38 | gcs_create_dataset()
|
= help: Use `airflow.providers.google.assets.bigquery.create_asset` instead
= help: Use `create_asset` from `airflow.providers.google.assets.bigquery` instead.
Safe fix
21 21 | DatasetInfo,
@@ -108,7 +108,7 @@ AIR301_provider_names_fix.py:38:1: AIR301 [*] `airflow.providers.google.datasets
39 | gcs_convert_dataset_to_openlineage()
40 | # airflow.providers.openlineage.utils.utils
|
= help: Use `airflow.providers.google.assets.gcs.create_asset` instead
= help: Use `create_asset` from `airflow.providers.google.assets.gcs` instead.
Safe fix
21 21 | DatasetInfo,
@@ -137,7 +137,7 @@ AIR301_provider_names_fix.py:39:1: AIR301 [*] `airflow.providers.google.datasets
40 | # airflow.providers.openlineage.utils.utils
41 | DatasetInfo()
|
= help: Use `airflow.providers.google.assets.gcs.convert_asset_to_openlineage` instead
= help: Use `convert_asset_to_openlineage` from `airflow.providers.google.assets.gcs` instead.
Safe fix
21 21 | DatasetInfo,
@@ -166,7 +166,7 @@ AIR301_provider_names_fix.py:41:1: AIR301 [*] `airflow.providers.openlineage.uti
42 | translate_airflow_dataset()
43 | #
|
= help: Use `airflow.providers.openlineage.utils.utils.AssetInfo` instead
= help: Use `AssetInfo` from `airflow.providers.openlineage.utils.utils` instead.
Safe fix
20 20 | from airflow.providers.openlineage.utils.utils import (
@@ -195,7 +195,7 @@ AIR301_provider_names_fix.py:42:1: AIR301 [*] `airflow.providers.openlineage.uti
43 | #
44 | # airflow.secrets.local_filesystem
|
= help: Use `airflow.providers.openlineage.utils.utils.translate_airflow_asset` instead
= help: Use `translate_airflow_asset` from `airflow.providers.openlineage.utils.utils` instead.
Safe fix
20 20 | from airflow.providers.openlineage.utils.utils import (

View File

@@ -9,7 +9,7 @@ AIR302_amazon.py:23:1: AIR302 `airflow.hooks.S3_hook.S3Hook` is moved into `amaz
| ^^^^^^ AIR302
24 | provide_bucket_name()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.hooks.s3.S3Hook` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `S3Hook` from `airflow.providers.amazon.aws.hooks.s3` instead.
AIR302_amazon.py:24:1: AIR302 `airflow.hooks.S3_hook.provide_bucket_name` is moved into `amazon` provider in Airflow 3.0;
|
@@ -19,7 +19,7 @@ AIR302_amazon.py:24:1: AIR302 `airflow.hooks.S3_hook.provide_bucket_name` is mov
25 |
26 | GCSToS3Operator()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.hooks.s3.provide_bucket_name` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `provide_bucket_name` from `airflow.providers.amazon.aws.hooks.s3` instead.
AIR302_amazon.py:26:1: AIR302 `airflow.operators.gcs_to_s3.GCSToS3Operator` is moved into `amazon` provider in Airflow 3.0;
|
@@ -30,7 +30,7 @@ AIR302_amazon.py:26:1: AIR302 `airflow.operators.gcs_to_s3.GCSToS3Operator` is m
27 |
28 | GoogleApiToS3Operator()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.transfers.gcs_to_s3.GCSToS3Operator` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `GCSToS3Operator` from `airflow.providers.amazon.aws.transfers.gcs_to_s3` instead.
AIR302_amazon.py:28:1: AIR302 `airflow.operators.google_api_to_s3_transfer.GoogleApiToS3Operator` is moved into `amazon` provider in Airflow 3.0;
|
@@ -40,7 +40,7 @@ AIR302_amazon.py:28:1: AIR302 `airflow.operators.google_api_to_s3_transfer.Googl
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
29 | GoogleApiToS3Transfer()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.transfers.google_api_to_s3.GoogleApiToS3Operator` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `GoogleApiToS3Operator` from `airflow.providers.amazon.aws.transfers.google_api_to_s3` instead.
AIR302_amazon.py:29:1: AIR302 `airflow.operators.google_api_to_s3_transfer.GoogleApiToS3Transfer` is moved into `amazon` provider in Airflow 3.0;
|
@@ -50,7 +50,7 @@ AIR302_amazon.py:29:1: AIR302 `airflow.operators.google_api_to_s3_transfer.Googl
30 |
31 | RedshiftToS3Operator()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.transfers.google_api_to_s3.GoogleApiToS3Operator` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `GoogleApiToS3Operator` from `airflow.providers.amazon.aws.transfers.google_api_to_s3` instead.
AIR302_amazon.py:31:1: AIR302 `airflow.operators.redshift_to_s3_operator.RedshiftToS3Operator` is moved into `amazon` provider in Airflow 3.0;
|
@@ -60,7 +60,7 @@ AIR302_amazon.py:31:1: AIR302 `airflow.operators.redshift_to_s3_operator.Redshif
| ^^^^^^^^^^^^^^^^^^^^ AIR302
32 | RedshiftToS3Transfer()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.transfers.redshift_to_s3.RedshiftToS3Operator` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `RedshiftToS3Operator` from `airflow.providers.amazon.aws.transfers.redshift_to_s3` instead.
AIR302_amazon.py:32:1: AIR302 `airflow.operators.redshift_to_s3_operator.RedshiftToS3Transfer` is moved into `amazon` provider in Airflow 3.0;
|
@@ -70,7 +70,7 @@ AIR302_amazon.py:32:1: AIR302 `airflow.operators.redshift_to_s3_operator.Redshif
33 |
34 | S3FileTransformOperator()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.transfers.redshift_to_s3.RedshiftToS3Operator` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `RedshiftToS3Operator` from `airflow.providers.amazon.aws.transfers.redshift_to_s3` instead.
AIR302_amazon.py:34:1: AIR302 `airflow.operators.s3_file_transform_operator.S3FileTransformOperator` is moved into `amazon` provider in Airflow 3.0;
|
@@ -81,7 +81,7 @@ AIR302_amazon.py:34:1: AIR302 `airflow.operators.s3_file_transform_operator.S3Fi
35 |
36 | S3ToRedshiftOperator()
|
= help: Install `apache-airflow-providers-amazon>=3.0.0` and use `airflow.providers.amazon.aws.operators.s3.S3FileTransformOperator` instead.
= help: Install `apache-airflow-providers-amazon>=3.0.0` and use `S3FileTransformOperator` from `airflow.providers.amazon.aws.operators.s3` instead.
AIR302_amazon.py:36:1: AIR302 `airflow.operators.s3_to_redshift_operator.S3ToRedshiftOperator` is moved into `amazon` provider in Airflow 3.0;
|
@@ -91,7 +91,7 @@ AIR302_amazon.py:36:1: AIR302 `airflow.operators.s3_to_redshift_operator.S3ToRed
| ^^^^^^^^^^^^^^^^^^^^ AIR302
37 | S3ToRedshiftTransfer()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.transfers.s3_to_redshift.S3ToRedshiftOperator` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `S3ToRedshiftOperator` from `airflow.providers.amazon.aws.transfers.s3_to_redshift` instead.
AIR302_amazon.py:37:1: AIR302 `airflow.operators.s3_to_redshift_operator.S3ToRedshiftTransfer` is moved into `amazon` provider in Airflow 3.0;
|
@@ -101,7 +101,7 @@ AIR302_amazon.py:37:1: AIR302 `airflow.operators.s3_to_redshift_operator.S3ToRed
38 |
39 | S3KeySensor()
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.transfers.s3_to_redshift.S3ToRedshiftOperator` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `S3ToRedshiftOperator` from `airflow.providers.amazon.aws.transfers.s3_to_redshift` instead.
AIR302_amazon.py:39:1: AIR302 `airflow.sensors.s3_key_sensor.S3KeySensor` is moved into `amazon` provider in Airflow 3.0;
|
@@ -110,4 +110,4 @@ AIR302_amazon.py:39:1: AIR302 `airflow.sensors.s3_key_sensor.S3KeySensor` is mov
39 | S3KeySensor()
| ^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `airflow.providers.amazon.aws.sensors.s3.S3KeySensor` instead.
= help: Install `apache-airflow-providers-amazon>=1.0.0` and use `S3KeySensor` from `airflow.providers.amazon.aws.sensors.s3` instead.

View File

@@ -10,7 +10,7 @@ AIR302_celery.py:9:1: AIR302 `airflow.config_templates.default_celery.DEFAULT_CE
10 |
11 | app
|
= help: Install `apache-airflow-providers-celery>=3.3.0` and use `airflow.providers.celery.executors.default_celery.DEFAULT_CELERY_CONFIG` instead.
= help: Install `apache-airflow-providers-celery>=3.3.0` and use `DEFAULT_CELERY_CONFIG` from `airflow.providers.celery.executors.default_celery` instead.
AIR302_celery.py:11:1: AIR302 `airflow.executors.celery_executor.app` is moved into `celery` provider in Airflow 3.0;
|
@@ -20,7 +20,7 @@ AIR302_celery.py:11:1: AIR302 `airflow.executors.celery_executor.app` is moved i
| ^^^ AIR302
12 | CeleryExecutor()
|
= help: Install `apache-airflow-providers-celery>=3.3.0` and use `airflow.providers.celery.executors.celery_executor_utils.app` instead.
= help: Install `apache-airflow-providers-celery>=3.3.0` and use `app` from `airflow.providers.celery.executors.celery_executor_utils` instead.
AIR302_celery.py:12:1: AIR302 `airflow.executors.celery_executor.CeleryExecutor` is moved into `celery` provider in Airflow 3.0;
|
@@ -28,4 +28,4 @@ AIR302_celery.py:12:1: AIR302 `airflow.executors.celery_executor.CeleryExecutor`
12 | CeleryExecutor()
| ^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-celery>=3.3.0` and use `airflow.providers.celery.executors.celery_executor.CeleryExecutor` instead.
= help: Install `apache-airflow-providers-celery>=3.3.0` and use `CeleryExecutor` from `airflow.providers.celery.executors.celery_executor` instead.

View File

@@ -10,7 +10,7 @@ AIR302_common_sql.py:10:1: AIR302 `airflow.hooks.dbapi.ConnectorProtocol` is mov
11 | DbApiHook()
12 | SQLCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `airflow.providers.common.sql.hooks.sql.ConnectorProtocol` instead.
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `ConnectorProtocol` from `airflow.providers.common.sql.hooks.sql` instead.
AIR302_common_sql.py:11:1: AIR302 `airflow.hooks.dbapi_hook.DbApiHook` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -19,7 +19,7 @@ AIR302_common_sql.py:11:1: AIR302 `airflow.hooks.dbapi_hook.DbApiHook` is moved
| ^^^^^^^^^ AIR302
12 | SQLCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `airflow.providers.common.sql.hooks.sql.DbApiHook` instead.
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `DbApiHook` from `airflow.providers.common.sql.hooks.sql` instead.
AIR302_common_sql.py:12:1: AIR302 `airflow.operators.check_operator.SQLCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -28,7 +28,7 @@ AIR302_common_sql.py:12:1: AIR302 `airflow.operators.check_operator.SQLCheckOper
12 | SQLCheckOperator()
| ^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:18:1: AIR302 `airflow.operators.sql.SQLCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -38,7 +38,7 @@ AIR302_common_sql.py:18:1: AIR302 `airflow.operators.sql.SQLCheckOperator` is mo
| ^^^^^^^^^^^^^^^^ AIR302
19 | CheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:19:1: AIR302 `airflow.operators.check_operator.CheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -46,7 +46,7 @@ AIR302_common_sql.py:19:1: AIR302 `airflow.operators.check_operator.CheckOperato
19 | CheckOperator()
| ^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:24:1: AIR302 `airflow.operators.druid_check_operator.CheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -55,7 +55,7 @@ AIR302_common_sql.py:24:1: AIR302 `airflow.operators.druid_check_operator.CheckO
24 | CheckOperator()
| ^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:29:1: AIR302 `airflow.operators.presto_check_operator.CheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -64,7 +64,7 @@ AIR302_common_sql.py:29:1: AIR302 `airflow.operators.presto_check_operator.Check
29 | CheckOperator()
| ^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:39:1: AIR302 `airflow.operators.druid_check_operator.DruidCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -75,7 +75,7 @@ AIR302_common_sql.py:39:1: AIR302 `airflow.operators.druid_check_operator.DruidC
40 | PrestoCheckOperator()
41 | IntervalCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:40:1: AIR302 `airflow.operators.presto_check_operator.PrestoCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -85,7 +85,7 @@ AIR302_common_sql.py:40:1: AIR302 `airflow.operators.presto_check_operator.Prest
41 | IntervalCheckOperator()
42 | SQLIntervalCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:41:1: AIR302 `airflow.operators.check_operator.IntervalCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -95,7 +95,7 @@ AIR302_common_sql.py:41:1: AIR302 `airflow.operators.check_operator.IntervalChec
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
42 | SQLIntervalCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLIntervalCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLIntervalCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:42:1: AIR302 `airflow.operators.check_operator.SQLIntervalCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -104,7 +104,7 @@ AIR302_common_sql.py:42:1: AIR302 `airflow.operators.check_operator.SQLIntervalC
42 | SQLIntervalCheckOperator()
| ^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLIntervalCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLIntervalCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:51:1: AIR302 `airflow.operators.presto_check_operator.IntervalCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -115,7 +115,7 @@ AIR302_common_sql.py:51:1: AIR302 `airflow.operators.presto_check_operator.Inter
52 | SQLIntervalCheckOperator()
53 | PrestoIntervalCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLIntervalCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLIntervalCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:52:1: AIR302 `airflow.operators.sql.SQLIntervalCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -124,7 +124,7 @@ AIR302_common_sql.py:52:1: AIR302 `airflow.operators.sql.SQLIntervalCheckOperato
| ^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
53 | PrestoIntervalCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLIntervalCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLIntervalCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:53:1: AIR302 `airflow.operators.presto_check_operator.PrestoIntervalCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -133,7 +133,7 @@ AIR302_common_sql.py:53:1: AIR302 `airflow.operators.presto_check_operator.Prest
53 | PrestoIntervalCheckOperator()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLIntervalCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLIntervalCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:61:1: AIR302 `airflow.operators.check_operator.SQLThresholdCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -143,7 +143,7 @@ AIR302_common_sql.py:61:1: AIR302 `airflow.operators.check_operator.SQLThreshold
| ^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
62 | ThresholdCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLThresholdCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLThresholdCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:62:1: AIR302 `airflow.operators.check_operator.ThresholdCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -151,7 +151,7 @@ AIR302_common_sql.py:62:1: AIR302 `airflow.operators.check_operator.ThresholdChe
62 | ThresholdCheckOperator()
| ^^^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLThresholdCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLThresholdCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:67:1: AIR302 `airflow.operators.sql.SQLThresholdCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -160,7 +160,7 @@ AIR302_common_sql.py:67:1: AIR302 `airflow.operators.sql.SQLThresholdCheckOperat
67 | SQLThresholdCheckOperator()
| ^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLThresholdCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLThresholdCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:75:1: AIR302 `airflow.operators.check_operator.SQLValueCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -170,7 +170,7 @@ AIR302_common_sql.py:75:1: AIR302 `airflow.operators.check_operator.SQLValueChec
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
76 | ValueCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLValueCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLValueCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:76:1: AIR302 `airflow.operators.check_operator.ValueCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -178,7 +178,7 @@ AIR302_common_sql.py:76:1: AIR302 `airflow.operators.check_operator.ValueCheckOp
76 | ValueCheckOperator()
| ^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLValueCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLValueCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:85:1: AIR302 `airflow.operators.sql.SQLValueCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -189,7 +189,7 @@ AIR302_common_sql.py:85:1: AIR302 `airflow.operators.sql.SQLValueCheckOperator`
86 | ValueCheckOperator()
87 | PrestoValueCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLValueCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLValueCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:86:1: AIR302 `airflow.operators.presto_check_operator.ValueCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -198,7 +198,7 @@ AIR302_common_sql.py:86:1: AIR302 `airflow.operators.presto_check_operator.Value
| ^^^^^^^^^^^^^^^^^^ AIR302
87 | PrestoValueCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLValueCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLValueCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:87:1: AIR302 `airflow.operators.presto_check_operator.PrestoValueCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -207,7 +207,7 @@ AIR302_common_sql.py:87:1: AIR302 `airflow.operators.presto_check_operator.Prest
87 | PrestoValueCheckOperator()
| ^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLValueCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLValueCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:99:1: AIR302 `airflow.operators.sql.BaseSQLOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -218,7 +218,7 @@ AIR302_common_sql.py:99:1: AIR302 `airflow.operators.sql.BaseSQLOperator` is mov
100 | BranchSQLOperator()
101 | SQLTableCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.BaseSQLOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `BaseSQLOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:100:1: AIR302 `airflow.operators.sql.BranchSQLOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -228,7 +228,7 @@ AIR302_common_sql.py:100:1: AIR302 `airflow.operators.sql.BranchSQLOperator` is
101 | SQLTableCheckOperator()
102 | SQLColumnCheckOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.BranchSQLOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `BranchSQLOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:101:1: AIR302 `airflow.operators.sql.SQLTableCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -239,7 +239,7 @@ AIR302_common_sql.py:101:1: AIR302 `airflow.operators.sql.SQLTableCheckOperator`
102 | SQLColumnCheckOperator()
103 | _convert_to_float_if_possible()
|
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `airflow.providers.common.sql.operators.sql.SQLTableCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.1.0` and use `SQLTableCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:102:1: AIR302 `airflow.operators.sql.SQLColumnCheckOperator` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -250,7 +250,7 @@ AIR302_common_sql.py:102:1: AIR302 `airflow.operators.sql.SQLColumnCheckOperator
103 | _convert_to_float_if_possible()
104 | parse_boolean()
|
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `airflow.providers.common.sql.operators.sql.SQLColumnCheckOperator` instead.
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `SQLColumnCheckOperator` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:103:1: AIR302 `airflow.operators.sql._convert_to_float_if_possible` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -260,7 +260,7 @@ AIR302_common_sql.py:103:1: AIR302 `airflow.operators.sql._convert_to_float_if_p
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
104 | parse_boolean()
|
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `airflow.providers.common.sql.operators.sql._convert_to_float_if_possible` instead.
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `_convert_to_float_if_possible` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:104:1: AIR302 `airflow.operators.sql.parse_boolean` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -269,7 +269,7 @@ AIR302_common_sql.py:104:1: AIR302 `airflow.operators.sql.parse_boolean` is move
104 | parse_boolean()
| ^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `airflow.providers.common.sql.operators.sql.parse_boolean` instead.
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `parse_boolean` from `airflow.providers.common.sql.operators.sql` instead.
AIR302_common_sql.py:109:1: AIR302 `airflow.sensors.sql.SqlSensor` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -278,7 +278,7 @@ AIR302_common_sql.py:109:1: AIR302 `airflow.sensors.sql.SqlSensor` is moved into
109 | SqlSensor()
| ^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `airflow.providers.common.sql.sensors.sql.SqlSensor` instead.
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `SqlSensor` from `airflow.providers.common.sql.sensors.sql` instead.
AIR302_common_sql.py:114:1: AIR302 `airflow.sensors.sql_sensor.SqlSensor` is moved into `common-sql` provider in Airflow 3.0;
|
@@ -287,147 +287,4 @@ AIR302_common_sql.py:114:1: AIR302 `airflow.sensors.sql_sensor.SqlSensor` is mov
114 | SqlSensor()
| ^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `airflow.providers.common.sql.sensors.sql.SqlSensor` instead.
AIR302_common_sql.py:124:1: AIR302 [*] `airflow.operators.jdbc_operator.JdbcOperator` is moved into `common-sql` provider in Airflow 3.0;
|
122 | from airflow.operators.sqlite_operator import SqliteOperator
123 |
124 | JdbcOperator()
| ^^^^^^^^^^^^ AIR302
125 | MsSqlOperator()
126 | MySqlOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.3.0` and use `airflow.providers.common.sql.operators.sql.SQLExecuteQueryOperator` instead.
Safe fix
120 120 | from airflow.operators.oracle_operator import OracleOperator
121 121 | from airflow.operators.postgres_operator import PostgresOperator
122 122 | from airflow.operators.sqlite_operator import SqliteOperator
123 |+from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
123 124 |
124 |-JdbcOperator()
125 |+SQLExecuteQueryOperator()
125 126 | MsSqlOperator()
126 127 | MySqlOperator()
127 128 | OracleOperator()
AIR302_common_sql.py:125:1: AIR302 [*] `airflow.operators.mssql_operator.MsSqlOperator` is moved into `common-sql` provider in Airflow 3.0;
|
124 | JdbcOperator()
125 | MsSqlOperator()
| ^^^^^^^^^^^^^ AIR302
126 | MySqlOperator()
127 | OracleOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.3.0` and use `airflow.providers.common.sql.operators.sql.SQLExecuteQueryOperator` instead.
Safe fix
120 120 | from airflow.operators.oracle_operator import OracleOperator
121 121 | from airflow.operators.postgres_operator import PostgresOperator
122 122 | from airflow.operators.sqlite_operator import SqliteOperator
123 |+from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
123 124 |
124 125 | JdbcOperator()
125 |-MsSqlOperator()
126 |+SQLExecuteQueryOperator()
126 127 | MySqlOperator()
127 128 | OracleOperator()
128 129 | PostgresOperator()
AIR302_common_sql.py:126:1: AIR302 [*] `airflow.operators.mysql_operator.MySqlOperator` is moved into `common-sql` provider in Airflow 3.0;
|
124 | JdbcOperator()
125 | MsSqlOperator()
126 | MySqlOperator()
| ^^^^^^^^^^^^^ AIR302
127 | OracleOperator()
128 | PostgresOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.3.0` and use `airflow.providers.common.sql.operators.sql.SQLExecuteQueryOperator` instead.
Safe fix
120 120 | from airflow.operators.oracle_operator import OracleOperator
121 121 | from airflow.operators.postgres_operator import PostgresOperator
122 122 | from airflow.operators.sqlite_operator import SqliteOperator
123 |+from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
123 124 |
124 125 | JdbcOperator()
125 126 | MsSqlOperator()
126 |-MySqlOperator()
127 |+SQLExecuteQueryOperator()
127 128 | OracleOperator()
128 129 | PostgresOperator()
129 130 | SqliteOperator()
AIR302_common_sql.py:127:1: AIR302 [*] `airflow.operators.oracle_operator.OracleOperator` is moved into `common-sql` provider in Airflow 3.0;
|
125 | MsSqlOperator()
126 | MySqlOperator()
127 | OracleOperator()
| ^^^^^^^^^^^^^^ AIR302
128 | PostgresOperator()
129 | SqliteOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.3.0` and use `airflow.providers.common.sql.operators.sql.SQLExecuteQueryOperator` instead.
Safe fix
120 120 | from airflow.operators.oracle_operator import OracleOperator
121 121 | from airflow.operators.postgres_operator import PostgresOperator
122 122 | from airflow.operators.sqlite_operator import SqliteOperator
123 |+from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
123 124 |
124 125 | JdbcOperator()
125 126 | MsSqlOperator()
126 127 | MySqlOperator()
127 |-OracleOperator()
128 |+SQLExecuteQueryOperator()
128 129 | PostgresOperator()
129 130 | SqliteOperator()
AIR302_common_sql.py:128:1: AIR302 [*] `airflow.operators.postgres_operator.PostgresOperator` is moved into `common-sql` provider in Airflow 3.0;
|
126 | MySqlOperator()
127 | OracleOperator()
128 | PostgresOperator()
| ^^^^^^^^^^^^^^^^ AIR302
129 | SqliteOperator()
|
= help: Install `apache-airflow-providers-common-sql>=1.3.0` and use `airflow.providers.common.sql.operators.sql.SQLExecuteQueryOperator` instead.
Safe fix
120 120 | from airflow.operators.oracle_operator import OracleOperator
121 121 | from airflow.operators.postgres_operator import PostgresOperator
122 122 | from airflow.operators.sqlite_operator import SqliteOperator
123 |+from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
123 124 |
124 125 | JdbcOperator()
125 126 | MsSqlOperator()
126 127 | MySqlOperator()
127 128 | OracleOperator()
128 |-PostgresOperator()
129 |+SQLExecuteQueryOperator()
129 130 | SqliteOperator()
AIR302_common_sql.py:129:1: AIR302 [*] `airflow.operators.sqlite_operator.SqliteOperator` is moved into `common-sql` provider in Airflow 3.0;
|
127 | OracleOperator()
128 | PostgresOperator()
129 | SqliteOperator()
| ^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-common-sql>=1.3.0` and use `airflow.providers.common.sql.operators.sql.SQLExecuteQueryOperator` instead.
Safe fix
120 120 | from airflow.operators.oracle_operator import OracleOperator
121 121 | from airflow.operators.postgres_operator import PostgresOperator
122 122 | from airflow.operators.sqlite_operator import SqliteOperator
123 |+from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
123 124 |
124 125 | JdbcOperator()
125 126 | MsSqlOperator()
126 127 | MySqlOperator()
127 128 | OracleOperator()
128 129 | PostgresOperator()
129 |-SqliteOperator()
130 |+SQLExecuteQueryOperator()
= help: Install `apache-airflow-providers-common-sql>=1.0.0` and use `SqlSensor` from `airflow.providers.common.sql.sensors.sql` instead.

View File

@@ -8,4 +8,4 @@ AIR302_daskexecutor.py:5:1: AIR302 `airflow.executors.dask_executor.DaskExecutor
5 | DaskExecutor()
| ^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-daskexecutor>=1.0.0` and use `airflow.providers.daskexecutor.executors.dask_executor.DaskExecutor` instead.
= help: Install `apache-airflow-providers-daskexecutor>=1.0.0` and use `DaskExecutor` from `airflow.providers.daskexecutor.executors.dask_executor` instead.

View File

@@ -9,7 +9,7 @@ AIR302_druid.py:12:1: AIR302 `airflow.hooks.druid_hook.DruidDbApiHook` is moved
| ^^^^^^^^^^^^^^ AIR302
13 | DruidHook()
|
= help: Install `apache-airflow-providers-apache-druid>=1.0.0` and use `airflow.providers.apache.druid.hooks.druid.DruidDbApiHook` instead.
= help: Install `apache-airflow-providers-apache-druid>=1.0.0` and use `DruidDbApiHook` from `airflow.providers.apache.druid.hooks.druid` instead.
AIR302_druid.py:13:1: AIR302 `airflow.hooks.druid_hook.DruidHook` is moved into `apache-druid` provider in Airflow 3.0;
|
@@ -19,7 +19,7 @@ AIR302_druid.py:13:1: AIR302 `airflow.hooks.druid_hook.DruidHook` is moved into
14 |
15 | HiveToDruidOperator()
|
= help: Install `apache-airflow-providers-apache-druid>=1.0.0` and use `airflow.providers.apache.druid.hooks.druid.DruidHook` instead.
= help: Install `apache-airflow-providers-apache-druid>=1.0.0` and use `DruidHook` from `airflow.providers.apache.druid.hooks.druid` instead.
AIR302_druid.py:15:1: AIR302 `airflow.operators.hive_to_druid.HiveToDruidOperator` is moved into `apache-druid` provider in Airflow 3.0;
|
@@ -29,7 +29,7 @@ AIR302_druid.py:15:1: AIR302 `airflow.operators.hive_to_druid.HiveToDruidOperato
| ^^^^^^^^^^^^^^^^^^^ AIR302
16 | HiveToDruidTransfer()
|
= help: Install `apache-airflow-providers-apache-druid>=1.0.0` and use `airflow.providers.apache.druid.transfers.hive_to_druid.HiveToDruidOperator` instead.
= help: Install `apache-airflow-providers-apache-druid>=1.0.0` and use `HiveToDruidOperator` from `airflow.providers.apache.druid.transfers.hive_to_druid` instead.
AIR302_druid.py:16:1: AIR302 `airflow.operators.hive_to_druid.HiveToDruidTransfer` is moved into `apache-druid` provider in Airflow 3.0;
|
@@ -37,4 +37,4 @@ AIR302_druid.py:16:1: AIR302 `airflow.operators.hive_to_druid.HiveToDruidTransfe
16 | HiveToDruidTransfer()
| ^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-apache-druid>=1.0.0` and use `airflow.providers.apache.druid.transfers.hive_to_druid.HiveToDruidOperator` instead.
= help: Install `apache-airflow-providers-apache-druid>=1.0.0` and use `HiveToDruidOperator` from `airflow.providers.apache.druid.transfers.hive_to_druid` instead.

View File

@@ -10,7 +10,7 @@ AIR302_fab.py:10:1: AIR302 `airflow.api.auth.backend.basic_auth.CLIENT_AUTH` is
11 | init_app()
12 | auth_current_user()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth.CLIENT_AUTH` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `CLIENT_AUTH` from `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth` instead.
AIR302_fab.py:11:1: AIR302 `airflow.api.auth.backend.basic_auth.init_app` is moved into `fab` provider in Airflow 3.0;
|
@@ -20,7 +20,7 @@ AIR302_fab.py:11:1: AIR302 `airflow.api.auth.backend.basic_auth.init_app` is mov
12 | auth_current_user()
13 | requires_authentication()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth.init_app` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `init_app` from `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth` instead.
AIR302_fab.py:12:1: AIR302 `airflow.api.auth.backend.basic_auth.auth_current_user` is moved into `fab` provider in Airflow 3.0;
|
@@ -30,7 +30,7 @@ AIR302_fab.py:12:1: AIR302 `airflow.api.auth.backend.basic_auth.auth_current_use
| ^^^^^^^^^^^^^^^^^ AIR302
13 | requires_authentication()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth.auth_current_user` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `auth_current_user` from `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth` instead.
AIR302_fab.py:13:1: AIR302 `airflow.api.auth.backend.basic_auth.requires_authentication` is moved into `fab` provider in Airflow 3.0;
|
@@ -41,7 +41,7 @@ AIR302_fab.py:13:1: AIR302 `airflow.api.auth.backend.basic_auth.requires_authent
14 |
15 | from airflow.api.auth.backend.kerberos_auth import (
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth.requires_authentication` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `requires_authentication` from `airflow.providers.fab.auth_manager.api.auth.backend.basic_auth` instead.
AIR302_fab.py:23:1: AIR302 `airflow.api.auth.backend.kerberos_auth.log` is moved into `fab` provider in Airflow 3.0;
|
@@ -52,7 +52,7 @@ AIR302_fab.py:23:1: AIR302 `airflow.api.auth.backend.kerberos_auth.log` is moved
24 | CLIENT_AUTH
25 | find_user()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.log` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `log` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:24:1: AIR302 `airflow.api.auth.backend.kerberos_auth.CLIENT_AUTH` is moved into `fab` provider in Airflow 3.0;
|
@@ -62,7 +62,7 @@ AIR302_fab.py:24:1: AIR302 `airflow.api.auth.backend.kerberos_auth.CLIENT_AUTH`
25 | find_user()
26 | init_app()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.CLIENT_AUTH` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `CLIENT_AUTH` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:25:1: AIR302 `airflow.api.auth.backend.kerberos_auth.find_user` is moved into `fab` provider in Airflow 3.0;
|
@@ -73,7 +73,7 @@ AIR302_fab.py:25:1: AIR302 `airflow.api.auth.backend.kerberos_auth.find_user` is
26 | init_app()
27 | requires_authentication()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.find_user` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `find_user` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:26:1: AIR302 `airflow.api.auth.backend.kerberos_auth.init_app` is moved into `fab` provider in Airflow 3.0;
|
@@ -83,7 +83,7 @@ AIR302_fab.py:26:1: AIR302 `airflow.api.auth.backend.kerberos_auth.init_app` is
| ^^^^^^^^ AIR302
27 | requires_authentication()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.init_app` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `init_app` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:27:1: AIR302 `airflow.api.auth.backend.kerberos_auth.requires_authentication` is moved into `fab` provider in Airflow 3.0;
|
@@ -94,7 +94,7 @@ AIR302_fab.py:27:1: AIR302 `airflow.api.auth.backend.kerberos_auth.requires_auth
28 |
29 | from airflow.auth.managers.fab.api.auth.backend.kerberos_auth import (
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.requires_authentication` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `requires_authentication` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:37:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_auth.log` is moved into `fab` provider in Airflow 3.0;
|
@@ -105,7 +105,7 @@ AIR302_fab.py:37:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_
38 | CLIENT_AUTH
39 | find_user()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.log` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `log` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:38:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_auth.CLIENT_AUTH` is moved into `fab` provider in Airflow 3.0;
|
@@ -115,7 +115,7 @@ AIR302_fab.py:38:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_
39 | find_user()
40 | init_app()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.CLIENT_AUTH` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `CLIENT_AUTH` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:39:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_auth.find_user` is moved into `fab` provider in Airflow 3.0;
|
@@ -126,7 +126,7 @@ AIR302_fab.py:39:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_
40 | init_app()
41 | requires_authentication()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.find_user` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `find_user` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:40:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_auth.init_app` is moved into `fab` provider in Airflow 3.0;
|
@@ -136,7 +136,7 @@ AIR302_fab.py:40:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_
| ^^^^^^^^ AIR302
41 | requires_authentication()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.init_app` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `init_app` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:41:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_auth.requires_authentication` is moved into `fab` provider in Airflow 3.0;
|
@@ -147,7 +147,7 @@ AIR302_fab.py:41:1: AIR302 `airflow.auth.managers.fab.api.auth.backend.kerberos_
42 |
43 | from airflow.auth.managers.fab.fab_auth_manager import FabAuthManager
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth.requires_authentication` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `requires_authentication` from `airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth` instead.
AIR302_fab.py:49:1: AIR302 `airflow.auth.managers.fab.fab_auth_manager.FabAuthManager` is moved into `fab` provider in Airflow 3.0;
|
@@ -158,7 +158,7 @@ AIR302_fab.py:49:1: AIR302 `airflow.auth.managers.fab.fab_auth_manager.FabAuthMa
50 | MAX_NUM_DATABASE_USER_SESSIONS
51 | FabAirflowSecurityManagerOverride()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `FabAuthManager` from `airflow.providers.fab.auth_manager.fab_auth_manager` instead.
AIR302_fab.py:50:1: AIR302 `airflow.auth.managers.fab.security_manager.override.MAX_NUM_DATABASE_USER_SESSIONS` is moved into `fab` provider in Airflow 3.0;
|
@@ -167,7 +167,7 @@ AIR302_fab.py:50:1: AIR302 `airflow.auth.managers.fab.security_manager.override.
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
51 | FabAirflowSecurityManagerOverride()
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.security_manager.override.MAX_NUM_DATABASE_USER_SESSIONS` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `MAX_NUM_DATABASE_USER_SESSIONS` from `airflow.providers.fab.auth_manager.security_manager.override` instead.
AIR302_fab.py:51:1: AIR302 `airflow.auth.managers.fab.security_manager.override.FabAirflowSecurityManagerOverride` is moved into `fab` provider in Airflow 3.0;
|
@@ -178,7 +178,7 @@ AIR302_fab.py:51:1: AIR302 `airflow.auth.managers.fab.security_manager.override.
52 |
53 | from airflow.www.security import FabAirflowSecurityManagerOverride
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.security_manager.override.FabAirflowSecurityManagerOverride` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `FabAirflowSecurityManagerOverride` from `airflow.providers.fab.auth_manager.security_manager.override` instead.
AIR302_fab.py:55:1: AIR302 `airflow.www.security.FabAirflowSecurityManagerOverride` is moved into `fab` provider in Airflow 3.0;
|
@@ -187,4 +187,4 @@ AIR302_fab.py:55:1: AIR302 `airflow.www.security.FabAirflowSecurityManagerOverri
55 | FabAirflowSecurityManagerOverride()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `airflow.providers.fab.auth_manager.security_manager.override.FabAirflowSecurityManagerOverride` instead.
= help: Install `apache-airflow-providers-fab>=1.0.0` and use `FabAirflowSecurityManagerOverride` from `airflow.providers.fab.auth_manager.security_manager.override` instead.

View File

@@ -9,7 +9,7 @@ AIR302_hdfs.py:6:1: AIR302 `airflow.hooks.webhdfs_hook.WebHDFSHook` is moved int
| ^^^^^^^^^^^ AIR302
7 | WebHdfsSensor()
|
= help: Install `apache-airflow-providers-apache-hdfs>=1.0.0` and use `airflow.providers.apache.hdfs.hooks.webhdfs.WebHDFSHook` instead.
= help: Install `apache-airflow-providers-apache-hdfs>=1.0.0` and use `WebHDFSHook` from `airflow.providers.apache.hdfs.hooks.webhdfs` instead.
AIR302_hdfs.py:7:1: AIR302 `airflow.sensors.web_hdfs_sensor.WebHdfsSensor` is moved into `apache-hdfs` provider in Airflow 3.0;
|
@@ -17,4 +17,4 @@ AIR302_hdfs.py:7:1: AIR302 `airflow.sensors.web_hdfs_sensor.WebHdfsSensor` is mo
7 | WebHdfsSensor()
| ^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-apache-hdfs>=1.0.0` and use `airflow.providers.apache.hdfs.sensors.web_hdfs.WebHdfsSensor` instead.
= help: Install `apache-airflow-providers-apache-hdfs>=1.0.0` and use `WebHdfsSensor` from `airflow.providers.apache.hdfs.sensors.web_hdfs` instead.

View File

@@ -9,7 +9,7 @@ AIR302_hive.py:36:1: AIR302 `airflow.macros.hive.closest_ds_partition` is moved
| ^^^^^^^^^^^^^^^^^^^^ AIR302
37 | max_partition()
|
= help: Install `apache-airflow-providers-apache-hive>=5.1.0` and use `airflow.providers.apache.hive.macros.hive.closest_ds_partition` instead.
= help: Install `apache-airflow-providers-apache-hive>=5.1.0` and use `closest_ds_partition` from `airflow.providers.apache.hive.macros.hive` instead.
AIR302_hive.py:37:1: AIR302 `airflow.macros.hive.max_partition` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -19,7 +19,7 @@ AIR302_hive.py:37:1: AIR302 `airflow.macros.hive.max_partition` is moved into `a
38 |
39 | HiveCliHook()
|
= help: Install `apache-airflow-providers-apache-hive>=5.1.0` and use `airflow.providers.apache.hive.macros.hive.max_partition` instead.
= help: Install `apache-airflow-providers-apache-hive>=5.1.0` and use `max_partition` from `airflow.providers.apache.hive.macros.hive` instead.
AIR302_hive.py:39:1: AIR302 `airflow.hooks.hive_hooks.HiveCliHook` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -30,7 +30,7 @@ AIR302_hive.py:39:1: AIR302 `airflow.hooks.hive_hooks.HiveCliHook` is moved into
40 | HiveMetastoreHook()
41 | HiveServer2Hook()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.hooks.hive.HiveCliHook` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HiveCliHook` from `airflow.providers.apache.hive.hooks.hive` instead.
AIR302_hive.py:40:1: AIR302 `airflow.hooks.hive_hooks.HiveMetastoreHook` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -40,7 +40,7 @@ AIR302_hive.py:40:1: AIR302 `airflow.hooks.hive_hooks.HiveMetastoreHook` is move
41 | HiveServer2Hook()
42 | HIVE_QUEUE_PRIORITIES
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.hooks.hive.HiveMetastoreHook` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HiveMetastoreHook` from `airflow.providers.apache.hive.hooks.hive` instead.
AIR302_hive.py:41:1: AIR302 `airflow.hooks.hive_hooks.HiveServer2Hook` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -50,7 +50,7 @@ AIR302_hive.py:41:1: AIR302 `airflow.hooks.hive_hooks.HiveServer2Hook` is moved
| ^^^^^^^^^^^^^^^ AIR302
42 | HIVE_QUEUE_PRIORITIES
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.hooks.hive.HiveServer2Hook` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HiveServer2Hook` from `airflow.providers.apache.hive.hooks.hive` instead.
AIR302_hive.py:42:1: AIR302 `airflow.hooks.hive_hooks.HIVE_QUEUE_PRIORITIES` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -61,7 +61,7 @@ AIR302_hive.py:42:1: AIR302 `airflow.hooks.hive_hooks.HIVE_QUEUE_PRIORITIES` is
43 |
44 | HiveOperator()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.hooks.hive.HIVE_QUEUE_PRIORITIES` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HIVE_QUEUE_PRIORITIES` from `airflow.providers.apache.hive.hooks.hive` instead.
AIR302_hive.py:44:1: AIR302 `airflow.operators.hive_operator.HiveOperator` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -72,7 +72,7 @@ AIR302_hive.py:44:1: AIR302 `airflow.operators.hive_operator.HiveOperator` is mo
45 |
46 | HiveStatsCollectionOperator()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.operators.hive.HiveOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HiveOperator` from `airflow.providers.apache.hive.operators.hive` instead.
AIR302_hive.py:46:1: AIR302 `airflow.operators.hive_stats_operator.HiveStatsCollectionOperator` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -83,7 +83,7 @@ AIR302_hive.py:46:1: AIR302 `airflow.operators.hive_stats_operator.HiveStatsColl
47 |
48 | HiveToMySqlOperator()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.operators.hive_stats.HiveStatsCollectionOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HiveStatsCollectionOperator` from `airflow.providers.apache.hive.operators.hive_stats` instead.
AIR302_hive.py:48:1: AIR302 `airflow.operators.hive_to_mysql.HiveToMySqlOperator` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -93,7 +93,7 @@ AIR302_hive.py:48:1: AIR302 `airflow.operators.hive_to_mysql.HiveToMySqlOperator
| ^^^^^^^^^^^^^^^^^^^ AIR302
49 | HiveToMySqlTransfer()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.hive_to_mysql.HiveToMySqlOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HiveToMySqlOperator` from `airflow.providers.apache.hive.transfers.hive_to_mysql` instead.
AIR302_hive.py:49:1: AIR302 `airflow.operators.hive_to_mysql.HiveToMySqlTransfer` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -103,7 +103,7 @@ AIR302_hive.py:49:1: AIR302 `airflow.operators.hive_to_mysql.HiveToMySqlTransfer
50 |
51 | HiveToSambaOperator()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.hive_to_mysql.HiveToMySqlOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HiveToMySqlOperator` from `airflow.providers.apache.hive.transfers.hive_to_mysql` instead.
AIR302_hive.py:51:1: AIR302 `airflow.operators.hive_to_samba_operator.HiveToSambaOperator` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -114,7 +114,7 @@ AIR302_hive.py:51:1: AIR302 `airflow.operators.hive_to_samba_operator.HiveToSamb
52 |
53 | MsSqlToHiveOperator()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.hive_to_samba.HiveToSambaOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HiveToSambaOperator` from `airflow.providers.apache.hive.transfers.hive_to_samba` instead.
AIR302_hive.py:53:1: AIR302 `airflow.operators.mssql_to_hive.MsSqlToHiveOperator` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -124,7 +124,7 @@ AIR302_hive.py:53:1: AIR302 `airflow.operators.mssql_to_hive.MsSqlToHiveOperator
| ^^^^^^^^^^^^^^^^^^^ AIR302
54 | MsSqlToHiveTransfer()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.mssql_to_hive.MsSqlToHiveOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `MsSqlToHiveOperator` from `airflow.providers.apache.hive.transfers.mssql_to_hive` instead.
AIR302_hive.py:54:1: AIR302 `airflow.operators.mssql_to_hive.MsSqlToHiveTransfer` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -134,7 +134,7 @@ AIR302_hive.py:54:1: AIR302 `airflow.operators.mssql_to_hive.MsSqlToHiveTransfer
55 |
56 | MySqlToHiveOperator()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.mssql_to_hive.MsSqlToHiveOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `MsSqlToHiveOperator` from `airflow.providers.apache.hive.transfers.mssql_to_hive` instead.
AIR302_hive.py:56:1: AIR302 `airflow.operators.mysql_to_hive.MySqlToHiveOperator` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -144,7 +144,7 @@ AIR302_hive.py:56:1: AIR302 `airflow.operators.mysql_to_hive.MySqlToHiveOperator
| ^^^^^^^^^^^^^^^^^^^ AIR302
57 | MySqlToHiveTransfer()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.mysql_to_hive.MySqlToHiveOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `MySqlToHiveOperator` from `airflow.providers.apache.hive.transfers.mysql_to_hive` instead.
AIR302_hive.py:57:1: AIR302 `airflow.operators.mysql_to_hive.MySqlToHiveTransfer` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -154,7 +154,7 @@ AIR302_hive.py:57:1: AIR302 `airflow.operators.mysql_to_hive.MySqlToHiveTransfer
58 |
59 | S3ToHiveOperator()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.mysql_to_hive.MySqlToHiveOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `MySqlToHiveOperator` from `airflow.providers.apache.hive.transfers.mysql_to_hive` instead.
AIR302_hive.py:59:1: AIR302 `airflow.operators.s3_to_hive_operator.S3ToHiveOperator` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -164,7 +164,7 @@ AIR302_hive.py:59:1: AIR302 `airflow.operators.s3_to_hive_operator.S3ToHiveOpera
| ^^^^^^^^^^^^^^^^ AIR302
60 | S3ToHiveTransfer()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.s3_to_hive.S3ToHiveOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `S3ToHiveOperator` from `airflow.providers.apache.hive.transfers.s3_to_hive` instead.
AIR302_hive.py:60:1: AIR302 `airflow.operators.s3_to_hive_operator.S3ToHiveTransfer` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -174,7 +174,7 @@ AIR302_hive.py:60:1: AIR302 `airflow.operators.s3_to_hive_operator.S3ToHiveTrans
61 |
62 | HivePartitionSensor()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.transfers.s3_to_hive.S3ToHiveOperator` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `S3ToHiveOperator` from `airflow.providers.apache.hive.transfers.s3_to_hive` instead.
AIR302_hive.py:62:1: AIR302 `airflow.sensors.hive_partition_sensor.HivePartitionSensor` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -185,7 +185,7 @@ AIR302_hive.py:62:1: AIR302 `airflow.sensors.hive_partition_sensor.HivePartition
63 |
64 | MetastorePartitionSensor()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.sensors.hive_partition.HivePartitionSensor` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `HivePartitionSensor` from `airflow.providers.apache.hive.sensors.hive_partition` instead.
AIR302_hive.py:64:1: AIR302 `airflow.sensors.metastore_partition_sensor.MetastorePartitionSensor` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -196,7 +196,7 @@ AIR302_hive.py:64:1: AIR302 `airflow.sensors.metastore_partition_sensor.Metastor
65 |
66 | NamedHivePartitionSensor()
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.sensors.metastore_partition.MetastorePartitionSensor` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `MetastorePartitionSensor` from `airflow.providers.apache.hive.sensors.metastore_partition` instead.
AIR302_hive.py:66:1: AIR302 `airflow.sensors.named_hive_partition_sensor.NamedHivePartitionSensor` is moved into `apache-hive` provider in Airflow 3.0;
|
@@ -205,4 +205,4 @@ AIR302_hive.py:66:1: AIR302 `airflow.sensors.named_hive_partition_sensor.NamedHi
66 | NamedHivePartitionSensor()
| ^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `airflow.providers.apache.hive.sensors.named_hive_partition.NamedHivePartitionSensor` instead.
= help: Install `apache-airflow-providers-apache-hive>=1.0.0` and use `NamedHivePartitionSensor` from `airflow.providers.apache.hive.sensors.named_hive_partition` instead.

View File

@@ -10,7 +10,7 @@ AIR302_http.py:7:1: AIR302 `airflow.hooks.http_hook.HttpHook` is moved into `htt
8 | SimpleHttpOperator()
9 | HttpSensor()
|
= help: Install `apache-airflow-providers-http>=1.0.0` and use `airflow.providers.http.hooks.http.HttpHook` instead.
= help: Install `apache-airflow-providers-http>=1.0.0` and use `HttpHook` from `airflow.providers.http.hooks.http` instead.
AIR302_http.py:8:1: AIR302 [*] `airflow.operators.http_operator.SimpleHttpOperator` is moved into `http` provider in Airflow 3.0;
|
@@ -19,7 +19,7 @@ AIR302_http.py:8:1: AIR302 [*] `airflow.operators.http_operator.SimpleHttpOperat
| ^^^^^^^^^^^^^^^^^^ AIR302
9 | HttpSensor()
|
= help: Install `apache-airflow-providers-http>=5.0.0` and use `airflow.providers.http.operators.http.HttpOperator` instead.
= help: Install `apache-airflow-providers-http>=5.0.0` and use `HttpOperator` from `airflow.providers.http.operators.http` instead.
Safe fix
3 3 | from airflow.hooks.http_hook import HttpHook
@@ -39,4 +39,4 @@ AIR302_http.py:9:1: AIR302 `airflow.sensors.http_sensor.HttpSensor` is moved int
9 | HttpSensor()
| ^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-http>=1.0.0` and use `airflow.providers.http.sensors.http.HttpSensor` instead.
= help: Install `apache-airflow-providers-http>=1.0.0` and use `HttpSensor` from `airflow.providers.http.sensors.http` instead.

View File

@@ -9,7 +9,7 @@ AIR302_jdbc.py:8:1: AIR302 `airflow.hooks.jdbc_hook.JdbcHook` is moved into `jdb
| ^^^^^^^^ AIR302
9 | jaydebeapi()
|
= help: Install `apache-airflow-providers-jdbc>=1.0.0` and use `airflow.providers.jdbc.hooks.jdbc.JdbcHook` instead.
= help: Install `apache-airflow-providers-jdbc>=1.0.0` and use `JdbcHook` from `airflow.providers.jdbc.hooks.jdbc` instead.
AIR302_jdbc.py:9:1: AIR302 `airflow.hooks.jdbc_hook.jaydebeapi` is moved into `jdbc` provider in Airflow 3.0;
|
@@ -17,4 +17,4 @@ AIR302_jdbc.py:9:1: AIR302 `airflow.hooks.jdbc_hook.jaydebeapi` is moved into `j
9 | jaydebeapi()
| ^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-jdbc>=1.0.0` and use `airflow.providers.jdbc.hooks.jdbc.jaydebeapi` instead.
= help: Install `apache-airflow-providers-jdbc>=1.0.0` and use `jaydebeapi` from `airflow.providers.jdbc.hooks.jdbc` instead.

View File

@@ -9,7 +9,7 @@ AIR302_kubernetes.py:29:1: AIR302 `airflow.executors.kubernetes_executor_types.A
| ^^^^^^^^^^^^^^ AIR302
30 | POD_EXECUTOR_DONE_KEY
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.executors.kubernetes_executor_types.ALL_NAMESPACES` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `ALL_NAMESPACES` from `airflow.providers.cncf.kubernetes.executors.kubernetes_executor_types` instead.
AIR302_kubernetes.py:30:1: AIR302 `airflow.executors.kubernetes_executor_types.POD_EXECUTOR_DONE_KEY` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -19,7 +19,7 @@ AIR302_kubernetes.py:30:1: AIR302 `airflow.executors.kubernetes_executor_types.P
31 |
32 | K8SModel()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.executors.kubernetes_executor_types.POD_EXECUTOR_DONE_KEY` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `POD_EXECUTOR_DONE_KEY` from `airflow.providers.cncf.kubernetes.executors.kubernetes_executor_types` instead.
AIR302_kubernetes.py:32:1: AIR302 `airflow.kubernetes.k8s_model.K8SModel` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -29,7 +29,7 @@ AIR302_kubernetes.py:32:1: AIR302 `airflow.kubernetes.k8s_model.K8SModel` is mov
| ^^^^^^^^ AIR302
33 | append_to_pod()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.k8s_model.K8SModel` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `K8SModel` from `airflow.providers.cncf.kubernetes.k8s_model` instead.
AIR302_kubernetes.py:33:1: AIR302 `airflow.kubernetes.k8s_model.append_to_pod` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -39,7 +39,7 @@ AIR302_kubernetes.py:33:1: AIR302 `airflow.kubernetes.k8s_model.append_to_pod` i
34 |
35 | _disable_verify_ssl()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.k8s_model.append_to_pod` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `append_to_pod` from `airflow.providers.cncf.kubernetes.k8s_model` instead.
AIR302_kubernetes.py:35:1: AIR302 `airflow.kubernetes.kube_client._disable_verify_ssl` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -50,7 +50,7 @@ AIR302_kubernetes.py:35:1: AIR302 `airflow.kubernetes.kube_client._disable_verif
36 | _enable_tcp_keepalive()
37 | get_kube_client()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kube_client._disable_verify_ssl` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `_disable_verify_ssl` from `airflow.providers.cncf.kubernetes.kube_client` instead.
AIR302_kubernetes.py:36:1: AIR302 `airflow.kubernetes.kube_client._enable_tcp_keepalive` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -59,7 +59,7 @@ AIR302_kubernetes.py:36:1: AIR302 `airflow.kubernetes.kube_client._enable_tcp_ke
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
37 | get_kube_client()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kube_client._enable_tcp_keepalive` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `_enable_tcp_keepalive` from `airflow.providers.cncf.kubernetes.kube_client` instead.
AIR302_kubernetes.py:37:1: AIR302 `airflow.kubernetes.kube_client.get_kube_client` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -70,7 +70,7 @@ AIR302_kubernetes.py:37:1: AIR302 `airflow.kubernetes.kube_client.get_kube_clien
38 |
39 | add_pod_suffix()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kube_client.get_kube_client` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `get_kube_client` from `airflow.providers.cncf.kubernetes.kube_client` instead.
AIR302_kubernetes.py:39:1: AIR302 [*] `airflow.kubernetes.kubernetes_helper_functions.add_pod_suffix` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -80,7 +80,7 @@ AIR302_kubernetes.py:39:1: AIR302 [*] `airflow.kubernetes.kubernetes_helper_func
| ^^^^^^^^^^^^^^ AIR302
40 | create_pod_id()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=10.0.0` and use `airflow.providers.cncf.kubernetes.kubernetes_helper_functions.add_unique_suffix` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=10.0.0` and use `add_unique_suffix` from `airflow.providers.cncf.kubernetes.kubernetes_helper_functions` instead.
Safe fix
25 25 | Port,
@@ -108,7 +108,7 @@ AIR302_kubernetes.py:40:1: AIR302 [*] `airflow.kubernetes.kubernetes_helper_func
41 |
42 | annotations_for_logging_task_metadata()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=10.0.0` and use `airflow.providers.cncf.kubernetes.kubernetes_helper_functions.create_unique_id` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=10.0.0` and use `create_unique_id` from `airflow.providers.cncf.kubernetes.kubernetes_helper_functions` instead.
Safe fix
25 25 | Port,
@@ -137,7 +137,7 @@ AIR302_kubernetes.py:42:1: AIR302 `airflow.kubernetes.kubernetes_helper_function
43 | annotations_to_key()
44 | get_logs_task_metadata()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kubernetes_helper_functions.annotations_for_logging_task_metadata` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `annotations_for_logging_task_metadata` from `airflow.providers.cncf.kubernetes.kubernetes_helper_functions` instead.
AIR302_kubernetes.py:43:1: AIR302 `airflow.kubernetes.kubernetes_helper_functions.annotations_to_key` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -147,7 +147,7 @@ AIR302_kubernetes.py:43:1: AIR302 `airflow.kubernetes.kubernetes_helper_function
44 | get_logs_task_metadata()
45 | rand_str()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kubernetes_helper_functions.annotations_to_key` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `annotations_to_key` from `airflow.providers.cncf.kubernetes.kubernetes_helper_functions` instead.
AIR302_kubernetes.py:44:1: AIR302 `airflow.kubernetes.kubernetes_helper_functions.get_logs_task_metadata` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -157,7 +157,7 @@ AIR302_kubernetes.py:44:1: AIR302 `airflow.kubernetes.kubernetes_helper_function
| ^^^^^^^^^^^^^^^^^^^^^^ AIR302
45 | rand_str()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kubernetes_helper_functions.get_logs_task_metadata` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `get_logs_task_metadata` from `airflow.providers.cncf.kubernetes.kubernetes_helper_functions` instead.
AIR302_kubernetes.py:45:1: AIR302 `airflow.kubernetes.kubernetes_helper_functions.rand_str` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -168,7 +168,7 @@ AIR302_kubernetes.py:45:1: AIR302 `airflow.kubernetes.kubernetes_helper_function
46 |
47 | Port()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kubernetes_helper_functions.rand_str` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `rand_str` from `airflow.providers.cncf.kubernetes.kubernetes_helper_functions` instead.
AIR302_kubernetes.py:47:1: AIR302 [*] `airflow.kubernetes.pod.Port` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -178,7 +178,7 @@ AIR302_kubernetes.py:47:1: AIR302 [*] `airflow.kubernetes.pod.Port` is moved int
| ^^^^ AIR302
48 | Resources()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `kubernetes.client.models.V1ContainerPort` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `V1ContainerPort` from `kubernetes.client.models` instead.
Safe fix
25 25 | Port,
@@ -204,7 +204,7 @@ AIR302_kubernetes.py:48:1: AIR302 [*] `airflow.kubernetes.pod.Resources` is move
48 | Resources()
| ^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `kubernetes.client.models.V1ResourceRequirements` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `V1ResourceRequirements` from `kubernetes.client.models` instead.
Safe fix
25 25 | Port,
@@ -233,7 +233,7 @@ AIR302_kubernetes.py:64:1: AIR302 `airflow.kubernetes.pod_generator.datetime_to_
65 | extend_object_field()
66 | label_safe_datestring_to_datetime()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.datetime_to_label_safe_datestring` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `datetime_to_label_safe_datestring` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:65:1: AIR302 `airflow.kubernetes.pod_generator.extend_object_field` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -243,7 +243,7 @@ AIR302_kubernetes.py:65:1: AIR302 `airflow.kubernetes.pod_generator.extend_objec
66 | label_safe_datestring_to_datetime()
67 | make_safe_label_value()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.extend_object_field` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `extend_object_field` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:66:1: AIR302 `airflow.kubernetes.pod_generator.label_safe_datestring_to_datetime` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -254,7 +254,7 @@ AIR302_kubernetes.py:66:1: AIR302 `airflow.kubernetes.pod_generator.label_safe_d
67 | make_safe_label_value()
68 | merge_objects()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.label_safe_datestring_to_datetime` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `label_safe_datestring_to_datetime` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:67:1: AIR302 `airflow.kubernetes.pod_generator.make_safe_label_value` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -265,7 +265,7 @@ AIR302_kubernetes.py:67:1: AIR302 `airflow.kubernetes.pod_generator.make_safe_la
68 | merge_objects()
69 | PodGenerator()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.make_safe_label_value` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `make_safe_label_value` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:68:1: AIR302 `airflow.kubernetes.pod_generator.merge_objects` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -276,7 +276,7 @@ AIR302_kubernetes.py:68:1: AIR302 `airflow.kubernetes.pod_generator.merge_object
69 | PodGenerator()
70 | PodDefaults()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.merge_objects` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `merge_objects` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:69:1: AIR302 `airflow.kubernetes.pod_generator.PodGenerator` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -287,7 +287,7 @@ AIR302_kubernetes.py:69:1: AIR302 `airflow.kubernetes.pod_generator.PodGenerator
70 | PodDefaults()
71 | PodGeneratorDeprecated()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.PodGenerator` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `PodGenerator` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:70:1: AIR302 `airflow.kubernetes.pod_generator.PodDefaults` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -298,7 +298,7 @@ AIR302_kubernetes.py:70:1: AIR302 `airflow.kubernetes.pod_generator.PodDefaults`
71 | PodGeneratorDeprecated()
72 | add_pod_suffix()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.utils.xcom_sidecar.PodDefaults` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `PodDefaults` from `airflow.providers.cncf.kubernetes.utils.xcom_sidecar` instead.
AIR302_kubernetes.py:71:1: AIR302 `airflow.kubernetes.pod_generator.PodGeneratorDeprecated` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -309,7 +309,7 @@ AIR302_kubernetes.py:71:1: AIR302 `airflow.kubernetes.pod_generator.PodGenerator
72 | add_pod_suffix()
73 | rand_str()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.PodGenerator` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `PodGenerator` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:72:1: AIR302 [*] `airflow.kubernetes.pod_generator.add_pod_suffix` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -319,7 +319,7 @@ AIR302_kubernetes.py:72:1: AIR302 [*] `airflow.kubernetes.pod_generator.add_pod_
| ^^^^^^^^^^^^^^ AIR302
73 | rand_str()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=10.0.0` and use `airflow.providers.cncf.kubernetes.kubernetes_helper_functions.add_unique_suffix` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=10.0.0` and use `add_unique_suffix` from `airflow.providers.cncf.kubernetes.kubernetes_helper_functions` instead.
Safe fix
60 60 | merge_objects,
@@ -346,7 +346,7 @@ AIR302_kubernetes.py:73:1: AIR302 `airflow.kubernetes.pod_generator.rand_str` is
73 | rand_str()
| ^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kubernetes_helper_functions.rand_str` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `rand_str` from `airflow.providers.cncf.kubernetes.kubernetes_helper_functions` instead.
AIR302_kubernetes.py:86:1: AIR302 `airflow.kubernetes.pod_generator_deprecated.PodDefaults` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -357,7 +357,7 @@ AIR302_kubernetes.py:86:1: AIR302 `airflow.kubernetes.pod_generator_deprecated.P
87 | PodGenerator()
88 | make_safe_label_value()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.utils.xcom_sidecar.PodDefaults` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `PodDefaults` from `airflow.providers.cncf.kubernetes.utils.xcom_sidecar` instead.
AIR302_kubernetes.py:87:1: AIR302 `airflow.kubernetes.pod_generator_deprecated.PodGenerator` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -366,7 +366,7 @@ AIR302_kubernetes.py:87:1: AIR302 `airflow.kubernetes.pod_generator_deprecated.P
| ^^^^^^^^^^^^ AIR302
88 | make_safe_label_value()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.PodGenerator` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `PodGenerator` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:88:1: AIR302 `airflow.kubernetes.pod_generator_deprecated.make_safe_label_value` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -377,7 +377,7 @@ AIR302_kubernetes.py:88:1: AIR302 `airflow.kubernetes.pod_generator_deprecated.m
89 |
90 | PodLauncher()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.pod_generator.make_safe_label_value` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `make_safe_label_value` from `airflow.providers.cncf.kubernetes.pod_generator` instead.
AIR302_kubernetes.py:90:1: AIR302 [*] `airflow.kubernetes.pod_launcher.PodLauncher` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -387,7 +387,7 @@ AIR302_kubernetes.py:90:1: AIR302 [*] `airflow.kubernetes.pod_launcher.PodLaunch
| ^^^^^^^^^^^ AIR302
91 | PodStatus()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=3.0.0` and use `airflow.providers.cncf.kubernetes.utils.pod_manager.PodManager` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=3.0.0` and use `PodManager` from `airflow.providers.cncf.kubernetes.utils.pod_manager` instead.
Safe fix
82 82 | PodLauncher,
@@ -411,7 +411,7 @@ AIR302_kubernetes.py:91:1: AIR302 [*] `airflow.kubernetes.pod_launcher.PodStatus
91 | PodStatus()
| ^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=3.0.0` and use ` airflow.providers.cncf.kubernetes.utils.pod_manager.PodPhase` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=3.0.0` and use `PodPhase` from ` airflow.providers.cncf.kubernetes.utils.pod_manager` instead.
Safe fix
82 82 | PodLauncher,
@@ -439,7 +439,7 @@ AIR302_kubernetes.py:108:1: AIR302 `airflow.kubernetes.pod_launcher_deprecated.P
109 | PodLauncher()
110 | PodStatus()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.utils.xcom_sidecar.PodDefaults` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `PodDefaults` from `airflow.providers.cncf.kubernetes.utils.xcom_sidecar` instead.
AIR302_kubernetes.py:109:1: AIR302 [*] `airflow.kubernetes.pod_launcher_deprecated.PodLauncher` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -449,7 +449,7 @@ AIR302_kubernetes.py:109:1: AIR302 [*] `airflow.kubernetes.pod_launcher_deprecat
110 | PodStatus()
111 | get_kube_client()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=3.0.0` and use `airflow.providers.cncf.kubernetes.utils.pod_manager.PodManager` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=3.0.0` and use `PodManager` from `airflow.providers.cncf.kubernetes.utils.pod_manager` instead.
Safe fix
104 104 | )
@@ -472,7 +472,7 @@ AIR302_kubernetes.py:110:1: AIR302 [*] `airflow.kubernetes.pod_launcher_deprecat
| ^^^^^^^^^ AIR302
111 | get_kube_client()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=3.0.0` and use ` airflow.providers.cncf.kubernetes.utils.pod_manager.PodPhase` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=3.0.0` and use `PodPhase` from ` airflow.providers.cncf.kubernetes.utils.pod_manager` instead.
Safe fix
104 104 | )
@@ -497,7 +497,7 @@ AIR302_kubernetes.py:111:1: AIR302 `airflow.kubernetes.pod_launcher_deprecated.g
112 |
113 | PodRuntimeInfoEnv()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.kube_client.get_kube_client` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `get_kube_client` from `airflow.providers.cncf.kubernetes.kube_client` instead.
AIR302_kubernetes.py:113:1: AIR302 [*] `airflow.kubernetes.pod_runtime_info_env.PodRuntimeInfoEnv` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -508,7 +508,7 @@ AIR302_kubernetes.py:113:1: AIR302 [*] `airflow.kubernetes.pod_runtime_info_env.
114 | K8SModel()
115 | Secret()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `kubernetes.client.models.V1EnvVar` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `V1EnvVar` from `kubernetes.client.models` instead.
Safe fix
104 104 | )
@@ -535,7 +535,7 @@ AIR302_kubernetes.py:114:1: AIR302 `airflow.kubernetes.secret.K8SModel` is moved
115 | Secret()
116 | Volume()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.k8s_model.K8SModel` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `K8SModel` from `airflow.providers.cncf.kubernetes.k8s_model` instead.
AIR302_kubernetes.py:115:1: AIR302 `airflow.kubernetes.secret.Secret` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -546,7 +546,7 @@ AIR302_kubernetes.py:115:1: AIR302 `airflow.kubernetes.secret.Secret` is moved i
116 | Volume()
117 | VolumeMount()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `airflow.providers.cncf.kubernetes.secret.Secret` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `Secret` from `airflow.providers.cncf.kubernetes.secret` instead.
AIR302_kubernetes.py:116:1: AIR302 [*] `airflow.kubernetes.volume.Volume` is moved into `cncf-kubernetes` provider in Airflow 3.0;
|
@@ -556,7 +556,7 @@ AIR302_kubernetes.py:116:1: AIR302 [*] `airflow.kubernetes.volume.Volume` is mov
| ^^^^^^ AIR302
117 | VolumeMount()
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `kubernetes.client.models.V1Volume` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `V1Volume` from `kubernetes.client.models` instead.
Safe fix
104 104 | )
@@ -581,7 +581,7 @@ AIR302_kubernetes.py:117:1: AIR302 [*] `airflow.kubernetes.volume_mount.VolumeMo
117 | VolumeMount()
| ^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `kubernetes.client.models.V1VolumeMount` instead.
= help: Install `apache-airflow-providers-cncf-kubernetes>=7.4.0` and use `V1VolumeMount` from `kubernetes.client.models` instead.
Safe fix
104 104 | )

View File

@@ -10,7 +10,7 @@ AIR302_mysql.py:9:1: AIR302 `airflow.hooks.mysql_hook.MySqlHook` is moved into `
10 | PrestoToMySqlOperator()
11 | PrestoToMySqlTransfer()
|
= help: Install `apache-airflow-providers-mysql>=1.0.0` and use `airflow.providers.mysql.hooks.mysql.MySqlHook` instead.
= help: Install `apache-airflow-providers-mysql>=1.0.0` and use `MySqlHook` from `airflow.providers.mysql.hooks.mysql` instead.
AIR302_mysql.py:10:1: AIR302 `airflow.operators.presto_to_mysql.PrestoToMySqlOperator` is moved into `mysql` provider in Airflow 3.0;
|
@@ -19,7 +19,7 @@ AIR302_mysql.py:10:1: AIR302 `airflow.operators.presto_to_mysql.PrestoToMySqlOpe
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
11 | PrestoToMySqlTransfer()
|
= help: Install `apache-airflow-providers-mysql>=1.0.0` and use `airflow.providers.mysql.transfers.presto_to_mysql.PrestoToMySqlOperator` instead.
= help: Install `apache-airflow-providers-mysql>=1.0.0` and use `PrestoToMySqlOperator` from `airflow.providers.mysql.transfers.presto_to_mysql` instead.
AIR302_mysql.py:11:1: AIR302 `airflow.operators.presto_to_mysql.PrestoToMySqlTransfer` is moved into `mysql` provider in Airflow 3.0;
|
@@ -28,4 +28,4 @@ AIR302_mysql.py:11:1: AIR302 `airflow.operators.presto_to_mysql.PrestoToMySqlTra
11 | PrestoToMySqlTransfer()
| ^^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-mysql>=1.0.0` and use `airflow.providers.mysql.transfers.presto_to_mysql.PrestoToMySqlOperator` instead.
= help: Install `apache-airflow-providers-mysql>=1.0.0` and use `PrestoToMySqlOperator` from `airflow.providers.mysql.transfers.presto_to_mysql` instead.

View File

@@ -8,4 +8,4 @@ AIR302_oracle.py:5:1: AIR302 `airflow.hooks.oracle_hook.OracleHook` is moved int
5 | OracleHook()
| ^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-oracle>=1.0.0` and use `airflow.providers.oracle.hooks.oracle.OracleHook` instead.
= help: Install `apache-airflow-providers-oracle>=1.0.0` and use `OracleHook` from `airflow.providers.oracle.hooks.oracle` instead.

View File

@@ -8,4 +8,4 @@ AIR302_papermill.py:5:1: AIR302 `airflow.operators.papermill_operator.PapermillO
5 | PapermillOperator()
| ^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-papermill>=1.0.0` and use `airflow.providers.papermill.operators.papermill.PapermillOperator` instead.
= help: Install `apache-airflow-providers-papermill>=1.0.0` and use `PapermillOperator` from `airflow.providers.papermill.operators.papermill` instead.

View File

@@ -9,7 +9,7 @@ AIR302_pig.py:6:1: AIR302 `airflow.hooks.pig_hook.PigCliHook` is moved into `apa
| ^^^^^^^^^^ AIR302
7 | PigOperator()
|
= help: Install `apache-airflow-providers-apache-pig>=1.0.0` and use `airflow.providers.apache.pig.hooks.pig.PigCliHook` instead.
= help: Install `apache-airflow-providers-apache-pig>=1.0.0` and use `PigCliHook` from `airflow.providers.apache.pig.hooks.pig` instead.
AIR302_pig.py:7:1: AIR302 `airflow.operators.pig_operator.PigOperator` is moved into `apache-pig` provider in Airflow 3.0;
|
@@ -17,4 +17,4 @@ AIR302_pig.py:7:1: AIR302 `airflow.operators.pig_operator.PigOperator` is moved
7 | PigOperator()
| ^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-apache-pig>=1.0.0` and use `airflow.providers.apache.pig.operators.pig.PigOperator` instead.
= help: Install `apache-airflow-providers-apache-pig>=1.0.0` and use `PigOperator` from `airflow.providers.apache.pig.operators.pig` instead.

View File

@@ -9,7 +9,7 @@ AIR302_postgres.py:6:1: AIR302 `airflow.hooks.postgres_hook.PostgresHook` is mov
| ^^^^^^^^^^^^ AIR302
7 | Mapping()
|
= help: Install `apache-airflow-providers-postgres>=1.0.0` and use `airflow.providers.postgres.hooks.postgres.PostgresHook` instead.
= help: Install `apache-airflow-providers-postgres>=1.0.0` and use `PostgresHook` from `airflow.providers.postgres.hooks.postgres` instead.
AIR302_postgres.py:7:1: AIR302 `airflow.operators.postgres_operator.Mapping` is removed in Airflow 3.0
|

View File

@@ -8,4 +8,4 @@ AIR302_presto.py:5:1: AIR302 `airflow.hooks.presto_hook.PrestoHook` is moved int
5 | PrestoHook()
| ^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-presto>=1.0.0` and use `airflow.providers.presto.hooks.presto.PrestoHook` instead.
= help: Install `apache-airflow-providers-presto>=1.0.0` and use `PrestoHook` from `airflow.providers.presto.hooks.presto` instead.

View File

@@ -8,4 +8,4 @@ AIR302_samba.py:5:1: AIR302 `airflow.hooks.samba_hook.SambaHook` is moved into `
5 | SambaHook()
| ^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-samba>=1.0.0` and use `airflow.providers.samba.hooks.samba.SambaHook` instead.
= help: Install `apache-airflow-providers-samba>=1.0.0` and use `SambaHook` from `airflow.providers.samba.hooks.samba` instead.

View File

@@ -10,7 +10,7 @@ AIR302_slack.py:6:1: AIR302 `airflow.hooks.slack_hook.SlackHook` is moved into `
7 | SlackAPIOperator()
8 | SlackAPIPostOperator()
|
= help: Install `apache-airflow-providers-slack>=1.0.0` and use `airflow.providers.slack.hooks.slack.SlackHook` instead.
= help: Install `apache-airflow-providers-slack>=1.0.0` and use `SlackHook` from `airflow.providers.slack.hooks.slack` instead.
AIR302_slack.py:7:1: AIR302 `airflow.operators.slack_operator.SlackAPIOperator` is moved into `slack` provider in Airflow 3.0;
|
@@ -19,7 +19,7 @@ AIR302_slack.py:7:1: AIR302 `airflow.operators.slack_operator.SlackAPIOperator`
| ^^^^^^^^^^^^^^^^ AIR302
8 | SlackAPIPostOperator()
|
= help: Install `apache-airflow-providers-slack>=1.0.0` and use `airflow.providers.slack.operators.slack.SlackAPIOperator` instead.
= help: Install `apache-airflow-providers-slack>=1.0.0` and use `SlackAPIOperator` from `airflow.providers.slack.operators.slack` instead.
AIR302_slack.py:8:1: AIR302 `airflow.operators.slack_operator.SlackAPIPostOperator` is moved into `slack` provider in Airflow 3.0;
|
@@ -28,4 +28,4 @@ AIR302_slack.py:8:1: AIR302 `airflow.operators.slack_operator.SlackAPIPostOperat
8 | SlackAPIPostOperator()
| ^^^^^^^^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-slack>=1.0.0` and use `airflow.providers.slack.operators.slack.SlackAPIPostOperator` instead.
= help: Install `apache-airflow-providers-slack>=1.0.0` and use `SlackAPIPostOperator` from `airflow.providers.slack.operators.slack` instead.

View File

@@ -10,7 +10,7 @@ AIR302_smtp.py:5:1: AIR302 `airflow.operators.email_operator.EmailOperator` is m
6 |
7 | from airflow.operators.email import EmailOperator
|
= help: Install `apache-airflow-providers-smtp>=1.0.0` and use `airflow.providers.smtp.operators.smtp.EmailOperator` instead.
= help: Install `apache-airflow-providers-smtp>=1.0.0` and use `EmailOperator` from `airflow.providers.smtp.operators.smtp` instead.
AIR302_smtp.py:9:1: AIR302 `airflow.operators.email.EmailOperator` is moved into `smtp` provider in Airflow 3.0;
|
@@ -19,4 +19,4 @@ AIR302_smtp.py:9:1: AIR302 `airflow.operators.email.EmailOperator` is moved into
9 | EmailOperator()
| ^^^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-smtp>=1.0.0` and use `airflow.providers.smtp.operators.smtp.EmailOperator` instead.
= help: Install `apache-airflow-providers-smtp>=1.0.0` and use `EmailOperator` from `airflow.providers.smtp.operators.smtp` instead.

View File

@@ -8,4 +8,4 @@ AIR302_sqlite.py:5:1: AIR302 `airflow.hooks.sqlite_hook.SqliteHook` is moved int
5 | SqliteHook()
| ^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-sqlite>=1.0.0` and use `airflow.providers.sqlite.hooks.sqlite.SqliteHook` instead.
= help: Install `apache-airflow-providers-sqlite>=1.0.0` and use `SqliteHook` from `airflow.providers.sqlite.hooks.sqlite` instead.

View File

@@ -10,7 +10,7 @@ AIR302_standard.py:25:1: AIR302 `airflow.operators.bash_operator.BashOperator` i
26 |
27 | TriggerDagRunLink()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.bash.BashOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `BashOperator` from `airflow.providers.standard.operators.bash` instead.
AIR302_standard.py:27:1: AIR302 `airflow.operators.dagrun_operator.TriggerDagRunLink` is moved into `standard` provider in Airflow 3.0;
|
@@ -21,7 +21,7 @@ AIR302_standard.py:27:1: AIR302 `airflow.operators.dagrun_operator.TriggerDagRun
28 | TriggerDagRunOperator()
29 | DummyOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunLink` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `TriggerDagRunLink` from `airflow.providers.standard.operators.trigger_dagrun` instead.
AIR302_standard.py:28:1: AIR302 `airflow.operators.dagrun_operator.TriggerDagRunOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -31,7 +31,7 @@ AIR302_standard.py:28:1: AIR302 `airflow.operators.dagrun_operator.TriggerDagRun
29 | DummyOperator()
30 | EmptyOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `TriggerDagRunOperator` from `airflow.providers.standard.operators.trigger_dagrun` instead.
AIR302_standard.py:29:1: AIR302 `airflow.operators.dummy.DummyOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -41,7 +41,7 @@ AIR302_standard.py:29:1: AIR302 `airflow.operators.dummy.DummyOperator` is moved
| ^^^^^^^^^^^^^ AIR302
30 | EmptyOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.empty.EmptyOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `EmptyOperator` from `airflow.providers.standard.operators.empty` instead.
AIR302_standard.py:30:1: AIR302 `airflow.operators.dummy.EmptyOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -52,7 +52,7 @@ AIR302_standard.py:30:1: AIR302 `airflow.operators.dummy.EmptyOperator` is moved
31 |
32 | LatestOnlyOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.empty.EmptyOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `EmptyOperator` from `airflow.providers.standard.operators.empty` instead.
AIR302_standard.py:32:1: AIR302 `airflow.operators.latest_only_operator.LatestOnlyOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -63,7 +63,7 @@ AIR302_standard.py:32:1: AIR302 `airflow.operators.latest_only_operator.LatestOn
33 |
34 | BranchPythonOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.operators.latest_only.LatestOnlyOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `LatestOnlyOperator` from `airflow.providers.standard.operators.latest_only` instead.
AIR302_standard.py:34:1: AIR302 `airflow.operators.python_operator.BranchPythonOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -74,7 +74,7 @@ AIR302_standard.py:34:1: AIR302 `airflow.operators.python_operator.BranchPythonO
35 | PythonOperator()
36 | PythonVirtualenvOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.python.BranchPythonOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `BranchPythonOperator` from `airflow.providers.standard.operators.python` instead.
AIR302_standard.py:35:1: AIR302 `airflow.operators.python_operator.PythonOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -84,7 +84,7 @@ AIR302_standard.py:35:1: AIR302 `airflow.operators.python_operator.PythonOperato
36 | PythonVirtualenvOperator()
37 | ShortCircuitOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.python.PythonOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `PythonOperator` from `airflow.providers.standard.operators.python` instead.
AIR302_standard.py:36:1: AIR302 `airflow.operators.python_operator.PythonVirtualenvOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -94,7 +94,7 @@ AIR302_standard.py:36:1: AIR302 `airflow.operators.python_operator.PythonVirtual
| ^^^^^^^^^^^^^^^^^^^^^^^^ AIR302
37 | ShortCircuitOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.python.PythonVirtualenvOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `PythonVirtualenvOperator` from `airflow.providers.standard.operators.python` instead.
AIR302_standard.py:37:1: AIR302 `airflow.operators.python_operator.ShortCircuitOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -105,7 +105,7 @@ AIR302_standard.py:37:1: AIR302 `airflow.operators.python_operator.ShortCircuitO
38 |
39 | ExternalTaskMarker()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.python.ShortCircuitOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `ShortCircuitOperator` from `airflow.providers.standard.operators.python` instead.
AIR302_standard.py:39:1: AIR302 `airflow.sensors.external_task_sensor.ExternalTaskMarker` is moved into `standard` provider in Airflow 3.0;
|
@@ -116,7 +116,7 @@ AIR302_standard.py:39:1: AIR302 `airflow.sensors.external_task_sensor.ExternalTa
40 | ExternalTaskSensor()
41 | ExternalTaskSensorLink()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.sensors.external_task.ExternalTaskMarker` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `ExternalTaskMarker` from `airflow.providers.standard.sensors.external_task` instead.
AIR302_standard.py:40:1: AIR302 `airflow.sensors.external_task_sensor.ExternalTaskSensor` is moved into `standard` provider in Airflow 3.0;
|
@@ -125,7 +125,7 @@ AIR302_standard.py:40:1: AIR302 `airflow.sensors.external_task_sensor.ExternalTa
| ^^^^^^^^^^^^^^^^^^ AIR302
41 | ExternalTaskSensorLink()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.sensors.external_task.ExternalTaskSensor` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `ExternalTaskSensor` from `airflow.providers.standard.sensors.external_task` instead.
AIR302_standard.py:41:1: AIR302 `airflow.sensors.external_task_sensor.ExternalTaskSensorLink` is moved into `standard` provider in Airflow 3.0;
|
@@ -136,7 +136,7 @@ AIR302_standard.py:41:1: AIR302 `airflow.sensors.external_task_sensor.ExternalTa
42 |
43 | from airflow.operators.dummy_operator import (
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.sensors.external_task.ExternalTaskSensorLink` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `ExternalTaskSensorLink` from `airflow.providers.standard.sensors.external_task` instead.
AIR302_standard.py:48:1: AIR302 `airflow.operators.dummy_operator.DummyOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -146,7 +146,7 @@ AIR302_standard.py:48:1: AIR302 `airflow.operators.dummy_operator.DummyOperator`
| ^^^^^^^^^^^^^ AIR302
49 | EmptyOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.empty.EmptyOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `EmptyOperator` from `airflow.providers.standard.operators.empty` instead.
AIR302_standard.py:49:1: AIR302 `airflow.operators.dummy_operator.EmptyOperator` is moved into `standard` provider in Airflow 3.0;
|
@@ -156,7 +156,7 @@ AIR302_standard.py:49:1: AIR302 `airflow.operators.dummy_operator.EmptyOperator`
50 |
51 | from airflow.hooks.subprocess import SubprocessResult
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.empty.EmptyOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `EmptyOperator` from `airflow.providers.standard.operators.empty` instead.
AIR302_standard.py:52:1: AIR302 `airflow.hooks.subprocess.SubprocessResult` is moved into `standard` provider in Airflow 3.0;
|
@@ -166,7 +166,7 @@ AIR302_standard.py:52:1: AIR302 `airflow.hooks.subprocess.SubprocessResult` is m
53 | from airflow.hooks.subprocess import working_directory
54 | working_directory()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.hooks.subprocess.SubprocessResult` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `SubprocessResult` from `airflow.providers.standard.hooks.subprocess` instead.
AIR302_standard.py:54:1: AIR302 `airflow.hooks.subprocess.working_directory` is moved into `standard` provider in Airflow 3.0;
|
@@ -177,7 +177,7 @@ AIR302_standard.py:54:1: AIR302 `airflow.hooks.subprocess.working_directory` is
55 | from airflow.operators.datetime import target_times_as_dates
56 | target_times_as_dates()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.hooks.subprocess.working_directory` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `working_directory` from `airflow.providers.standard.hooks.subprocess` instead.
AIR302_standard.py:56:1: AIR302 `airflow.operators.datetime.target_times_as_dates` is moved into `standard` provider in Airflow 3.0;
|
@@ -188,7 +188,7 @@ AIR302_standard.py:56:1: AIR302 `airflow.operators.datetime.target_times_as_date
57 | from airflow.operators.trigger_dagrun import TriggerDagRunLink
58 | TriggerDagRunLink()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.datetime.target_times_as_dates` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `target_times_as_dates` from `airflow.providers.standard.operators.datetime` instead.
AIR302_standard.py:58:1: AIR302 `airflow.operators.trigger_dagrun.TriggerDagRunLink` is moved into `standard` provider in Airflow 3.0;
|
@@ -199,7 +199,7 @@ AIR302_standard.py:58:1: AIR302 `airflow.operators.trigger_dagrun.TriggerDagRunL
59 | from airflow.sensors.external_task import ExternalTaskSensorLink
60 | ExternalTaskSensorLink()
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunLink` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `TriggerDagRunLink` from `airflow.providers.standard.operators.trigger_dagrun` instead.
AIR302_standard.py:60:1: AIR302 [*] `airflow.sensors.external_task.ExternalTaskSensorLink` is moved into `standard` provider in Airflow 3.0;
|
@@ -210,7 +210,7 @@ AIR302_standard.py:60:1: AIR302 [*] `airflow.sensors.external_task.ExternalTaskS
61 | from airflow.sensors.time_delta import WaitSensor
62 | WaitSensor()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.sensors.external_task.ExternalDagLink` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `ExternalDagLink` from `airflow.providers.standard.sensors.external_task` instead.
Safe fix
57 57 | from airflow.operators.trigger_dagrun import TriggerDagRunLink
@@ -229,4 +229,4 @@ AIR302_standard.py:62:1: AIR302 `airflow.sensors.time_delta.WaitSensor` is moved
62 | WaitSensor()
| ^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.sensors.time_delta.WaitSensor` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `WaitSensor` from `airflow.providers.standard.sensors.time_delta` instead.

View File

@@ -8,4 +8,4 @@ AIR302_zendesk.py:5:1: AIR302 `airflow.hooks.zendesk_hook.ZendeskHook` is moved
5 | ZendeskHook()
| ^^^^^^^^^^^ AIR302
|
= help: Install `apache-airflow-providers-zendesk>=1.0.0` and use `airflow.providers.zendesk.hooks.zendesk.ZendeskHook` instead.
= help: Install `apache-airflow-providers-zendesk>=1.0.0` and use `ZendeskHook` from `airflow.providers.zendesk.hooks.zendesk` instead.

View File

@@ -9,7 +9,7 @@ AIR311_names.py:27:1: AIR311 [*] `airflow.Dataset` is removed in Airflow 3.0; It
28 |
29 | # airflow.datasets
|
= help: Use `airflow.sdk.Asset` instead
= help: Use `Asset` from `airflow.sdk` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -32,7 +32,7 @@ AIR311_names.py:30:1: AIR311 [*] `airflow.datasets.Dataset` is removed in Airflo
31 | DatasetAlias()
32 | DatasetAll()
|
= help: Use `airflow.sdk.Asset` instead
= help: Use `Asset` from `airflow.sdk` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -59,7 +59,7 @@ AIR311_names.py:31:1: AIR311 [*] `airflow.datasets.DatasetAlias` is removed in A
32 | DatasetAll()
33 | DatasetAny()
|
= help: Use `airflow.sdk.AssetAlias` instead
= help: Use `AssetAlias` from `airflow.sdk` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -87,7 +87,7 @@ AIR311_names.py:32:1: AIR311 [*] `airflow.datasets.DatasetAll` is removed in Air
33 | DatasetAny()
34 | Metadata()
|
= help: Use `airflow.sdk.AssetAll` instead
= help: Use `AssetAll` from `airflow.sdk` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -116,7 +116,7 @@ AIR311_names.py:33:1: AIR311 [*] `airflow.datasets.DatasetAny` is removed in Air
34 | Metadata()
35 | expand_alias_to_datasets()
|
= help: Use `airflow.sdk.AssetAny` instead
= help: Use `AssetAny` from `airflow.sdk` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -144,7 +144,7 @@ AIR311_names.py:34:1: AIR311 `airflow.datasets.metadata.Metadata` is removed in
| ^^^^^^^^ AIR311
35 | expand_alias_to_datasets()
|
= help: Use `airflow.sdk.Metadata` instead
= help: Use `Metadata` from `airflow.sdk` instead.
AIR311_names.py:35:1: AIR311 [*] `airflow.datasets.expand_alias_to_datasets` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -155,7 +155,7 @@ AIR311_names.py:35:1: AIR311 [*] `airflow.datasets.expand_alias_to_datasets` is
36 |
37 | # airflow.decorators
|
= help: Use `airflow.sdk.expand_alias_to_assets` instead
= help: Use `expand_alias_to_assets` from `airflow.sdk` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -183,7 +183,7 @@ AIR311_names.py:38:1: AIR311 `airflow.decorators.dag` is removed in Airflow 3.0;
39 | task()
40 | task_group()
|
= help: Use `airflow.sdk.dag` instead
= help: Use `dag` from `airflow.sdk` instead.
AIR311_names.py:39:1: AIR311 `airflow.decorators.task` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -194,7 +194,7 @@ AIR311_names.py:39:1: AIR311 `airflow.decorators.task` is removed in Airflow 3.0
40 | task_group()
41 | setup()
|
= help: Use `airflow.sdk.task` instead
= help: Use `task` from `airflow.sdk` instead.
AIR311_names.py:40:1: AIR311 `airflow.decorators.task_group` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -205,7 +205,7 @@ AIR311_names.py:40:1: AIR311 `airflow.decorators.task_group` is removed in Airfl
41 | setup()
42 | teardown()
|
= help: Use `airflow.sdk.task_group` instead
= help: Use `task_group` from `airflow.sdk` instead.
AIR311_names.py:41:1: AIR311 `airflow.decorators.setup` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -215,7 +215,7 @@ AIR311_names.py:41:1: AIR311 `airflow.decorators.setup` is removed in Airflow 3.
| ^^^^^ AIR311
42 | teardown()
|
= help: Use `airflow.sdk.setup` instead
= help: Use `setup` from `airflow.sdk` instead.
AIR311_names.py:42:1: AIR311 `airflow.decorators.teardown` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -226,7 +226,7 @@ AIR311_names.py:42:1: AIR311 `airflow.decorators.teardown` is removed in Airflow
43 |
44 | # airflow.io
|
= help: Use `airflow.sdk.teardown` instead
= help: Use `teardown` from `airflow.sdk` instead.
AIR311_names.py:45:1: AIR311 `airflow.io.path.ObjectStoragePath` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -235,7 +235,7 @@ AIR311_names.py:45:1: AIR311 `airflow.io.path.ObjectStoragePath` is removed in A
| ^^^^^^^^^^^^^^^^^ AIR311
46 | attach()
|
= help: Use `airflow.sdk.ObjectStoragePath` instead
= help: Use `ObjectStoragePath` from `airflow.sdk` instead.
AIR311_names.py:46:1: AIR311 `airflow.io.storage.attach` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -246,7 +246,7 @@ AIR311_names.py:46:1: AIR311 `airflow.io.storage.attach` is removed in Airflow 3
47 |
48 | # airflow.models
|
= help: Use `airflow.sdk.io.attach` instead
= help: Use `attach` from `airflow.sdk.io` instead.
AIR311_names.py:49:1: AIR311 `airflow.models.Connection` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -256,7 +256,7 @@ AIR311_names.py:49:1: AIR311 `airflow.models.Connection` is removed in Airflow 3
50 | DAGFromModel()
51 | Variable()
|
= help: Use `airflow.sdk.Connection` instead
= help: Use `Connection` from `airflow.sdk` instead.
AIR311_names.py:50:1: AIR311 [*] `airflow.models.DAG` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -266,7 +266,7 @@ AIR311_names.py:50:1: AIR311 [*] `airflow.models.DAG` is removed in Airflow 3.0;
| ^^^^^^^^^^^^ AIR311
51 | Variable()
|
= help: Use `airflow.sdk.DAG` instead
= help: Use `DAG` from `airflow.sdk` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -295,7 +295,7 @@ AIR311_names.py:51:1: AIR311 `airflow.models.Variable` is removed in Airflow 3.0
52 |
53 | # airflow.models.baseoperator
|
= help: Use `airflow.sdk.Variable` instead
= help: Use `Variable` from `airflow.sdk` instead.
AIR311_names.py:54:1: AIR311 `airflow.models.baseoperator.chain` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -305,7 +305,7 @@ AIR311_names.py:54:1: AIR311 `airflow.models.baseoperator.chain` is removed in A
55 | chain_linear()
56 | cross_downstream()
|
= help: Use `airflow.sdk.chain` instead
= help: Use `chain` from `airflow.sdk` instead.
AIR311_names.py:55:1: AIR311 `airflow.models.baseoperator.chain_linear` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -315,7 +315,7 @@ AIR311_names.py:55:1: AIR311 `airflow.models.baseoperator.chain_linear` is remov
| ^^^^^^^^^^^^ AIR311
56 | cross_downstream()
|
= help: Use `airflow.sdk.chain_linear` instead
= help: Use `chain_linear` from `airflow.sdk` instead.
AIR311_names.py:56:1: AIR311 `airflow.models.baseoperator.cross_downstream` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -326,7 +326,7 @@ AIR311_names.py:56:1: AIR311 `airflow.models.baseoperator.cross_downstream` is r
57 |
58 | # airflow.models.baseoperatolinker
|
= help: Use `airflow.sdk.cross_downstream` instead
= help: Use `cross_downstream` from `airflow.sdk` instead.
AIR311_names.py:59:1: AIR311 `airflow.models.baseoperatorlink.BaseOperatorLink` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -336,7 +336,7 @@ AIR311_names.py:59:1: AIR311 `airflow.models.baseoperatorlink.BaseOperatorLink`
60 |
61 | # airflow.models.dag
|
= help: Use `airflow.sdk.definitions.baseoperatorlink.BaseOperatorLink` instead
= help: Use `BaseOperatorLink` from `airflow.sdk.definitions.baseoperatorlink` instead.
AIR311_names.py:62:1: AIR311 [*] `airflow.models.dag.DAG` is removed in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -346,7 +346,7 @@ AIR311_names.py:62:1: AIR311 [*] `airflow.models.dag.DAG` is removed in Airflow
63 | # airflow.timetables.datasets
64 | DatasetOrTimeSchedule()
|
= help: Use `airflow.sdk.DAG` instead
= help: Use `DAG` from `airflow.sdk` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -375,7 +375,7 @@ AIR311_names.py:64:1: AIR311 [*] `airflow.timetables.datasets.DatasetOrTimeSched
65 |
66 | # airflow.utils.dag_parsing_context
|
= help: Use `airflow.timetables.assets.AssetOrTimeSchedule` instead
= help: Use `AssetOrTimeSchedule` from `airflow.timetables.assets` instead.
Safe fix
22 22 | from airflow.models.dag import DAG as DAGFromDag
@@ -401,4 +401,4 @@ AIR311_names.py:67:1: AIR311 `airflow.utils.dag_parsing_context.get_parsing_cont
67 | get_parsing_context()
| ^^^^^^^^^^^^^^^^^^^ AIR311
|
= help: Use `airflow.sdk.get_parsing_context` instead
= help: Use `get_parsing_context` from `airflow.sdk` instead.

View File

@@ -10,7 +10,7 @@ AIR312.py:32:1: AIR312 `airflow.hooks.filesystem.FSHook` is deprecated and moved
33 | PackageIndexHook()
34 | SubprocessHook()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.hooks.filesystem.FSHook` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `FSHook` from `airflow.providers.standard.hooks.filesystem` instead.
AIR312.py:33:1: AIR312 `airflow.hooks.package_index.PackageIndexHook` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -20,7 +20,7 @@ AIR312.py:33:1: AIR312 `airflow.hooks.package_index.PackageIndexHook` is depreca
34 | SubprocessHook()
35 | BashOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.hooks.package_index.PackageIndexHook` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `PackageIndexHook` from `airflow.providers.standard.hooks.package_index` instead.
AIR312.py:34:1: AIR312 `airflow.hooks.subprocess.SubprocessHook` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -31,7 +31,7 @@ AIR312.py:34:1: AIR312 `airflow.hooks.subprocess.SubprocessHook` is deprecated a
35 | BashOperator()
36 | BranchDateTimeOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.hooks.subprocess.SubprocessHook` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `SubprocessHook` from `airflow.providers.standard.hooks.subprocess` instead.
AIR312.py:35:1: AIR312 `airflow.operators.bash.BashOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -42,7 +42,7 @@ AIR312.py:35:1: AIR312 `airflow.operators.bash.BashOperator` is deprecated and m
36 | BranchDateTimeOperator()
37 | TriggerDagRunOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.bash.BashOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `BashOperator` from `airflow.providers.standard.operators.bash` instead.
AIR312.py:36:1: AIR312 `airflow.operators.datetime.BranchDateTimeOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -53,7 +53,7 @@ AIR312.py:36:1: AIR312 `airflow.operators.datetime.BranchDateTimeOperator` is de
37 | TriggerDagRunOperator()
38 | EmptyOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.datetime.BranchDateTimeOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `BranchDateTimeOperator` from `airflow.providers.standard.operators.datetime` instead.
AIR312.py:37:1: AIR312 `airflow.operators.trigger_dagrun.TriggerDagRunOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -64,7 +64,7 @@ AIR312.py:37:1: AIR312 `airflow.operators.trigger_dagrun.TriggerDagRunOperator`
38 | EmptyOperator()
39 | LatestOnlyOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `TriggerDagRunOperator` from `airflow.providers.standard.operators.trigger_dagrun` instead.
AIR312.py:38:1: AIR312 `airflow.operators.empty.EmptyOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -75,7 +75,7 @@ AIR312.py:38:1: AIR312 `airflow.operators.empty.EmptyOperator` is deprecated and
39 | LatestOnlyOperator()
40 | (
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.operators.empty.EmptyOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `EmptyOperator` from `airflow.providers.standard.operators.empty` instead.
AIR312.py:39:1: AIR312 `airflow.operators.latest_only.LatestOnlyOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -86,7 +86,7 @@ AIR312.py:39:1: AIR312 `airflow.operators.latest_only.LatestOnlyOperator` is dep
40 | (
41 | BranchPythonOperator(),
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.operators.latest_only.LatestOnlyOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `LatestOnlyOperator` from `airflow.providers.standard.operators.latest_only` instead.
AIR312.py:41:5: AIR312 `airflow.operators.python.BranchPythonOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -97,7 +97,7 @@ AIR312.py:41:5: AIR312 `airflow.operators.python.BranchPythonOperator` is deprec
42 | PythonOperator(),
43 | PythonVirtualenvOperator(),
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.python.BranchPythonOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `BranchPythonOperator` from `airflow.providers.standard.operators.python` instead.
AIR312.py:42:5: AIR312 `airflow.operators.python.PythonOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -108,7 +108,7 @@ AIR312.py:42:5: AIR312 `airflow.operators.python.PythonOperator` is deprecated a
43 | PythonVirtualenvOperator(),
44 | ShortCircuitOperator(),
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.python.PythonOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `PythonOperator` from `airflow.providers.standard.operators.python` instead.
AIR312.py:43:5: AIR312 `airflow.operators.python.PythonVirtualenvOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -119,7 +119,7 @@ AIR312.py:43:5: AIR312 `airflow.operators.python.PythonVirtualenvOperator` is de
44 | ShortCircuitOperator(),
45 | )
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.python.PythonVirtualenvOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `PythonVirtualenvOperator` from `airflow.providers.standard.operators.python` instead.
AIR312.py:44:5: AIR312 `airflow.operators.python.ShortCircuitOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -130,7 +130,7 @@ AIR312.py:44:5: AIR312 `airflow.operators.python.ShortCircuitOperator` is deprec
45 | )
46 | BranchDayOfWeekOperator()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.python.ShortCircuitOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `ShortCircuitOperator` from `airflow.providers.standard.operators.python` instead.
AIR312.py:46:1: AIR312 `airflow.operators.weekday.BranchDayOfWeekOperator` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -141,7 +141,7 @@ AIR312.py:46:1: AIR312 `airflow.operators.weekday.BranchDayOfWeekOperator` is de
47 | DateTimeSensor(), DateTimeSensorAsync()
48 | ExternalTaskMarker(), ExternalTaskSensor()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.operators.weekday.BranchDayOfWeekOperator` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `BranchDayOfWeekOperator` from `airflow.providers.standard.operators.weekday` instead.
AIR312.py:47:1: AIR312 `airflow.sensors.date_time.DateTimeSensor` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -152,7 +152,7 @@ AIR312.py:47:1: AIR312 `airflow.sensors.date_time.DateTimeSensor` is deprecated
48 | ExternalTaskMarker(), ExternalTaskSensor()
49 | FileSensor()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.sensors.date_time.DateTimeSensor` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `DateTimeSensor` from `airflow.providers.standard.sensors.date_time` instead.
AIR312.py:47:19: AIR312 `airflow.sensors.date_time.DateTimeSensorAsync` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -163,7 +163,7 @@ AIR312.py:47:19: AIR312 `airflow.sensors.date_time.DateTimeSensorAsync` is depre
48 | ExternalTaskMarker(), ExternalTaskSensor()
49 | FileSensor()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.sensors.date_time.DateTimeSensorAsync` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `DateTimeSensorAsync` from `airflow.providers.standard.sensors.date_time` instead.
AIR312.py:48:1: AIR312 `airflow.sensors.external_task.ExternalTaskMarker` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -174,7 +174,7 @@ AIR312.py:48:1: AIR312 `airflow.sensors.external_task.ExternalTaskMarker` is dep
49 | FileSensor()
50 | TimeSensor(), TimeSensorAsync()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.sensors.external_task.ExternalTaskMarker` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `ExternalTaskMarker` from `airflow.providers.standard.sensors.external_task` instead.
AIR312.py:48:23: AIR312 `airflow.sensors.external_task.ExternalTaskSensor` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -185,7 +185,7 @@ AIR312.py:48:23: AIR312 `airflow.sensors.external_task.ExternalTaskSensor` is de
49 | FileSensor()
50 | TimeSensor(), TimeSensorAsync()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.sensors.external_task.ExternalTaskSensor` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `ExternalTaskSensor` from `airflow.providers.standard.sensors.external_task` instead.
AIR312.py:49:1: AIR312 `airflow.sensors.filesystem.FileSensor` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -196,7 +196,7 @@ AIR312.py:49:1: AIR312 `airflow.sensors.filesystem.FileSensor` is deprecated and
50 | TimeSensor(), TimeSensorAsync()
51 | TimeDeltaSensor(), TimeDeltaSensorAsync()
|
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `airflow.providers.standard.sensors.filesystem.FileSensor` instead.
= help: Install `apache-airflow-providers-standard>=0.0.2` and use `FileSensor` from `airflow.providers.standard.sensors.filesystem` instead.
AIR312.py:50:1: AIR312 `airflow.sensors.time_sensor.TimeSensor` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -207,7 +207,7 @@ AIR312.py:50:1: AIR312 `airflow.sensors.time_sensor.TimeSensor` is deprecated an
51 | TimeDeltaSensor(), TimeDeltaSensorAsync()
52 | DayOfWeekSensor()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.sensors.time.TimeSensor` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `TimeSensor` from `airflow.providers.standard.sensors.time` instead.
AIR312.py:50:15: AIR312 `airflow.sensors.time_sensor.TimeSensorAsync` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -218,7 +218,7 @@ AIR312.py:50:15: AIR312 `airflow.sensors.time_sensor.TimeSensorAsync` is depreca
51 | TimeDeltaSensor(), TimeDeltaSensorAsync()
52 | DayOfWeekSensor()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.sensors.time.TimeSensorAsync` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `TimeSensorAsync` from `airflow.providers.standard.sensors.time` instead.
AIR312.py:51:1: AIR312 `airflow.sensors.time_delta.TimeDeltaSensor` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -229,7 +229,7 @@ AIR312.py:51:1: AIR312 `airflow.sensors.time_delta.TimeDeltaSensor` is deprecate
52 | DayOfWeekSensor()
53 | DagStateTrigger(), WorkflowTrigger()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.sensors.time_delta.TimeDeltaSensor` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `TimeDeltaSensor` from `airflow.providers.standard.sensors.time_delta` instead.
AIR312.py:51:20: AIR312 `airflow.sensors.time_delta.TimeDeltaSensorAsync` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -240,7 +240,7 @@ AIR312.py:51:20: AIR312 `airflow.sensors.time_delta.TimeDeltaSensorAsync` is dep
52 | DayOfWeekSensor()
53 | DagStateTrigger(), WorkflowTrigger()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.sensors.time_delta.TimeDeltaSensorAsync` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `TimeDeltaSensorAsync` from `airflow.providers.standard.sensors.time_delta` instead.
AIR312.py:52:1: AIR312 `airflow.sensors.weekday.DayOfWeekSensor` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -251,7 +251,7 @@ AIR312.py:52:1: AIR312 `airflow.sensors.weekday.DayOfWeekSensor` is deprecated a
53 | DagStateTrigger(), WorkflowTrigger()
54 | FileTrigger()
|
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `airflow.providers.standard.sensors.weekday.DayOfWeekSensor` instead.
= help: Install `apache-airflow-providers-standard>=0.0.1` and use `DayOfWeekSensor` from `airflow.providers.standard.sensors.weekday` instead.
AIR312.py:53:1: AIR312 `airflow.triggers.external_task.DagStateTrigger` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -262,7 +262,7 @@ AIR312.py:53:1: AIR312 `airflow.triggers.external_task.DagStateTrigger` is depre
54 | FileTrigger()
55 | DateTimeTrigger(), TimeDeltaTrigger()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.triggers.external_task.DagStateTrigger` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `DagStateTrigger` from `airflow.providers.standard.triggers.external_task` instead.
AIR312.py:53:20: AIR312 `airflow.triggers.external_task.WorkflowTrigger` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -273,7 +273,7 @@ AIR312.py:53:20: AIR312 `airflow.triggers.external_task.WorkflowTrigger` is depr
54 | FileTrigger()
55 | DateTimeTrigger(), TimeDeltaTrigger()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.triggers.external_task.WorkflowTrigger` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `WorkflowTrigger` from `airflow.providers.standard.triggers.external_task` instead.
AIR312.py:54:1: AIR312 `airflow.triggers.file.FileTrigger` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -283,7 +283,7 @@ AIR312.py:54:1: AIR312 `airflow.triggers.file.FileTrigger` is deprecated and mov
| ^^^^^^^^^^^ AIR312
55 | DateTimeTrigger(), TimeDeltaTrigger()
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.triggers.file.FileTrigger` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `FileTrigger` from `airflow.providers.standard.triggers.file` instead.
AIR312.py:55:1: AIR312 `airflow.triggers.temporal.DateTimeTrigger` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -292,7 +292,7 @@ AIR312.py:55:1: AIR312 `airflow.triggers.temporal.DateTimeTrigger` is deprecated
55 | DateTimeTrigger(), TimeDeltaTrigger()
| ^^^^^^^^^^^^^^^ AIR312
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.triggers.temporal.DateTimeTrigger` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `DateTimeTrigger` from `airflow.providers.standard.triggers.temporal` instead.
AIR312.py:55:20: AIR312 `airflow.triggers.temporal.TimeDeltaTrigger` is deprecated and moved into `standard` provider in Airflow 3.0; It still works in Airflow 3.0 but is expected to be removed in a future version.
|
@@ -301,4 +301,4 @@ AIR312.py:55:20: AIR312 `airflow.triggers.temporal.TimeDeltaTrigger` is deprecat
55 | DateTimeTrigger(), TimeDeltaTrigger()
| ^^^^^^^^^^^^^^^^ AIR312
|
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `airflow.providers.standard.triggers.temporal.TimeDeltaTrigger` instead.
= help: Install `apache-airflow-providers-standard>=0.0.3` and use `TimeDeltaTrigger` from `airflow.providers.standard.triggers.temporal` instead.

View File

@@ -52,17 +52,20 @@ impl Violation for SysVersionCmpStr3 {
/// ## What it does
/// Checks for equality comparisons against the major version returned by
/// `sys.version_info` (e.g., `sys.version_info[0] == 3`).
/// `sys.version_info` (e.g., `sys.version_info[0] == 3` or `sys.version_info[0] != 3`).
///
/// ## Why is this bad?
/// Using `sys.version_info[0] == 3` to verify that the major version is
/// Python 3 or greater will fail if the major version number is ever
/// incremented (e.g., to Python 4). This is likely unintended, as code
/// that uses this comparison is likely intended to be run on Python 2,
/// but would now run on Python 4 too.
/// but would now run on Python 4 too. Similarly, using `sys.version_info[0] != 3`
/// to check for Python 2 will also fail if the major version number is
/// incremented.
///
/// Instead, use `>=` to check if the major version number is 3 or greater,
/// to future-proof the code.
/// or `<` to check if the major version number is less than 3, to future-proof
/// the code.
///
/// ## Example
/// ```python
@@ -88,12 +91,18 @@ impl Violation for SysVersionCmpStr3 {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)]
pub(crate) struct SysVersionInfo0Eq3;
pub(crate) struct SysVersionInfo0Eq3 {
eq: bool,
}
impl Violation for SysVersionInfo0Eq3 {
#[derive_message_formats]
fn message(&self) -> String {
"`sys.version_info[0] == 3` referenced (python4), use `>=`".to_string()
if self.eq {
"`sys.version_info[0] == 3` referenced (python4), use `>=`".to_string()
} else {
"`sys.version_info[0] != 3` referenced (python4), use `<`".to_string()
}
}
}
@@ -235,7 +244,7 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
{
if *i == 0 {
if let (
[CmpOp::Eq | CmpOp::NotEq],
[operator @ (CmpOp::Eq | CmpOp::NotEq)],
[
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(n),
@@ -246,7 +255,9 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
{
if *n == 3 && checker.enabled(Rule::SysVersionInfo0Eq3) {
checker.report_diagnostic(Diagnostic::new(
SysVersionInfo0Eq3,
SysVersionInfo0Eq3 {
eq: matches!(*operator, CmpOp::Eq),
},
left.range(),
));
}

View File

@@ -20,7 +20,7 @@ YTT201.py:8:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
10 | PY2 = version_info[0] != 3
|
YTT201.py:9:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
YTT201.py:9:7: YTT201 `sys.version_info[0] != 3` referenced (python4), use `<`
|
7 | PY3 = sys.version_info[0] == 3
8 | PY3 = version_info[0] == 3
@@ -29,7 +29,7 @@ YTT201.py:9:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
10 | PY2 = version_info[0] != 3
|
YTT201.py:10:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
YTT201.py:10:7: YTT201 `sys.version_info[0] != 3` referenced (python4), use `<`
|
8 | PY3 = version_info[0] == 3
9 | PY2 = sys.version_info[0] != 3

View File

@@ -62,7 +62,25 @@ pub(crate) fn async_zero_sleep(checker: &Checker, call: &ExprCall) {
return;
}
let Some(arg) = call.arguments.find_argument_value("seconds", 0) else {
let Some(qualified_name) = checker
.semantic()
.resolve_qualified_name(call.func.as_ref())
else {
return;
};
let Some(module) = AsyncModule::try_from(&qualified_name) else {
return;
};
// Determine the correct argument name
let arg_name = match module {
AsyncModule::Trio => "seconds",
AsyncModule::AnyIo => "delay",
AsyncModule::AsyncIo => return,
};
let Some(arg) = call.arguments.find_argument_value(arg_name, 0) else {
return;
};

View File

@@ -71,7 +71,25 @@ pub(crate) fn long_sleep_not_forever(checker: &Checker, call: &ExprCall) {
return;
}
let Some(arg) = call.arguments.find_argument_value("seconds", 0) else {
let Some(qualified_name) = checker
.semantic()
.resolve_qualified_name(call.func.as_ref())
else {
return;
};
let Some(module) = AsyncModule::try_from(&qualified_name) else {
return;
};
// Determine the correct argument name
let arg_name = match module {
AsyncModule::Trio => "seconds",
AsyncModule::AnyIo => "delay",
AsyncModule::AsyncIo => return,
};
let Some(arg) = call.arguments.find_argument_value(arg_name, 0) else {
return;
};

View File

@@ -214,3 +214,41 @@ ASYNC115.py:128:15: ASYNC115 [*] Use `anyio.lowlevel.checkpoint()` instead of `a
129 129 |
130 130 |
131 131 | def func():
ASYNC115.py:156:11: ASYNC115 [*] Use `anyio.lowlevel.checkpoint()` instead of `anyio.sleep(0)`
|
154 | await anyio.sleep(seconds=1) # OK
155 |
156 | await anyio.sleep(delay=0) # ASYNC115
| ^^^^^^^^^^^^^^^^^^^^ ASYNC115
157 | await anyio.sleep(seconds=0) # OK
|
= help: Replace with `anyio.lowlevel.checkpoint()`
Safe fix
153 153 | await anyio.sleep(delay=1) # OK
154 154 | await anyio.sleep(seconds=1) # OK
155 155 |
156 |- await anyio.sleep(delay=0) # ASYNC115
156 |+ await anyio.lowlevel.checkpoint() # ASYNC115
157 157 | await anyio.sleep(seconds=0) # OK
158 158 |
159 159 |
ASYNC115.py:166:11: ASYNC115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
164 | await trio.sleep(delay=1) # OK
165 |
166 | await trio.sleep(seconds=0) # ASYNC115
| ^^^^^^^^^^^^^^^^^^^^^ ASYNC115
167 | await trio.sleep(delay=0) # OK
|
= help: Replace with `trio.lowlevel.checkpoint()`
Safe fix
163 163 | await trio.sleep(seconds=1) # OK
164 164 | await trio.sleep(delay=1) # OK
165 165 |
166 |- await trio.sleep(seconds=0) # ASYNC115
166 |+ await trio.lowlevel.checkpoint() # ASYNC115
167 167 | await trio.sleep(delay=0) # OK

View File

@@ -289,3 +289,44 @@ ASYNC116.py:110:11: ASYNC116 [*] `anyio.sleep()` with >24 hour interval should u
109 110 | # catch from import
110 |- await sleep(86401) # error: 116, "async"
111 |+ await sleep_forever() # error: 116, "async"
111 112 |
112 113 |
113 114 | async def test_anyio_async116_helpers():
ASYNC116.py:119:11: ASYNC116 [*] `anyio.sleep()` with >24 hour interval should usually be `anyio.sleep_forever()`
|
117 | await anyio.sleep(seconds=1) # OK
118 |
119 | await anyio.sleep(delay=86401) # ASYNC116
| ^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC116
120 | await anyio.sleep(seconds=86401) # OK
|
= help: Replace with `anyio.sleep_forever()`
Unsafe fix
116 116 | await anyio.sleep(delay=1) # OK
117 117 | await anyio.sleep(seconds=1) # OK
118 118 |
119 |- await anyio.sleep(delay=86401) # ASYNC116
119 |+ await anyio.sleep_forever() # ASYNC116
120 120 | await anyio.sleep(seconds=86401) # OK
121 121 |
122 122 |
ASYNC116.py:129:11: ASYNC116 [*] `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()`
|
127 | await trio.sleep(delay=1) # OK
128 |
129 | await trio.sleep(seconds=86401) # ASYNC116
| ^^^^^^^^^^^^^^^^^^^^^^^^^ ASYNC116
130 | await trio.sleep(delay=86401) # OK
|
= help: Replace with `trio.sleep_forever()`
Unsafe fix
126 126 | await trio.sleep(seconds=1) # OK
127 127 | await trio.sleep(delay=1) # OK
128 128 |
129 |- await trio.sleep(seconds=86401) # ASYNC116
129 |+ await trio.sleep_forever() # ASYNC116
130 130 | await trio.sleep(delay=86401) # OK

View File

@@ -1,7 +1,7 @@
use itertools::Itertools;
use ruff_python_ast::{Alias, Stmt};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Fix};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged;
@@ -123,9 +123,19 @@ pub(crate) fn unnecessary_future_import(checker: &Checker, stmt: &Stmt, names: &
checker.stylist(),
checker.indexer(),
)?;
Ok(Fix::safe_edit(edit).isolate(Checker::isolation(
checker.semantic().current_statement_parent_id(),
)))
let range = edit.range();
let applicability = if checker.comment_ranges().intersects(range) {
Applicability::Unsafe
} else {
Applicability::Safe
};
Ok(
Fix::applicable_edit(edit, applicability).isolate(Checker::isolation(
checker.semantic().current_statement_parent_id(),
)),
)
});
checker.report_diagnostic(diagnostic);
}

View File

@@ -156,6 +156,7 @@ UP010.py:13:5: UP010 [*] Unnecessary `__future__` import `generator_stop` for ta
13 | from __future__ import generator_stop
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP010
14 | from __future__ import invalid_module, generators
15 | from __future__ import generators # comment
|
= help: Remove unnecessary `__future__` import
@@ -165,6 +166,7 @@ UP010.py:13:5: UP010 [*] Unnecessary `__future__` import `generator_stop` for ta
12 12 | if True:
13 |- from __future__ import generator_stop
14 13 | from __future__ import invalid_module, generators
15 14 | from __future__ import generators # comment
UP010.py:14:5: UP010 [*] Unnecessary `__future__` import `generators` for target Python version
|
@@ -172,6 +174,7 @@ UP010.py:14:5: UP010 [*] Unnecessary `__future__` import `generators` for target
13 | from __future__ import generator_stop
14 | from __future__ import invalid_module, generators
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP010
15 | from __future__ import generators # comment
|
= help: Remove unnecessary `__future__` import
@@ -181,3 +184,19 @@ UP010.py:14:5: UP010 [*] Unnecessary `__future__` import `generators` for target
13 13 | from __future__ import generator_stop
14 |- from __future__ import invalid_module, generators
14 |+ from __future__ import invalid_module
15 15 | from __future__ import generators # comment
UP010.py:15:5: UP010 [*] Unnecessary `__future__` import `generators` for target Python version
|
13 | from __future__ import generator_stop
14 | from __future__ import invalid_module, generators
15 | from __future__ import generators # comment
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP010
|
= help: Remove unnecessary `__future__` import
Unsafe fix
12 12 | if True:
13 13 | from __future__ import generator_stop
14 14 | from __future__ import invalid_module, generators
15 |- from __future__ import generators # comment

View File

@@ -41,6 +41,8 @@ getrandom = { workspace = true, features = ["wasm_js"] }
serde = { workspace = true }
serde-wasm-bindgen = { workspace = true }
wasm-bindgen = { workspace = true }
# Not a direct dependency but required to compile for Wasm.
uuid = { workspace = true, features = ["js"] }
[dev-dependencies]
wasm-bindgen-test = { workspace = true }

7
crates/ty/docs/cli.md generated
View File

@@ -1,3 +1,5 @@
<!-- WARNING: This file is auto-generated (cargo dev generate-all). Edit the doc comments in 'crates/ty/src/args.rs' if you want to change anything here. -->
# CLI Reference
## ty
@@ -41,7 +43,10 @@ ty check [OPTIONS] [PATH]...
<li><code>auto</code>: Display colors if the output goes to an interactive terminal</li>
<li><code>always</code>: Always display colors</li>
<li><code>never</code>: Never display colors</li>
</ul></dd><dt id="ty-check--config"><a href="#ty-check--config"><code>--config</code></a>, <code>-c</code> <i>config-option</i></dt><dd><p>A TOML <code>&lt;KEY&gt; = &lt;VALUE&gt;</code> pair</p>
</ul></dd><dt id="ty-check--config"><a href="#ty-check--config"><code>--config</code></a>, <code>-c</code> <i>config-option</i></dt><dd><p>A TOML <code>&lt;KEY&gt; = &lt;VALUE&gt;</code> pair (such as you might find in a <code>ty.toml</code> configuration file)
overriding a specific configuration option.</p>
<p>Overrides of individual settings using this option always take precedence
over all configuration files.</p>
</dd><dt id="ty-check--error"><a href="#ty-check--error"><code>--error</code></a> <i>rule</i></dt><dd><p>Treat the given rule as having severity 'error'. Can be specified multiple times.</p>
</dd><dt id="ty-check--error-on-warning"><a href="#ty-check--error-on-warning"><code>--error-on-warning</code></a></dt><dd><p>Use exit code 1 if there are any warning-level diagnostics</p>
</dd><dt id="ty-check--exit-zero"><a href="#ty-check--exit-zero"><code>--exit-zero</code></a></dt><dd><p>Always use exit code 0, even when there are error-level diagnostics</p>

View File

@@ -1,3 +1,5 @@
<!-- WARNING: This file is auto-generated (cargo dev generate-all). Update the doc comments on the 'Options' struct in 'crates/ty_project/src/metadata/options.rs' if you want to change anything here. -->
# Configuration
#### `respect-ignore-files`
@@ -170,6 +172,9 @@ If left unspecified, ty will try to detect common project layouts and initialize
* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
* otherwise, default to `.` (flat layout)
Besides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
it will also be included in the first party search path.
**Default value**: `null`
**Type**: `str`

110
crates/ty/docs/rules.md generated
View File

@@ -1,3 +1,5 @@
<!-- WARNING: This file is auto-generated (cargo dev generate-all). Edit the lint-declarations in 'crates/ty_python_semantic/src/types/diagnostic.rs' if you want to change anything here. -->
# Rules
## `byte-string-type-annotation`
@@ -50,7 +52,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L90)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L91)
</details>
## `conflicting-argument-forms`
@@ -81,7 +83,7 @@ f(int) # error
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L134)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L135)
</details>
## `conflicting-declarations`
@@ -111,7 +113,7 @@ a = 1
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L160)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L161)
</details>
## `conflicting-metaclass`
@@ -142,7 +144,7 @@ class C(A, B): ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L185)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L186)
</details>
## `cyclic-class-definition`
@@ -173,7 +175,7 @@ class B(A): ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L211)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L212)
</details>
## `duplicate-base`
@@ -199,7 +201,7 @@ class B(A, A): ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L255)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L256)
</details>
## `escape-character-in-forward-annotation`
@@ -336,7 +338,7 @@ TypeError: multiple bases have instance lay-out conflict
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20incompatible-slots)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L276)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L277)
</details>
## `inconsistent-mro`
@@ -365,7 +367,7 @@ class C(A, B): ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L362)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L363)
</details>
## `index-out-of-bounds`
@@ -390,7 +392,7 @@ t[3] # IndexError: tuple index out of range
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L386)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L387)
</details>
## `invalid-argument-type`
@@ -416,7 +418,7 @@ func("foo") # error: [invalid-argument-type]
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L406)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L407)
</details>
## `invalid-assignment`
@@ -443,7 +445,7 @@ a: int = ''
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L446)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L447)
</details>
## `invalid-attribute-access`
@@ -476,7 +478,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1394)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1395)
</details>
## `invalid-base`
@@ -499,7 +501,7 @@ class A(42): ... # error: [invalid-base]
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L468)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L469)
</details>
## `invalid-context-manager`
@@ -525,7 +527,7 @@ with 1:
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L519)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L520)
</details>
## `invalid-declaration`
@@ -553,7 +555,7 @@ a: str
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L540)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L541)
</details>
## `invalid-exception-caught`
@@ -594,7 +596,7 @@ except ZeroDivisionError:
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L563)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L564)
</details>
## `invalid-generic-class`
@@ -625,7 +627,7 @@ class C[U](Generic[T]): ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L599)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L600)
</details>
## `invalid-legacy-type-variable`
@@ -658,7 +660,7 @@ def f(t: TypeVar("U")): ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L625)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L626)
</details>
## `invalid-metaclass`
@@ -690,7 +692,7 @@ class B(metaclass=f): ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L674)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L675)
</details>
## `invalid-overload`
@@ -738,7 +740,7 @@ def foo(x: int) -> int: ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L701)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L702)
</details>
## `invalid-parameter-default`
@@ -763,7 +765,7 @@ def f(a: int = ''): ...
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L744)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L745)
</details>
## `invalid-protocol`
@@ -796,7 +798,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L334)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L335)
</details>
## `invalid-raise`
@@ -844,7 +846,7 @@ def g():
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L764)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L765)
</details>
## `invalid-return-type`
@@ -868,7 +870,7 @@ def func() -> int:
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L427)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L428)
</details>
## `invalid-super-argument`
@@ -912,7 +914,7 @@ super(B, A) # error: `A` does not satisfy `issubclass(A, B)`
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L807)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L808)
</details>
## `invalid-syntax-in-forward-annotation`
@@ -952,7 +954,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L653)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L654)
</details>
## `invalid-type-checking-constant`
@@ -981,7 +983,7 @@ TYPE_CHECKING = ''
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L846)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L847)
</details>
## `invalid-type-form`
@@ -1010,7 +1012,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L870)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L871)
</details>
## `invalid-type-variable-constraints`
@@ -1044,7 +1046,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L894)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L895)
</details>
## `missing-argument`
@@ -1068,7 +1070,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L923)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L924)
</details>
## `no-matching-overload`
@@ -1096,7 +1098,7 @@ func("string") # error: [no-matching-overload]
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L942)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L943)
</details>
## `non-subscriptable`
@@ -1119,7 +1121,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L965)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L966)
</details>
## `not-iterable`
@@ -1144,7 +1146,7 @@ for i in 34: # TypeError: 'int' object is not iterable
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L983)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L984)
</details>
## `parameter-already-assigned`
@@ -1170,7 +1172,7 @@ f(1, x=2) # Error raised here
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1034)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1035)
</details>
## `raw-string-type-annotation`
@@ -1229,7 +1231,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1370)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1371)
</details>
## `subclass-of-final-class`
@@ -1257,7 +1259,7 @@ class B(A): ... # Error raised here
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1125)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1126)
</details>
## `too-many-positional-arguments`
@@ -1283,7 +1285,7 @@ f("foo") # Error raised here
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1170)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1171)
</details>
## `type-assertion-failure`
@@ -1310,7 +1312,7 @@ def _(x: int):
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1148)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1149)
</details>
## `unavailable-implicit-super-arguments`
@@ -1354,7 +1356,7 @@ class A:
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1191)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1192)
</details>
## `unknown-argument`
@@ -1380,7 +1382,7 @@ f(x=1, y=2) # Error raised here
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1248)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1249)
</details>
## `unresolved-attribute`
@@ -1407,7 +1409,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1269)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1270)
</details>
## `unresolved-import`
@@ -1431,7 +1433,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1291)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1292)
</details>
## `unresolved-reference`
@@ -1455,7 +1457,7 @@ print(x) # NameError: name 'x' is not defined
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1310)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1311)
</details>
## `unsupported-bool-conversion`
@@ -1491,7 +1493,7 @@ b1 < b2 < b1 # exception raised here
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1003)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1004)
</details>
## `unsupported-operator`
@@ -1518,7 +1520,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1329)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1330)
</details>
## `zero-stepsize-in-slice`
@@ -1542,7 +1544,7 @@ l[1:10:0] # ValueError: slice step cannot be zero
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1351)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1352)
</details>
## `invalid-ignore-comment`
@@ -1598,7 +1600,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1055)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1056)
</details>
## `possibly-unbound-implicit-call`
@@ -1629,7 +1631,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-implicit-call)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L108)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L109)
</details>
## `possibly-unbound-import`
@@ -1660,7 +1662,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1077)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1078)
</details>
## `redundant-cast`
@@ -1686,7 +1688,7 @@ cast(int, f()) # Redundant
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1422)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1423)
</details>
## `undefined-reveal`
@@ -1709,7 +1711,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1230)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1231)
</details>
## `unknown-rule`
@@ -1777,7 +1779,7 @@ class D(C): ... # error: [unsupported-base]
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L486)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L487)
</details>
## `division-by-zero`
@@ -1800,7 +1802,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L237)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L238)
</details>
## `possibly-unresolved-reference`
@@ -1827,7 +1829,7 @@ print(x) # NameError: name 'x' is not defined
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1103)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1104)
</details>
## `unused-ignore-comment`

View File

@@ -322,9 +322,11 @@ pub(crate) enum TerminalColor {
/// Never display colors.
Never,
}
/// A TOML `<KEY> = <VALUE>` pair
/// (such as you might find in a `ty.toml` configuration file)
/// overriding a specific configuration option.
///
/// Overrides of individual settings using this option always take precedence
/// over all configuration files.
#[derive(Debug, Clone)]
@@ -359,7 +361,15 @@ impl clap::Args for ConfigsArg {
.short('c')
.long("config")
.value_name("CONFIG_OPTION")
.help("A TOML `<KEY> = <VALUE>` pair")
.help("A TOML `<KEY> = <VALUE>` pair overriding a specific configuration option.")
.long_help(
"
A TOML `<KEY> = <VALUE>` pair (such as you might find in a `ty.toml` configuration file)
overriding a specific configuration option.
Overrides of individual settings using this option always take precedence
over all configuration files.",
)
.action(ArgAction::Append),
)
}

View File

@@ -243,12 +243,14 @@ impl MainLoop {
MainLoopMessage::CheckWorkspace => {
let db = db.clone();
let sender = self.sender.clone();
let mut reporter = R::default();
// Spawn a new task that checks the project. This needs to be done in a separate thread
// to prevent blocking the main loop here.
rayon::spawn(move || {
match db.check_with_reporter(&mut reporter) {
match salsa::Cancelled::catch(|| {
let mut reporter = R::default();
db.check_with_reporter(&mut reporter)
}) {
Ok(result) => {
// Send the result back to the main loop for printing.
sender

View File

@@ -1454,10 +1454,10 @@ fn cli_config_args_toml_string_basic() -> anyhow::Result<()> {
}
#[test]
fn cli_config_args_overrides_knot_toml() -> anyhow::Result<()> {
fn cli_config_args_overrides_ty_toml() -> anyhow::Result<()> {
let case = TestCase::with_files(vec![
(
"knot.toml",
"ty.toml",
r#"
[terminal]
error-on-warning = true
@@ -1465,6 +1465,27 @@ fn cli_config_args_overrides_knot_toml() -> anyhow::Result<()> {
),
("test.py", r"print(x) # [unresolved-reference]"),
])?;
// Exit code of 1 due to the setting in `ty.toml`
assert_cmd_snapshot!(case.command().arg("--warn").arg("unresolved-reference"), @r"
success: false
exit_code: 1
----- stdout -----
warning[unresolved-reference]: Name `x` used when not defined
--> test.py:1:7
|
1 | print(x) # [unresolved-reference]
| ^
|
info: rule `unresolved-reference` was selected on the command line
Found 1 diagnostic
----- stderr -----
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
");
// Exit code of 0 because the `ty.toml` setting is overwritten by `--config`
assert_cmd_snapshot!(case.command().arg("--warn").arg("unresolved-reference").arg("--config").arg("terminal.error-on-warning=false"), @r"
success: true
exit_code: 0
@@ -1534,6 +1555,83 @@ fn cli_config_args_invalid_option() -> anyhow::Result<()> {
Ok(())
}
/// The `site-packages` directory is used by ty for external import.
/// Ty does the following checks to discover the `site-packages` directory in the order:
/// 1) If `VIRTUAL_ENV` environment variable is set
/// 2) If `CONDA_PREFIX` environment variable is set
/// 3) If a `.venv` directory exists at the project root
///
/// This test is aiming at validating the logic around `CONDA_PREFIX`.
///
/// A conda-like environment file structure is used
/// We test by first not setting the `CONDA_PREFIX` and expect a fail.
/// Then we test by setting `CONDA_PREFIX` to `conda-env` and expect a pass.
///
/// ├── project
/// │ └── test.py
/// └── conda-env
/// └── lib
/// └── python3.13
/// └── site-packages
/// └── package1
/// └── __init__.py
///
/// test.py imports package1
/// And the command is run in the `project` directory.
#[test]
fn check_conda_prefix_var_to_resolve_path() -> anyhow::Result<()> {
let conda_package1_path = if cfg!(windows) {
"conda-env/Lib/site-packages/package1/__init__.py"
} else {
"conda-env/lib/python3.13/site-packages/package1/__init__.py"
};
let case = TestCase::with_files([
(
"project/test.py",
r#"
import package1
"#,
),
(
conda_package1_path,
r#"
"#,
),
])?;
assert_cmd_snapshot!(case.command().current_dir(case.root().join("project")), @r"
success: false
exit_code: 1
----- stdout -----
error[unresolved-import]: Cannot resolve imported module `package1`
--> test.py:2:8
|
2 | import package1
| ^^^^^^^^
|
info: make sure your Python environment is properly configured: https://github.com/astral-sh/ty/blob/main/docs/README.md#python-environment
info: rule `unresolved-import` is enabled by default
Found 1 diagnostic
----- stderr -----
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
");
// do command : CONDA_PREFIX=<temp_dir>/conda_env
assert_cmd_snapshot!(case.command().current_dir(case.root().join("project")).env("CONDA_PREFIX", case.root().join("conda-env")), @r"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
");
Ok(())
}
struct TestCase {
_temp_dir: TempDir,
_settings_scope: SettingsBindDropGuard,

View File

@@ -1135,7 +1135,7 @@ print(sys.last_exc, os.getegid())
Ok(())
})?;
let diagnostics = case.db.check().context("Failed to check project.")?;
let diagnostics = case.db.check();
assert_eq!(diagnostics.len(), 2);
assert_eq!(
@@ -1160,7 +1160,7 @@ print(sys.last_exc, os.getegid())
})
.expect("Search path settings to be valid");
let diagnostics = case.db.check().context("Failed to check project.")?;
let diagnostics = case.db.check();
assert!(diagnostics.is_empty());
Ok(())
@@ -1763,10 +1763,7 @@ fn changes_to_user_configuration() -> anyhow::Result<()> {
let foo = case
.system_file(case.project_path("foo.py"))
.expect("foo.py to exist");
let diagnostics = case
.db()
.check_file(foo)
.context("Failed to check project.")?;
let diagnostics = case.db().check_file(foo);
assert!(
diagnostics.is_empty(),
@@ -1786,10 +1783,7 @@ fn changes_to_user_configuration() -> anyhow::Result<()> {
case.apply_changes(changes);
let diagnostics = case
.db()
.check_file(foo)
.context("Failed to check project.")?;
let diagnostics = case.db().check_file(foo);
assert!(
diagnostics.len() == 1,

View File

@@ -0,0 +1,30 @@
"""
Make sure that types are inferred for all subexpressions of the following
annotations involving ty_extension `_SpecialForm`s.
This is a regression test for https://github.com/astral-sh/ty/issues/366
"""
from ty_extensions import CallableTypeOf, Intersection, Not, TypeOf
class A: ...
class B: ...
def _(x: Not[A]):
pass
def _(x: Intersection[A], y: Intersection[A, B]):
pass
def _(x: TypeOf[1j]):
pass
def _(x: CallableTypeOf[str]):
pass

View File

@@ -8,8 +8,8 @@ use ruff_db::files::{File, Files};
use ruff_db::system::System;
use ruff_db::vendored::VendoredFileSystem;
use ruff_db::{Db as SourceDb, Upcast};
use salsa::Event;
use salsa::plumbing::ZalsaDatabase;
use salsa::{Cancelled, Event};
use ty_ide::Db as IdeDb;
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
use ty_python_semantic::{Db as SemanticDb, Program};
@@ -76,24 +76,21 @@ impl ProjectDatabase {
}
/// Checks all open files in the project and its dependencies.
pub fn check(&self) -> Result<Vec<Diagnostic>, Cancelled> {
pub fn check(&self) -> Vec<Diagnostic> {
let mut reporter = DummyReporter;
let reporter = AssertUnwindSafe(&mut reporter as &mut dyn Reporter);
self.with_db(|db| db.project().check(db, reporter))
self.project().check(self, reporter)
}
/// Checks all open files in the project and its dependencies, using the given reporter.
pub fn check_with_reporter(
&self,
reporter: &mut dyn Reporter,
) -> Result<Vec<Diagnostic>, Cancelled> {
pub fn check_with_reporter(&self, reporter: &mut dyn Reporter) -> Vec<Diagnostic> {
let reporter = AssertUnwindSafe(reporter);
self.with_db(|db| db.project().check(db, reporter))
self.project().check(self, reporter)
}
#[tracing::instrument(level = "debug", skip(self))]
pub fn check_file(&self, file: File) -> Result<Vec<Diagnostic>, Cancelled> {
self.with_db(|db| self.project().check_file(db, file))
pub fn check_file(&self, file: File) -> Vec<Diagnostic> {
self.project().check_file(self, file)
}
/// Returns a mutable reference to the system.
@@ -107,13 +104,6 @@ impl ProjectDatabase {
Arc::get_mut(&mut self.system)
.expect("ref count should be 1 because `zalsa_mut` drops all other DB references.")
}
pub(crate) fn with_db<F, T>(&self, f: F) -> Result<T, Cancelled>
where
F: FnOnce(&ProjectDatabase) -> T + std::panic::UnwindSafe,
{
Cancelled::catch(|| f(self))
}
}
impl Upcast<dyn SemanticDb> for ProjectDatabase {

View File

@@ -1029,6 +1029,52 @@ expected `.`, `]`
Ok(())
}
#[test]
fn src_root_with_tests() -> anyhow::Result<()> {
let system = TestSystem::default();
let root = SystemPathBuf::from("/app");
// pytest will find `tests/test_foo.py` and realize it is NOT part of a package
// given that there's no `__init__.py` file in the same folder.
// It will then add `tests` to `sys.path`
// in order to import `test_foo.py` as the module `test_foo`.
system
.memory_file_system()
.write_files_all([
(root.join("src/main.py"), ""),
(root.join("tests/conftest.py"), ""),
(root.join("tests/test_foo.py"), ""),
])
.context("Failed to write files")?;
let metadata = ProjectMetadata::discover(&root, &system)?;
let settings = metadata
.options
.to_program_settings(&root, "my_package", &system);
assert_eq!(
settings.search_paths.src_roots,
vec![root.clone(), root.join("src"), root.join("tests")]
);
// If `tests/__init__.py` is present, it is considered a package and `tests` is not added to `sys.path`.
system
.memory_file_system()
.write_file(root.join("tests/__init__.py"), "")
.context("Failed to write tests/__init__.py")?;
let metadata = ProjectMetadata::discover(&root, &system)?;
let settings = metadata
.options
.to_program_settings(&root, "my_package", &system);
assert_eq!(
settings.search_paths.src_roots,
vec![root.clone(), root.join("src")]
);
Ok(())
}
#[track_caller]
fn assert_error_eq(error: &ProjectDiscoveryError, message: &str) {
assert_eq!(error.to_string().replace('\\', "/"), message);

View File

@@ -135,7 +135,7 @@ impl Options {
} else {
let src = project_root.join("src");
if system.is_directory(&src) {
let mut roots = if system.is_directory(&src) {
// Default to `src` and the project root if `src` exists and the root hasn't been specified.
// This corresponds to the `src-layout`
tracing::debug!(
@@ -154,7 +154,24 @@ impl Options {
// Default to a [flat project structure](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/).
tracing::debug!("Defaulting `src.root` to `.`");
vec![project_root.to_path_buf()]
};
// Considering pytest test discovery conventions,
// we also include the `tests` directory if it exists and is not a package.
let tests_dir = project_root.join("tests");
if system.is_directory(&tests_dir)
&& !system.is_file(&tests_dir.join("__init__.py"))
&& !roots.contains(&tests_dir)
{
// If the `tests` directory exists and is not a package, include it as a source root.
tracing::debug!(
"Including `./tests` in `src.root` because a `./tests` directory exists"
);
roots.push(tests_dir);
}
roots
};
let (extra_paths, python, typeshed) = self
@@ -186,6 +203,11 @@ impl Options {
.ok()
.map(PythonPath::from_virtual_env_var)
})
.or_else(|| {
std::env::var("CONDA_PREFIX")
.ok()
.map(PythonPath::from_conda_prefix_var)
})
.unwrap_or_else(|| PythonPath::Discover(project_root.to_path_buf())),
}
}
@@ -387,6 +409,9 @@ pub struct SrcOptions {
/// * if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat)
/// * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
/// * otherwise, default to `.` (flat layout)
///
/// Besides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
/// it will also be included in the first party search path.
#[serde(skip_serializing_if = "Option::is_none")]
#[option(
default = r#"null"#,

View File

@@ -305,4 +305,37 @@ def _(c: Callable[[int], int]):
reveal_type(c.__call__) # revealed: (int, /) -> int
```
Unlike other type checkers, we do _not_ allow attributes to be accessed that would only be available
on function-like callables:
```py
def f_wrong(c: Callable[[], None]):
# error: [unresolved-attribute] "Type `() -> None` has no attribute `__qualname__`"
c.__qualname__
# error: [unresolved-attribute] "Unresolved attribute `__qualname__` on type `() -> None`."
c.__qualname__ = "my_callable"
```
We do this, because at runtime, calls to `f_wrong` with a non-function callable would raise an
`AttributeError`:
```py
class MyCallable:
def __call__(self) -> None:
pass
f_wrong(MyCallable()) # raises `AttributeError` at runtime
```
If users want to write to attributes such as `__qualname__`, they need to check the existence of the
attribute first:
```py
def f_okay(c: Callable[[], None]):
if hasattr(c, "__qualname__"):
c.__qualname__ # okay
c.__qualname__ = "my_callable" # also okay
```
[gradual form]: https://typing.python.org/en/latest/spec/glossary.html#term-gradual-form

View File

@@ -81,23 +81,22 @@ import typing
class ListSubclass(typing.List): ...
# revealed: tuple[<class 'ListSubclass'>, <class 'list[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
# revealed: tuple[<class 'ListSubclass'>, <class 'list[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(ListSubclass.__mro__)
class DictSubclass(typing.Dict): ...
# TODO: should not have multiple `Generic[]` elements
# revealed: tuple[<class 'DictSubclass'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], <class 'object'>]
# revealed: tuple[<class 'DictSubclass'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(DictSubclass.__mro__)
class SetSubclass(typing.Set): ...
# revealed: tuple[<class 'SetSubclass'>, <class 'set[Unknown]'>, <class 'MutableSet[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
# revealed: tuple[<class 'SetSubclass'>, <class 'set[Unknown]'>, <class 'MutableSet[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(SetSubclass.__mro__)
class FrozenSetSubclass(typing.FrozenSet): ...
# revealed: tuple[<class 'FrozenSetSubclass'>, <class 'frozenset[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
# revealed: tuple[<class 'FrozenSetSubclass'>, <class 'frozenset[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(FrozenSetSubclass.__mro__)
####################
@@ -106,30 +105,26 @@ reveal_type(FrozenSetSubclass.__mro__)
class ChainMapSubclass(typing.ChainMap): ...
# TODO: should not have multiple `Generic[]` elements
# revealed: tuple[<class 'ChainMapSubclass'>, <class 'ChainMap[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], <class 'object'>]
# revealed: tuple[<class 'ChainMapSubclass'>, <class 'ChainMap[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(ChainMapSubclass.__mro__)
class CounterSubclass(typing.Counter): ...
# TODO: Should have one `Generic[]` element, not three(!)
# revealed: tuple[<class 'CounterSubclass'>, <class 'Counter[Unknown]'>, <class 'dict[Unknown, int]'>, <class 'MutableMapping[Unknown, int]'>, <class 'Mapping[Unknown, int]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], typing.Generic[_T], <class 'object'>]
# revealed: tuple[<class 'CounterSubclass'>, <class 'Counter[Unknown]'>, <class 'dict[Unknown, int]'>, <class 'MutableMapping[Unknown, int]'>, <class 'Mapping[Unknown, int]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(CounterSubclass.__mro__)
class DefaultDictSubclass(typing.DefaultDict): ...
# TODO: Should not have multiple `Generic[]` elements
# revealed: tuple[<class 'DefaultDictSubclass'>, <class 'defaultdict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], <class 'object'>]
# revealed: tuple[<class 'DefaultDictSubclass'>, <class 'defaultdict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(DefaultDictSubclass.__mro__)
class DequeSubclass(typing.Deque): ...
# revealed: tuple[<class 'DequeSubclass'>, <class 'deque[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
# revealed: tuple[<class 'DequeSubclass'>, <class 'deque[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(DequeSubclass.__mro__)
class OrderedDictSubclass(typing.OrderedDict): ...
# TODO: Should not have multiple `Generic[]` elements
# revealed: tuple[<class 'OrderedDictSubclass'>, <class 'OrderedDict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], <class 'object'>]
# revealed: tuple[<class 'OrderedDictSubclass'>, <class 'OrderedDict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(OrderedDictSubclass.__mro__)
```

View File

@@ -1527,6 +1527,65 @@ def _(ns: argparse.Namespace):
reveal_type(ns.whatever) # revealed: Any
```
## Classes with custom `__getattribute__` methods
If a type provides a custom `__getattribute__`, we use its return type as the type for unknown
attributes. Note that this behavior differs from runtime, where `__getattribute__` is called
unconditionally, even for known attributes. The rationale for doing this is that it allows users to
specify more precise types for specific attributes, such as `x: str` in the example below. This
behavior matches other type checkers such as mypy and pyright.
```py
from typing import Any
class Foo:
x: str
def __getattribute__(self, attr: str) -> Any:
return 42
reveal_type(Foo().x) # revealed: str
reveal_type(Foo().y) # revealed: Any
```
A standard library example for a class with a custom `__getattribute__` method is `SimpleNamespace`:
```py
from types import SimpleNamespace
sn = SimpleNamespace(a="a")
reveal_type(sn.a) # revealed: Any
```
`__getattribute__` takes precedence over `__getattr__`:
```py
class C:
def __getattribute__(self, name: str) -> int:
return 1
def __getattr__(self, name: str) -> str:
return "a"
c = C()
reveal_type(c.x) # revealed: int
```
Like all dunder methods, `__getattribute__` is not looked up on instances:
```py
def external_getattribute(name) -> int:
return 1
class ThisFails:
def __init__(self):
self.__getattribute__ = external_getattribute
# error: [unresolved-attribute]
ThisFails().x
```
## Classes with custom `__setattr__` methods
### Basic
@@ -1698,6 +1757,36 @@ reveal_type(outer.nested.inner.Outer.Nested.Inner.attr) # revealed: int
outer.nested.inner.Outer.Nested.Inner.attr = "a"
```
### Unions of module attributes
`mod1.py`:
```py
global_symbol: str = "a"
```
`mod2.py`:
```py
global_symbol: str = "a"
```
```py
import mod1
import mod2
def _(flag: bool):
if flag:
mod = mod1
else:
mod = mod2
mod.global_symbol = "b"
# error: [invalid-assignment] "Object of type `Literal[1]` is not assignable to attribute `global_symbol` on type `<module 'mod1'> | <module 'mod2'>`"
mod.global_symbol = 1
```
## Literal types
### Function-literal attributes
@@ -2020,6 +2109,52 @@ reveal_type(Foo.BAR.value) # revealed: @Todo(Attribute access on enum classes)
reveal_type(Foo.__members__) # revealed: @Todo(Attribute access on enum classes)
```
## Errors for unresolved and invalid attribute assignments
```py
class C:
class_attr: int = 1
def __init__(self) -> None:
self.instance_attr: int = 1
c = C()
C.class_attr = 2
c.class_attr = 2
c.instance_attr = 2
C.class_attr = "invalid" # error: [invalid-assignment]
c.class_attr = "invalid" # error: [invalid-assignment]
c.instance_attr = "invalid" # error: [invalid-assignment]
# TODO: The following attribute assignments are flagged by mypy/pyright (Type can
# not be declared in assignment to non-self attribute). We currently do not emit
# any errors and also don't validate the type of the annotation in any form. This
# should probably be changed.
C.class_attr: None = 2
c.class_attr: None = 2
c.instance_attr: None = 2
# TODO: Similar here. We do report `invalid-assignment` errors, but only because
# the value type does not match.
C.class_attr: str = "invalid" # error: [invalid-assignment]
c.class_attr: str = "invalid" # error: [invalid-assignment]
c.instance_attr: str = "invalid" # error: [invalid-assignment]
# For non-existent attributes, we emit `unresolved-attribute` errors:
C.non_existent = 2 # error: [unresolved-attribute]
c.non_existent = 2 # error: [unresolved-attribute]
# TODO: Similar to above, these should either be forbidden or validated.
#
# Make sure that we emit `unresolved-attribute` errors, at least:
C.non_existent: int = 2 # error: [unresolved-attribute]
c.non_existent: int = 2 # error: [unresolved-attribute]
C.non_existent: None = 2 # error: [unresolved-attribute]
c.non_existent: None = 2 # error: [unresolved-attribute]
```
## References
Some of the tests in the *Class and instance variables* section draw inspiration from

View File

@@ -239,3 +239,37 @@ def _(flag: bool):
# error: [possibly-unbound-implicit-call]
reveal_type(c[0]) # revealed: str
```
## Dunder methods cannot be looked up on instances
Class-level annotations with no value assigned are considered instance-only, and aren't available as
dunder methods:
```py
from typing import Callable
class C:
__call__: Callable[..., None]
# error: [call-non-callable]
C()()
# error: [invalid-assignment]
_: Callable[..., None] = C()
```
And of course the same is true if we have only an implicit assignment inside a method:
```py
from typing import Callable
class C:
def __init__(self):
self.__call__ = lambda *a, **kw: None
# error: [call-non-callable]
C()()
# error: [invalid-assignment]
_: Callable[..., None] = C()
```

View File

@@ -109,23 +109,50 @@ def _(o: object):
### Unsupported operators for positive contributions
Raise an error if any of the positive contributions to the intersection type are unsupported for the
given operator:
Raise an error if the given operator is unsupported for all positive contributions to the
intersection type:
```py
class NonContainer1: ...
class NonContainer2: ...
def _(x: object):
if isinstance(x, NonContainer1):
if isinstance(x, NonContainer2):
reveal_type(x) # revealed: NonContainer1 & NonContainer2
# error: [unsupported-operator] "Operator `in` is not supported for types `int` and `NonContainer1`"
reveal_type(2 in x) # revealed: bool
```
Do not raise an error if at least one of the positive contributions to the intersection type support
the operator:
```py
class Container:
def __contains__(self, x) -> bool:
return False
class NonContainer: ...
def _(x: object):
if isinstance(x, Container):
if isinstance(x, NonContainer):
reveal_type(x) # revealed: Container & NonContainer
if isinstance(x, NonContainer1):
if isinstance(x, Container):
if isinstance(x, NonContainer2):
reveal_type(x) # revealed: NonContainer1 & Container & NonContainer2
reveal_type(2 in x) # revealed: bool
```
# error: [unsupported-operator] "Operator `in` is not supported for types `int` and `NonContainer`"
reveal_type(2 in x) # revealed: bool
Do also raise an error if the intersection has no positive contributions at all, unless the operator
is supported on `object`:
```py
def _(x: object):
if not isinstance(x, NonContainer1):
reveal_type(x) # revealed: ~NonContainer1
# error: [unsupported-operator] "Operator `in` is not supported for types `int` and `object`, in comparing `Literal[2]` with `~NonContainer1`"
reveal_type(2 in x) # revealed: bool
reveal_type(2 is x) # revealed: bool
```
### Unsupported operators for negative contributions

View File

@@ -24,9 +24,7 @@ class:
```py
class Bad(Generic[T], Generic[T]): ... # error: [duplicate-base]
# TODO: should emit an error (fails at runtime)
class AlsoBad(Generic[T], Generic[S]): ...
class AlsoBad(Generic[T], Generic[S]): ... # error: [duplicate-base]
```
You cannot use the same typevar more than once.

View File

@@ -527,6 +527,45 @@ reveal_type(unknown_object) # revealed: Unknown
reveal_type(unknown_object.__mro__) # revealed: Unknown
```
## MROs of classes that use multiple inheritance with generic aliases and subscripted `Generic`
```py
from typing import Generic, TypeVar, Iterator
T = TypeVar("T")
class peekable(Generic[T], Iterator[T]): ...
# revealed: tuple[<class 'peekable[Unknown]'>, <class 'Iterator[T]'>, <class 'Iterable[T]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(peekable.__mro__)
class peekable2(Iterator[T], Generic[T]): ...
# revealed: tuple[<class 'peekable2[Unknown]'>, <class 'Iterator[T]'>, <class 'Iterable[T]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(peekable2.__mro__)
class Base: ...
class Intermediate(Base, Generic[T]): ...
class Sub(Intermediate[T], Base): ...
# revealed: tuple[<class 'Sub[Unknown]'>, <class 'Intermediate[T]'>, <class 'Base'>, typing.Generic, <class 'object'>]
reveal_type(Sub.__mro__)
```
## Unresolvable MROs involving generics have the original bases reported in the error message, not the resolved bases
<!-- snapshot-diagnostics -->
```py
from typing_extensions import Protocol, TypeVar, Generic
T = TypeVar("T")
class Foo(Protocol): ...
class Bar(Protocol[T]): ...
class Baz(Protocol[T], Foo, Bar[T]): ... # error: [inconsistent-mro]
```
## Classes that inherit from themselves
These are invalid, but we need to be able to handle them gracefully without panicking.

View File

@@ -67,12 +67,10 @@ It's an error to include both bare `Protocol` and subscripted `Protocol[]` in th
simultaneously:
```py
# TODO: should emit a `[duplicate-bases]` error here:
class DuplicateBases(Protocol, Protocol[T]):
class DuplicateBases(Protocol, Protocol[T]): # error: [duplicate-base]
x: T
# TODO: should not have `Protocol` or `Generic` multiple times
# revealed: tuple[<class 'DuplicateBases[Unknown]'>, typing.Protocol, typing.Generic, typing.Protocol[T], typing.Generic[T], <class 'object'>]
# revealed: tuple[<class 'DuplicateBases[Unknown]'>, Unknown, <class 'object'>]
reveal_type(DuplicateBases.__mro__)
```
@@ -377,8 +375,7 @@ class Foo(Protocol):
def method_member(self) -> bytes:
return b"foo"
# TODO: actually a frozenset (requires support for legacy generics)
reveal_type(get_protocol_members(Foo)) # revealed: tuple[Literal["method_member"], Literal["x"], Literal["y"], Literal["z"]]
reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["method_member", "x", "y", "z"]]
```
Certain special attributes and methods are not considered protocol members at runtime, and should
@@ -396,8 +393,7 @@ class Lumberjack(Protocol):
def __init__(self, x: int) -> None:
self.x = x
# TODO: actually a frozenset
reveal_type(get_protocol_members(Lumberjack)) # revealed: tuple[Literal["x"]]
reveal_type(get_protocol_members(Lumberjack)) # revealed: frozenset[Literal["x"]]
```
A sub-protocol inherits and extends the members of its superclass protocol(s):
@@ -409,13 +405,11 @@ class Bar(Protocol):
class Baz(Bar, Protocol):
ham: memoryview
# TODO: actually a frozenset
reveal_type(get_protocol_members(Baz)) # revealed: tuple[Literal["ham"], Literal["spam"]]
reveal_type(get_protocol_members(Baz)) # revealed: frozenset[Literal["ham", "spam"]]
class Baz2(Bar, Foo, Protocol): ...
# TODO: actually a frozenset
# revealed: tuple[Literal["method_member"], Literal["spam"], Literal["x"], Literal["y"], Literal["z"]]
# revealed: frozenset[Literal["method_member", "spam", "x", "y", "z"]]
reveal_type(get_protocol_members(Baz2))
```
@@ -443,8 +437,7 @@ class Foo(Protocol):
e = 56
def f(self) -> None: ...
# TODO: actually a frozenset
reveal_type(get_protocol_members(Foo)) # revealed: tuple[Literal["d"], Literal["e"], Literal["f"]]
reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["d", "e", "f"]]
```
## Invalid calls to `get_protocol_members()`
@@ -675,8 +668,7 @@ class LotsOfBindings(Protocol):
case l: # TODO: this should error with `[invalid-protocol]` (`l` is not declared)
...
# TODO: actually a frozenset
# revealed: tuple[Literal["Nested"], Literal["NestedProtocol"], Literal["a"], Literal["b"], Literal["c"], Literal["d"], Literal["e"], Literal["f"], Literal["g"], Literal["h"], Literal["i"], Literal["j"], Literal["k"], Literal["l"]]
# revealed: frozenset[Literal["Nested", "NestedProtocol", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]]
reveal_type(get_protocol_members(LotsOfBindings))
```
@@ -704,9 +696,7 @@ class Foo(Protocol):
# Note: the list of members does not include `a`, `b` or `c`,
# as none of these attributes is declared in the class body.
#
# TODO: actually a frozenset
reveal_type(get_protocol_members(Foo)) # revealed: tuple[Literal["non_init_method"], Literal["x"], Literal["y"]]
reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["non_init_method", "x", "y"]]
```
If a member is declared in a superclass of a protocol class, it is fine for it to be assigned to in
@@ -719,9 +709,8 @@ class Super(Protocol):
class Sub(Super, Protocol):
x = 42 # no error here, since it's declared in the superclass
# TODO: actually frozensets
reveal_type(get_protocol_members(Super)) # revealed: tuple[Literal["x"]]
reveal_type(get_protocol_members(Sub)) # revealed: tuple[Literal["x"]]
reveal_type(get_protocol_members(Super)) # revealed: frozenset[Literal["x"]]
reveal_type(get_protocol_members(Sub)) # revealed: frozenset[Literal["x"]]
```
If a protocol has 0 members, then all other types are assignable to it, and all fully static types

View File

@@ -0,0 +1,37 @@
---
source: crates/ty_test/src/lib.rs
expression: snapshot
---
---
mdtest name: mro.md - Method Resolution Order tests - Unresolvable MROs involving generics have the original bases reported in the error message, not the resolved bases
mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md
---
# Python source files
## mdtest_snippet.py
```
1 | from typing_extensions import Protocol, TypeVar, Generic
2 |
3 | T = TypeVar("T")
4 |
5 | class Foo(Protocol): ...
6 | class Bar(Protocol[T]): ...
7 | class Baz(Protocol[T], Foo, Bar[T]): ... # error: [inconsistent-mro]
```
# Diagnostics
```
error[inconsistent-mro]: Cannot create a consistent method resolution order (MRO) for class `Baz` with bases list `[typing.Protocol[T], <class 'Foo'>, <class 'Bar[T]'>]`
--> src/mdtest_snippet.py:7:1
|
5 | class Foo(Protocol): ...
6 | class Bar(Protocol[T]): ...
7 | class Baz(Protocol[T], Foo, Bar[T]): ... # error: [inconsistent-mro]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
info: rule `inconsistent-mro` is enabled by default
```

View File

@@ -16,7 +16,7 @@ class Foo[T]: ...
class Bar(Foo[Bar]): ...
reveal_type(Bar) # revealed: <class 'Bar'>
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, <class 'Foo[Bar]'>, <class 'object'>]
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, <class 'Foo[Bar]'>, typing.Generic, <class 'object'>]
```
## Access to attributes declared in stubs

View File

@@ -83,7 +83,7 @@ python-version = "3.9"
```py
class A(tuple[int, str]): ...
# revealed: tuple[<class 'A'>, <class 'tuple[@Todo(Generic tuple specializations), ...]'>, <class 'Sequence[@Todo(Generic tuple specializations)]'>, <class 'Reversible[@Todo(Generic tuple specializations)]'>, <class 'Collection[@Todo(Generic tuple specializations)]'>, <class 'Iterable[@Todo(Generic tuple specializations)]'>, <class 'Container[@Todo(Generic tuple specializations)]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
# revealed: tuple[<class 'A'>, <class 'tuple[@Todo(Generic tuple specializations), ...]'>, <class 'Sequence[@Todo(Generic tuple specializations)]'>, <class 'Reversible[@Todo(Generic tuple specializations)]'>, <class 'Collection[@Todo(Generic tuple specializations)]'>, <class 'Iterable[@Todo(Generic tuple specializations)]'>, <class 'Container[@Todo(Generic tuple specializations)]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(A.__mro__)
```
@@ -114,6 +114,6 @@ from typing import Tuple
class C(Tuple): ...
# revealed: tuple[<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
# revealed: tuple[<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(C.__mro__)
```

View File

@@ -0,0 +1,20 @@
# Type compendium
The type compendium contains "fact sheets" about important, interesting, and peculiar types in (ty's
interpretation of) Python's type system. It is meant to be an educational reference for developers
and users of ty. It is also a living document that ensures that our implementation of these types
and their properties is consistent with the specification.
## Table of contents
- [`Never`](never.md)
- [`Object`](object.md)
- [`None`](none.md)
- [Integer `Literal`s](integer_literals.md)
- String `Literal`s, `LiteralString`
- [`tuple` types](tuple.md)
- Class instance types
- [`Any`](any.md)
- Class literal types, `type[C]`, `type[object]`, `type[Any]`
- [`AlwaysTruthy`, `AlwaysFalsy`](always_truthy_falsy.md)
- [`Not[T]`](not_t.md)

View File

@@ -0,0 +1,175 @@
# `AlwaysTruthy` and `AlwaysFalsy`
```toml
[environment]
python-version = "3.12"
```
The types `AlwaysTruthy` and `AlwaysFalsy` describe the set of values that are always truthy or
always falsy, respectively. More concretely, a value `at` is of type `AlwaysTruthy` if we can
statically infer that `bool(at)` is always `True`, i.e. that the expression `bool(at)` has type
`Literal[True]`. Conversely, a value `af` is of type `AlwaysFalsy` if we can statically infer that
`bool(af)` is always `False`, i.e. that `bool(af)` has type `Literal[False]`.
## Examples
Here, we give a few examples of values that belong to these types:
```py
from ty_extensions import AlwaysTruthy, AlwaysFalsy
from typing_extensions import Literal
class CustomAlwaysTruthyType:
def __bool__(self) -> Literal[True]:
return True
class CustomAlwaysFalsyType:
def __bool__(self) -> Literal[False]:
return False
at: AlwaysTruthy
at = True
at = 1
at = 123
at = -1
at = "non empty"
at = b"non empty"
at = CustomAlwaysTruthyType()
af: AlwaysFalsy
af = False
af = None
af = 0
af = ""
af = b""
af = CustomAlwaysFalsyType()
```
## `AlwaysTruthy` and `AlwaysFalsy` are disjoint
It follows directly from the definition that `AlwaysTruthy` and `AlwaysFalsy` are disjoint types:
```py
from ty_extensions import static_assert, is_disjoint_from, AlwaysTruthy, AlwaysFalsy
static_assert(is_disjoint_from(AlwaysTruthy, AlwaysFalsy))
```
## `Truthy` and `Falsy`
It is useful to also define the types `Truthy = ~AlwaysFalsy` and `Falsy = ~AlwaysTruthy`. These
types describe the set of values that *can* be truthy (`bool(t)` can return `True`) or falsy
(`bool(f)` can return `False`), respectively.
Finally, we can also define the type `AmbiguousTruthiness = Truthy & Falsy`, which describes the set
of values that can be truthy *and* falsy. This intersection is not empty. In the following, we give
examples for values that belong to these three types:
```py
from ty_extensions import static_assert, is_equivalent_to, is_disjoint_from, Not, Intersection, AlwaysTruthy, AlwaysFalsy
from typing_extensions import Never
from random import choice
type Truthy = Not[AlwaysFalsy]
type Falsy = Not[AlwaysTruthy]
type AmbiguousTruthiness = Intersection[Truthy, Falsy]
static_assert(is_disjoint_from(AlwaysTruthy, AmbiguousTruthiness))
static_assert(is_disjoint_from(AlwaysFalsy, AmbiguousTruthiness))
static_assert(not is_disjoint_from(Truthy, Falsy))
class CustomAmbiguousTruthinessType:
def __bool__(self) -> bool:
return choice((True, False))
def maybe_empty_list() -> list[int]:
return choice(([], [1, 2, 3]))
reveal_type(bool(maybe_empty_list())) # revealed: bool
reveal_type(bool(CustomAmbiguousTruthinessType())) # revealed: bool
t: Truthy
t = True
t = 1
# TODO: This assignment should be okay
t = maybe_empty_list() # error: [invalid-assignment]
# TODO: This assignment should be okay
t = CustomAmbiguousTruthinessType() # error: [invalid-assignment]
a: AmbiguousTruthiness
# TODO: This assignment should be okay
a = maybe_empty_list() # error: [invalid-assignment]
# TODO: This assignment should be okay
a = CustomAmbiguousTruthinessType() # error: [invalid-assignment]
f: Falsy
f = False
f = None
# TODO: This assignment should be okay
f = maybe_empty_list() # error: [invalid-assignment]
# TODO: This assignment should be okay
f = CustomAmbiguousTruthinessType() # error: [invalid-assignment]
```
## Subtypes of `AlwaysTruthy`, `AlwaysFalsy`
```py
from ty_extensions import static_assert, is_subtype_of, is_disjoint_from, AlwaysTruthy, AlwaysFalsy
from typing_extensions import Literal
```
These two types are disjoint, so types (that are not equivalent to Never) can only be a subtype of
either one of them.
```py
static_assert(is_disjoint_from(AlwaysTruthy, AlwaysFalsy))
```
Types that only contain always-truthy values
```py
static_assert(is_subtype_of(Literal[True], AlwaysTruthy))
static_assert(is_subtype_of(Literal[1], AlwaysTruthy))
static_assert(is_subtype_of(Literal[-1], AlwaysTruthy))
static_assert(is_subtype_of(Literal["non empty"], AlwaysTruthy))
static_assert(is_subtype_of(Literal[b"non empty"], AlwaysTruthy))
```
Types that only contain always-falsy values
```py
static_assert(is_subtype_of(None, AlwaysFalsy))
static_assert(is_subtype_of(Literal[False], AlwaysFalsy))
static_assert(is_subtype_of(Literal[0], AlwaysFalsy))
static_assert(is_subtype_of(Literal[""], AlwaysFalsy))
static_assert(is_subtype_of(Literal[b""], AlwaysFalsy))
static_assert(is_subtype_of(Literal[False] | Literal[0], AlwaysFalsy))
```
Ambiguous truthiness types
```py
static_assert(not is_subtype_of(bool, AlwaysTruthy))
static_assert(not is_subtype_of(bool, AlwaysFalsy))
static_assert(not is_subtype_of(list[int], AlwaysTruthy))
static_assert(not is_subtype_of(list[int], AlwaysFalsy))
```
## Open questions
Is `tuple[()]` always falsy? We currently model it this way, but this is
[under discussion](https://github.com/astral-sh/ruff/issues/15528).
```py
from ty_extensions import static_assert, is_subtype_of, AlwaysFalsy
static_assert(is_subtype_of(tuple[()], AlwaysFalsy))
```
## References
See also:
- Our test suite on [narrowing for `if x` and `if not x`](../narrow/truthiness.md).

View File

@@ -0,0 +1,141 @@
# `Any`
## Introduction
The type `Any` is the dynamic type in Python's gradual type system. It represents an unknown
fully-static type, which means that it represents an *unknown* set of runtime values.
```py
from ty_extensions import static_assert, is_fully_static
from typing import Any
```
`Any` is a dynamic type:
```py
static_assert(not is_fully_static(Any))
```
## Every type is assignable to `Any`, and `Any` is assignable to every type
```py
from ty_extensions import static_assert, is_fully_static, is_assignable_to
from typing_extensions import Never, Any
class C: ...
static_assert(is_assignable_to(C, Any))
static_assert(is_assignable_to(Any, C))
static_assert(is_assignable_to(object, Any))
static_assert(is_assignable_to(Any, object))
static_assert(is_assignable_to(Never, Any))
static_assert(is_assignable_to(Any, Never))
static_assert(is_assignable_to(type, Any))
static_assert(is_assignable_to(Any, type))
static_assert(is_assignable_to(type[Any], Any))
static_assert(is_assignable_to(Any, type[Any]))
```
`Any` is also assignable to itself (like every type):
```py
static_assert(is_assignable_to(Any, Any))
```
## Unions with `Any`: `Any | T`
The union `Any | T` of `Any` with a fully static type `T` describes an unknown set of values that is
*at least as large* as the set of values described by `T`. It represents an unknown fully-static
type with *lower bound* `T`. Again, this can be demonstrated using the assignable-to relation:
```py
from ty_extensions import static_assert, is_assignable_to, is_equivalent_to
from typing_extensions import Any
# A class hierarchy Small <: Medium <: Big
class Big: ...
class Medium(Big): ...
class Small(Medium): ...
static_assert(is_assignable_to(Any | Medium, Big))
static_assert(is_assignable_to(Any | Medium, Medium))
# `Any | Medium` is at least as large as `Medium`, so we can not assign it to `Small`:
static_assert(not is_assignable_to(Any | Medium, Small))
```
The union `Any | object` is equivalent to `object`. This is true for every union with `object`, but
it is worth demonstrating:
```py
static_assert(is_equivalent_to(Any | object, object))
static_assert(is_equivalent_to(object | Any, object))
```
## Intersections with `Any`: `Any & T`
The intersection `Any & T` of `Any` with a fully static type `T` describes an unknown set of values
that is *no larger than* the set of values described by `T`. It represents an unknown fully-static
type with *upper bound* `T`:
```py
from ty_extensions import static_assert, is_assignable_to, Intersection, is_equivalent_to
from typing import Any
class Big: ...
class Medium(Big): ...
class Small(Medium): ...
static_assert(is_assignable_to(Small, Intersection[Any, Medium]))
static_assert(is_assignable_to(Medium, Intersection[Any, Medium]))
```
`Any & Medium` is no larger than `Medium`, so we can not assign `Big` to it. There is no possible
materialization of `Any & Medium` that would make it as big as `Big`:
```py
static_assert(not is_assignable_to(Big, Intersection[Any, Medium]))
```
`Any & Never` represents an "unknown" fully-static type which is no larger than `Never`. There is no
such fully-static type, except for `Never` itself. So `Any & Never` is equivalent to `Never`:
```py
from typing_extensions import Never
static_assert(is_equivalent_to(Intersection[Any, Never], Never))
static_assert(is_equivalent_to(Intersection[Never, Any], Never))
```
## Tuples with `Any`
This section demonstrates the following passage from the [type system concepts] documentation on
gradual types:
> A type such as `tuple[int, Any]` […] does not represent a single set of Python objects; rather, it
> represents a (bounded) range of possible sets of values. […] In the same way that `Any` does not
> represent "the set of all Python objects" but rather "an unknown set of objects",
> `tuple[int, Any]` does not represent "the set of all length-two tuples whose first element is an
> integer". That is a fully static type, spelled `tuple[int, object]`. By contrast,
> `tuple[int, Any]` represents some unknown set of tuple values; it might be the set of all tuples
> of two integers, or the set of all tuples of an integer and a string, or some other set of tuple
> values.
>
> In practice, this difference is seen (for example) in the fact that we can assign an expression of
> type `tuple[int, Any]` to a target typed as `tuple[int, int]`, whereas assigning
> `tuple[int, object]` to `tuple[int, int]` is a static type error.
```py
from ty_extensions import static_assert, is_assignable_to
from typing import Any
static_assert(is_assignable_to(tuple[int, Any], tuple[int, int]))
static_assert(not is_assignable_to(tuple[int, object], tuple[int, int]))
```
[type system concepts]: https://typing.readthedocs.io/en/latest/spec/concepts.html#gradual-types

View File

@@ -0,0 +1,234 @@
# Integer `Literal`s
An integer literal type represents the set of all integer objects with one specific value. For
example, the type `Literal[54165]` represents the set of all integer objects with the value `54165`.
## Integer `Literal`s are not singleton types
This does not necessarily mean that the type is a singleton type, i.e., a type with only one
inhabitant. The reason for this is that there might be multiple Python runtime objects (at different
memory locations) that all represent the same integer value. For example, the following code snippet
may print `False`.
```py
x = 54165
y = 54165
print(x is y)
```
In practice, on CPython 3.13.0, this program prints `True` when executed as a script, but `False`
when executed in the REPL.
Since this is an implementation detail of the Python runtime, we model all integer literals as
non-singleton types:
```py
from ty_extensions import static_assert, is_singleton
from typing import Literal
static_assert(not is_singleton(Literal[0]))
static_assert(not is_singleton(Literal[1]))
static_assert(not is_singleton(Literal[54165]))
```
This has implications for type-narrowing. For example, you can not use the `is not` operator to
check whether a variable has a specific integer literal type, but this is not a recommended practice
anyway.
```py
def f(x: int):
if x is 54165:
# This works, because if `x` is the same object as that left-hand-side literal, then it
# must have the same value.
reveal_type(x) # revealed: Literal[54165]
if x is not 54165:
# But here, we can not narrow the type (to `int & ~Literal[54165]`), because `x` might also
# have the value `54165`, but a different object identity.
reveal_type(x) # revealed: int
```
## Integer `Literal`s are single-valued types
There is a slightly weaker property that integer literals have. They are single-valued types, which
means that all objects of the type have the same value, i.e. they compare equal to each other:
```py
from ty_extensions import static_assert, is_single_valued
from typing import Literal
static_assert(is_single_valued(Literal[0]))
static_assert(is_single_valued(Literal[1]))
static_assert(is_single_valued(Literal[54165]))
```
And this can be used for type-narrowing using not-equal comparisons:
```py
def f(x: int):
if x == 54165:
# The reason that no narrowing occurs here is that there might be subclasses of `int`
# that override `__eq__`. This is not specific to integer literals though, and generally
# applies to `==` comparisons.
reveal_type(x) # revealed: int
if x != 54165:
reveal_type(x) # revealed: int & ~Literal[54165]
```
## Subtyping relationships
### Subtypes of `int`
All integer literals are subtypes of `int`:
```py
from ty_extensions import static_assert, is_subtype_of
from typing import Literal
static_assert(is_subtype_of(Literal[0], int))
static_assert(is_subtype_of(Literal[1], int))
static_assert(is_subtype_of(Literal[54165], int))
```
It is tempting to think that `int` is equivalent to the union of all integer literals,
`… | Literal[-1] | Literal[0] | Literal[1] | …`, but this is not the case. `True` and `False` are
also inhabitants of the `int` type, but they are not inhabitants of any integer literal type:
```py
static_assert(is_subtype_of(Literal[True], int))
static_assert(is_subtype_of(Literal[False], int))
static_assert(not is_subtype_of(Literal[True], Literal[1]))
static_assert(not is_subtype_of(Literal[False], Literal[0]))
```
Also, `int` can be subclassed, and instances of that subclass are also subtypes of `int`:
```py
class CustomInt(int):
pass
static_assert(is_subtype_of(CustomInt, int))
```
### No subtypes of `float` and `complex`
```toml
[environment]
python-version = "3.12"
```
Integer literals are _not_ subtypes of `float`, but the typing spec describes a special case for
[`float` and `complex`] which accepts integers (and therefore also integer literals) in places where
a `float` or `complex` is expected. We use the types `JustFloat` and `JustComplex` below, because ty
recognizes an annotation of `float` as `int | float` to support that typing system special case.
```py
from ty_extensions import static_assert, is_subtype_of, JustFloat, JustComplex
from typing import Literal
# Not subtypes of `float` and `complex`
static_assert(not is_subtype_of(Literal[0], JustFloat) and not is_subtype_of(Literal[0], JustComplex))
static_assert(not is_subtype_of(Literal[1], JustFloat) and not is_subtype_of(Literal[1], JustComplex))
static_assert(not is_subtype_of(Literal[54165], JustFloat) and not is_subtype_of(Literal[54165], JustComplex))
```
The typing system special case can be seen in the following example:
```py
a: JustFloat = 1 # error: [invalid-assignment]
b: JustComplex = 1 # error: [invalid-assignment]
x: float = 1
y: complex = 1
```
### Subtypes of integer `Literal`s?
The only subtypes of an integer literal type _that can be named_ are the type itself and `Never`:
```py
from ty_extensions import static_assert, is_subtype_of
from typing_extensions import Never, Literal
static_assert(is_subtype_of(Literal[54165], Literal[54165]))
static_assert(is_subtype_of(Never, Literal[54165]))
```
## Disjointness of integer `Literal`s
Two integer literal types `Literal[a]` and `Literal[b]` are disjoint if `a != b`:
```py
from ty_extensions import static_assert, is_disjoint_from
from typing import Literal
static_assert(is_disjoint_from(Literal[0], Literal[1]))
static_assert(is_disjoint_from(Literal[0], Literal[54165]))
static_assert(not is_disjoint_from(Literal[0], Literal[0]))
static_assert(not is_disjoint_from(Literal[54165], Literal[54165]))
```
## Integer literal math
```toml
[environment]
python-version = "3.12"
```
We support a whole range of arithmetic operations on integer literal types. For example, we can
statically verify that (3, 4, 5) is a Pythagorean triple:
```py
from ty_extensions import static_assert
static_assert(3**2 + 4**2 == 5**2)
```
Using unions of integer literals, we can even use this to solve equations over a finite domain
(determine whether there is a solution or not):
```py
from typing import Literal, assert_type
type Nat = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def pythagorean_triples(a: Nat, b: Nat, c: Nat):
# Answer is `bool`, because solutions do exist (3² + 4² = 5²)
assert_type(a**2 + b**2 == c**2, bool)
def fermats_last_theorem(a: Nat, b: Nat, c: Nat):
# Answer is `Literal[False]`, because no solutions exist
assert_type(a**3 + b**3 == c**3, Literal[False])
```
## Truthiness
Integer literals are always-truthy, except for `0`, which is always-falsy:
```py
from ty_extensions import static_assert
static_assert(-54165)
static_assert(-1)
static_assert(not 0)
static_assert(1)
static_assert(54165)
```
This can be used for type-narrowing:
```py
from typing_extensions import Literal, assert_type
def f(x: Literal[0, 1, 54365]):
if x:
assert_type(x, Literal[1, 54365])
else:
assert_type(x, Literal[0])
```
[`float` and `complex`]: https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex

View File

@@ -0,0 +1,185 @@
# `Never`
`Never` represents the empty set of values.
## `Never` is a subtype of every type
The `Never` type is the bottom type of Python's type system. It is a subtype of every type, but no
type is a subtype of `Never`, except for `Never` itself.
```py
from ty_extensions import static_assert, is_subtype_of
from typing_extensions import Never
class C: ...
static_assert(is_subtype_of(Never, int))
static_assert(is_subtype_of(Never, object))
static_assert(is_subtype_of(Never, C))
static_assert(is_subtype_of(Never, Never))
static_assert(not is_subtype_of(int, Never))
```
## `Never` is assignable to every type
`Never` is assignable to every type. This fact is useful when calling error-handling functions in a
context that requires a value of a specific type. For example, changing the `Never` return type to
`None` below would cause a type error:
```py
from ty_extensions import static_assert, is_assignable_to
from typing_extensions import Never, Any
static_assert(is_assignable_to(Never, int))
static_assert(is_assignable_to(Never, object))
static_assert(is_assignable_to(Never, Any))
static_assert(is_assignable_to(Never, Never))
def raise_error() -> Never:
raise Exception("...")
def f(divisor: int) -> None:
x: float = (1 / divisor) if divisor != 0 else raise_error()
```
## `Never` in annotations
`Never` can be used in functions to indicate that the function never returns. For example, if a
function always raises an exception, if it calls `sys.exit()`, if it enters an infinite loop, or if
it calls itself recursively. All of these functions "Never" return control back to the caller:
```py
from typing_extensions import Never
def raises_unconditionally() -> Never:
raise Exception("This function always raises an exception")
def exits_unconditionally() -> Never:
import sys
return sys.exit(1)
def loops_forever() -> Never:
while True:
pass
def recursive_never() -> Never:
return recursive_never()
```
Similarly, if `Never` is used in parameter positions, it indicates that the function can "Never" be
called, because it can never be passed a value of type `Never` (there are none):
```py
def can_not_be_called(n: Never) -> int:
return 0
```
## `Never` is disjoint from every other type
Two types `A` and `B` are disjoint if their intersection is empty. Since `Never` has no inhabitants,
it is disjoint from every other type:
```py
from ty_extensions import static_assert, is_disjoint_from
from typing_extensions import Never
class C: ...
static_assert(is_disjoint_from(Never, int))
static_assert(is_disjoint_from(Never, object))
static_assert(is_disjoint_from(Never, C))
static_assert(is_disjoint_from(Never, Never))
```
## Unions with `Never`
`Never` can always be removed from unions:
```py
from ty_extensions import static_assert, is_equivalent_to
from typing_extensions import Never
class P: ...
class Q: ...
static_assert(is_equivalent_to(P | Never | Q | None, P | Q | None))
```
## Intersections with `Never`
Intersecting with `Never` results in `Never`:
```py
from ty_extensions import static_assert, is_equivalent_to, Intersection
from typing_extensions import Never
class P: ...
class Q: ...
static_assert(is_equivalent_to(Intersection[P, Never, Q], Never))
```
## `Never` is the complement of `object`
`object` describes the set of all possible values, while `Never` describes the empty set. The two
types are complements of each other:
```py
from ty_extensions import static_assert, is_equivalent_to, Not
from typing_extensions import Never
static_assert(is_equivalent_to(Not[object], Never))
static_assert(is_equivalent_to(Not[Never], object))
```
This duality is also reflected in other facts:
- `Never` is a subtype of every type, while `object` is a supertype of every type.
- `Never` is assignable to every type, while `object` is assignable from every type.
- `Never` is disjoint from every type, while `object` overlaps with every type.
- Building a union with `Never` is a no-op, intersecting with `object` is a no-op.
- Interecting with `Never` results in `Never`, building a union with `object` results in `object`.
## Lists of `Never`
`list[Never]` is a reasonable type that is *not* equivalent to `Never`. The empty list inhabits this
type:
```py
from typing_extensions import Never
x: list[Never] = []
```
## Tuples involving `Never`
A type like `tuple[int, Never]` has no inhabitants, and so it is equivalent to `Never`:
```py
from ty_extensions import static_assert, is_equivalent_to
from typing_extensions import Never
static_assert(is_equivalent_to(tuple[int, Never], Never))
```
Note that this is not the case for the homogenous tuple type `tuple[Never, ...]` though, because
that type is inhabited by the empty tuple:
```py
static_assert(not is_equivalent_to(tuple[Never, ...], Never))
t: tuple[Never, ...] = ()
```
## `NoReturn` is the same as `Never`
The `NoReturn` type is a different name for `Never`:
```py
from ty_extensions import static_assert, is_equivalent_to
from typing_extensions import NoReturn, Never
static_assert(is_equivalent_to(NoReturn, Never))
```

View File

@@ -0,0 +1,80 @@
# `None`
## `None` as a singleton type
The type `None` (or `NoneType`, see below) is a singleton type that has only one inhabitant: the
object `None`.
```py
from ty_extensions import static_assert, is_singleton, is_equivalent_to
n: None = None
static_assert(is_singleton(None))
```
Just like for other singleton types, the only subtypes of `None` are `None` itself and `Never`:
```py
from ty_extensions import static_assert, is_subtype_of
from typing_extensions import Never
static_assert(is_subtype_of(None, None))
static_assert(is_subtype_of(Never, None))
```
## Relationship to `Optional[T]`
The type `Optional[T]` is an alias for `T | None` (or `Union[T, None]`):
```py
from ty_extensions import static_assert, is_equivalent_to
from typing import Optional, Union
class T: ...
static_assert(is_equivalent_to(Optional[T], T | None))
static_assert(is_equivalent_to(Optional[T], Union[T, None]))
```
## Type narrowing using `is`
Just like for other singleton types, we support type narrowing using `is` or `is not` checks:
```py
from typing_extensions import assert_type
class T: ...
def f(x: T | None):
if x is None:
assert_type(x, None)
else:
assert_type(x, T)
assert_type(x, T | None)
if x is not None:
assert_type(x, T)
else:
assert_type(x, None)
```
## `NoneType`
`None` is special in that the name of the instance at runtime can be used as a type as well: The
object `None` is an instance of type `None`. When a distinction between the two is needed, the
spelling `NoneType` can be used, which is available since Python 3.10. `NoneType` is equivalent to
`None`:
```toml
[environment]
python-version = "3.10"
```
```py
from ty_extensions import static_assert, is_equivalent_to
from types import NoneType
static_assert(is_equivalent_to(NoneType, None))
```

View File

@@ -0,0 +1,120 @@
# `Not[T]`
The type `Not[T]` is the complement of the type `T`. It describes the set of all values that are
*not* in `T`.
## `Not[T]` is disjoint from `T`
`Not[T]` is disjoint from `T`:
```py
from ty_extensions import Not, static_assert, is_disjoint_from
class T: ...
class S(T): ...
static_assert(is_disjoint_from(Not[T], T))
static_assert(is_disjoint_from(Not[T], S))
```
## The union of `T` and `Not[T]` is equivalent to `object`
Together, `T` and `Not[T]` describe the set of all values. So the union of both types is equivalent
to `object`:
```py
from ty_extensions import Not, static_assert, is_equivalent_to
class T: ...
static_assert(is_equivalent_to(T | Not[T], object))
```
## `Not[T]` reverses subtyping relationships
If `S <: T`, then `Not[T] <: Not[S]`:, similar to how negation in logic reverses the order of `<=`:
```py
from ty_extensions import Not, static_assert, is_subtype_of
class T: ...
class S(T): ...
static_assert(is_subtype_of(S, T))
static_assert(is_subtype_of(Not[T], Not[S]))
```
## `Not[T]` reverses assignability relationships
Assignability relationships are similarly reversed:
```py
from ty_extensions import Not, Intersection, static_assert, is_assignable_to
from typing import Any
class T: ...
class S(T): ...
static_assert(is_assignable_to(S, T))
static_assert(is_assignable_to(Not[T], Not[S]))
static_assert(is_assignable_to(Intersection[Any, S], Intersection[Any, T]))
static_assert(is_assignable_to(Not[Intersection[Any, S]], Not[Intersection[Any, T]]))
```
## Subtyping and disjointness
If two types `P` and `Q` are disjoint, then `P` must be a subtype of `Not[Q]`, and vice versa:
```py
from ty_extensions import Not, static_assert, is_subtype_of, is_disjoint_from
from typing import final
@final
class P: ...
@final
class Q: ...
static_assert(is_disjoint_from(P, Q))
static_assert(is_subtype_of(P, Not[Q]))
static_assert(is_subtype_of(Q, Not[P]))
```
## De-Morgan's laws
Given two unrelated types `P` and `Q`, we can demonstrate De-Morgan's laws in the context of
set-theoretic types:
```py
from ty_extensions import Not, static_assert, is_equivalent_to, Intersection
class P: ...
class Q: ...
```
The negation of a union is the intersection of the negations:
```py
static_assert(is_equivalent_to(Not[P | Q], Intersection[Not[P], Not[Q]]))
```
Conversely, the negation of an intersection is the union of the negations:
```py
static_assert(is_equivalent_to(Not[Intersection[P, Q]], Not[P] | Not[Q]))
```
## Negation of gradual types
`Any` represents an unknown set of values. So `Not[Any]` also represents an unknown set of values.
The two gradual types are equivalent:
```py
from ty_extensions import static_assert, is_gradual_equivalent_to, Not
from typing import Any
static_assert(is_gradual_equivalent_to(Not[Any], Any))
```

View File

@@ -0,0 +1,78 @@
# `object`
The `object` type represents the set of all Python objects.
## `object` is a supertype of all types
It is the top type in Python's type system, i.e., it is a supertype of all other types:
```py
from ty_extensions import static_assert, is_subtype_of
static_assert(is_subtype_of(int, object))
static_assert(is_subtype_of(str, object))
static_assert(is_subtype_of(type, object))
static_assert(is_subtype_of(object, object))
```
## Every type is assignable to `object`
Everything can be assigned to the type `object`. This fact can be used to create heterogeneous
collections of objects (but also erases more specific type information):
```py
from ty_extensions import static_assert, is_assignable_to
from typing_extensions import Any, Never
static_assert(is_assignable_to(int, object))
static_assert(is_assignable_to(str | bytes, object))
static_assert(is_assignable_to(type, object))
static_assert(is_assignable_to(object, object))
static_assert(is_assignable_to(Never, object))
static_assert(is_assignable_to(Any, object))
x: list[object] = [1, "a", ()]
```
## `object` overlaps with all types
There is no type that is disjoint from `object` except for `Never`:
```py
from ty_extensions import static_assert, is_disjoint_from
from typing_extensions import Any, Never
static_assert(not is_disjoint_from(int, object))
static_assert(not is_disjoint_from(str, object))
static_assert(not is_disjoint_from(type, object))
static_assert(not is_disjoint_from(object, object))
static_assert(not is_disjoint_from(Any, object))
static_assert(is_disjoint_from(Never, object))
```
## Unions with `object`
Unions with `object` are equivalent to `object`:
```py
from ty_extensions import static_assert, is_equivalent_to
static_assert(is_equivalent_to(int | object | None, object))
```
## Intersections with `object`
Intersecting with `object` is equivalent to the original type:
```py
from ty_extensions import static_assert, is_equivalent_to, Intersection
class P: ...
class Q: ...
static_assert(is_equivalent_to(Intersection[P, object, Q], Intersection[P, Q]))
```
## `object` is the complement of `Never`
See corresponding section in the fact sheet for [`Never`](never.md).

View File

@@ -0,0 +1,166 @@
# Tuples
## Tuples as product types
Tuples can be used to construct product types. Inhabitants of the type `tuple[P, Q]` are ordered
pairs `(p, q)` where `p` is an inhabitant of `P` and `q` is an inhabitant of `Q`, analogous to the
Cartesian product of sets.
```py
from typing_extensions import assert_type
class P: ...
class Q: ...
def _(p: P, q: Q):
assert_type((p, q), tuple[P, Q])
```
## Subtyping relationships
The type `tuple[S1, S2]` is a subtype of `tuple[T1, T2]` if and only if `S1` is a subtype of `T1`
and `S2` is a subtype of `T2`, and similar for other lengths of tuples:
```py
from ty_extensions import static_assert, is_subtype_of
class T1: ...
class S1(T1): ...
class T2: ...
class S2(T2): ...
static_assert(is_subtype_of(tuple[S1], tuple[T1]))
static_assert(not is_subtype_of(tuple[T1], tuple[S1]))
static_assert(is_subtype_of(tuple[S1, S2], tuple[T1, T2]))
static_assert(not is_subtype_of(tuple[T1, S2], tuple[S1, T2]))
static_assert(not is_subtype_of(tuple[S1, T2], tuple[T1, S2]))
```
Different-length tuples are not related via subtyping:
```py
static_assert(not is_subtype_of(tuple[S1], tuple[T1, T2]))
```
## The empty tuple
The type of the empty tuple `()` is spelled `tuple[()]`. It is [not a singleton type], because
different instances of `()` are not guaranteed to be the same object (even if this is the case in
CPython at the time of writing).
The empty tuple can also be subclassed (further clarifying that it is not a singleton type):
```py
from ty_extensions import static_assert, is_singleton, is_subtype_of, is_equivalent_to, is_assignable_to
static_assert(not is_singleton(tuple[()]))
class AnotherEmptyTuple(tuple[()]): ...
static_assert(not is_equivalent_to(AnotherEmptyTuple, tuple[()]))
# TODO: These should not be errors
# error: [static-assert-error]
static_assert(is_subtype_of(AnotherEmptyTuple, tuple[()]))
# error: [static-assert-error]
static_assert(is_assignable_to(AnotherEmptyTuple, tuple[()]))
```
## Non-empty tuples
For the same reason as above (two instances of a tuple with the same elements might not be the same
object), non-empty tuples are also not singleton types — even if all their elements are singletons:
```py
from ty_extensions import static_assert, is_singleton
static_assert(is_singleton(None))
static_assert(not is_singleton(tuple[None]))
```
## Disjointness
A tuple `tuple[P1, P2]` is disjoint from a tuple `tuple[Q1, Q2]` if either `P1` is disjoint from
`Q1` or if `P2` is disjoint from `Q2`:
```py
from ty_extensions import static_assert, is_disjoint_from
from typing import final
@final
class F1: ...
@final
class F2: ...
class N1: ...
class N2: ...
static_assert(is_disjoint_from(F1, F2))
static_assert(not is_disjoint_from(N1, N2))
static_assert(is_disjoint_from(tuple[F1, F2], tuple[F2, F1]))
static_assert(is_disjoint_from(tuple[F1, N1], tuple[F2, N2]))
static_assert(is_disjoint_from(tuple[N1, F1], tuple[N2, F2]))
static_assert(not is_disjoint_from(tuple[N1, N2], tuple[N2, N1]))
```
We currently model tuple types to *not* be disjoint from arbitrary instance types, because we allow
for the possibility of `tuple` to be subclassed
```py
class C: ...
static_assert(not is_disjoint_from(tuple[int, str], C))
class CommonSubtype(tuple[int, str], C): ...
```
Note: This is inconsistent with the fact that we model heterogeneous tuples to be disjoint from
other heterogeneous tuples above:
```py
class I1(tuple[F1, F2]): ...
class I2(tuple[F2, F1]): ...
# TODO
# This is a subtype of both `tuple[F1, F2]` and `tuple[F2, F1]`, so those two heterogeneous tuples
# should not be disjoint from each other (see conflicting test above).
class CommonSubtypeOfTuples(I1, I2): ...
```
## Truthiness
The truthiness of the empty tuple is `False`:
```py
from typing_extensions import assert_type, Literal
assert_type(bool(()), Literal[False])
```
The truthiness of non-empty tuples is always `True`, even if all elements are falsy:
```py
from typing_extensions import assert_type, Literal
assert_type(bool((False,)), Literal[True])
assert_type(bool((False, False)), Literal[True])
```
Both of these results are conflicting with the fact that tuples can be subclassed, and that we
currently allow subclasses of `tuple` to overwrite `__bool__` (or `__len__`):
```py
class NotAlwaysTruthyTuple(tuple[int]):
def __bool__(self) -> bool:
return False
# TODO: This assignment should be allowed
# error: [invalid-assignment]
t: tuple[int] = NotAlwaysTruthyTuple((1,))
```
[not a singleton type]: https://discuss.python.org/t/should-we-specify-in-the-language-reference-that-the-empty-tuple-is-a-singleton/67957

View File

@@ -672,6 +672,29 @@ def f(x: int, y: str) -> None: ...
c1: Callable[[int], None] = partial(f, y="a")
```
### Classes with `__call__` as attribute
An instance type is assignable to a compatible callable type if the instance type's class has a
callable `__call__` attribute.
TODO: for the moment, we don't consider the callable type as a bound-method descriptor, but this may
change for better compatibility with mypy/pyright.
```py
from typing import Callable
from ty_extensions import static_assert, is_assignable_to
def call_impl(a: int) -> str:
return ""
class A:
__call__: Callable[[int], str] = call_impl
static_assert(is_assignable_to(A, Callable[[int], str]))
static_assert(not is_assignable_to(A, Callable[[int], int]))
reveal_type(A()(1)) # revealed: str
```
## Generics
### Assignability of generic types parameterized by gradual types

View File

@@ -21,10 +21,7 @@ See the [typing documentation] for more information.
as `int | float` and `int | float | complex`, respectively.
```py
from ty_extensions import is_subtype_of, static_assert, TypeOf
type JustFloat = TypeOf[1.0]
type JustComplex = TypeOf[1j]
from ty_extensions import is_subtype_of, static_assert, JustFloat, JustComplex
static_assert(is_subtype_of(bool, bool))
static_assert(is_subtype_of(bool, int))
@@ -88,9 +85,7 @@ static_assert(is_subtype_of(C, object))
```py
from typing_extensions import Literal, LiteralString
from ty_extensions import is_subtype_of, static_assert, TypeOf
type JustFloat = TypeOf[1.0]
from ty_extensions import is_subtype_of, static_assert, TypeOf, JustFloat
# Boolean literals
static_assert(is_subtype_of(Literal[True], bool))
@@ -1157,6 +1152,29 @@ def f(fn: Callable[[int], int]) -> None: ...
f(a)
```
### Classes with `__call__` as attribute
An instance type can be a subtype of a compatible callable type if the instance type's class has a
callable `__call__` attribute.
TODO: for the moment, we don't consider the callable type as a bound-method descriptor, but this may
change for better compatibility with mypy/pyright.
```py
from typing import Callable
from ty_extensions import static_assert, is_subtype_of
def call_impl(a: int) -> str:
return ""
class A:
__call__: Callable[[int], str] = call_impl
static_assert(is_subtype_of(A, Callable[[int], str]))
static_assert(not is_subtype_of(A, Callable[[int], int]))
reveal_type(A()(1)) # revealed: str
```
### Class literals
#### Classes with metaclasses

View File

@@ -202,6 +202,10 @@ impl PythonPath {
Self::SysPrefix(path.into(), SysPrefixPathOrigin::VirtualEnvVar)
}
pub fn from_conda_prefix_var(path: impl Into<SystemPathBuf>) -> Self {
Self::Resolve(path.into(), SysPrefixPathOrigin::CondaPrefixVar)
}
pub fn from_cli_flag(path: SystemPathBuf) -> Self {
Self::Resolve(path, SysPrefixPathOrigin::PythonCliFlag)
}

View File

@@ -580,6 +580,7 @@ impl fmt::Display for SysPrefixPath {
pub enum SysPrefixPathOrigin {
PythonCliFlag,
VirtualEnvVar,
CondaPrefixVar,
Derived,
LocalVenv,
}
@@ -590,7 +591,7 @@ impl SysPrefixPathOrigin {
pub(crate) fn must_be_virtual_env(self) -> bool {
match self {
Self::LocalVenv | Self::VirtualEnvVar => true,
Self::PythonCliFlag | Self::Derived => false,
Self::PythonCliFlag | Self::Derived | Self::CondaPrefixVar => false,
}
}
}
@@ -600,6 +601,7 @@ impl Display for SysPrefixPathOrigin {
match self {
Self::PythonCliFlag => f.write_str("`--python` argument"),
Self::VirtualEnvVar => f.write_str("`VIRTUAL_ENV` environment variable"),
Self::CondaPrefixVar => f.write_str("`CONDA_PREFIX` environment variable"),
Self::Derived => f.write_str("derived `sys.prefix` path"),
Self::LocalVenv => f.write_str("local virtual environment"),
}

View File

@@ -598,6 +598,10 @@ impl<'db> Type<'db> {
matches!(self, Type::Dynamic(DynamicType::Todo(_)))
}
pub const fn is_generic_alias(&self) -> bool {
matches!(self, Type::GenericAlias(_))
}
/// Replace references to the class `class` with a self-reference marker. This is currently
/// used for recursive protocols, but could probably be extended to self-referential type-
/// aliases and similar.
@@ -1291,12 +1295,20 @@ impl<'db> Type<'db> {
}
(Type::NominalInstance(_) | Type::ProtocolInstance(_), Type::Callable(_)) => {
let call_symbol = self.member(db, "__call__").symbol;
match call_symbol {
Symbol::Type(Type::BoundMethod(call_function), _) => call_function
.into_callable_type(db)
.is_subtype_of(db, target),
_ => false,
let call_symbol = self
.member_lookup_with_policy(
db,
Name::new_static("__call__"),
MemberLookupPolicy::NO_INSTANCE_FALLBACK,
)
.symbol;
// If the type of __call__ is a subtype of a callable type, this instance is.
// Don't add other special cases here; our subtyping of a callable type
// shouldn't get out of sync with the calls we will actually allow.
if let Symbol::Type(t, Boundness::Bound) = call_symbol {
t.is_subtype_of(db, target)
} else {
false
}
}
(Type::ProtocolInstance(left), Type::ProtocolInstance(right)) => {
@@ -1641,12 +1653,20 @@ impl<'db> Type<'db> {
}
(Type::NominalInstance(_) | Type::ProtocolInstance(_), Type::Callable(_)) => {
let call_symbol = self.member(db, "__call__").symbol;
match call_symbol {
Symbol::Type(Type::BoundMethod(call_function), _) => call_function
.into_callable_type(db)
.is_assignable_to(db, target),
_ => false,
let call_symbol = self
.member_lookup_with_policy(
db,
Name::new_static("__call__"),
MemberLookupPolicy::NO_INSTANCE_FALLBACK,
)
.symbol;
// If the type of __call__ is assignable to a callable type, this instance is.
// Don't add other special cases here; our assignability to a callable type
// shouldn't get out of sync with the calls we will actually allow.
if let Symbol::Type(t, Boundness::Bound) = call_symbol {
t.is_assignable_to(db, target)
} else {
false
}
}
@@ -2547,9 +2567,13 @@ impl<'db> Type<'db> {
// `Type::NominalInstance(type)` is equivalent to looking up the name in the
// MRO of the class `object`.
Type::NominalInstance(instance) if instance.class.is_known(db, KnownClass::Type) => {
KnownClass::Object
.to_class_literal(db)
.find_name_in_mro_with_policy(db, name, policy)
if policy.mro_no_object_fallback() {
Some(Symbol::Unbound.into())
} else {
KnownClass::Object
.to_class_literal(db)
.find_name_in_mro_with_policy(db, name, policy)
}
}
Type::FunctionLiteral(_)
@@ -2746,6 +2770,7 @@ impl<'db> Type<'db> {
instance.display(db),
owner.display(db)
);
let descr_get = self.class_member(db, "__get__".into()).symbol;
if let Symbol::Type(descr_get, descr_get_boundness) = descr_get {
@@ -3175,6 +3200,28 @@ impl<'db> Type<'db> {
.into()
};
let custom_getattribute_result = || {
// Avoid cycles when looking up `__getattribute__`
if "__getattribute__" == name.as_str() {
return Symbol::Unbound.into();
}
// Typeshed has a `__getattribute__` method defined on `builtins.object` so we
// explicitly hide it here using `MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK`.
self.try_call_dunder_with_policy(
db,
"__getattribute__",
&mut CallArgumentTypes::positional([Type::StringLiteral(
StringLiteralType::new(db, Box::from(name.as_str())),
)]),
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK,
)
.map(|outcome| Symbol::bound(outcome.return_type(db)))
// TODO: Handle call errors here.
.unwrap_or(Symbol::Unbound)
.into()
};
match result {
member @ SymbolAndQualifiers {
symbol: Symbol::Type(_, Boundness::Bound),
@@ -3183,11 +3230,13 @@ impl<'db> Type<'db> {
member @ SymbolAndQualifiers {
symbol: Symbol::Type(_, Boundness::PossiblyUnbound),
qualifiers: _,
} => member.or_fall_back_to(db, custom_getattr_result),
} => member
.or_fall_back_to(db, custom_getattribute_result)
.or_fall_back_to(db, custom_getattr_result),
SymbolAndQualifiers {
symbol: Symbol::Unbound,
qualifiers: _,
} => custom_getattr_result(),
} => custom_getattribute_result().or_fall_back_to(db, custom_getattr_result),
}
}
@@ -4365,7 +4414,7 @@ impl<'db> Type<'db> {
db,
name,
&mut argument_types,
MemberLookupPolicy::NO_INSTANCE_FALLBACK,
MemberLookupPolicy::default(),
)
}
@@ -4373,6 +4422,9 @@ impl<'db> Type<'db> {
/// particular, this allows to specify `MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK` to avoid
/// looking up dunder methods on `object`, which is needed for functions like `__init__`,
/// `__new__`, or `__setattr__`.
///
/// Note that `NO_INSTANCE_FALLBACK` is always added to the policy, since implicit calls to
/// dunder methods never access instance members.
fn try_call_dunder_with_policy(
self,
db: &'db dyn Db,
@@ -4380,8 +4432,14 @@ impl<'db> Type<'db> {
argument_types: &mut CallArgumentTypes<'_, 'db>,
policy: MemberLookupPolicy,
) -> Result<Bindings<'db>, CallDunderError<'db>> {
// Implicit calls to dunder methods never access instance members, so we pass
// `NO_INSTANCE_FALLBACK` here in addition to other policies:
match self
.member_lookup_with_policy(db, name.into(), policy)
.member_lookup_with_policy(
db,
name.into(),
policy | MemberLookupPolicy::NO_INSTANCE_FALLBACK,
)
.symbol
{
Symbol::Type(dunder_callable, boundness) => {

View File

@@ -667,15 +667,15 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::GetProtocolMembers) => {
if let [Some(Type::ClassLiteral(class))] = overload.parameter_types() {
if let Some(protocol_class) = class.into_protocol_class(db) {
// TODO: actually a frozenset at runtime (requires support for legacy generic classes)
overload.set_return_type(Type::Tuple(TupleType::new(
db,
protocol_class
.interface(db)
.members(db)
.map(|member| Type::string_literal(db, member.name()))
.collect::<Box<[Type<'db>]>>(),
)));
let member_names = protocol_class
.interface(db)
.members(db)
.map(|member| Type::string_literal(db, member.name()));
let specialization = UnionType::from_elements(db, member_names);
overload.set_return_type(
KnownClass::FrozenSet
.to_specialized_instance(db, [specialization]),
);
}
}
}

View File

@@ -223,6 +223,10 @@ impl<'db> ClassType<'db> {
}
}
pub(super) const fn is_generic(self) -> bool {
matches!(self, Self::Generic(_))
}
/// Returns the class literal and specialization for this class. For a non-generic class, this
/// is the class itself. For a generic alias, this is the alias's origin.
pub(crate) fn class_literal(
@@ -352,7 +356,7 @@ impl<'db> ClassType<'db> {
ClassBase::Dynamic(_) => false,
// Protocol and Generic are not represented by a ClassType.
ClassBase::Protocol(_) | ClassBase::Generic(_) => false,
ClassBase::Protocol | ClassBase::Generic => false,
ClassBase::Class(base) => match (base, other) {
(ClassType::NonGeneric(base), ClassType::NonGeneric(other)) => base == other,
@@ -390,7 +394,7 @@ impl<'db> ClassType<'db> {
ClassBase::Dynamic(_) => false,
// Protocol and Generic are not represented by a ClassType.
ClassBase::Protocol(_) | ClassBase::Generic(_) => false,
ClassBase::Protocol | ClassBase::Generic => false,
ClassBase::Class(base) => match (base, other) {
(ClassType::NonGeneric(base), ClassType::NonGeneric(other)) => base == other,
@@ -602,11 +606,6 @@ impl<'db> ClassLiteral<'db> {
)
}
/// Return `true` if this class represents the builtin class `object`
pub(crate) fn is_object(self, db: &'db dyn Db) -> bool {
self.is_known(db, KnownClass::Object)
}
fn file(self, db: &dyn Db) -> File {
self.body_scope(db).file(db)
}
@@ -1068,7 +1067,7 @@ impl<'db> ClassLiteral<'db> {
for superclass in mro_iter {
match superclass {
ClassBase::Generic(_) | ClassBase::Protocol(_) => {
ClassBase::Generic | ClassBase::Protocol => {
// Skip over these very special class bases that aren't really classes.
}
ClassBase::Dynamic(_) => {
@@ -1427,7 +1426,7 @@ impl<'db> ClassLiteral<'db> {
for superclass in self.iter_mro(db, specialization) {
match superclass {
ClassBase::Generic(_) | ClassBase::Protocol(_) => {
ClassBase::Generic | ClassBase::Protocol => {
// Skip over these very special class bases that aren't really classes.
}
ClassBase::Dynamic(_) => {

View File

@@ -1,5 +1,5 @@
use crate::Db;
use crate::types::generics::{GenericContext, Specialization};
use crate::types::generics::Specialization;
use crate::types::{
ClassType, DynamicType, KnownClass, KnownInstanceType, MroError, MroIterator, Type,
TypeMapping, todo_type,
@@ -19,11 +19,11 @@ pub enum ClassBase<'db> {
Class(ClassType<'db>),
/// Although `Protocol` is not a class in typeshed's stubs, it is at runtime,
/// and can appear in the MRO of a class.
Protocol(Option<GenericContext<'db>>),
Protocol,
/// Bare `Generic` cannot be subclassed directly in user code,
/// but nonetheless appears in the MRO of classes that inherit from `Generic[T]`,
/// `Protocol[T]`, or bare `Protocol`.
Generic(Option<GenericContext<'db>>),
Generic,
}
impl<'db> ClassBase<'db> {
@@ -35,60 +35,18 @@ impl<'db> ClassBase<'db> {
match self {
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
Self::Class(class) => Self::Class(class.normalized(db)),
Self::Protocol(generic_context) => {
Self::Protocol(generic_context.map(|context| context.normalized(db)))
}
Self::Generic(generic_context) => {
Self::Generic(generic_context.map(|context| context.normalized(db)))
}
Self::Protocol | Self::Generic => self,
}
}
pub(crate) fn display(self, db: &'db dyn Db) -> impl std::fmt::Display + 'db {
struct Display<'db> {
base: ClassBase<'db>,
db: &'db dyn Db,
}
impl std::fmt::Display for Display<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.base {
ClassBase::Dynamic(dynamic) => dynamic.fmt(f),
ClassBase::Class(class @ ClassType::NonGeneric(_)) => {
write!(f, "<class '{}'>", class.name(self.db))
}
ClassBase::Class(ClassType::Generic(alias)) => {
write!(f, "<class '{}'>", alias.display(self.db))
}
ClassBase::Protocol(generic_context) => {
f.write_str("typing.Protocol")?;
if let Some(generic_context) = generic_context {
generic_context.display(self.db).fmt(f)?;
}
Ok(())
}
ClassBase::Generic(generic_context) => {
f.write_str("typing.Generic")?;
if let Some(generic_context) = generic_context {
generic_context.display(self.db).fmt(f)?;
}
Ok(())
}
}
}
}
Display { base: self, db }
}
pub(crate) fn name(self, db: &'db dyn Db) -> &'db str {
match self {
ClassBase::Class(class) => class.name(db),
ClassBase::Dynamic(DynamicType::Any) => "Any",
ClassBase::Dynamic(DynamicType::Unknown) => "Unknown",
ClassBase::Dynamic(DynamicType::Todo(_) | DynamicType::TodoPEP695ParamSpec) => "@Todo",
ClassBase::Protocol(_) => "Protocol",
ClassBase::Generic(_) => "Generic",
ClassBase::Protocol => "Protocol",
ClassBase::Generic => "Generic",
}
}
@@ -255,12 +213,8 @@ impl<'db> ClassBase<'db> {
KnownInstanceType::Callable => {
Self::try_from_type(db, todo_type!("Support for Callable as a base class"))
}
KnownInstanceType::Protocol(generic_context) => {
Some(ClassBase::Protocol(generic_context))
}
KnownInstanceType::Generic(generic_context) => {
Some(ClassBase::Generic(generic_context))
}
KnownInstanceType::Protocol(_) => Some(ClassBase::Protocol),
KnownInstanceType::Generic(_) => Some(ClassBase::Generic),
},
}
}
@@ -268,14 +222,14 @@ impl<'db> ClassBase<'db> {
pub(super) fn into_class(self) -> Option<ClassType<'db>> {
match self {
Self::Class(class) => Some(class),
Self::Dynamic(_) | Self::Generic(_) | Self::Protocol(_) => None,
Self::Dynamic(_) | Self::Generic | Self::Protocol => None,
}
}
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
match self {
Self::Class(class) => Self::Class(class.apply_type_mapping(db, type_mapping)),
Self::Dynamic(_) | Self::Generic(_) | Self::Protocol(_) => self,
Self::Dynamic(_) | Self::Generic | Self::Protocol => self,
}
}
@@ -299,7 +253,7 @@ impl<'db> ClassBase<'db> {
.try_mro(db, specialization)
.is_err_and(MroError::is_cycle)
}
ClassBase::Dynamic(_) | ClassBase::Generic(_) | ClassBase::Protocol(_) => false,
ClassBase::Dynamic(_) | ClassBase::Generic | ClassBase::Protocol => false,
}
}
@@ -310,12 +264,8 @@ impl<'db> ClassBase<'db> {
additional_specialization: Option<Specialization<'db>>,
) -> impl Iterator<Item = ClassBase<'db>> {
match self {
ClassBase::Protocol(context) => {
ClassBaseMroIterator::length_3(db, self, ClassBase::Generic(context))
}
ClassBase::Dynamic(_) | ClassBase::Generic(_) => {
ClassBaseMroIterator::length_2(db, self)
}
ClassBase::Protocol => ClassBaseMroIterator::length_3(db, self, ClassBase::Generic),
ClassBase::Dynamic(_) | ClassBase::Generic => ClassBaseMroIterator::length_2(db, self),
ClassBase::Class(class) => {
ClassBaseMroIterator::from_class(db, class, additional_specialization)
}
@@ -338,12 +288,8 @@ impl<'db> From<ClassBase<'db>> for Type<'db> {
match value {
ClassBase::Dynamic(dynamic) => Type::Dynamic(dynamic),
ClassBase::Class(class) => class.into(),
ClassBase::Protocol(generic_context) => {
Type::KnownInstance(KnownInstanceType::Protocol(generic_context))
}
ClassBase::Generic(generic_context) => {
Type::KnownInstance(KnownInstanceType::Generic(generic_context))
}
ClassBase::Protocol => Type::KnownInstance(KnownInstanceType::Protocol(None)),
ClassBase::Generic => Type::KnownInstance(KnownInstanceType::Generic(None)),
}
}
}

View File

@@ -13,6 +13,7 @@ use crate::types::string_annotation::{
};
use crate::types::{KnownFunction, KnownInstanceType, Type, protocol_class::ProtocolClassLiteral};
use crate::{Program, PythonVersionWithSource, declare_lint};
use itertools::Itertools;
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, Span, SubDiagnostic};
use ruff_db::files::system_path_to_file;
use ruff_python_ast::{self as ast, AnyNodeRef};
@@ -1698,10 +1699,7 @@ pub(super) fn report_implicit_return_type(
let Some(class) = enclosing_class_of_method else {
return;
};
if class
.iter_mro(db, None)
.any(|base| matches!(base, ClassBase::Protocol(_)))
{
if class.iter_mro(db, None).contains(&ClassBase::Protocol) {
diagnostic.info(
"Only functions in stub files, methods on protocol classes, \
or methods with `@abstractmethod` are permitted to have empty bodies",

Some files were not shown because too many files have changed in this diff Show More