Skip to content

Commit 9ded0e4

Browse files
committed
chore: pre-commit all exports
1 parent 441c157 commit 9ded0e4

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed

.pre-commit-config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,16 @@ repos:
66
entry: ruff format --no-cache
77
language: system
88
types_or: [python]
9+
910
- id: ruff-check
1011
name: ruff-check
1112
entry: ruff check --fix --no-cache
1213
language: system
1314
types: [python]
15+
16+
- id: check-all-exports
17+
name: all-check
18+
entry: python scripts/check_all_exports.py
19+
language: system
20+
pass_filenames: false
21+
files: __init__.py$

blocket_api/blocket.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ def search(
7272

7373
return _request(url=url, params=params).json()
7474

75+
def test(self) -> Any: ...
76+
7577
def search_car(
7678
self,
7779
query: str | None = None,

scripts/check_all_exports.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import ast
2+
import sys
3+
from pathlib import Path
4+
5+
6+
def get_public_names_from_file(filepath: Path) -> dict[str, Path]:
7+
with open(filepath) as f:
8+
tree = ast.parse(f.read())
9+
10+
public_names = {}
11+
for node in ast.walk(tree):
12+
if isinstance(node, ast.ClassDef):
13+
if not node.name.startswith("_"):
14+
public_names[node.name] = filepath
15+
elif isinstance(node, ast.FunctionDef):
16+
if not node.name.startswith("_"):
17+
public_names[node.name] = filepath
18+
19+
return public_names
20+
21+
22+
def get_all_exports(init_file: Path) -> set[str]:
23+
with open(init_file) as f:
24+
tree = ast.parse(f.read())
25+
26+
for node in ast.walk(tree):
27+
if isinstance(node, ast.Assign):
28+
for target in node.targets:
29+
if isinstance(target, ast.Name) and target.id == "__all__":
30+
if isinstance(node.value, ast.List):
31+
return {
32+
elt.value
33+
for elt in node.value.elts
34+
if isinstance(elt, ast.Constant)
35+
}
36+
return set()
37+
38+
39+
def main():
40+
EXCLUDED_NAMES = {
41+
"QueryParam", # internal
42+
"MobilityAd", # internal
43+
"extend", # internal
44+
"parse", # internal
45+
"url", # internal
46+
"get_ad", # used with api.get_ad()
47+
"search", # used with api.search()
48+
"search_car", # used with api.search_car()
49+
"search_boat", # used with api.search_boat()
50+
"search_mc", # used with api.search_mc()
51+
}
52+
53+
package_dir = Path("blocket_api")
54+
init_file = package_dir / "__init__.py"
55+
56+
source_files = [
57+
package_dir / "constants.py",
58+
package_dir / "ad_parser.py",
59+
package_dir / "blocket.py",
60+
]
61+
62+
if not init_file.exists():
63+
print(f"Error: {init_file} not found")
64+
sys.exit(1)
65+
66+
all_public_names = {}
67+
for source_file in source_files:
68+
if source_file.exists():
69+
names = get_public_names_from_file(source_file)
70+
all_public_names.update(names)
71+
else:
72+
print(f"Warning: {source_file} not found, skipping")
73+
74+
all_exports = get_all_exports(init_file)
75+
76+
missing_from_all = {
77+
name: filepath
78+
for name, filepath in all_public_names.items()
79+
if name not in all_exports and name not in EXCLUDED_NAMES
80+
}
81+
82+
errors = []
83+
84+
if missing_from_all:
85+
for name, filepath in sorted(missing_from_all.items()):
86+
errors.append(f" {filepath.relative_to(package_dir.parent)}: {name}")
87+
88+
if errors:
89+
print(
90+
"Make sure to add new public names to __all__ in __init__.py or exclude it:"
91+
)
92+
print("\n".join(errors))
93+
94+
sys.exit(1)
95+
96+
print(f"✅ All {len(all_public_names)} public names are in __all__")
97+
sys.exit(0)
98+
99+
100+
if __name__ == "__main__":
101+
main()

0 commit comments

Comments
 (0)