diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9bc1301aa9..d63c894fc0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -52,6 +52,11 @@ repos: pass_filenames: false entry: python -m utils.sort_api_reference language: python + - id: check-slotted-classes + name: check-slotted-classes + pass_filenames: false + entry: python -m utils.check_slotted_classes + language: python - id: imports-are-banned name: import are banned (use `get_pandas` instead of `import pandas`) entry: python utils/import_check.py diff --git a/narwhals/dtypes.py b/narwhals/dtypes.py index 831f704be8..e73e6ee93a 100644 --- a/narwhals/dtypes.py +++ b/narwhals/dtypes.py @@ -60,6 +60,8 @@ def _validate_into_dtype(dtype: Any) -> None: class DType: + __slots__ = () + def __repr__(self) -> str: # pragma: no cover return self.__class__.__qualname__ @@ -152,30 +154,44 @@ def __hash__(self) -> int: class NumericType(DType): """Base class for numeric data types.""" + __slots__ = () + class IntegerType(NumericType): """Base class for integer data types.""" + __slots__ = () + class SignedIntegerType(IntegerType): """Base class for signed integer data types.""" + __slots__ = () + class UnsignedIntegerType(IntegerType): """Base class for unsigned integer data types.""" + __slots__ = () + class FloatType(NumericType): """Base class for float data types.""" + __slots__ = () + class TemporalType(DType): """Base class for temporal data types.""" + __slots__ = () + class NestedType(DType): """Base class for nested data types.""" + __slots__ = () + class Decimal(NumericType): """Decimal type. @@ -188,6 +204,8 @@ class Decimal(NumericType): Decimal """ + __slots__ = () + class Int128(SignedIntegerType): """128-bit signed integer type. @@ -208,6 +226,8 @@ class Int128(SignedIntegerType): Int128 """ + __slots__ = () + class Int64(SignedIntegerType): """64-bit signed integer type. @@ -221,6 +241,8 @@ class Int64(SignedIntegerType): Int64 """ + __slots__ = () + class Int32(SignedIntegerType): """32-bit signed integer type. @@ -234,6 +256,8 @@ class Int32(SignedIntegerType): Int32 """ + __slots__ = () + class Int16(SignedIntegerType): """16-bit signed integer type. @@ -247,6 +271,8 @@ class Int16(SignedIntegerType): Int16 """ + __slots__ = () + class Int8(SignedIntegerType): """8-bit signed integer type. @@ -260,6 +286,8 @@ class Int8(SignedIntegerType): Int8 """ + __slots__ = () + class UInt128(UnsignedIntegerType): """128-bit unsigned integer type. @@ -274,6 +302,8 @@ class UInt128(UnsignedIntegerType): UInt128 """ + __slots__ = () + class UInt64(UnsignedIntegerType): """64-bit unsigned integer type. @@ -287,6 +317,8 @@ class UInt64(UnsignedIntegerType): UInt64 """ + __slots__ = () + class UInt32(UnsignedIntegerType): """32-bit unsigned integer type. @@ -300,6 +332,8 @@ class UInt32(UnsignedIntegerType): UInt32 """ + __slots__ = () + class UInt16(UnsignedIntegerType): """16-bit unsigned integer type. @@ -313,6 +347,8 @@ class UInt16(UnsignedIntegerType): UInt16 """ + __slots__ = () + class UInt8(UnsignedIntegerType): """8-bit unsigned integer type. @@ -326,6 +362,8 @@ class UInt8(UnsignedIntegerType): UInt8 """ + __slots__ = () + class Float64(FloatType): """64-bit floating point type. @@ -339,6 +377,8 @@ class Float64(FloatType): Float64 """ + __slots__ = () + class Float32(FloatType): """32-bit floating point type. @@ -352,6 +392,8 @@ class Float32(FloatType): Float32 """ + __slots__ = () + class String(DType): """UTF-8 encoded string type. @@ -364,6 +406,8 @@ class String(DType): String """ + __slots__ = () + class Boolean(DType): """Boolean type. @@ -376,6 +420,8 @@ class Boolean(DType): Boolean """ + __slots__ = () + class Object(DType): """Data type for wrapping arbitrary Python objects. @@ -389,6 +435,8 @@ class Object(DType): Object """ + __slots__ = () + class Unknown(DType): """Type representing DataType values that could not be determined statically. @@ -401,6 +449,8 @@ class Unknown(DType): Unknown """ + __slots__ = () + class _DatetimeMeta(type): @property @@ -438,6 +488,8 @@ class Datetime(TemporalType, metaclass=_DatetimeMeta): Datetime(time_unit='ms', time_zone='Africa/Accra') """ + __slots__ = ("time_unit", "time_zone") + def __init__( self, time_unit: TimeUnit = "us", time_zone: str | timezone | None = None ) -> None: @@ -521,6 +573,8 @@ class Duration(TemporalType, metaclass=_DurationMeta): Duration(time_unit='ms') """ + __slots__ = ("time_unit",) + def __init__(self, time_unit: TimeUnit = "us") -> None: if time_unit not in {"s", "ms", "us", "ns"}: msg = ( @@ -573,6 +627,8 @@ class Categorical(DType): Categorical """ + __slots__ = () + class Enum(DType): """A fixed categorical encoding of a unique set of strings. @@ -586,6 +642,8 @@ class Enum(DType): Enum(categories=['beluga', 'narwhal', 'orca']) """ + __slots__ = ("_cached_categories", "_delayed_categories") + def __init__(self, categories: Iterable[str] | type[enum.Enum]) -> None: self._delayed_categories: _DeferredIterable[str] | None = None self._cached_categories: tuple[str, ...] | None = None @@ -655,6 +713,7 @@ class Field: [Field('a', Int64), Field('b', List(String))] """ + __slots__ = ("dtype", "name") name: str """The name of the field within its parent `Struct`.""" dtype: IntoDType @@ -713,6 +772,7 @@ class Struct(NestedType): Struct({'a': Int64, 'b': List(String)}) """ + __slots__ = ("fields",) fields: list[Field] """The fields that make up the struct.""" @@ -782,6 +842,7 @@ class List(NestedType): List(String) """ + __slots__ = ("inner",) inner: IntoDType """The DType of the values within each list.""" @@ -832,6 +893,7 @@ class Array(NestedType): Array(Int32, shape=(2,)) """ + __slots__ = ("inner", "shape", "size") inner: IntoDType """The DType of the values within each array.""" size: int @@ -910,6 +972,8 @@ class Date(TemporalType): Date """ + __slots__ = () + class Time(TemporalType): """Data type representing the time of day. @@ -935,6 +999,8 @@ class Time(TemporalType): Time """ + __slots__ = () + class Binary(DType): """Binary type. @@ -958,3 +1024,5 @@ class Binary(DType): >>> nw.from_native(rel).collect_schema()["t"] Binary """ + + __slots__ = () diff --git a/narwhals/stable/v1/_dtypes.py b/narwhals/stable/v1/_dtypes.py index 060980c562..98b490b56f 100644 --- a/narwhals/stable/v1/_dtypes.py +++ b/narwhals/stable/v1/_dtypes.py @@ -48,6 +48,8 @@ class Datetime(NwDatetime): + __slots__ = NwDatetime.__slots__ + @inherit_doc(NwDatetime) def __init__( self, time_unit: TimeUnit = "us", time_zone: str | timezone | None = None @@ -59,6 +61,8 @@ def __hash__(self) -> int: class Duration(NwDuration): + __slots__ = NwDuration.__slots__ + @inherit_doc(NwDuration) def __init__(self, time_unit: TimeUnit = "us") -> None: super().__init__(time_unit) @@ -81,6 +85,8 @@ class Enum(NwEnum): Enum """ + __slots__ = () + def __init__(self) -> None: super(NwEnum, self).__init__() diff --git a/pyproject.toml b/pyproject.toml index da62cde2e4..27623fb567 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -210,8 +210,8 @@ extend-ignore-names = [ "PLR0916", # too-many-boolean-expressions ] "tpch/tests/*" = ["S101"] -"utils/*" = ["S311"] -"utils/bump_version.py" = ["S603", "S607", "T201"] +"utils/*" = ["S311", "T201"] +"utils/bump_version.py" = ["S603", "S607"] "tpch/execute/*" = ["T201"] "tpch/notebooks/*" = [ "ANN001", diff --git a/tests/dtypes_test.py b/tests/dtypes_test.py index d63384647f..fe2dec98e7 100644 --- a/tests/dtypes_test.py +++ b/tests/dtypes_test.py @@ -17,9 +17,59 @@ if TYPE_CHECKING: from collections.abc import Iterable + from typing_extensions import TypeAlias + from narwhals.typing import IntoFrame, IntoSeries, NonNestedDType from tests.utils import Constructor, ConstructorPandasLike +NestedOrEnumDType: TypeAlias = "nw.List | nw.Array | nw.Struct | nw.Enum" +"""`DType`s which **cannot** be used as bare types.""" + + +@pytest.fixture( + params=[ + nw.Boolean, + nw.Categorical, + nw.Date, + nw.Datetime, + nw.Decimal, + nw.Duration, + nw.Float32, + nw.Float64, + nw.Int8, + nw.Int16, + nw.Int32, + nw.Int64, + nw.Int128, + nw.Object, + nw.String, + nw.Time, + nw.UInt8, + nw.UInt16, + nw.UInt32, + nw.UInt64, + nw.UInt128, + nw.Unknown, + nw.Binary, + ], + ids=lambda tp: tp.__name__, +) +def non_nested_type(request: pytest.FixtureRequest) -> type[NonNestedDType]: + return request.param # type: ignore[no-any-return] + + +@pytest.fixture( + params=[ + nw.List(nw.Float32), + nw.Array(nw.String, 2), + nw.Struct({"a": nw.Boolean}), + nw.Enum(["beluga", "narwhal"]), + ], + ids=lambda obj: type(obj).__name__, +) +def nested_dtype(request: pytest.FixtureRequest) -> NestedOrEnumDType: + return request.param # type: ignore[no-any-return] + @pytest.mark.parametrize("time_unit", ["us", "ns", "ms"]) @pytest.mark.parametrize("time_zone", ["Europe/Rome", timezone.utc, None]) @@ -534,43 +584,13 @@ def test_datetime_w_tz_pyspark() -> None: # pragma: no cover assert result["a"] == nw.List(nw.Datetime("us", "UTC")) -@pytest.mark.parametrize( - "dtype", - [ - nw.Boolean, - nw.Categorical, - nw.Date, - nw.Datetime, - nw.Decimal, - nw.Duration, - nw.Float32, - nw.Float64, - nw.Int8, - nw.Int16, - nw.Int32, - nw.Int64, - nw.Int128, - nw.Object, - nw.String, - nw.Time, - nw.UInt8, - nw.UInt16, - nw.UInt32, - nw.UInt64, - nw.UInt128, - nw.Unknown, - nw.Binary, - ], -) -def test_dtype_base_type_non_nested(dtype: type[NonNestedDType]) -> None: - assert dtype.base_type() is dtype().base_type() +def test_dtype_base_type_non_nested(non_nested_type: type[NonNestedDType]) -> None: + assert non_nested_type.base_type() is non_nested_type().base_type() -def test_dtype_base_type_nested() -> None: - assert nw.List.base_type() is nw.List(nw.Float32).base_type() - assert nw.Array.base_type() is nw.Array(nw.String, 2).base_type() - assert nw.Struct.base_type() is nw.Struct({"a": nw.Boolean}).base_type() - assert nw.Enum.base_type() is nw.Enum(["beluga", "narwhal"]).base_type() +def test_dtype_base_type_nested(nested_dtype: NestedOrEnumDType) -> None: + base = nested_dtype.base_type() + assert base.base_type() == nested_dtype.base_type() @pytest.mark.parametrize( @@ -594,3 +614,21 @@ def test_pandas_datetime_ignored_time_unit_warns( ctx = does_not_warn() if PANDAS_VERSION >= (2,) else context with ctx: df.select(expr) + + +def test_dtype___slots___non_nested(non_nested_type: type[NonNestedDType]) -> None: + dtype = non_nested_type() + with pytest.raises(AttributeError): + dtype.i_dont_exist = 100 # type: ignore[union-attr] + with pytest.raises(AttributeError): + dtype.__dict__ # noqa: B018 + _ = dtype.__slots__ + + +def test_dtype___slots___nested(nested_dtype: NestedOrEnumDType) -> None: + with pytest.raises(AttributeError): + nested_dtype.i_dont_exist = 999 # type: ignore[union-attr] + with pytest.raises(AttributeError): + nested_dtype.__dict__ # noqa: B018 + slots = nested_dtype.__slots__ + assert len(slots) != 0, slots diff --git a/tests/expr_and_series/cast_test.py b/tests/expr_and_series/cast_test.py index 230643a4be..09a19073b5 100644 --- a/tests/expr_and_series/cast_test.py +++ b/tests/expr_and_series/cast_test.py @@ -375,7 +375,7 @@ def test_cast_typing_invalid() -> None: # feel free to update the types used # See (https://github.com/narwhals-dev/narwhals/pull/2654#discussion_r2142263770) - with pytest.raises(AttributeError): + with pytest.raises(TypeError): df.select(a.cast(nw.Struct)) # type: ignore[arg-type] with pytest.raises(AttributeError): @@ -393,7 +393,7 @@ def test_cast_typing_invalid() -> None: with pytest.raises((ValueError, AttributeError)): df.select(a.cast(nw.Struct({"a": nw.Int16, "b": nw.Enum}))) # type: ignore[dict-item] - with pytest.raises(AttributeError): + with pytest.raises(TypeError): df.select(a.cast(nw.List(nw.Struct))) # type: ignore[arg-type] with pytest.raises(AttributeError): diff --git a/tests/v1_test.py b/tests/v1_test.py index 37d5dc1779..67660f3614 100644 --- a/tests/v1_test.py +++ b/tests/v1_test.py @@ -50,6 +50,7 @@ from typing_extensions import assert_type from narwhals._typing import EagerAllowed + from narwhals.dtypes import DType from narwhals.stable.v1.typing import IntoDataFrameT from narwhals.typing import IntoDType, _1DArray, _2DArray from tests.utils import Constructor, ConstructorEager @@ -1115,3 +1116,11 @@ def test_mode_different_lengths(constructor_eager: ConstructorEager) -> None: df = nw_v1.from_native(constructor_eager({"a": [1, 1, 2], "b": [4, 5, 6]})) with pytest.raises(ShapeError): df.select(nw_v1.col("a", "b").mode()) + + +@pytest.mark.parametrize( + "dtype", [nw_v1.Datetime(), nw_v1.Duration(), nw_v1.Enum()], ids=str +) +def test_dtype___slots__(dtype: DType) -> None: + with pytest.raises(AttributeError): + dtype.i_also_dont_exist = 528329 # type: ignore[attr-defined] diff --git a/utils/check_api_reference.py b/utils/check_api_reference.py index 3233b24b1c..03ad115a5e 100644 --- a/utils/check_api_reference.py +++ b/utils/check_api_reference.py @@ -118,48 +118,48 @@ def read_documented_members(source: str | Path) -> list[str]: documented = read_documented_members(DIR_API_REF / "narwhals.md") if missing := set(top_level_functions).difference(documented).difference({"annotations"}): - print("top-level functions: not documented") # noqa: T201 - print(missing) # noqa: T201 + print("top-level functions: not documented") + print(missing) ret = 1 if extra := set(documented).difference(top_level_functions): - print("top-level functions: outdated") # noqa: T201 - print(extra) # noqa: T201 + print("top-level functions: outdated") + print(extra) ret = 1 # DataFrame methods dataframe_methods = list(iter_api_reference_names(nw.DataFrame)) documented = read_documented_members(DIR_API_REF / "dataframe.md") if missing := set(dataframe_methods).difference(documented): - print("DataFrame: not documented") # noqa: T201 - print(missing) # noqa: T201 + print("DataFrame: not documented") + print(missing) ret = 1 if extra := set(documented).difference(dataframe_methods): - print("DataFrame: outdated") # noqa: T201 - print(extra) # noqa: T201 + print("DataFrame: outdated") + print(extra) ret = 1 # LazyFrame methods lazyframe_methods = list(iter_api_reference_names(nw.LazyFrame)) documented = read_documented_members(DIR_API_REF / "lazyframe.md") if missing := set(lazyframe_methods).difference(documented): - print("LazyFrame: not documented") # noqa: T201 - print(missing) # noqa: T201 + print("LazyFrame: not documented") + print(missing) ret = 1 if extra := set(documented).difference(lazyframe_methods): - print("LazyFrame: outdated") # noqa: T201 - print(extra) # noqa: T201 + print("LazyFrame: outdated") + print(extra) ret = 1 # Series methods series_methods = list(iter_api_reference_names(nw.Series)) documented = read_documented_members(DIR_API_REF / "series.md") if missing := set(series_methods).difference(documented).difference(NAMESPACES): - print("Series: not documented") # noqa: T201 - print(missing) # noqa: T201 + print("Series: not documented") + print(missing) ret = 1 if extra := set(documented).difference(series_methods): - print("Series: outdated") # noqa: T201 - print(extra) # noqa: T201 + print("Series: outdated") + print(extra) ret = 1 # Series.{cat, dt, list, str} methods @@ -171,24 +171,24 @@ def read_documented_members(source: str | Path) -> list[str]: ] documented = read_documented_members(DIR_API_REF / f"series_{namespace}.md") if missing := set(series_ns_methods).difference(documented): - print(f"Series.{namespace}: not documented") # noqa: T201 - print(missing) # noqa: T201 + print(f"Series.{namespace}: not documented") + print(missing) ret = 1 if extra := set(documented).difference(series_ns_methods): - print(f"Series.{namespace}: outdated") # noqa: T201 - print(extra) # noqa: T201 + print(f"Series.{namespace}: outdated") + print(extra) ret = 1 # Expr methods expr_methods = list(iter_api_reference_names(nw.Expr)) documented = read_documented_members(DIR_API_REF / "expr.md") if missing := set(expr_methods).difference(documented).difference(NAMESPACES): - print("Expr: not documented") # noqa: T201 - print(missing) # noqa: T201 + print("Expr: not documented") + print(missing) ret = 1 if extra := set(documented).difference(expr_methods): - print("Expr: outdated") # noqa: T201 - print(extra) # noqa: T201 + print("Expr: outdated") + print(extra) ret = 1 # Expr.{cat, dt, list, name, str} methods @@ -200,24 +200,24 @@ def read_documented_members(source: str | Path) -> list[str]: ] documented = read_documented_members(DIR_API_REF / f"expr_{namespace}.md") if missing := set(expr_ns_methods).difference(documented): - print(f"Expr.{namespace}: not documented") # noqa: T201 - print(missing) # noqa: T201 + print(f"Expr.{namespace}: not documented") + print(missing) ret = 1 if extra := set(documented).difference(expr_ns_methods): - print(f"Expr.{namespace}: outdated") # noqa: T201 - print(extra) # noqa: T201 + print(f"Expr.{namespace}: outdated") + print(extra) ret = 1 # DTypes dtypes = list(iter_api_reference_names_dtypes(nw.dtypes)) documented = read_documented_members(DIR_API_REF / "dtypes.md") if missing := set(dtypes).difference(documented): - print("Dtype: not documented") # noqa: T201 - print(missing) # noqa: T201 + print("Dtype: not documented") + print(missing) ret = 1 if extra := set(documented).difference(dtypes): - print("Dtype: outdated") # noqa: T201 - print(extra) # noqa: T201 + print("Dtype: outdated") + print(extra) ret = 1 # Schema @@ -228,22 +228,22 @@ def read_documented_members(source: str | Path) -> list[str]: .difference(documented) .difference(iter_api_reference_names(OrderedDict)) ): - print("Schema: not documented") # noqa: T201 - print(missing) # noqa: T201 + print("Schema: not documented") + print(missing) ret = 1 if extra := set(documented).difference(schema_methods): - print("Schema: outdated") # noqa: T201 - print(extra) # noqa: T201 + print("Schema: outdated") + print(extra) ret = 1 # Check Expr vs Series if missing := set(expr_methods).difference(series_methods).difference(EXPR_ONLY_METHODS): - print("In Expr but not in Series") # noqa: T201 - print(missing) # noqa: T201 + print("In Expr but not in Series") + print(missing) ret = 1 if extra := set(series_methods).difference(expr_methods).difference(SERIES_ONLY_METHODS): - print("In Series but not in Expr") # noqa: T201 - print(extra) # noqa: T201 + print("In Series but not in Expr") + print(extra) ret = 1 # Check Expr vs Series internal methods @@ -259,12 +259,12 @@ def read_documented_members(source: str | Path) -> list[str]: if not i[0].isupper() and i[0] != "_" ] if missing := set(expr_internal).difference(series_internal): - print(f"In Expr.{namespace} but not in Series.{namespace}") # noqa: T201 - print(missing) # noqa: T201 + print(f"In Expr.{namespace} but not in Series.{namespace}") + print(missing) ret = 1 if extra := set(series_internal).difference(expr_internal): - print(f"In Series.{namespace} but not in Expr.{namespace}") # noqa: T201 - print(extra) # noqa: T201 + print(f"In Series.{namespace} but not in Expr.{namespace}") + print(extra) ret = 1 sys.exit(ret) diff --git a/utils/check_dist_content.py b/utils/check_dist_content.py index 9a9922ef8d..62a387e5f6 100644 --- a/utils/check_dist_content.py +++ b/utils/check_dist_content.py @@ -18,7 +18,7 @@ } if unexpected_wheel_dirs: - print(f"🚨 Unexpected directories in wheel: {unexpected_wheel_dirs}") # noqa: T201 + print(f"🚨 Unexpected directories in wheel: {unexpected_wheel_dirs}") sys.exit(1) with TarFile.open(sdist_path, mode="r:gz") as sdist_file: @@ -35,7 +35,7 @@ } if unexpected_sdist_dirs := sdist_dirs - allowed_sdist_dirs: - print(f"🚨 Unexpected directories or files in sdist: {unexpected_sdist_dirs}") # noqa: T201 + print(f"🚨 Unexpected directories or files in sdist: {unexpected_sdist_dirs}") sys.exit(1) sys.exit(0) diff --git a/utils/check_docstrings.py b/utils/check_docstrings.py index 12a6d6faaf..a3049eabd2 100644 --- a/utils/check_docstrings.py +++ b/utils/check_docstrings.py @@ -77,11 +77,11 @@ def report_errors(errors: list[str], temp_files: list[tuple[Path, str]]) -> None if not errors: return - print("❌ Ruff issues found in examples:\n") # noqa: T201 + print("❌ Ruff issues found in examples:\n") for line in errors: for temp_file, original_context in temp_files: if str(temp_file) in line: - print(f"{original_context}{line.replace(str(temp_file), '')}") # noqa: T201 + print(f"{original_context}{line.replace(str(temp_file), '')}") break diff --git a/utils/check_slotted_classes.py b/utils/check_slotted_classes.py new file mode 100644 index 0000000000..ffa73269e0 --- /dev/null +++ b/utils/check_slotted_classes.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import sys +from typing import TYPE_CHECKING, TypeVar + +from narwhals._utils import Version, qualified_type_name +from narwhals.dtypes import DType, Field + +if TYPE_CHECKING: + from collections.abc import Iterator + + +T_co = TypeVar("T_co", covariant=True) + +# NOTE: For `__subclasses__` to work, all modules that descendants are defined in must be imported +_ = Version.MAIN.dtypes +_ = Version.V1.dtypes +_ = Version.V2.dtypes + + +def _iter_descendants(*bases: type[T_co]) -> Iterator[type[T_co]]: + seen = set[T_co]() + for base in bases: + yield base + if (children := (base.__subclasses__())) and ( + unseen := set(children).difference(seen) + ): + yield from _iter_descendants(*unseen) + + +def iter_unslotted_classes(*bases: type[T_co]) -> Iterator[str]: + """Find classes in that inherit from `bases` but don't define `__slots__`.""" + for tp in sorted(set(_iter_descendants(*bases)), key=qualified_type_name): + if "__slots__" not in tp.__dict__: + yield qualified_type_name(tp) + + +ret = 0 +unslotted_classes = tuple(iter_unslotted_classes(DType, Field)) + +if unslotted_classes: + ret = 1 + msg = "The following classes are expected to define `__slots__` but they don't:\n" + cls_list = "\n".join(f" * {name}" for name in unslotted_classes) + url = "https://docs.python.org/3/reference/datamodel.html#slots" + hint = f"Hint: For more details see {url!r}" + print(f"{msg}{cls_list}") + +sys.exit(ret) diff --git a/utils/import_check.py b/utils/import_check.py index 17f6616fa6..d292b40790 100644 --- a/utils/import_check.py +++ b/utils/import_check.py @@ -56,7 +56,7 @@ def visit_Import(self, node: ast.Import) -> None: and alias.name not in self.allowed_imports and "# ignore-banned-import" not in self.lines[node.lineno - 1] ): - print( # noqa: T201 + print( f"{self.file_name}:{node.lineno}:{node.col_offset}: found {alias.name} import" ) self.found_import = True @@ -69,7 +69,7 @@ def visit_ImportFrom(self, node: ast.ImportFrom) -> None: and "# ignore-banned-import" not in self.lines[node.lineno - 1] and node.module not in self.allowed_imports ): - print( # noqa: T201 + print( f"{self.file_name}:{node.lineno}:{node.col_offset}: found {node.module} import" ) self.found_import = True diff --git a/utils/sort_api_reference.py b/utils/sort_api_reference.py index 550e4ab089..1b417ed63a 100644 --- a/utils/sort_api_reference.py +++ b/utils/sort_api_reference.py @@ -1,5 +1,4 @@ """Script to automatically sort members lists in API reference markdown files.""" -# ruff: noqa: T201 from __future__ import annotations