Skip to content

Commit 0f6dd1f

Browse files
committed
More pathlib adoption
1 parent b1f923b commit 0f6dd1f

27 files changed

+195
-139
lines changed

pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,6 @@ ignore = [
236236
"D102", # Missing docstring in public method (currently in too many places)
237237
"FBT",
238238
"PLR",
239-
"PTH",
240239
"TRY",
241240
]
242241
select = ["ALL"]
@@ -257,6 +256,11 @@ known-first-party = ["ansiblelint"]
257256
"test/**/*.py" = ["S"]
258257
"src/ansiblelint/rules/*.py" = ["S"]
259258
"src/ansiblelint/testing/*.py" = ["S"]
259+
# Temporary disabled until we fix them:
260+
"src/ansiblelint/{testing,schemas,rules}/*.py" = ["PTH"]
261+
"src/ansiblelint/{utils,file_utils,runner,loaders,constants,config,cli,_mockings,__main__}.py" = [
262+
"PTH",
263+
]
260264

261265
[tool.setuptools.dynamic]
262266
optional-dependencies.docs = { file = [".config/requirements-docs.txt"] }

src/ansiblelint/__main__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import subprocess
3232
import sys
3333
from contextlib import contextmanager
34+
from pathlib import Path
3435
from typing import TYPE_CHECKING, Any, Callable, TextIO
3536

3637
from ansible_compat.config import ansible_version
@@ -226,11 +227,11 @@ def main(argv: list[str] | None = None) -> int: # noqa: C901
226227
for level, message in log_entries:
227228
_logger.log(level, message)
228229
_logger.debug("Options: %s", options)
229-
_logger.debug(os.getcwd())
230+
_logger.debug("CWD: %s", Path.cwd())
230231

231232
if not options.offline:
232233
# pylint: disable=import-outside-toplevel
233-
from ansiblelint.schemas import refresh_schemas
234+
from ansiblelint.schemas.__main__ import refresh_schemas
234235

235236
refresh_schemas()
236237

src/ansiblelint/_mockings.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,46 @@
33

44
import contextlib
55
import logging
6-
import os
76
import re
87
import sys
8+
from typing import TYPE_CHECKING
99

1010
from ansiblelint.config import options
1111
from ansiblelint.constants import ANSIBLE_MOCKED_MODULE, RC
1212

13+
if TYPE_CHECKING:
14+
from pathlib import Path
15+
1316
_logger = logging.getLogger(__name__)
1417

1518

