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

Adopt ruff and address lint #809

Merged
merged 2 commits into from Dec 8, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/dependabot.yml
@@ -1,8 +1,10 @@
version: 2
updates:
# Set update schedule for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every weekday
interval: "weekly"
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
23 changes: 14 additions & 9 deletions .github/workflows/tests.yml
Expand Up @@ -57,6 +57,19 @@ jobs:
run: |
hatch run test:nowarn || hatch run test:nowarn --lf

test_lint:
name: Test Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- name: Run Linters
run: |
hatch run typing:test
hatch run lint:style
pipx run 'validate-pyproject[all]' pyproject.toml
pipx run doc8 --max-line-length=200

test_docs:
timeout-minutes: 10
runs-on: ubuntu-latest
Expand Down Expand Up @@ -121,19 +134,11 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}

pre_commit:
name: pre-commit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- uses: jupyterlab/maintainer-tools/.github/actions/pre-commit@v1

tests_check: # This job does nothing and is only used for the branch protection
if: always()
needs:
- tests
- pre_commit
- test_lint
- test_docs
- test_minimum_versions
- test_prereleases
Expand Down
81 changes: 18 additions & 63 deletions .pre-commit-config.yaml
@@ -1,85 +1,40 @@
ci:
autoupdate_schedule: monthly

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: debug-statements
stages: [manual]
- id: end-of-file-fixer
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: requirements-txt-fixer
- id: check-added-large-files
stages: [manual]
- id: check-case-conflict
stages: [manual]
- id: check-toml
stages: [manual]
- id: check-yaml
stages: [manual]
- id: debug-statements
- id: forbid-new-submodules
stages: [manual]
- id: check-builtin-literals
stages: [manual]
- id: check-case-conflict
stages: [manual]
- id: check-executables-have-shebangs
stages: [manual]
- id: end-of-file-fixer
- id: requirements-txt-fixer
- id: trailing-whitespace

- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
args: ["--line-length", "100"]

- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: isort
files: \.py$
args: [--profile=black]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
hooks:
- id: mypy
args: ["--config-file", "pyproject.toml"]
exclude: "traitlets/.*tests/.*.py"
additional_dependencies: [pytest, types-docutils, sphinx]
stages: [manual]

- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.10.1
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.19.2
hooks:
- id: validate-pyproject
stages: [manual]
- id: check-github-workflows

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.16
hooks:
- id: mdformat

- repo: https://github.com/asottile/pyupgrade
rev: v3.3.0
hooks:
- id: pyupgrade
args: [--py37-plus]

- repo: https://github.com/PyCQA/doc8
rev: v1.0.0
hooks:
- id: doc8
args: [--max-line-length=200]
stages: [manual]

- repo: https://github.com/john-hen/Flake8-pyproject
rev: 1.2.2
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: Flake8-pyproject
alias: flake8
additional_dependencies:
["flake8-bugbear==22.6.22", "flake8-implicit-str-concat==0.2.0"]
stages: [manual]
- id: black

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.19.2
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.165
hooks:
- id: check-github-workflows
- id: ruff
args: ["--fix"]
2 changes: 1 addition & 1 deletion docs/source/conf.py
Expand Up @@ -29,7 +29,7 @@

