lange-python 0.3.3__tar.gz → 0.3.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lange_python-0.3.3 → lange_python-0.3.5}/PKG-INFO +2 -3
- {lange_python-0.3.3 → lange_python-0.3.5}/README.md +1 -2
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/code/_stats.py +70 -11
- {lange_python-0.3.3 → lange_python-0.3.5}/pyproject.toml +1 -1
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/__init__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/__main__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/_util/__init__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/_util/_base_client.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/_util/_key_handling.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/__init__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/build/__init__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/build/_command.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/build/_discovery.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/build/_docker.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/build/_poetry.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/build/_types.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/code/__init__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/distribution/__init__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/cli/distribution/_command.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/distribution/__init__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/distribution/_client.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/distribution/_util.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/tunnel/__init__.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/tunnel/_client.py +0 -0
- {lange_python-0.3.3 → lange_python-0.3.5}/lange/tunnel/_util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lange-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: A bundeld set of tools, clients for the lange-suite of tools and more.
|
|
5
5
|
Author: contact@robertlange.me
|
|
6
6
|
Requires-Python: >=3.13
|
|
@@ -37,8 +37,7 @@ from lange.tunnel import Tunnel
|
|
|
37
37
|
|
|
38
38
|
tunnel = Tunnel(
|
|
39
39
|
host="wss://tunnel.lange-labs.com",
|
|
40
|
-
|
|
41
|
-
tunnel_name="dev-edge", # Optional but recommended when one key is linked to multiple tunnels.
|
|
40
|
+
api_key="your-bearer-token",
|
|
42
41
|
target="http://localhost:3000",
|
|
43
42
|
)
|
|
44
43
|
|
|
@@ -22,8 +22,7 @@ from lange.tunnel import Tunnel
|
|
|
22
22
|
|
|
23
23
|
tunnel = Tunnel(
|
|
24
24
|
host="wss://tunnel.lange-labs.com",
|
|
25
|
-
|
|
26
|
-
tunnel_name="dev-edge", # Optional but recommended when one key is linked to multiple tunnels.
|
|
25
|
+
api_key="your-bearer-token",
|
|
27
26
|
target="http://localhost:3000",
|
|
28
27
|
)
|
|
29
28
|
|
|
@@ -7,7 +7,7 @@ SUPPORTED_EXTENSIONS: tuple[str, ...] = (
|
|
|
7
7
|
# Python
|
|
8
8
|
".py",
|
|
9
9
|
# JavaScript / TypeScript / React
|
|
10
|
-
".js", ".jsx", ".ts", ".tsx",
|
|
10
|
+
".js", ".jsx", ".test.ts", ".test.tsx", ".ts", ".tsx",
|
|
11
11
|
# Web (Markup / Styling)
|
|
12
12
|
".html", ".css", ".scss", ".sass", ".less",
|
|
13
13
|
# Shell / Bash
|
|
@@ -59,12 +59,13 @@ IGNORED_DIRECTORIES: tuple[str, ...] = (
|
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def
|
|
62
|
+
def render_stats_table(stats: dict[str, int], label: str = "File-Type") -> str:
|
|
63
63
|
"""
|
|
64
64
|
Render LOC statistics as an ASCII box table.
|
|
65
65
|
|
|
66
|
-
:param stats: Mapping from
|
|
67
|
-
:
|
|
66
|
+
:param stats: Mapping from group label to line count.
|
|
67
|
+
:param label: Heading for the first column.
|
|
68
|
+
:returns: Table string with label, LOC and percentage values.
|
|
68
69
|
"""
|
|
69
70
|
total = sum(stats.values())
|
|
70
71
|
rows: list[tuple[str, str, str]] = []
|
|
@@ -80,7 +81,7 @@ def _render_stats_table(stats: dict[str, int]) -> str:
|
|
|
80
81
|
total_percentage = 100.0 if total else 0.0
|
|
81
82
|
rows.append(("TOTAL", str(total), f"{total_percentage:.2f}%"))
|
|
82
83
|
|
|
83
|
-
headers = (
|
|
84
|
+
headers = (label, "LOC", "Percentage")
|
|
84
85
|
col_widths = [
|
|
85
86
|
max(len(headers[index]), max((len(row[index]) for row in rows), default=0))
|
|
86
87
|
for index in range(3)
|
|
@@ -98,7 +99,29 @@ def _render_stats_table(stats: dict[str, int]) -> str:
|
|
|
98
99
|
return "\n".join(lines)
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
def
|
|
102
|
+
def _match_supported_extension(file_name: str, extensions: Iterable[str]) -> str | None:
|
|
103
|
+
"""
|
|
104
|
+
Resolve the longest supported file ending for a file name.
|
|
105
|
+
|
|
106
|
+
:param file_name: File name that should be matched.
|
|
107
|
+
:param extensions: Allowed file endings, including the leading dot.
|
|
108
|
+
:returns: The longest matching ending, or ``None`` when unsupported.
|
|
109
|
+
"""
|
|
110
|
+
normalized_file_name = file_name.lower()
|
|
111
|
+
sorted_extensions = sorted(
|
|
112
|
+
(extension.lower() for extension in extensions),
|
|
113
|
+
key=len,
|
|
114
|
+
reverse=True,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
for extension in sorted_extensions:
|
|
118
|
+
if normalized_file_name.endswith(extension):
|
|
119
|
+
return extension
|
|
120
|
+
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def count_lines_by_extension(root: Path, extensions: Iterable[str]) -> dict[str, int]:
|
|
102
125
|
"""
|
|
103
126
|
Count file lines recursively grouped by file ending.
|
|
104
127
|
|
|
@@ -113,8 +136,8 @@ def _count_lines_by_extension(root: Path, extensions: Iterable[str]) -> dict[str
|
|
|
113
136
|
directories[:] = [name for name in directories if name not in IGNORED_DIRECTORIES]
|
|
114
137
|
|
|
115
138
|
for file_name in files:
|
|
116
|
-
suffix =
|
|
117
|
-
if suffix
|
|
139
|
+
suffix = _match_supported_extension(file_name, normalized_extensions)
|
|
140
|
+
if suffix is None:
|
|
118
141
|
continue
|
|
119
142
|
|
|
120
143
|
file_path = Path(current_root) / file_name
|
|
@@ -128,6 +151,39 @@ def _count_lines_by_extension(root: Path, extensions: Iterable[str]) -> dict[str
|
|
|
128
151
|
return counts
|
|
129
152
|
|
|
130
153
|
|
|
154
|
+
def count_lines_by_subfolder(root: Path, extensions: Iterable[str]) -> dict[str, int]:
|
|
155
|
+
"""
|
|
156
|
+
Count file lines recursively grouped by top-level folder from the root.
|
|
157
|
+
|
|
158
|
+
:param root: Directory that should be scanned recursively.
|
|
159
|
+
:param extensions: Allowed file endings, including the leading dot.
|
|
160
|
+
:returns: Mapping from immediate subfolder name to counted LOC.
|
|
161
|
+
"""
|
|
162
|
+
counts: dict[str, int] = {}
|
|
163
|
+
|
|
164
|
+
for current_root, directories, files in os.walk(root, topdown=True):
|
|
165
|
+
directories[:] = [name for name in directories if name not in IGNORED_DIRECTORIES]
|
|
166
|
+
|
|
167
|
+
for file_name in files:
|
|
168
|
+
suffix = _match_supported_extension(file_name, extensions)
|
|
169
|
+
if suffix is None:
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
file_path = Path(current_root) / file_name
|
|
173
|
+
try:
|
|
174
|
+
with file_path.open("r", encoding="utf-8", errors="ignore") as file_handle:
|
|
175
|
+
line_count = sum(1 for _ in file_handle)
|
|
176
|
+
except Exception:
|
|
177
|
+
# Silently skip files that can't be opened (e.g., permissions issues)
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
relative_parts = file_path.relative_to(root).parts
|
|
181
|
+
folder_name = relative_parts[0] if len(relative_parts) > 1 else "."
|
|
182
|
+
counts[folder_name] = counts.get(folder_name, 0) + line_count
|
|
183
|
+
|
|
184
|
+
return counts
|
|
185
|
+
|
|
186
|
+
|
|
131
187
|
@click.command("stats")
|
|
132
188
|
def code_stats() -> None:
|
|
133
189
|
"""
|
|
@@ -135,7 +191,8 @@ def code_stats() -> None:
|
|
|
135
191
|
|
|
136
192
|
:returns: ``None``.
|
|
137
193
|
"""
|
|
138
|
-
stats =
|
|
194
|
+
stats = count_lines_by_extension(Path.cwd(), SUPPORTED_EXTENSIONS)
|
|
195
|
+
subfolder_stats = count_lines_by_subfolder(Path.cwd(), SUPPORTED_EXTENSIONS)
|
|
139
196
|
|
|
140
197
|
# Filter out empty languages for the recognized printout so it's not overwhelming
|
|
141
198
|
active_extensions = [ext for ext, count in stats.items() if count > 0]
|
|
@@ -144,8 +201,10 @@ def code_stats() -> None:
|
|
|
144
201
|
click.echo()
|
|
145
202
|
click.echo(f"Recognized file endings found: {' '.join(active_extensions) if active_extensions else 'None'}")
|
|
146
203
|
click.echo(f"Ignored folders: {' '.join(IGNORED_DIRECTORIES)}")
|
|
147
|
-
click.echo(
|
|
204
|
+
click.echo(render_stats_table(stats))
|
|
205
|
+
click.echo()
|
|
206
|
+
click.echo(render_stats_table(subfolder_stats, label="Folder"))
|
|
148
207
|
|
|
149
208
|
|
|
150
209
|
if __name__ == '__main__':
|
|
151
|
-
code_stats()
|
|
210
|
+
code_stats()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|