Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add linting with ruff #926

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ jobs:
run: poetry install
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'

- name: Run ruff
run: make ruff
- name: Run pylama
run: make pylama
- name: Run black
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ nbval:
docs/tutorial/ \
docs/howto/

.PHONY: ruff
ruff:
poetry run ruff check .

.PHONY: tests
tests: black pylama mypy nbval pytest sphinx
tests: ruff black pylama mypy nbval pytest sphinx

.PHONY: docker-tests
docker-tests: docker
Expand Down
41 changes: 28 additions & 13 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

197 changes: 197 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ types-pkg-resources = "^0.1.3"
nornir-jinja2 = "0.2.0"
nornir-utils = "0.2.0"
nornir-napalm = "0.4.0"
ruff = "^0.4.9"

[tool.poetry.group.docs.dependencies]
sphinx = "6.2.1"
Expand All @@ -56,3 +57,199 @@ nbsphinx = "0.9.2"
pygments = "2.16.1"
sphinx-issues = "3.0.1"

[tool.ruff]
line-length = 100

[tool.ruff.lint]
preview = true

select = [
"A", # flake8-builtins
"AIR", # Airflow
"ANN", # flake8-annotations
"ARG", # flake8-unused-arguments
"ASYNC", # flake8-async
"B", # flake8-bugbear
"BLE", # flake8-blind-except
"C4", # flake8-comprehensions
"C90", # mccabe complexity
"COM", # flake8-commas
"DJ", # flake8-django
"DTZ", # flake8-datetimez
"E", # pycodestyle errors
"ERA", # eradicate
"EXE", # flake8-executable
"F", # pyflakes
"FBT", # flake8-boolean-trap
"FIX", # flake8-fixme
"FLY", # flynt
"FURB", # refurb
"I", # isort-like checks
"ICN", # flake8-import-conventions
"LOG", # flake8-logging
"INP", # flake8-no-pep420
"INT", # flake8-gettext
"ISC", # flake8-implicit-str-concat
"G", # flake8-logging-format
"N", # pep8-naming
"NPY", # NumPy-specific rules
"PD", # pandas-vet
"PERF", # Perflint
"PGH", # pygrep-hooks
"PIE", # flake8-pie
"PL", # pylint
"PT", # flake8-pytest-style
"PTH", # flake8-use-pathlib
"PYI", # flake8-pyi
"Q", # flake8-quotes
"RET", # flake8-return
"RSE", # flake8-raise
"RUF", # Ruff-specific rules
"S", # flake8-bandit
"SIM", # flake8-simplify
"SLF", # flake8-self
"SLOT", # flake8-slots
"TCH", # flake8-type-checking
"TD", # flake8-todos
"TRIO", # flake8-trio
"T10", # flake8-debugger
"T20", # flake8-print
"TID", # flake8-tidy-imports
"TRY", # tryceratops
"UP", # pyupgrade
"W", # pycodestyle warnings
"YTT", # flake8-2020
]