# We load the ipython release info into a dict by explicit execution
_release = {} # type:ignore
exec(
exec( # noqa
compile(
open(osp.join(ROOT, "traitlets/_version.py")).read(),
"../../traitlets/_version.py",
Expand Down
10 changes: 7 additions & 3 deletions docs/sphinxext/github.py
Expand Up @@ -19,7 +19,7 @@

from docutils import nodes, utils
from docutils.parsers.rst.roles import set_classes
from sphinx.util.logging import getLogger
from sphinx.util.logging import getLogger # type:ignore

info = getLogger(__name__).info

Expand All @@ -41,7 +41,9 @@ def make_link_node(rawtext, app, type, slug, options):
if not base.endswith("/"):
base += "/"
except AttributeError as err:
raise ValueError("github_project_url configuration value is not set (%s)" % str(err))
raise ValueError(
"github_project_url configuration value is not set (%s)" % str(err)
) from err

ref = base + type + "/" + slug + "/"
set_classes(options)
Expand Down Expand Up @@ -147,7 +149,9 @@ def ghcommit_role(name, rawtext, text, lineno, inliner, options=None, content=No
if not base.endswith("/"):
base += "/"
except AttributeError as err:
raise ValueError("github_project_url configuration value is not set (%s)" % str(err))
raise ValueError(
"github_project_url configuration value is not set (%s)" % str(err)
) from err

ref = base + text
node = nodes.reference(rawtext, text[:6], refuri=ref, **options)
Expand Down
106 changes: 85 additions & 21 deletions pyproject.toml
Expand Up @@ -28,6 +28,8 @@ docs = [
"pydata-sphinx-theme",
"sphinx"
]
lint = ["black>=22.6.0", "mdformat>0.7", "ruff>=0.0.156"]
typing = ["mypy>=0.990"]

[tool.hatch.version]
path = "traitlets/_version.py"
Expand All @@ -50,6 +52,26 @@ dependencies = ["coverage", "pytest-cov"]
test = "python -m pytest -vv --cov traitlets --cov-branch --cov-report term-missing:skip-covered {args}"
nowarn = "test -W default {args}"

[tool.hatch.envs.typing]
features = ["test", "typing"]
dependencies = ["mypy>=0.990"]
[tool.hatch.envs.typing.scripts]
test = "mypy --install-types --non-interactive {args:.}"

[tool.hatch.envs.lint]
features = ["lint"]
[tool.hatch.envs.lint.scripts]
style = [
"ruff {args:.}",
"black --check --diff {args:.}",
"mdformat --check {args:docs *.md}"
]
fmt = [
"black {args:.}",
"ruff --fix {args:.}",
"mdformat {args:docs *.md}"
]

[tool.mypy]
check_untyped_defs = true
disallow_any_generics = true
Expand Down Expand Up @@ -115,27 +137,69 @@ exclude_lines = [
"@(abc\\.)?abstractmethod",
]

[tool.flake8]
ignore = "E501, W503, E402"
builtins = "c, get_config"
exclude = [
".cache",
".github",
"docs",
"setup.py",
[tool.black]
line-length = 100
skip-string-normalization = true
target-version = ["py37"]

[tool.ruff]
target-version = "py37"
line-length = 100
select = [
"A", "B", "C", "E", "F", "FBT", "I", "N", "Q", "RUF", "S", "T",
"UP", "W", "YTT",
]
enable-extensions = "G"
extend-ignore = [
"G001", "G002", "G004", "G200", "G201", "G202",
# black adds spaces around ':'
"E203",
ignore = [
# Allow non-abstract empty methods in abstract base classes
"B027",
# Ignore McCabe complexity
"C901",
# Allow boolean positional values in function calls, like `dict.get(... True)`
"FBT003",
# Use of `assert` detected
"S101",
# Line too long
"E501",
# Relative imports are banned
"I252",
# Boolean ... in function definition
"FBT001", "FBT002",
# Module level import not at top of file
"E402",
# A001/A002/A003 .. is shadowing a python builtin
"A001", "A002", "A003",
# Possible hardcoded password
"S105", "S106",
# Q000 Single quotes found but double quotes preferred
"Q000",
# N806 Variable `B` in function should be lowercase
"N806",
# T201 `print` found
"T201",
# N802 Function name `CreateWellKnownSid` should be lowercase
"N802", "N803",
# C408 Unnecessary `dict` call (rewrite as a literal)
"C408",
# N801 Class name `directional_link` should use CapWords convention
"N801",
]
per-file-ignores = [
# B011: Do not call assert False since python -O removes these calls
# F841 local variable 'foo' is assigned to but never used
"traitlets/tests/*: B011", "F841",
# F401 'foo' imported but unused
# F403 'from foo import *' used; unable to detect undefined names
"traitlets/__init__.py: F401", "F403",
"traitlets/*/__init__.py: F401", "F403",
unfixable = [
# Don't touch print statements
"T201",
# Don't touch noqa lines
"RUF100",
]

[tool.ruff.per-file-ignores]
# B011 Do not call assert False since python -O removes these calls
# F841 local variable 'foo' is assigned to but never used
# C408 Unnecessary `dict` call
# E402 Module level import not at top of file
# T201 `print` found
# B007 Loop control variable `i` not used within the loop body.
# N802 Function name `assertIn` should be lowercase
# F841 Local variable `t` is assigned to but never used
"traitlets/tests/*" = ["B011", "F841", "C408", "E402", "T201", "B007", "N802", "F841"]
# F401 `_version.__version__` imported but unused
# F403 `from .traitlets import *` used; unable to detect undefined names
"traitlets/*__init__.py" = ["F401", "F403"]
19 changes: 11 additions & 8 deletions traitlets/config/application.py
Expand Up @@ -366,7 +366,9 @@ def _log_default(self):
# flags are loaded from this dict by '--key' flags
# this must be a dict of two-tuples, the first element being the Config/dict
# and the second being the help string for the flag
flags: t.Dict[t.Union[str, t.Tuple[str, ...]], t.Tuple[t.Union[t.Dict, Config], str]] = {
flags: t.Dict[
t.Union[str, t.Tuple[str, ...]], t.Tuple[t.Union[t.Dict[str, t.Any], Config], str]
] = {
"debug": (
{
"Application": {
Expand Down Expand Up @@ -537,12 +539,13 @@ def emit_alias_help(self):
fhelp = cls.class_get_trait_help(trait, helptext=fhelp).splitlines()

if not isinstance(alias, tuple):
alias = (alias,) # type:ignore[assignment]
alias = (alias,)
alias = sorted(alias, key=len) # type:ignore[assignment]
alias = ", ".join(("--%s" if len(m) > 1 else "-%s") % m for m in alias)

# reformat first line
fhelp[0] = fhelp[0].replace("--" + longname, alias)
assert fhelp is not None
fhelp[0] = fhelp[0].replace("--" + longname, alias) # type:ignore
yield from fhelp
yield indent("Equivalent to: [--%s]" % longname)
except Exception as ex:
Expand All @@ -561,7 +564,7 @@ def emit_flag_help(self):
for flags, (cfg, fhelp) in self.flags.items():
try:
if not isinstance(flags, tuple):
flags = (flags,) # type:ignore[assignment]
flags = (flags,)
flags = sorted(flags, key=len) # type:ignore[assignment]
flags = ", ".join(("--%s" if len(m) > 1 else "-%s") % m for m in flags)
yield flags
Expand Down Expand Up @@ -748,7 +751,7 @@ def flatten_flags(self):
for alias, longname in self.aliases.items():
if isinstance(longname, tuple):
longname, _ = longname
cls, trait = longname.split(".", 1)
cls, trait = longname.split(".", 1) # type:ignore
children = mro_tree[cls] # type:ignore[index]
if len(children) == 1:
# exactly one descendent, promote alias
Expand All @@ -763,7 +766,7 @@ def flatten_flags(self):
flags = {}
for key, (flagdict, help) in self.flags.items():
newflag: t.Dict[t.Any, t.Any] = {}
for cls, subdict in flagdict.items():
for cls, subdict in flagdict.items(): # type:ignore
children = mro_tree[cls] # type:ignore[index]
# exactly one descendent, promote flag section
if len(children) == 1:
Expand All @@ -775,7 +778,7 @@ def flatten_flags(self):
newflag[cls] = subdict

if not isinstance(key, tuple):
key = (key,) # type:ignore[assignment]
key = (key,)
for k in key:
flags[k] = (newflag, help)
return flags, aliases
Expand Down Expand Up @@ -952,7 +955,7 @@ def generate_config_file(self, classes=None):
"""generate default config file from Configurables"""
lines = ["# Configuration file for %s." % self.name]
lines.append("")
lines.append("c = get_config() # noqa")
lines.append("c = get_config() #" + "noqa")
lines.append("")
classes = self.classes if classes is None else classes
config_classes = list(self._classes_with_config_traits(classes))
Expand Down