1619
def _make_module_stub(module_name: str) -> None:
20+
if not options.cache_dir:
21+
msg = "Cache directory not set"
22+
raise RuntimeError(msg)
1723
# a.b.c is treated a collection
1824
if re.match(r"^(\w+|\w+\.\w+\.[\.\w]+)$", module_name):
1925
parts = module_name.split(".")
2026
if len(parts) < 3:
21-
path = f"{options.cache_dir}/modules"
27+
path = options.cache_dir / "modules"
2228
module_file = f"{options.cache_dir}/modules/{module_name}.py"
2329
namespace = None
2430
collection = None
2531
else:
2632
namespace = parts[0]
2733
collection = parts[1]
28-
path = f"{ options.cache_dir }/collections/ansible_collections/{ namespace }/{ collection }/plugins/modules/{ '/'.join(parts[2:-1]) }"
34+
path = (
35+
options.cache_dir
36+
/ "collections"
37+
/ "ansible_collections"
38+
/ namespace
39+
/ collection
40+
/ "plugins"
41+
/ "modules"
42+
/ ("/".join(parts[2:-1]))
43+
)
2944
module_file = f"{path}/{parts[-1]}.py"
30-
os.makedirs(path, exist_ok=True)
45+
path.mkdir(exist_ok=True, parents=True)
3146
_write_module_stub(
3247
filename=module_file,
3348
name=module_file,
@@ -58,17 +73,29 @@ def _write_module_stub(
5873
# pylint: disable=too-many-branches
5974
def _perform_mockings() -> None:
6075
"""Mock modules and roles."""
76+
path: Path
77+
if not options.cache_dir:
78+
msg = "Cache directory not set"
79+
raise RuntimeError(msg)
6180
for role_name in options.mock_roles:
6281
if re.match(r"\w+\.\w+\.\w+$", role_name):
6382
namespace, collection, role_dir = role_name.split(".")
64-
path = f"{options.cache_dir}/collections/ansible_collections/{ namespace }/{ collection }/roles/{ role_dir }/"
83+
path = (
84+
options.cache_dir
85+
/ "collections"
86+
/ "ansible_collections"
87+
/ namespace
88+
/ collection
89+
/ "roles"
90+
/ role_dir
91+
)
6592
else:
66-
path = f"{options.cache_dir}/roles/{role_name}"
93+
path = options.cache_dir / "roles" / role_name
6794
# Avoid error from makedirs if destination is a broken symlink
68-
if os.path.islink(path) and not os.path.exists(path): # pragma: no cover
95+
if path.is_symlink() and not path.exists(): # pragma: no cover
6996
_logger.warning("Removed broken symlink from %s", path)
70-
os.unlink(path)
71-
os.makedirs(path, exist_ok=True)
97+
path.unlink(missing_ok=True)
98+
path.mkdir(exist_ok=True, parents=True)
7299

73100
if options.mock_modules:
74101
for module_name in options.mock_modules:
@@ -77,11 +104,22 @@ def _perform_mockings() -> None:
77104

78105
def _perform_mockings_cleanup() -> None:
79106
"""Clean up mocked modules and roles."""
107+
if not options.cache_dir:
108+
msg = "Cache directory not set"
109+
raise RuntimeError(msg)
80110
for role_name in options.mock_roles:
81111
if re.match(r"\w+\.\w+\.\w+$", role_name):
82112
namespace, collection, role_dir = role_name.split(".")
83-
path = f"{options.cache_dir}/collections/ansible_collections/{ namespace }/{ collection }/roles/{ role_dir }/"
113+
path = (
114+
options.cache_dir
115+
/ "collections"
116+
/ "ansible_collections"
117+
/ namespace
118+
/ collection
119+
/ "roles"
120+
/ role_dir
121+
)
84122
else:
85-
path = f"{options.cache_dir}/roles/{role_name}"
123+
path = options.cache_dir / "roles" / role_name
86124
with contextlib.suppress(OSError):
87-
os.rmdir(path)
125+
path.unlink()

src/ansiblelint/_vendor/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import os
21
import pkgutil
32
import sys
43
import warnings
4+
from pathlib import Path
55

66
# This package exists to host vendored top-level Python packages for downstream packaging. Any Python packages
77
# installed beneath this one will be masked from the Ansible loader, and available from the front of sys.path.
@@ -18,7 +18,7 @@
1818
def _ensure_vendored_path_entry() -> None:
1919
"""Ensure that any downstream-bundled content beneath this package is available at the top of sys.path."""
2020
# patch our vendored dir onto sys.path
21-
vendored_path_entry = os.path.dirname(__file__)
21+
vendored_path_entry = str(Path(__file__).parent)
2222
vendored_module_names = {
2323
m[1] for m in pkgutil.iter_modules([vendored_path_entry], "")
2424
} # m[1] == m.name

src/ansiblelint/app.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import logging
66
import os
77
from functools import lru_cache
8+
from pathlib import Path
89
from typing import TYPE_CHECKING, Any
910

1011
from ansible_compat.runtime import Runtime
@@ -101,7 +102,7 @@ def render_matches(self, matches: list[MatchError]) -> None: # noqa: C901
101102
if self.options.sarif_file:
102103
sarif = formatters.SarifFormatter(self.options.cwd, True)
103104
json = sarif.format_result(matches)
104-
with open(self.options.sarif_file, "w", encoding="utf-8") as sarif_file:
105+
with self.options.sarif_file.open("w", encoding="utf-8") as sarif_file:
105106
sarif_file.write(json)
106107

107108
def count_results(self, matches: list[MatchError]) -> SummarizedResults:
@@ -177,11 +178,12 @@ def report_outcome(self, result: LintResult, mark_as_success: bool = False) -> i
177178
matched_rules = self._get_matched_skippable_rules(result.matches)
178179

179180
if matched_rules and self.options.generate_ignore:
180-
console_stderr.print(f"Writing ignore file to {IGNORE_FILE.default}")
181+
ignore_file_path = Path(IGNORE_FILE.default)
182+
console_stderr.print(f"Writing ignore file to {ignore_file_path}")
181183
lines = set()
182184
for rule in result.matches:
183185
lines.add(f"{rule.filename} {rule.tag}\n")
184-
with open(IGNORE_FILE.default, "w", encoding="utf-8") as ignore_file:
186+
with ignore_file_path.open("w", encoding="utf-8") as ignore_file:
185187
ignore_file.write(
186188
"# This file contains ignores rule violations for ansible-lint\n",
187189
)

src/ansiblelint/cli.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,12 @@ def get_cli_parser() -> argparse.ArgumentParser:
272272
],
273273
help="stdout formatting, json being an alias for codeclimate. (default: %(default)s)",
274274
)
275-
parser.add_argument("--sarif-file", default=None, help="SARIF output file")
275+
parser.add_argument(
276+
"--sarif-file",
277+
default=None,
278+
type=Path,
279+
help="SARIF output file",
280+
)
276281
parser.add_argument(
277282
"-q",
278283
dest="quiet",

src/ansiblelint/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Constants used by AnsibleLint."""
22
import os.path
33
from enum import Enum
4+
from pathlib import Path
45
from typing import Literal
56

67
DEFAULT_RULESDIR = os.path.join(os.path.dirname(__file__), "rules")
@@ -162,7 +163,7 @@ def main():
162163
# reusable actions, where the mounted volume might have different owner.
163164
#
164165
# https://github.com/ansible/ansible-lint-action/issues/138
165-
GIT_CMD = ["git", "-c", f"safe.directory={os.getcwd()}"]
166+
GIT_CMD = ["git", "-c", f"safe.directory={Path.cwd()}"]
166167

167168
CONFIG_FILENAMES = [".ansible-lint", ".config/ansible-lint.yml"]
168169

src/ansiblelint/rules/__init__.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ class RulesCollection:
369369

370370
def __init__(
371371
self,
372-
rulesdirs: list[str] | None = None,
372+
rulesdirs: list[str] | list[Path] | None = None,
373373
options: Options = default_options,
374374
profile_name: str | None = None,
375375
conditional: bool = True,
@@ -379,9 +379,8 @@ def __init__(
379379
self.profile = []
380380
if profile_name:
381381
self.profile = PROFILES[profile_name]
382-
if rulesdirs is None:
383-
rulesdirs = []
384-
self.rulesdirs = expand_paths_vars(rulesdirs)
382+
rulesdirs_str = [] if rulesdirs is None else [str(r) for r in rulesdirs]
383+
self.rulesdirs = expand_paths_vars(rulesdirs_str)
385384
self.rules: list[BaseRule] = []
386385
# internal rules included in order to expose them for docs as they are
387386
# not directly loaded by our rule loader.
@@ -393,7 +392,7 @@ def __init__(
393392
WarningRule(),
394393
],
395394
)
396-
for rule in load_plugins(rulesdirs):
395+
for rule in load_plugins(rulesdirs_str):
397396
self.register(rule, conditional=conditional)
398397
self.rules = sorted(self.rules)
399398

src/ansiblelint/runner.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
if TYPE_CHECKING:
2121
from collections.abc import Generator
22+
from pathlib import Path
2223

2324
from ansiblelint.config import Options
2425
from ansiblelint.rules import RulesCollection
@@ -40,7 +41,7 @@ class Runner:
4041
# pylint: disable=too-many-arguments,too-many-instance-attributes
4142
def __init__(
4243
self,
43-
*lintables: Lintable | str,
44+
*lintables: Lintable | str | Path,
4445
rules: RulesCollection,
4546
tags: frozenset[Any] = frozenset(),
4647
skip_list: list[str] | None = None,
Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
11
"""Module containing cached JSON schemas."""
2-
from ansiblelint.schemas.__main__ import refresh_schemas
3-
from ansiblelint.schemas.main import validate_file_schema
4-
5-
__all__ = ("refresh_schemas", "validate_file_schema")

0 commit comments

Comments
 (0)