Skip to content

Commit a746107

Browse files
committed
Configure linters and fix errors. Clean up pyproject.toml
1 parent e85998a commit a746107

File tree

7 files changed

+115
-95
lines changed

7 files changed

+115
-95
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22
build
33
dist
44
src/beku_stackabletech.egg-info
5-

.pre-commit-config.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# See https://pre-commit.com for more information
2+
# See https://pre-commit.com/hooks.html for more hooks
3+
repos:
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: v3.2.0
6+
hooks:
7+
- id: trailing-whitespace
8+
- id: end-of-file-fixer
9+
- id: check-yaml
10+
- id: check-added-large-files
11+
- repo: https://github.com/Yelp/detect-secrets
12+
rev: v1.4.0
13+
hooks:
14+
- id: detect-secrets
15+
- repo: https://github.com/pre-commit/mirrors-autopep8
16+
rev: v2.0.1
17+
hooks:
18+
- id: autopep8
19+
entry: autopep8
20+
- repo: local
21+
# These hooks are run from the local virtual env (and it's assumed they are there)
22+
# to allow these tools to find project dependencies.
23+
hooks:
24+
- id: pylint
25+
name: pylint
26+
entry: pylint
27+
language: system
28+
types: [ python ]
29+
- id: mypy
30+
name: mypy
31+
entry: mypy
32+
language: system
33+
types: [ python ]
34+
args: ['--install-types', '--non-interactive']

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# beku
22

3-
Version: 0.0.5
3+
Version: 0.0.6-dev
44

55
## Installation
66

@@ -23,13 +23,12 @@ Fast Kuttl tests expander for Stackable integration tests.
2323
Update the version in:
2424

2525
* `pyptoject.toml`
26-
* `setup.cfg`
2726
* `__init__.py`
2827
* `README.md`
2928

