From e49fac59f14e7a1a850beea5d15cd39cc987ddad Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:15:05 +0000 Subject: [PATCH 1/6] chore(ruff): Ignore `TC008` false positives https://github.com/astral-sh/ruff/pull/15201#issuecomment-2582432791 --- altair/utils/schemapi.py | 2 +- tests/__init__.py | 2 +- tools/schemapi/schemapi.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/altair/utils/schemapi.py b/altair/utils/schemapi.py index e7708f0d7..3a49b928d 100644 --- a/altair/utils/schemapi.py +++ b/altair/utils/schemapi.py @@ -58,7 +58,7 @@ from typing import Never, Self else: from typing_extensions import Never, Self - _OptionalModule: TypeAlias = "ModuleType | None" + _OptionalModule: TypeAlias = "ModuleType | None" # noqa: TC008 ValidationErrorList: TypeAlias = list[jsonschema.exceptions.ValidationError] GroupedValidationErrors: TypeAlias = dict[str, ValidationErrorList] diff --git a/tests/__init__.py b/tests/__init__.py index 617cfca80..706f6d181 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -22,7 +22,7 @@ from _pytest.mark import ParameterSet MarksType: TypeAlias = ( - "pytest.MarkDecorator | Collection[pytest.MarkDecorator | pytest.Mark]" + "pytest.MarkDecorator | Collection[pytest.MarkDecorator | pytest.Mark]" # noqa: TC008 ) diff --git a/tools/schemapi/schemapi.py b/tools/schemapi/schemapi.py index 60e8182a7..0b926ec69 100644 --- a/tools/schemapi/schemapi.py +++ b/tools/schemapi/schemapi.py @@ -56,7 +56,7 @@ from typing import Never, Self else: from typing_extensions import Never, Self - _OptionalModule: TypeAlias = "ModuleType | None" + _OptionalModule: TypeAlias = "ModuleType | None" # noqa: TC008 ValidationErrorList: TypeAlias = list[jsonschema.exceptions.ValidationError] GroupedValidationErrors: TypeAlias = dict[str, ValidationErrorList] From cc9aa8492b05a7e678b3a48eb2d57851a98e2a20 Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:24:56 +0000 Subject: [PATCH 2/6] style(ruff): Apply 2025 format style Running `ruff format` on `>=0.9.0` https://astral.sh/blog/ruff-v0.9.0#the-ruff-2025-style-guide ] --- altair/utils/save.py | 2 +- altair/vegalite/v5/api.py | 10 ++++------ altair/vegalite/v5/display.py | 2 +- sphinxext/code_ref.py | 2 +- sphinxext/schematable.py | 2 +- tests/utils/test_plugin_registry.py | 4 ++-- tests/utils/test_schemapi.py | 4 +--- tools/generate_schema_wrapper.py | 8 +++----- tools/update_init_file.py | 5 ++--- tools/vega_expr.py | 3 +-- 10 files changed, 17 insertions(+), 25 deletions(-) diff --git a/altair/utils/save.py b/altair/utils/save.py index 042d457dc..0b17c8e1f 100644 --- a/altair/utils/save.py +++ b/altair/utils/save.py @@ -65,7 +65,7 @@ def set_inspect_mode_argument( mode = "vega-lite" if mode != "vega-lite": - msg = "mode must be 'vega-lite', " f"not '{mode}'" + msg = f"mode must be 'vega-lite', not '{mode}'" raise ValueError(msg) if mode == "vega-lite" and vegalite_version is None: diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index e46e3070a..9a6feb667 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -1138,7 +1138,7 @@ def __repr__(self) -> str: args = f"{COND}{self.condition!r}".replace("\n", "\n ") else: conds = "\n ".join(f"{c!r}" for c in self.condition) - args = f"{COND}[\n " f"{conds}\n ]" + args = f"{COND}[\n {conds}\n ]" return f"{name}({LB}\n {args}\n{RB})" @@ -1166,9 +1166,7 @@ def __init__( def __repr__(self) -> str: return ( - f"{type(self).__name__}(\n" - f" {self._conditions!r},\n {self._condition!r}\n" - ")" + f"{type(self).__name__}(\n {self._conditions!r},\n {self._condition!r}\n)" ) def then(self, statement: _StatementType, /, **kwds: Any) -> Then[_Conditions]: @@ -3771,7 +3769,7 @@ def show(self) -> None: def _set_resolve(self, **kwargs: Any): # noqa: ANN202 """Copy the chart and update the resolve property with kwargs.""" if not hasattr(self, "resolve"): - msg = f"{self.__class__} object has no attribute " "'resolve'" + msg = f"{self.__class__} object has no attribute 'resolve'" raise ValueError(msg) copy = _top_schema_base(self).copy(deep=["resolve"]) if copy.resolve is Undefined: @@ -4726,7 +4724,7 @@ def interactive( """ if not self.layer: - msg = "LayerChart: cannot call interactive() until a " "layer is defined" + msg = "LayerChart: cannot call interactive() until a layer is defined" raise ValueError(msg) copy = self.copy(deep=["layer"]) copy.layer[0] = copy.layer[0].interactive( diff --git a/altair/vegalite/v5/display.py b/altair/vegalite/v5/display.py index dcc506958..9c909b2d0 100644 --- a/altair/vegalite/v5/display.py +++ b/altair/vegalite/v5/display.py @@ -41,7 +41,7 @@ # The display message when rendering fails DEFAULT_DISPLAY: Final = f"""\ - + If you see this message, it means the renderer has not been properly enabled for the frontend that you are using. For more information, see diff --git a/sphinxext/code_ref.py b/sphinxext/code_ref.py index efe9ef5e1..4ddea8a60 100644 --- a/sphinxext/code_ref.py +++ b/sphinxext/code_ref.py @@ -64,7 +64,7 @@ def validate_packages(packages: Any) -> str: if len(split) == 1: return f'["{split[0]}"]' else: - return f'[{",".join(split)}]' + return f"[{','.join(split)}]" def raw_html(text: str, /) -> nodes.raw: diff --git a/sphinxext/schematable.py b/sphinxext/schematable.py index 323f8dfe7..0b3a9c3c6 100644 --- a/sphinxext/schematable.py +++ b/sphinxext/schematable.py @@ -40,7 +40,7 @@ def type_description(schema: dict[str, Any]) -> str: ) else: warnings.warn( - f"cannot infer type for schema with keys {schema.keys()}" "", + f"cannot infer type for schema with keys {schema.keys()}", stacklevel=1, ) return "--" diff --git a/tests/utils/test_plugin_registry.py b/tests/utils/test_plugin_registry.py index d200bbe96..759a316df 100644 --- a/tests/utils/test_plugin_registry.py +++ b/tests/utils/test_plugin_registry.py @@ -32,7 +32,7 @@ def test_plugin_registry(): assert plugins.active == "" assert plugins.get() is None assert repr(plugins) == ( - "TypedCallableRegistry(active='', " "registered=['new_plugin'])" + "TypedCallableRegistry(active='', registered=['new_plugin'])" ) plugins.enable("new_plugin") @@ -42,7 +42,7 @@ def test_plugin_registry(): assert fn is not None assert fn(3) == 9 assert repr(plugins) == ( - "TypedCallableRegistry(active='new_plugin', " "registered=['new_plugin'])" + "TypedCallableRegistry(active='new_plugin', registered=['new_plugin'])" ) diff --git a/tests/utils/test_schemapi.py b/tests/utils/test_schemapi.py index af528d9b7..f8b994153 100644 --- a/tests/utils/test_schemapi.py +++ b/tests/utils/test_schemapi.py @@ -362,9 +362,7 @@ def test_attribute_error(): invalid_attr = "invalid_attribute" with pytest.raises(AttributeError) as err: getattr(m, invalid_attr) - assert str(err.value) == ( - "'MySchema' object has no attribute " "'invalid_attribute'" - ) + assert str(err.value) == ("'MySchema' object has no attribute 'invalid_attribute'") def test_to_from_json(dct): diff --git a/tools/generate_schema_wrapper.py b/tools/generate_schema_wrapper.py index df1b32681..1200de88e 100644 --- a/tools/generate_schema_wrapper.py +++ b/tools/generate_schema_wrapper.py @@ -766,7 +766,7 @@ def generate_vegalite_schema_wrapper(fp: Path, /) -> ModuleDef[str]: f"from altair.vegalite.v5.api import {CHART_DATA_TYPE}", "from ._typing import * # noqa: F403", ), - "\n" f"__all__ = {all_}\n", + f"\n__all__ = {all_}\n", LOAD_SCHEMA.format(schemafile=SCHEMA_FILE), BASE_SCHEMA.format(basename=basename), schema_class( @@ -905,7 +905,7 @@ def generate_vegalite_channel_wrappers(fp: Path, /) -> ModuleDef[list[str]]: f"from altair.vegalite.v5.api import {', '.join(TYPING_API)}", textwrap.indent(import_typing_extensions((3, 11), "Self"), " "), ), - "\n" f"__all__ = {all_}\n", + f"\n__all__ = {all_}\n", CHANNEL_MIXINS, *class_defs, *generate_encoding_artifacts( @@ -1016,9 +1016,7 @@ def generate_typed_dict( f"{args}\n " f"__extra_items__: {finalize_type_reprs(kwds_all_tps, target=TARGET)}" ) - doc = ( - f"{doc}\n" f"{EXTRA_ITEMS_MESSAGE.format(invalid_kwds=repr(sorted(kwds)))}" - ) + doc = f"{doc}\n{EXTRA_ITEMS_MESSAGE.format(invalid_kwds=repr(sorted(kwds)))}" return UNIVERSAL_TYPED_DICT.format( name=name, diff --git a/tools/update_init_file.py b/tools/update_init_file.py index 7faa837d3..0dfbd6c62 100644 --- a/tools/update_init_file.py +++ b/tools/update_init_file.py @@ -83,7 +83,7 @@ def update__all__variable() -> None: ruff.write_lint_format(init_path, new_lines) for source in DYNAMIC_ALL: - print(f"Updating `__all__`\n " f"{source!r}\n ->{normalize_source(source)!s}") + print(f"Updating `__all__`\n {source!r}\n ->{normalize_source(source)!s}") update_dynamic__all__(source) @@ -149,8 +149,7 @@ def _retrieve_all(name: str, /) -> list[str]: found = _import_module(name).__all__ if not found: msg = ( - f"Expected to find a populated `__all__` for {name!r},\n" - f"but got: {found!r}" + f"Expected to find a populated `__all__` for {name!r},\nbut got: {found!r}" ) raise AttributeError(msg) return found diff --git a/tools/vega_expr.py b/tools/vega_expr.py index 7e7a33035..9f7e010a3 100644 --- a/tools/vega_expr.py +++ b/tools/vega_expr.py @@ -391,8 +391,7 @@ def _compile(self) -> Pattern[str]: if not self._mapping: name = self._mapping.__qualname__ # type: ignore[attr-defined] msg = ( - f"Requires {name!r} to be populated, but got:\n" - f"{name}={self._mapping!r}" + f"Requires {name!r} to be populated, but got:\n{name}={self._mapping!r}" ) raise TypeError(msg) return re.compile(rf"{self._fmt_match.format('|'.join(self._mapping))}") From 406d54467681fe367423f01045ca1c85be3392b7 Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:27:33 +0000 Subject: [PATCH 3/6] ci(ruff): Remove default format settings https://docs.astral.sh/ruff/configuration/ --- pyproject.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index be5241455..fc9683ec6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -424,9 +424,6 @@ Import `dtypes` from `narwhals.stable.v1` instead. [tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false line-ending = "lf" # https://docs.astral.sh/ruff/formatter/#docstring-formatting docstring-code-format = true From 2645a1040b9323ea3a1897aa3097cf9e6532d068 Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:37:26 +0000 Subject: [PATCH 4/6] ci(ruff): Remove unused ignores - `E` rules here are not triggered on the current codebase - `B905` seems to be accounting for `tool.ruff.target-version = "py39"` --- pyproject.toml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fc9683ec6..9616da2ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -336,16 +336,9 @@ select = [ # complex-structure "C901", ] -ignore = [ - # Whitespace before ':' - "E203", - # Too many leading '#' for block comment - "E266", +ignore = [ # Line too long "E501", - # zip() without an explicit strict= parameter set. - # python>=3.10 only - "B905", # mutable-class-default "RUF012", # used-dummy-variable From 947eb5004659761051cdb9784e3867c410269c6b Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Fri, 10 Jan 2025 13:03:11 +0000 Subject: [PATCH 5/6] ci(ruff): Apply `0.8.0` updates - (https://github.com/astral-sh/ruff/releases/tag/0.8.0) - `RUF022` stablized - `TCH` -> `TC` --- pyproject.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9616da2ac..325645d76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -229,7 +229,7 @@ extend-safe-fixes=[ "UP006", "UP007", "UP008", - "TCH", + "TC", # assign exception msg to variable # # -------------------------------- # "EM101", @@ -238,8 +238,6 @@ extend-safe-fixes=[ "W291", # blank line contains whitespace "W293", - # unsorted-dunder-all - "RUF022", # pydocstyle # # ---------- # # fits-on-one-line @@ -297,7 +295,7 @@ select = [ # flake8-simplify "SIM", # flake8-type-checking - "TCH", + "TC", # flake8-tidy-imports "TID", # pyupgrade From 92f5947fffcf17f66966fabf577363dfea1d634f Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:11:16 +0000 Subject: [PATCH 6/6] style(ruff): Reformat tables w/ `taplo` Using an adaptation of (https://github.com/vega/vega-datasets/blob/c53c1beaa4ea71e93f612d7d88c12f15c2c64c84/taplo.toml) ```toml #:schema taplo://taplo.toml [formatting] align_entries = true allowed_blank_lines = 1 column_width = 88 compact_entries = false crlf = false indent_string = " " reorder_arrays = true reorder_inline_tables = true reorder_keys = true ``` --- pyproject.toml | 272 +++++++++++++++++-------------------------------- 1 file changed, 93 insertions(+), 179 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 325645d76..db119806e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -203,184 +203,102 @@ publish-clean-build = [ ] [tool.ruff] -target-version = "py39" -line-length = 88 -indent-width = 4 exclude = [ ".git", - "build", "__pycache__", + "build", "tests/examples_arguments_syntax", "tests/examples_methods_syntax", ] +indent-width = 4 +line-length = 88 +target-version = "py39" [tool.ruff.lint] -# https://docs.astral.sh/ruff/preview/ -preview = true - -# https://docs.astral.sh/ruff/settings/#lint_extend-safe-fixes -extend-safe-fixes=[ - # unnecessary-comprehension-in-call - "C419", - # literal-membership - "PLR6201", - # from __future__ import annotations # - # ---------------------------------- # - "UP006", - "UP007", - "UP008", - "TC", - # assign exception msg to variable # - # -------------------------------- # - "EM101", - "EM102", - # trailing-whitespace - "W291", - # blank line contains whitespace - "W293", - # pydocstyle # - # ---------- # - # fits-on-one-line - "D200", - # escape-sequence-in-docstring - "D301", - # ends-in-period - "D400", - # missing-return-type-special-method - "ANN204", - # unnecessary-dict-comprehension-for-iterable - "C420", -] - -# https://docs.astral.sh/ruff/preview/#using-rules-that-are-in-preview -extend-select=[ - # refurb - "FURB", - # pylint (preview) autofix # - # ------------------------ # - # unnecessary-dunder-call - "PLC2801", - # unnecessary-dict-index-lookup - "PLR1733", - # unnecessary-list-index-lookup - "PLR1736", - # literal-membership - "PLR6201", - # unspecified-encoding - "PLW1514", +extend-safe-fixes = [ # https://docs.astral.sh/ruff/settings/#lint_extend-safe-fixes + "ANN204", # missing-return-type-special-method + "C419", # unnecessary-comprehension-in-call + "C420", # unnecessary-dict-comprehension-for-iterable + "D200", # fits-on-one-line + "D301", # escape-sequence-in-docstring + "D400", # ends-in-period + "EM101", # raw-string-in-exception + "EM102", # f-string-in-exception + "PLR6201", # literal-membership + "TC", # flake8-type-checking + "UP006", # non-pep585-annotation + "UP007", # non-pep604-annotation-union + "UP008", # super-call-with-parameters + "W291", # trailing-whitespace + "W293", # blank line contains whitespace ] -select = [ - # flake8-bugbear - "B", - # flake8-comprehensions - "C4", - # pycodestyle-error - "E", - # flake8-errmsg - "EM", - # pyflakes - "F", - # flake8-future-annotations - "FA", - # flynt - "FLY", - # flake8-pie - "PIE", - # flake8-pytest-style - "PT", - # flake8-use-pathlib - "PTH", - # Ruff-specific rules - "RUF", - # flake8-simplify - "SIM", - # flake8-type-checking - "TC", - # flake8-tidy-imports - "TID", - # pyupgrade - "UP", - # pycodestyle-warning - "W", - # pylint (stable) autofix # - # ----------------------- # - # iteration-over-set - "PLC0208", - # manual-from-import - "PLR0402", - # useless-return - "PLR1711", - # repeated-equality-comparison - "PLR1714", - # collapsible-else-if - "PLR5501", - # useless-else-on-loop - "PLW0120", - # subprocess-run-without-check - "PLW1510", - # nested-min-max - "PLW3301", - # pydocstyle # - # ---------- # - "D", - # multi-line-summary-second-line - "D213", - # numpy-specific-rules - "NPY", - # flake8-annotations - "ANN", - # unsorted-imports - "I001", - # complex-structure - "C901", +extend-select = [ # https://docs.astral.sh/ruff/preview/#using-rules-that-are-in-preview + "FURB", # refurb + "PLC2801", # unnecessary-dunder-call + "PLR1733", # unnecessary-dict-index-lookup + "PLR1736", # unnecessary-list-index-lookup + "PLR6201", # literal-membership + "PLW1514", # unspecified-encoding ] ignore = [ - # Line too long - "E501", - # mutable-class-default - "RUF012", - # used-dummy-variable - "RUF052", - # suppressible-exception - # https://github.com/vega/altair/pull/3431#discussion_r1629808660 - "SIM105", - # pydocstyle/ https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules # - # ------------------------------------------------------------------------- # - # undocumented-public-module - "D100", - # undocumented-public-class - "D101", - # undocumented-public-method - "D102", - # undocumented-public-function - "D103", - # undocumented-public-package - "D104", - # undocumented-magic-method - "D105", - # undocumented-public-init - "D107", - # indent-with-spaces - "D206", - # multi-line-summary-first-line ((D213) is the opposite of this) - "D212", - # Imperative mood - "D401", - # Blank line after last section - "D413", - # doc-line-too-long - "W505", - # Any as annotation - "ANN401" + "ANN401", # any-type + "D100", # undocumented-public-module + "D101", # undocumented-public-class + "D102", # undocumented-public-method + "D103", # undocumented-public-function + "D104", # undocumented-public-package + "D105", # undocumented-magic-method + "D107", # undocumented-public-init + "D206", # indent-with-spaces + "D212", # multi-line-summary-first-line ((D213) is the opposite of this) + "D401", # non-imperative-mood + "D413", # missing-blank-line-after-last-section + "E501", # line-too-long (https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules) + "RUF012", # mutable-class-default + "RUF052", # used-dummy-variable + "SIM105", # suppressible-exception (https://github.com/vega/altair/pull/3431#discussion_r1629808660) + "W505", # doc-line-too-long +] +mccabe.max-complexity = 10 +preview = true # https://docs.astral.sh/ruff/preview/ +pydocstyle.convention = "numpy" # https://docs.astral.sh/ruff/settings/#lintpydocstyle +select = [ + "ANN", # flake8-annotations + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "C901", # complex-structure + "D", # pydocstyle + "D213", # multi-line-summary-second-line + "E", # pycodestyle-error + "EM", # flake8-errmsg + "F", # pyflakes + "FA", # flake8-future-annotations + "FLY", # flynt + "I001", # unsorted-imports + "NPY", # numpy-specific-rules + "PIE", # flake8-pie + "PLC0208", # iteration-over-set + "PLR0402", # manual-from-import + "PLR1711", # useless-return + "PLR1714", # repeated-equality-comparison + "PLR5501", # collapsible-else-if + "PLW0120", # useless-else-on-loop + "PLW1510", # subprocess-run-without-check + "PLW3301", # nested-min-max + "PT", # flake8-pytest-style + "PTH", # flake8-use-pathlib + "RUF", # Ruff-specific rules + "SIM", # flake8-simplify + "TC", # flake8-type-checking + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # pycodestyle-warning ] -# https://docs.astral.sh/ruff/settings/#lintpydocstyle -pydocstyle={ convention="numpy" } -mccabe={ max-complexity=10 } [tool.ruff.lint.isort] -classes = ["expr", "datum"] +classes = ["datum", "expr"] extra-standard-library = ["typing_extensions"] -known-first-party=[ +known-first-party = [ "altair_tiles", "sphinxext_altair", "vega_datasets", @@ -391,34 +309,30 @@ split-on-trailing-comma = false [tool.ruff.lint.flake8-tidy-imports.banned-api] # https://docs.astral.sh/ruff/settings/#lint_flake8-tidy-imports_banned-api -"typing.Optional".msg = """ -Use `Union[T, None]` instead. -`typing.Optional` is likely to be confused with `altair.typing.Optional`, \ -which have a similar but different semantic meaning. -See https://github.com/vega/altair/pull/3449 -""" "narwhals.dependencies".msg = """ Import `dependencies` from `narwhals.stable.v1` instead. """ +"narwhals.dtypes".msg = """ +Import `dtypes` from `narwhals.stable.v1` instead. +""" "narwhals.typing".msg = """ Import `typing` from `narwhals.stable.v1` instead. """ -"narwhals.dtypes".msg = """ -Import `dtypes` from `narwhals.stable.v1` instead. +"typing.Optional".msg = """ +Use `Union[T, None]` instead. +`typing.Optional` is likely to be confused with `altair.typing.Optional`, \ +which have a similar but different semantic meaning. +See https://github.com/vega/altair/pull/3449 """ [tool.ruff.lint.per-file-ignores] -# Only enforce type annotation rules on public api -"!altair/vegalite/v5/api.py" = ["ANN"] -# Allow complex if/elif branching during tests -"tests/**/*.py"= ["C901"] - +"!altair/vegalite/v5/api.py" = ["ANN"] # Only enforce annotation rules on public api +"tests/**/*.py" = ["C901"] # Allow complex if/elif branching during tests [tool.ruff.format] -line-ending = "lf" -# https://docs.astral.sh/ruff/formatter/#docstring-formatting -docstring-code-format = true +docstring-code-format = true # https://docs.astral.sh/ruff/formatter/#docstring-formatting docstring-code-line-length = 88 +line-ending = "lf" [tool.pytest.ini_options] # Pytest does not need to search these folders for test functions.