Expand documentation around flake8-type-checking rules for SQLAlchemy (#6570)

## Summary

Not addressing the root issue as much as improving the documentation.

Closes https://github.com/astral-sh/ruff/issues/6510.
This commit is contained in:
Charlie Marsh 2023-08-14 15:47:10 -04:00 committed by GitHub
parent 5ddf143cae
commit cd634a9489
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 17 deletions

View file

@ -0,0 +1,12 @@
from __future__ import annotations
from datetime import date
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Birthday(DeclarativeBase):
__tablename__ = "birthday"
id: Mapped[int] = mapped_column(primary_key=True)
day: Mapped[date]

View file

@ -89,13 +89,20 @@ mod tests {
Rule::TypingOnlyStandardLibraryImport,
Path::new("runtime_evaluated_base_classes_3.py")
)]
#[test_case(
Rule::TypingOnlyStandardLibraryImport,
Path::new("runtime_evaluated_base_classes_4.py")
)]
fn runtime_evaluated_base_classes(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
let diagnostics = test_path(
Path::new("flake8_type_checking").join(path).as_path(),
&settings::Settings {
flake8_type_checking: super::settings::Settings {
runtime_evaluated_base_classes: vec!["pydantic.BaseModel".to_string()],
runtime_evaluated_base_classes: vec![
"pydantic.BaseModel".to_string(),
"sqlalchemy.orm.DeclarativeBase".to_string(),
],
..Default::default()
},
..settings::Settings::for_rule(rule_code)

View file

@ -20,17 +20,25 @@ use crate::rules::isort::{categorize, ImportSection, ImportType};
///
/// ## Why is this bad?
/// Unused imports add a performance overhead at runtime, and risk creating
/// import cycles.
/// import cycles. If an import is _only_ used in typing-only contexts, it can
/// instead be imported conditionally under an `if TYPE_CHECKING:` block to
/// minimize runtime overhead.
///
/// If a class _requires_ that type annotations be available at runtime (as is
/// the case for Pydantic, SQLAlchemy, and other libraries), consider using
/// the [`flake8-type-checking.runtime-evaluated-base-classes`] and
/// [`flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
/// as such.
///
/// ## Example
/// ```python
/// from __future__ import annotations
///
/// import A
/// import local_module
///
///
/// def foo(a: A) -> int:
/// return len(a)
/// def func(sized: local_module.Container) -> int:
/// return len(sized)
/// ```
///
/// Use instead:
@ -40,13 +48,17 @@ use crate::rules::isort::{categorize, ImportSection, ImportType};
/// from typing import TYPE_CHECKING
///
/// if TYPE_CHECKING:
/// import A
/// import local_module
///
///
/// def foo(a: A) -> int:
/// return len(a)
/// def func(sized: local_module.Container) -> int:
/// return len(sized)
/// ```
///
/// ## Options
/// - `flake8-type-checking.runtime-evaluated-base-classes`
/// - `flake8-type-checking.runtime-evaluated-decorators`
///
/// ## References
/// - [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking)
#[violation]
@ -76,7 +88,15 @@ impl Violation for TypingOnlyFirstPartyImport {
///
/// ## Why is this bad?
/// Unused imports add a performance overhead at runtime, and risk creating
/// import cycles.
/// import cycles. If an import is _only_ used in typing-only contexts, it can
/// instead be imported conditionally under an `if TYPE_CHECKING:` block to
/// minimize runtime overhead.
///
/// If a class _requires_ that type annotations be available at runtime (as is
/// the case for Pydantic, SQLAlchemy, and other libraries), consider using
/// the [`flake8-type-checking.runtime-evaluated-base-classes`] and
/// [`flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
/// as such.
///
/// ## Example
/// ```python
@ -85,7 +105,7 @@ impl Violation for TypingOnlyFirstPartyImport {
/// import pandas as pd
///
///
/// def foo(df: pd.DataFrame) -> int:
/// def func(df: pd.DataFrame) -> int:
/// return len(df)
/// ```
///
@ -99,10 +119,14 @@ impl Violation for TypingOnlyFirstPartyImport {
/// import pandas as pd
///
///
/// def foo(df: pd.DataFrame) -> int:
/// def func(df: pd.DataFrame) -> int:
/// return len(df)
/// ```
///
/// ## Options
/// - `flake8-type-checking.runtime-evaluated-base-classes`
/// - `flake8-type-checking.runtime-evaluated-decorators`
///
/// ## References
/// - [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking)
#[violation]
@ -132,7 +156,15 @@ impl Violation for TypingOnlyThirdPartyImport {
///
/// ## Why is this bad?
/// Unused imports add a performance overhead at runtime, and risk creating
/// import cycles.
/// import cycles. If an import is _only_ used in typing-only contexts, it can
/// instead be imported conditionally under an `if TYPE_CHECKING:` block to
/// minimize runtime overhead.
///
/// If a class _requires_ that type annotations be available at runtime (as is
/// the case for Pydantic, SQLAlchemy, and other libraries), consider using
/// the [`flake8-type-checking.runtime-evaluated-base-classes`] and
/// [`flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
/// as such.
///
/// ## Example
/// ```python
@ -141,7 +173,7 @@ impl Violation for TypingOnlyThirdPartyImport {
/// from pathlib import Path
///
///
/// def foo(path: Path) -> str:
/// def func(path: Path) -> str:
/// return str(path)
/// ```
///
@ -155,10 +187,14 @@ impl Violation for TypingOnlyThirdPartyImport {
/// from pathlib import Path
///
///
/// def foo(path: Path) -> str:
/// def func(path: Path) -> str:
/// return str(path)
/// ```
///
/// ## Options
/// - `flake8-type-checking.runtime-evaluated-base-classes`
/// - `flake8-type-checking.runtime-evaluated-decorators`
///
/// ## References
/// - [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking)
#[violation]

View file

@ -23,6 +23,7 @@ pub struct Options {
)]
/// Enforce TC001, TC002, and TC003 rules even when valid runtime imports
/// are present for the same module.
///
/// See flake8-type-checking's [strict](https://github.com/snok/flake8-type-checking#strict) option.
pub strict: Option<bool>,
#[option(
@ -39,11 +40,19 @@ pub struct Options {
default = "[]",
value_type = "list[str]",
example = r#"
runtime-evaluated-base-classes = ["pydantic.BaseModel"]
runtime-evaluated-base-classes = ["pydantic.BaseModel", "sqlalchemy.orm.DeclarativeBase"]
"#
)]
/// Exempt classes that list any of the enumerated classes as a base class
/// from needing to be moved into type-checking blocks.
///
/// Common examples include Pydantic's `pydantic.BaseModel` and SQLAlchemy's
/// `sqlalchemy.orm.DeclarativeBase`, but can also support user-defined
/// classes that inherit from those base classes. For example, if you define
/// a common `DeclarativeBase` subclass that's used throughout your project
/// (e.g., `class Base(DeclarativeBase) ...` in `base.py`), you can add it to
/// this list (`runtime-evaluated-base-classes = ["base.Base"]`) to exempt
/// models from being moved into type-checking blocks.
pub runtime_evaluated_base_classes: Option<Vec<String>>,
#[option(
default = "[]",

4
ruff.schema.json generated
View file

@ -1090,7 +1090,7 @@
}
},
"runtime-evaluated-base-classes": {
"description": "Exempt classes that list any of the enumerated classes as a base class from needing to be moved into type-checking blocks.",
"description": "Exempt classes that list any of the enumerated classes as a base class from needing to be moved into type-checking blocks.\n\nCommon examples include Pydantic's `pydantic.BaseModel` and SQLAlchemy's `sqlalchemy.orm.DeclarativeBase`, but can also support user-defined classes that inherit from those base classes. For example, if you define a common `DeclarativeBase` subclass that's used throughout your project (e.g., `class Base(DeclarativeBase) ...` in `base.py`), you can add it to this list (`runtime-evaluated-base-classes = [\"base.Base\"]`) to exempt models from being moved into type-checking blocks.",
"type": [
"array",
"null"
@ -1110,7 +1110,7 @@
}
},
"strict": {
"description": "Enforce TC001, TC002, and TC003 rules even when valid runtime imports are present for the same module. See flake8-type-checking's [strict](https://github.com/snok/flake8-type-checking#strict) option.",
"description": "Enforce TC001, TC002, and TC003 rules even when valid runtime imports are present for the same module.\n\nSee flake8-type-checking's [strict](https://github.com/snok/flake8-type-checking#strict) option.",
"type": [
"boolean",
"null"