Skip to content

Commit 0bd4630

Browse files
authored
Merge pull request #170 from YunoHost/typing
2 parents 675a1fb + c3a304e commit 0bd4630

File tree

9 files changed

+226
-246
lines changed

9 files changed

+226
-246
lines changed

lib/lib_package_linter.py

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import os
44
import time
5-
import urllib
5+
import urllib.request
6+
from typing import Any, Callable, Generator, TypeVar
7+
68
import jsonschema
79

810
from lib.print import _print
@@ -25,10 +27,13 @@ class c:
2527

2628

2729
class TestReport:
28-
def __init__(self, message):
30+
style: str
31+
test_name: str
32+
33+
def __init__(self, message: str) -> None:
2934
self.message = message
3035

31-
def display(self, prefix=""):
36+
def display(self, prefix: str = "") -> None:
3237
_print(prefix + self.style % self.message)
3338

3439

@@ -52,38 +57,38 @@ class Critical(TestReport):
5257
style = c.FAIL + " ✘✘✘ %s" + c.END
5358

5459

55-
def report_warning_not_reliable(str):
56-
_print(c.MAYBE_FAIL + "?", str, c.END)
60+
def report_warning_not_reliable(message: str) -> None:
61+
_print(c.MAYBE_FAIL + "?", message, c.END)
5762

5863

59-
def print_happy(str):
60-
_print(c.OKGREEN + " ☺ ", str, "♥")
64+
def print_happy(message: str) -> None:
65+
_print(c.OKGREEN + " ☺ ", message, "♥")
6166

6267

63-
def urlopen(url):
68+
def urlopen(url: str) -> tuple[int, str]:
6469
try:
6570
conn = urllib.request.urlopen(url)
6671
except urllib.error.HTTPError as e:
67-
return {"content": "", "code": e.code}
72+
return e.code, ""
6873
except urllib.error.URLError as e:
6974
_print("Could not fetch %s : %s" % (url, e))
70-
return {"content": "", "code": 0}
71-
return {"content": conn.read().decode("UTF8"), "code": 200}
75+
return 0, ""
76+
return 200, conn.read().decode("UTF8")
7277

7378

74-
def file_exists(file_path):
79+
def file_exists(file_path: str) -> bool:
7580
return os.path.isfile(file_path) and os.stat(file_path).st_size > 0
7681

7782

78-
def cache_file(cachefile: str, ttl_s: int):
79-
def cache_is_fresh():
83+
def cache_file(cachefile: str, ttl_s: int) -> Callable[[Callable[..., str]], Callable[..., str]]:
84+
def cache_is_fresh() -> bool:
8085
return (
8186
os.path.exists(cachefile)
8287
and time.time() - os.path.getmtime(cachefile) < ttl_s
8388
)
8489

85-
def decorator(function):
86-
def wrapper(*args, **kwargs):
90+
def decorator(function: Callable[..., str]) -> Callable[..., str]:
91+
def wrapper(*args: Any, **kwargs: Any) -> str:
8792
if not cache_is_fresh():
8893
with open(cachefile, "w+") as outfile:
8994
outfile.write(function(*args, **kwargs))
@@ -95,47 +100,47 @@ def wrapper(*args, **kwargs):
95100

96101

97102
@cache_file(".spdx_licenses", 3600)
98-
def spdx_licenses():
99-
return urlopen("https://spdx.org/licenses/")["content"]
103+
def spdx_licenses() -> str:
104+
return urlopen("https://spdx.org/licenses/")[1]
100105

101106

102107
@cache_file(".manifest.v2.schema.json", 3600)
103-
def manifest_v2_schema():
104-
return urlopen(
105-
"https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json"
106-
)["content"]
108+
def manifest_v2_schema() -> str:
109+
url = "https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json"
110+
return urlopen(url)[1]
107111

108112

109113
@cache_file(".tests.v1.schema.json", 3600)
110-
def tests_v1_schema():
111-
return urlopen(
112-
"https://raw.githubusercontent.com/YunoHost/apps/master/schemas/tests.v1.schema.json"
113-
)["content"]
114+
def tests_v1_schema() -> str:
115+
url = "https://raw.githubusercontent.com/YunoHost/apps/master/schemas/tests.v1.schema.json"
116+
return urlopen(url)[1]
114117