3029
Update the CHANGELOG.
3130
Commit and tag.
3231
Build and publish:
3332
rm -rf dist/
34-
python -m build --sdist --wheel .
33+
python -m build --wheel .
3534
twine upload dist/*

pyproject.toml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "beku-stackabletech"
3-
version = "0.0.5"
3+
version = "0.0.6-dev"
44
authors = [
55
{ name="Razvan Mihai", email="razvan.mihai@stackable.tech" }
66
]
@@ -12,16 +12,16 @@ classifiers = [
1212
"License :: OSI Approved :: MIT License",
1313
"Operating System :: OS Independent",
1414
]
15+
dependencies = [
16+
"Jinja2>=3.1.2",
17+
"PyYAML>=6.0"
18+
]
19+
[project.optional-dependencies]
20+
lint = ['pre-commit', 'pylint', 'mypy']
1521

1622
[project.scripts]
1723
beku = "beku:main"
1824

19-
[build-system]
20-
requires = [
21-
"setuptools >= 40.6.2",
22-
"wheel >= 0.30.0"
23-
]
24-
build-backend = "setuptools.build_meta"
25-
2625
[project.urls]
27-
Homepage = "https://github.com/stackabletech/beku.py"
26+
"Homepage" = "https://github.com/stackabletech/beku.py"
27+
"Bug Tracker" = "https://github.com/stackabletech/beku.py/issues"

setup.cfg

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/beku/__init__.py

Lines changed: 68 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
from typing import Dict, List, TypeVar, Type, Tuple
1+
"""Expand kuttl tests from templates."""
2+
import logging
3+
import os
4+
import re
5+
import sys
26
from argparse import ArgumentParser, Namespace
37
from dataclasses import dataclass, field
48
from itertools import product, chain
5-
from jinja2 import Environment, FileSystemLoader
69
from os import walk, path, makedirs
710
from shutil import copy2
8-
from sys import exit
9-
from yaml import safe_load
10-
import logging
11-
import re
12-
import os
11+
from typing import Dict, List, TypeVar, Type, Tuple
1312

13+
from jinja2 import Environment, FileSystemLoader
14+
from yaml import safe_load
1415

15-
__version_info__ = (0, 0, 5)
16+
__version_info__ = (0, 0, '6-dev')
1617
__version__ = ".".join(map(str, __version_info__))
1718

1819

@@ -22,7 +23,7 @@ def ansible_lookup(loc: str, what: str) -> str:
2223
Simulates the Ansible `lookup()` function which is made available by Ansible Jinja templates.
2324
Raises an exception if `loc` is not `env`.
2425
"""
25-
if not loc is 'env':
26+
if loc != 'env':
2627
raise ValueError("Can only lookup() in 'env'")
2728
result = ""
2829
try:
@@ -34,6 +35,7 @@ def ansible_lookup(loc: str, what: str) -> str:
3435

3536
@dataclass
3637
class TestCase:
38+
"""Test case definition."""
3739
name: str
3840
values: Dict[str, str]
3941
tid: str = field(init=False)
@@ -48,7 +50,8 @@ def __post_init__(self):
4850
)
4951

5052
def expand(self, template_dir: str, target_dir: str) -> None:
51-
logging.info(f"Expanding test case id [{self.tid}]")
53+
"""Expand test case."""
54+
logging.info("Expanding test case id [%s]", self.tid)
5255
td_root = path.join(template_dir, self.name)
5356
tc_root = path.join(target_dir, self.name, self.tid)
5457
_mkdir_ignore_exists(tc_root)
@@ -63,115 +66,121 @@ def expand(self, template_dir: str, target_dir: str) -> None:
6366
if sub_level == 5:
6467
# Sanity check
6568
raise ValueError("Maximum recursive level (5) reached.")
66-
for d in dirs:
69+
for dir_name in dirs:
6770
_mkdir_ignore_exists(
68-
path.join(tc_root, root[len(td_root) + 1:], d))
69-
for f in files:
70-
source = path.join(root, f)
71+
path.join(tc_root, root[len(td_root) + 1:], dir_name))
72+
for file_name in files:
73+
source = path.join(root, file_name)
7174
dest = ""
7275
f_mode = os.stat(source).st_mode
73-
if f.endswith(".j2"):
74-
logging.debug(f"Render template {f} to {dest}")
75-
dest = path.join(tc_root, root[len(td_root) + 1:], f[:-3:])
76-
self._expand_template(f, dest, test_env)
76+
if file_name.endswith(".j2"):
77+
logging.debug("Render template %s to %s", file_name, dest)
78+
dest = path.join(
79+
tc_root, root[len(td_root) + 1:], file_name[:-3:])
80+
self._expand_template(file_name, dest, test_env)
7781
else:
78-
dest = path.join(tc_root, root[len(td_root) + 1:], f)
79-
logging.debug(f"Copy file {f} to {dest}")
82+
dest = path.join(
83+
tc_root, root[len(td_root) + 1:], file_name)
84+
logging.debug("Copy file %s to %s", file_name, dest)
8085
copy2(source, dest)
8186
# restore file permissions (especially the executable bit is important here)
82-
logging.debug(f"Update file mode for {dest}")
87+
logging.debug("Update file mode for %s", dest)
8388
os.chmod(dest, f_mode)
8489

85-
def _expand_template(self, template: str, dest: str, env: Environment) -> None:
86-
logging.debug(f"Expanding template {template}")
87-
t = env.get_template(template)
90+
def _expand_template(self, template_file: str, dest: str, env: Environment) -> None:
91+
logging.debug("Expanding template %s", template_file)
92+
template = env.get_template(template_file)
8893
with open(dest, encoding="utf8", mode="w") as stream:
8994
print(
90-
t.render({"test_scenario": {"values": self.values}}), file=stream)
95+
template.render({"test_scenario": {"values": self.values}}), file=stream)
9196

9297

9398
@dataclass
9499
class TestDimension:
100+
"""Test dimension."""
95101
name: str
96102
values: List[str]
97103

98104
def expand(self) -> List[Tuple[str, str]]:
105+
"""Return a list of tuples in the form of (<dimension>, <value>)"""
99106
return [(self.name, v) for v in self.values]
100107

101108

102109
@dataclass
103110
class TestDefinition:
111+
"""Test case definition."""
104112
name: str
105113
dimensions: List[str]
106114

107115

108-
TTestSuite = TypeVar("TTestSuite", bound="TestSuite")
116+
TTestSuite = TypeVar( # pylint: disable=invalid-name
117+
"TTestSuite", bound="TestSuite")
109118

110119

111-
@dataclass
120+
@dataclass(frozen=True)
112121
class TestSuite:
113-
source: str
114-
test_cases: List[TestCase]
122+
"""Test suite template."""
123+
source: str = field()
124+
test_cases: List[TestCase] = field(default_factory=list)
115125

116-
def __init__(self, source: str) -> None:
117-
self.source = source
118-
with open(source, encoding="utf8") as stream:
126+
def __post_init__(self) -> None:
127+
with open(self.source, encoding="utf8") as stream:
119128
tin = safe_load(stream)
120129
dimensions = [
121130
TestDimension(d["name"], d["values"]) for d in tin["dimensions"]
122131
]
123132
test_def = [
124133
TestDefinition(t["name"], t["dimensions"]) for t in tin["tests"]
125134
]
126-
self.test_cases = self._build(dimensions, test_def)
135+
self.test_cases.extend(self._build(dimensions, test_def))
127136

128137
@classmethod
129138
def _build(
130-
cls: Type[TTestSuite], dims: List[TestDimension], tests: List[TestDefinition]
139+
cls: Type[TTestSuite], dims: List[TestDimension], tests: List[TestDefinition]
131140
) -> List[TestCase]:
132141
"""
133142
>>> TestSuite._build([TestDimension(name='trino', values=['234', '235'])],
134143
... [TestDefinition(name='smoke', dimensions=['trino'])])
135-
[TestCase(name='smoke', values={'trino': '234'}, name='smoke_trino-234'), TestCase(name='smoke', values={'trino': '235'}, name='smoke_trino-235')]
144+
[TestCase(name='smoke', values={'trino': '234'}, name='smoke_trino-234'), \
145+
TestCase(name='smoke', values={'trino': '235'}, name='smoke_trino-235')]
136146
"""
137147
result = []
138-
for t in tests:
139-
used_dims = [d for d in dims if d.name in t.dimensions]
148+
for test in tests:
149+
used_dims = [d for d in dims if d.name in test.dimensions]
140150
expanded_test_dims: List[List[Tuple[str, str]]] = [
141151
d.expand() for d in used_dims
142152
]
143153
for tc_dim in product(*expanded_test_dims):
144-
result.append(TestCase(name=t.name, values=dict(tc_dim)))
154+
result.append(TestCase(name=test.name, values=dict(tc_dim)))
145155
return result
146156

147157
def __repr__(self) -> str:
148158
return f"TestSuite(source={self.source})"
149159

150160
def expand(self, template_dir: str, output_dir: str, kuttl_tests: str) -> int:
151-
logging.info(f"Expanding test suite from {self.source}")
152-
self._sanity_checks(template_dir, output_dir, kuttl_tests)
161+
"""Expand test suite."""
162+
logging.info("Expanding test suite from %s", self.source)
163+
self._sanity_checks(template_dir, kuttl_tests)
153164
_mkdir_ignore_exists(output_dir)
154165
self._expand_kuttl_tests(output_dir, kuttl_tests)
155166
for test_case in self.test_cases:
156167
test_case.expand(template_dir, output_dir)
157168
return 0
158169

159-
def _sanity_checks(
160-
self, template_dir: str, output_dir: str, kuttl_tests: str
161-
) -> None:
162-
for tc in self.test_cases:
163-
td_root = path.join(template_dir, tc.name)
170+
def _sanity_checks(self, template_dir: str, kuttl_tests: str) -> None:
171+
for test_case in self.test_cases:
172+
td_root = path.join(template_dir, test_case.name)
164173
if not path.isdir(td_root):
165174
raise ValueError(
166175
f"Test definition directory not found [{td_root}]")
167-
if not (path.isfile(kuttl_tests)):
176+
if not path.isfile(kuttl_tests):
168177
raise ValueError(
169178
f"Kuttl test config template not found [{kuttl_tests}]")
170179

171180
def _expand_kuttl_tests(self, output_dir: str, kuttl_tests: str) -> None:
172181
env = Environment(loader=FileSystemLoader(path.dirname(kuttl_tests)))
173182
kt_base_name = path.basename(kuttl_tests)
174-
t = env.get_template(kt_base_name)
183+
template = env.get_template(kt_base_name)
175184
kt_dest_name = re.sub(r"\.j(inja)?2$", "", kt_base_name)
176185
# Compatibility warning: Assume output_dir ends with 'tests' and remove
177186
# it from the destination file
@@ -181,20 +190,21 @@ def _expand_kuttl_tests(self, output_dir: str, kuttl_tests: str) -> None:
181190
"tests": [{"name": tn} for tn in {tc.name for tc in self.test_cases}]
182191
}
183192
}
184-
logging.debug(f"kuttl vars {kuttl_vars}")
193+
logging.debug("kuttl vars %s", kuttl_vars)
185194
with open(dest, encoding="utf8", mode="w") as stream:
186-
print(t.render(kuttl_vars), file=stream)
195+
print(template.render(kuttl_vars), file=stream)
187196

188197

189198
def parse_cli_args() -> Namespace:
199+
"""Parse command line args."""
190200
parser = ArgumentParser(
191201
description="Kuttl test expander for the Stackable Data Platform")
192202
parser.add_argument(
193203
"-v",
194204
"--version",
195205
help="Display application version",
196206
action='version',
197-
version='%(prog)s {version}'.format(version=__version__)
207+
version=f'%(prog)s {__version__}'
198208
)
199209

200210
parser.add_argument(
@@ -247,27 +257,27 @@ def parse_cli_args() -> Namespace:
247257
def _cli_log_level(cli_arg: str) -> int:
248258
if cli_arg == "debug":
249259
return logging.DEBUG
250-
else:
251-
return logging.INFO
260+
return logging.INFO
252261

253262

254-
def _mkdir_ignore_exists(path: str) -> None:
263+
def _mkdir_ignore_exists(dir_name: str) -> None:
255264
try:
256-
logging.debug(f"Creating directory {path}")
257-
makedirs(path)
265+
logging.debug("Creating directory %s", dir_name)
266+
makedirs(dir_name)
258267
except FileExistsError:
259268
pass
260269

261270

262271
def main() -> int:
272+
"""Main"""
263273
cli_args = parse_cli_args()
264274
logging.basicConfig(
265275
encoding="utf-8", level=_cli_log_level(cli_args.log_level))
266-
ts = TestSuite(cli_args.test_definition)
276+
test_suite = TestSuite(cli_args.test_definition)
267277
# Compatibility warning: add 'tests' to output_dir
268278
output_dir = path.join(cli_args.output_dir, "tests")
269-
return ts.expand(cli_args.template_dir, output_dir, cli_args.kuttl_test)
279+
return test_suite.expand(cli_args.template_dir, output_dir, cli_args.kuttl_test)
270280

271281

272282
if __name__ == "__main__":
273-
exit(main())
283+
sys.exit(main())

0 commit comments

Comments
 (0)