Skip to content

Commit

Permalink
Add linting with ruff (#926)
Browse files Browse the repository at this point in the history
  • Loading branch information
ogenstad committed Jun 24, 2024
1 parent a013a99 commit e8e6e3b
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 14 deletions.
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
]

0 comments on commit e8e6e3b

Please sign in to comment.