115118

116119
@cache_file(".config_panel.v1.schema.json", 3600)
117-
def config_panel_v1_schema():
118-
return urlopen(
119-
"https://raw.githubusercontent.com/YunoHost/apps/master/schemas/config_panel.v1.schema.json"
120-
)["content"]
120+
def config_panel_v1_schema() -> str:
121+
url = "https://raw.githubusercontent.com/YunoHost/apps/master/schemas/config_panel.v1.schema.json"
122+
return urlopen(url)[1]
121123

122124

123-
def validate_schema(name: str, schema, data):
125+
def validate_schema(name: str, schema: dict[str, Any], data: dict[str, Any]) -> Generator[Info, None, None]:
124126
v = jsonschema.Draft7Validator(schema)
125127

126128
for error in v.iter_errors(data):
127129
try:
128130
error_path = " > ".join(error.path)
129-
except:
131+
except TypeError:
130132
error_path = str(error.path)
131133

132134
yield Info(
133135
f"Error validating {name} using schema: in key {error_path}\n {error.message}"
134136
)
135137

136138

137-
tests = {}
138-
tests_reports = {
139+
TestResult = Generator[TestReport, None, None]
140+
TestFn = Callable[[Any], TestResult]
141+
142+
tests: dict[str, list[tuple[TestFn, Any]]] = {}
143+
tests_reports: dict[str, list[Any]] = {
139144
"success": [],
140145
"info": [],
141146
"warning": [],
@@ -144,8 +149,9 @@ def validate_schema(name: str, schema, data):
144149
}
145150

146151

147-
def test(**kwargs):
148-
def decorator(f):
152+
153+
def test(**kwargs: Any) -> Callable[[TestFn], TestFn]:
154+
def decorator(f: TestFn) -> TestFn:
149155
clsname = f.__qualname__.split(".")[0]
150156
if clsname not in tests:
151157
tests[clsname] = []
@@ -155,8 +161,11 @@ def decorator(f):
155161
return decorator
156162

157163

158-
class TestSuite:
159-
def run_tests(self):
164+
class TestSuite():
165+
name: str
166+
test_suite_name: str
167+
168+
def run_tests(self) -> None:
160169

161170
reports = []
162171

@@ -174,7 +183,7 @@ def run_tests(self):
174183

175184
# Display part
176185

177-
def report_type(report):
186+
def report_type(report: TestReport) -> str:
178187
return report.__class__.__name__.lower()
179188

180189
if any(report_type(r) in ["warning", "error", "critical"] for r in reports):
@@ -198,11 +207,11 @@ def report_type(report):
198207
for report in reports:
199208
tests_reports[report_type(report)].append((report.test_name, report))
200209

201-
def run_single_test(self, test):
210+
def run_single_test(self, test: TestFn) -> None:
202211

203212
reports = list(test(self))
204213

205-
def report_type(report):
214+
def report_type(report: TestReport) -> str:
206215
return report.__class__.__name__.lower()
207216

208217
for report in reports:

lib/nginxparser/nginxparser.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#!/usr/bin/env python3
2+
# type: ignore
13
"""Very low-level nginx config parser based on pyparsing."""
24

35
# Taken from https://github.com/certbot/certbot (Apache licensed)

lib/print.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
#!/usr/bin/env python3
22

3+
from typing import Any
4+
35
output = "plain"
46

57

6-
def _print(*args, **kwargs):
8+
def _print(*args: Any, **kwargs: Any) -> None:
79
if not is_json_output():
810
print(*args, **kwargs)
911

1012

11-
def set_output_json():
13+
def set_output_json() -> None:
1214
global output
1315
output = "json"
1416

package_linter.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from lib.lib_package_linter import c
88
from lib.print import _print, set_output_json
9-
109
from tests.test_app import App
1110

1211

0 commit comments

Comments
 (0)