Skip to content

Commit 9866ca5

Browse files
authored
Add ruff (#75)
1 parent 8a59136 commit 9866ca5

File tree

9 files changed

+133
-111
lines changed

9 files changed

+133
-111
lines changed

.github/workflows/check.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- "3.8"
2525
- "3.7"
2626
os:
27-
- ubuntu-22.04
27+
- ubuntu-latest
2828
- windows-2022
2929
- macos-12
3030

@@ -65,7 +65,7 @@ jobs:
6565
fail-fast: false
6666
matrix:
6767
os:
68-
- ubuntu-22.04
68+
- ubuntu-latest
6969
- windows-2022
7070
tox_env:
7171
- dev

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55

66
jobs:
77
release:
8-
runs-on: ubuntu-22.04
8+
runs-on: ubuntu-latest
99
environment:
1010
name: release
1111
url: https://pypi.org/p/devpi-process

.pre-commit-config.yaml

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,68 +2,32 @@ repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
33
rev: v4.4.0
44
hooks:
5-
- id: check-ast
6-
- id: check-builtin-literals
7-
- id: check-docstring-first
8-
- id: check-merge-conflict
9-
- id: check-yaml
10-
- id: check-toml
11-
- id: debug-statements
125
- id: end-of-file-fixer
136
- id: trailing-whitespace
14-
- repo: https://github.com/asottile/pyupgrade
15-
rev: v3.3.2
7+
- repo: https://github.com/astral-sh/ruff-pre-commit
8+
rev: "v0.0.272"
169
hooks:
17-
- id: pyupgrade
18-
args: ["--py37-plus"]
19-
- repo: https://github.com/PyCQA/isort
20-
rev: 5.12.0
21-
hooks:
22-
- id: isort
10+
- id: ruff
11+
args: [--fix, --exit-non-zero-on-fix]
2312
- repo: https://github.com/psf/black
2413
rev: 23.3.0
2514
hooks:
2615
- id: black
27-
args: [--safe]
28-
- repo: https://github.com/asottile/blacken-docs
29-
rev: 1.13.0
30-
hooks:
31-
- id: blacken-docs
32-
additional_dependencies: [black==23.3]
33-
- repo: https://github.com/tox-dev/pyproject-fmt
34-
rev: "0.11.1"
35-
hooks:
36-
- id: pyproject-fmt
3716
- repo: https://github.com/tox-dev/tox-ini-fmt
3817
rev: "1.3.0"
3918
hooks:
4019
- id: tox-ini-fmt
4120
args: ["-p", "fix"]
42-
- repo: https://github.com/PyCQA/flake8
43-
rev: 6.0.0
21+
- repo: https://github.com/tox-dev/pyproject-fmt
22+
rev: "0.11.2"
4423
hooks:
45-
- id: flake8
46-
additional_dependencies:
47-
- flake8-bugbear==23.3.23
48-
- flake8-comprehensions==3.12
49-
- flake8-pytest-style==1.7.2
50-
- flake8-spellcheck==0.28
51-
- flake8-unused-arguments==0.0.13
52-
- flake8-noqa==1.3.1
53-
- pep8-naming==0.13.3
54-
- flake8-pyproject==1.2.3
24+
- id: pyproject-fmt
25+
additional_dependencies: ["tox>=4.6"]
5526
- repo: https://github.com/pre-commit/mirrors-prettier
56-
rev: "v2.7.1"
27+
rev: "v3.0.0-alpha.9-for-vscode"
5728
hooks:
5829
- id: prettier
59-
additional_dependencies:
60-
61-
- "@prettier/[email protected]"
6230
args: ["--print-width=120", "--prose-wrap=always"]
63-
- repo: https://github.com/igorshubovych/markdownlint-cli
64-
rev: v0.34.0
65-
hooks:
66-
- id: markdownlint
6731
- repo: meta
6832
hooks:
6933
- id: check-hooks-apply

pyproject.toml

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,6 @@ version.source = "vcs"
6262
[tool.black]
6363
line-length = 120
6464

65-
[tool.isort]
66-
profile = "black"
67-
known_first_party = ["devpi_process"]
68-
line_length = 120
69-
add_imports = ["from __future__ import annotations"]
70-
71-
[tool.flake8]
72-
max-complexity = 22
73-
max-line-length = 120
74-
unused-arguments-ignore-abstract-functions = true
75-
noqa-require-code = true
76-
dictionaries = ["en_US", "python", "technical", "django"]
77-
7865
[tool.coverage]
7966
html.show_contexts = true
8067
html.skip_covered = false
@@ -96,5 +83,24 @@ python_version = "3.11"
9683
show_error_codes = true
9784
strict = true
9885

99-
[tool.pep8]
100-
max-line-length = "120"
86+
[tool.ruff]
87+
select = ["ALL"]
88+
line-length = 120
89+
target-version = "py37"
90+
isort = {known-first-party = ["devpi_process"], required-imports = ["from __future__ import annotations"]}
91+
ignore = [
92+
"ANN101", # no typoe annotation for self
93+
"ANN401", # allow Any as type annotation
94+
"D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible
95+
"D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible
96+
"S104", # Possible binding to all interface
97+
]
98+
[tool.ruff.per-file-ignores]
99+
"tests/**/*.py" = [
100+
"S101", # asserts allowed in tests...
101+
"FBT", # don"t care about booleans as positional arguments in tests
102+
"INP001", # no implicit namespace
103+
"D", # don"t care about documentation in tests
104+
"S603", # `subprocess` call: check for execution of untrusted input
105+
"PLR2004", # Magic value used in comparison, consider replacing with a constant variable
106+
]

src/devpi_process/__init__.py

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Devpi PyPI to test with."""
12
from __future__ import annotations
23

34
import random
@@ -9,50 +10,85 @@
910
from pathlib import Path
1011
from subprocess import PIPE, Popen, run
1112
from threading import Thread
12-
from types import TracebackType
13-
from typing import IO, Iterator, Sequence, cast
13+
from typing import IO, TYPE_CHECKING, Iterator, Sequence, cast
1414

1515
from ._version import __version__
1616

17+
if TYPE_CHECKING:
18+
from types import TracebackType
19+
1720

1821
def _check_call(cmd: list[str]) -> None:
19-
run(cmd, check=True, capture_output=True)
22+
run(cmd, check=True, capture_output=True) # noqa: S603
2023

2124

2225
class Index:
26+
"""Index."""
27+
2328
def __init__(self, base_url: str, name: str, user: str, client_cmd_base: list[str]) -> None:
29+
"""
30+
Create index.
31+
32+
:param base_url: base url
33+
:param name: name for the index server
34+
:param user: the username to use
35+
:param client_cmd_base:
36+
"""
2437
self._client_cmd_base = client_cmd_base
2538
self._server_url = base_url
2639
self.name = name
2740
self.user = user
2841

2942
@property
3043
def url(self) -> str:
44+
""":return: the URL to the index server"""
3145
return f"{self._server_url}/{self.name}/+simple/"
3246

3347
def use(self) -> None:
34-
_check_call(self._client_cmd_base + ["use", f"{self.user}/{self.name}"])
48+
"""Use this index server."""
49+
_check_call([*self._client_cmd_base, "use", f"{self.user}/{self.name}"])
3550

3651
def upload(self, *files: Path) -> None:
52+
"""
53+
Upload packages to the index.
54+
55+
:param files: the files to upload
56+
"""
3757
cmd = self._client_cmd_base + ["upload", "--index", self.name] + [str(i) for i in files]
3858
_check_call(cmd)
3959

4060
def __repr__(self) -> str:
61+
""":return: repr of the index"""
4162
return f"{self.__class__.__name__}(url={self.url})"
4263

4364

4465
class IndexServer:
45-
def __init__(self, path: Path, with_root_pypi: bool = False, start_args: Sequence[str] | None = None) -> None:
66+
"""A PyPI index server locally."""
67+
68+
def __init__(
69+
self,
70+
path: Path,
71+
with_root_pypi: bool = False, # noqa: FBT001, FBT002
72+
start_args: Sequence[str] | None = None,
73+
) -> None:
74+
"""
75+
Create the local index server.
76+
77+
:param path: the path where to host files
78+
:param with_root_pypi: access to upstream PyPI
79+
:param start_args: additional arguments to start the server
80+
"""
4681
self.path = path
4782
self._with_root_pypi = with_root_pypi
4883
self._start_args: Sequence[str] = [] if start_args is None else start_args
4984

5085
self.host, self.port = "localhost", _find_free_port()
51-
self._passwd = "".join(random.choices(string.ascii_letters, k=8))
86+
self._passwd = "".join(random.choices(string.ascii_letters, k=8)) # noqa: S311
5287

5388
scripts_dir = sysconfig.get_path("scripts")
5489
if scripts_dir is None:
55-
raise RuntimeError("could not get scripts folder of host interpreter") # pragma: no cover
90+
msg = "could not get scripts folder of host interpreter" # pragma: no cover
91+
raise RuntimeError(msg) # pragma: no cover
5692

5793
def _exe(name: str) -> str:
5894
return str(Path(scripts_dir) / f"{name}{'.exe' if sys.platform == 'win32' else ''}")
@@ -70,9 +106,11 @@ def _exe(name: str) -> str:
70106

71107
@property
72108
def user(self) -> str:
109+
""":return: username of the index server"""
73110
return "root"
74111

75112
def __enter__(self) -> IndexServer:
113+
""":return: start the index server"""
76114
self._create_and_start_server()
77115
self._setup_client()
78116
return self
@@ -89,7 +127,7 @@ def _create_and_start_server(self) -> None:
89127
# 2. start the server
90128
cmd = [self._server, "--serverdir", server_at, "--port", str(self.port)]
91129
cmd.extend(self._start_args)
92-
self._process = Popen(cmd, stdout=PIPE, universal_newlines=True)
130+
self._process = Popen(cmd, stdout=PIPE, universal_newlines=True) # noqa: S603
93131
stdout = self._drain_stdout()
94132
for line in stdout: # pragma: no branch # will always loop at least once
95133
if "serving at url" in line:
@@ -108,42 +146,59 @@ def _drain_stdout(self) -> Iterator[str]:
108146
stdout = cast(IO[str], process.stdout)
109147
while True:
110148
if process.poll() is not None: # pragma: no cover
111-
print(f"devpi server with pid {process.pid} at {self._server_dir} died")
149+
print(f"devpi server with pid {process.pid} at {self._server_dir} died") # noqa: T201
112150
break
113151
yield stdout.readline()
114152

115153
def _setup_client(self) -> None:
116-
"""create a user on the server and authenticate it"""
154+
"""Create a user on the server and authenticate it."""
117155
self._client_dir.mkdir(exist_ok=True)
118156
base = ["--clientdir", str(self._client_dir)]
119-
_check_call([self._client, "use"] + base + [self.url])
120-
_check_call([self._client, "login"] + base + [self.user, "--password", self._passwd])
157+
_check_call([self._client, "use", *base, self.url])
158+
_check_call([self._client, "login", *base, self.user, "--password", self._passwd])
121159

122160
def create_index(self, name: str, *args: str) -> Index:
161+
"""
162+
Create an index on the server.
163+
164+
:param name: with name
165+
:param args: additional arguments
166+
:return: the created index
167+
"""
123168
if name in self._indexes: # pragma: no cover
124-
raise ValueError(f"index {name} already exists")
169+
msg = f"index {name} already exists"
170+
raise ValueError(msg)
125171
base = [self._client, "--clientdir", str(self._client_dir)]
126-
_check_call(base + ["index", "-c", name, *args])
172+
_check_call([*base, "index", "-c", name, *args])
127173
index = Index(f"{self.url}/{self.user}", name, self.user, base)
128174
self._indexes[name] = index
129175
return index
130176

131177
def __exit__(
132178
self,
133-
exc_type: type[BaseException] | None, # noqa: U100
134-
exc_val: BaseException | None, # noqa: U100
135-
exc_tb: TracebackType | None, # noqa: U100
179+
exc_type: type[BaseException] | None,
180+
exc_val: BaseException | None,
181+
exc_tb: TracebackType | None,
136182
) -> None:
183+
"""
184+
Stop the index server.
185+
186+
:param exc_type:
187+
:param exc_val:
188+
:param exc_tb:
189+
"""
137190
if self._process is not None: # pragma: no cover # defend against devpi startup fail
138191
self._process.terminate()
139192
if self._stdout_drain is not None and self._stdout_drain.is_alive(): # pragma: no cover # devpi startup fail
140193
self._stdout_drain.join()
141194

142195
@property
143196
def url(self) -> str:
197+
""":return: url to the index server"""
144198
return f"http://{self.host}:{self.port}"
145199

146200
def __repr__(self) -> str:
201+
""":return: repr of the index server"""
147202
return f"{self.__class__.__name__}(url={self.url}, indexes={list(self._indexes)})"
148203

149204

0 commit comments

Comments
 (0)