ignore = [
"N801", # Class name should use CapWords convention
"N818", # Exception name should be named with an Error suffix
##################################################################################################
# The ignored rules below should be removed once the code has been updated, they are included #
# like this so that we can reactivate them one by one. Alternatively ignored after further #
# investigation if they are deemed to not make sense. #
##################################################################################################
"ANN204", # Missing return type annotation for special method `__init__`
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed
"ARG002", # Unused method argument
"B028", # No explicit `stacklevel` keyword argument found
"C419", # Unnecessary list comprehension
"COM812", # Trailing comma missing
"ERA001", # Found commented-out code
"EXE001", # Shebang is present but file is not executable
"FBT001", # Boolean-typed positional argument in function definition
"FBT002", # Boolean default positional argument in function definition
"FURB101", # `open` and `read` should be replaced by `Path(filename).read_text()`
"INP001", # File is part of an implicit namespace package. Add an `__init__.py`.
"N804", # First argument of a class method should be named `cls`
"PERF203", # `try`-`except` within a loop incurs performance overhead
"PGH004", # Use specific rule codes when using `noqa`
"PIE790", # Unnecessary `pass` statement
"PIE800", # Unnecessary spread `**`
"PLC0205", # Class `__slots__` should be a non-string iterable
"PLC2801", # Unnecessary dunder call to `__getattribute__`. Access attribute directly or use getattr built-in function
"PLR1704", # Redefining argument with the local name
"PLR6201", # Use a `set` literal when testing for membership
"PLR6301", # Method `run` could be a function, class method, or static method
"PLW1514", # `open` in text mode without explicit `encoding` argument
"PLW1641", # Object does not implement `__hash__` method
"PTH100", # `os.path.abspath()` should be replaced by `Path.resolve()`
"PTH120", # `os.path.dirname()` should be replaced by `Path.parent`
"PTH123", # `open()` should be replaced by `Path.open()`
"PYI036", # The first argument in `__exit__` should be annotated with `object` or `type[BaseException] | None`
"RET505", # Unnecessary `else` after `return` statement
"RET504", # Unnecessary assignment before `return` statement
"RSE102", # Unnecessary parentheses on raised exception
"RUF001", # String contains ambiguous `–` (EN DASH). Did you mean `-` (HYPHEN-MINUS)?
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
"RUF023", # `__slots__` is not sorted
"RUF100", # Unused blanket `noqa` directive
"S701", # By default, jinja2 sets `autoescape` to `False`. Consider using `autoescape=True` or the `select_autoescape` function to mitigate XSS vulnerabilities.
"SLF001", # Private member accessed
"SIM108", # Use ternary operator `config = Config.from_file(config_file, **kwargs) if config_file else Config.from_dict(**kwargs)` instead of `if`-`else`-block
"SIM110", # Use `return any(g.name == group or g.has_parent_group(group) for g in self.groups)` instead of `for` loop
"TRY002", # Create your own exception
"TRY003", # Avoid specifying long messages outside the exception class
"TRY004", # Prefer `TypeError` exception for invalid type
"TRY300", # Consider moving this statement to an `else` block
"TRY400", # Use `logging.exception` instead of `logging.error`
"UP004", # Class inherits from `object`
"UP009", # UTF-8 encoding declaration is unnecessary
"UP015", # Unnecessary open mode parameters
"UP032", # Use f-string instead of `format` call
"UP034", # Avoid extraneous parentheses
"UP037", # Remove quotes from type annotation
]

[tool.ruff.lint.mccabe]
max-complexity = 12


[tool.ruff.lint.pylint]
max-args = 11
max-branches = 16
max-positional-args = 10
max-public-methods = 22
max-returns = 11

[tool.ruff.lint.per-file-ignores]

"docs/conf.py" = [
"A001", # Variable `copyright` is shadowing a Python builtin
]

"docs/highlighter.py" = [
"ANN001", # Missing type annotation for function argument
"ANN201", # Missing return type annotation for public function
]

"nornir/core/configuration.py" = [
"A002", # Argument `format` / `help` is shadowing a Python builtin
]

"nornir/core/task.py" = [
"BLE001", # Do not catch blind exception: `Exception`
]

"nornir/init_nornir.py" = [
"N802", # Function name `InitNornir` should be lowercase
]

"tests/**.py" = [
"BLE001", # Do not catch blind exception: `Exception`
"N802", # Function name should be lowercase
"S101", # Use of assert detected
"S105", # Possible hardcoded password assigned

##################################################################################################
# The ignored rules below should be removed once the code has been updated, they are included #
# like this so that we can reactivate them one by one. Alternatively ignored after further #
# investigation if they are deemed to not make sense. #
##################################################################################################
"ANN001", # Missing type annotation for function argument
"ANN002", # Missing type annotation for `*args`
"ANN003", # Missing type annotation for `**kwargs`
"ANN201", # Missing return type annotation for public function
"ANN202", # Missing return type annotation for private function
"ANN206", # Missing return type annotation for classmethod
"ARG001", # Unused function argument
"B007", # Loop control variable `host` not used within loop body
"C414", # Unnecessary `list` call within `sorted()`
"DTZ005", # `datetime.datetime.now()` called without a `tz` argument
"LOG009", # Use of undocumented `logging.WARN` constant
"N999", # Invalid module name
"PLC1901", # Can be simplified as an empty string is falsey
"PLR0904", # Too many public methods
"PLR2004", # Magic value used in comparison, consider replacing with a constant variable
"PTH118", # `os.path.join()` should be replaced by `Path` with `/` operator
"PTH120", # `os.path.dirname()` should be replaced by `Path.parent`
"PT003", # `scope='function'` is implied in `@pytest.fixture()`
"PT004", # Fixture does not return anything, add leading underscore
"PT006", # Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
"PT012", # `pytest.raises()` block should contain a single simple statement
"PT018", # Assertion should be broken down into multiple parts
"PT011", # `pytest.raises(ValueError)` is too broad, set the `match` parameter or use a more specific exception
"SIM118", # Use `key in dict` instead of `key in dict.keys()`
"SIM300", # Yoda conditions are discouraged
]