rust-crate-pipeline 1.3.2__py3-none-any.whl → 1.3.4__py3-none-any.whl
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.
- rust_crate_pipeline/network.py +321 -327
- rust_crate_pipeline/progress_monitor.py +334 -0
- rust_crate_pipeline/version.py +74 -74
- {rust_crate_pipeline-1.3.2.dist-info → rust_crate_pipeline-1.3.4.dist-info}/METADATA +1 -1
- {rust_crate_pipeline-1.3.2.dist-info → rust_crate_pipeline-1.3.4.dist-info}/RECORD +9 -8
- {rust_crate_pipeline-1.3.2.dist-info → rust_crate_pipeline-1.3.4.dist-info}/WHEEL +0 -0
- {rust_crate_pipeline-1.3.2.dist-info → rust_crate_pipeline-1.3.4.dist-info}/entry_points.txt +0 -0
- {rust_crate_pipeline-1.3.2.dist-info → rust_crate_pipeline-1.3.4.dist-info}/licenses/LICENSE +0 -0
- {rust_crate_pipeline-1.3.2.dist-info → rust_crate_pipeline-1.3.4.dist-info}/top_level.txt +0 -0
rust_crate_pipeline/network.py
CHANGED
@@ -1,327 +1,321 @@
|
|
1
|
-
# network.py
|
2
|
-
import os
|
3
|
-
import sys
|
4
|
-
import re
|
5
|
-
import time
|
6
|
-
import logging
|
7
|
-
import requests
|
8
|
-
from bs4 import BeautifulSoup, Tag
|
9
|
-
from typing import Any, Union
|
10
|
-
from .config import PipelineConfig
|
11
|
-
|
12
|
-
# Import utilities
|
13
|
-
# Add the parent directory to the path to import utils
|
14
|
-
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
15
|
-
|
16
|
-
|
17
|
-
class GitHubBatchClient:
|
18
|
-
def __init__(self, config: PipelineConfig) -> None:
|
19
|
-
self.config = config
|
20
|
-
# Simple headers without dependency on HTTPClientUtils
|
21
|
-
self.headers = {
|
22
|
-
"Accept": "application/vnd.github.v3+json",
|
23
|
-
"User-Agent": "SigilDERG-Data-Production/1.3.2",
|
24
|
-
}
|
25
|
-
if config.github_token:
|
26
|
-
self.headers["Authorization"] = f"token {config.github_token}"
|
27
|
-
|
28
|
-
# Simple session without dependency on HTTPClientUtils
|
29
|
-
self.session = requests.Session()
|
30
|
-
self.session.headers.update(self.headers)
|
31
|
-
self.remaining_calls = 5000
|
32
|
-
self.reset_time = 0
|
33
|
-
|
34
|
-
def check_rate_limit(self) -> None:
|
35
|
-
"""Check and update current rate limit status"""
|
36
|
-
try:
|
37
|
-
response = self.session.get(
|
38
|
-
"https://api.github.com/rate_limit", headers=self.headers
|
39
|
-
)
|
40
|
-
if response.ok:
|
41
|
-
data = response.json()
|
42
|
-
self.remaining_calls = data["resources"]["core"]["remaining"]
|
43
|
-
self.reset_time = data["resources"]["core"]["reset"]
|
44
|
-
|
45
|
-
if self.remaining_calls < 100:
|
46
|
-
reset_in = self.reset_time - time.time()
|
47
|
-
logging.warning(
|
48
|
-
f"GitHub API rate limit low: {self.remaining_calls} remaining. "
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
response
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
self.
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
)
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
"
|
213
|
-
"
|
214
|
-
"
|
215
|
-
"
|
216
|
-
"
|
217
|
-
"
|
218
|
-
"
|
219
|
-
"
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
"
|
270
|
-
"
|
271
|
-
"
|
272
|
-
"
|
273
|
-
"
|
274
|
-
"
|
275
|
-
"
|
276
|
-
"
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
"
|
309
|
-
"
|
310
|
-
"
|
311
|
-
"
|
312
|
-
"
|
313
|
-
"
|
314
|
-
"
|
315
|
-
"
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
}
|
323
|
-
except Exception:
|
324
|
-
pass
|
325
|
-
|
326
|
-
# If all sources fail
|
327
|
-
return None
|
1
|
+
# network.py
|
2
|
+
import os
|
3
|
+
import sys
|
4
|
+
import re
|
5
|
+
import time
|
6
|
+
import logging
|
7
|
+
import requests
|
8
|
+
from bs4 import BeautifulSoup, Tag
|
9
|
+
from typing import Any, Union
|
10
|
+
from .config import PipelineConfig
|
11
|
+
|
12
|
+
# Import utilities
|
13
|
+
# Add the parent directory to the path to import utils
|
14
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
15
|
+
|
16
|
+
|
17
|
+
class GitHubBatchClient:
|
18
|
+
def __init__(self, config: PipelineConfig) -> None:
|
19
|
+
self.config = config
|
20
|
+
# Simple headers without dependency on HTTPClientUtils
|
21
|
+
self.headers = {
|
22
|
+
"Accept": "application/vnd.github.v3+json",
|
23
|
+
"User-Agent": "SigilDERG-Data-Production/1.3.2",
|
24
|
+
}
|
25
|
+
if config.github_token:
|
26
|
+
self.headers["Authorization"] = f"token {config.github_token}"
|
27
|
+
|
28
|
+
# Simple session without dependency on HTTPClientUtils
|
29
|
+
self.session = requests.Session()
|
30
|
+
self.session.headers.update(self.headers)
|
31
|
+
self.remaining_calls = 5000
|
32
|
+
self.reset_time = 0
|
33
|
+
|
34
|
+
def check_rate_limit(self) -> None:
|
35
|
+
"""Check and update current rate limit status"""
|
36
|
+
try:
|
37
|
+
response = self.session.get(
|
38
|
+
"https://api.github.com/rate_limit", headers=self.headers
|
39
|
+
)
|
40
|
+
if response.ok:
|
41
|
+
data = response.json()
|
42
|
+
self.remaining_calls = data["resources"]["core"]["remaining"]
|
43
|
+
self.reset_time = data["resources"]["core"]["reset"]
|
44
|
+
|
45
|
+
if self.remaining_calls < 100:
|
46
|
+
reset_in = self.reset_time - time.time()
|
47
|
+
logging.warning(
|
48
|
+
f"GitHub API rate limit low: {self.remaining_calls} remaining. Resets in {reset_in / 60:.1f} minutes"
|
49
|
+
)
|
50
|
+
except Exception:
|
51
|
+
pass
|
52
|
+
|
53
|
+
def get_repo_stats(self, owner: str, repo: str) -> "dict[str, Any]":
|
54
|
+
"""Get repository statistics"""
|
55
|
+
try:
|
56
|
+
url = f"https://api.github.com/repos/{owner}/{repo}"
|
57
|
+
response = self.session.get(url, headers=self.headers)
|
58
|
+
if response.ok:
|
59
|
+
return response.json()
|
60
|
+
else:
|
61
|
+
logging.warning(
|
62
|
+
f"Failed to get repo stats for {owner}/{repo}: {response.status_code}"
|
63
|
+
)
|
64
|
+
return {}
|
65
|
+
except Exception as e:
|
66
|
+
logging.error(f"Error fetching repo stats: {str(e)}")
|
67
|
+
return {}
|
68
|
+
|
69
|
+
def batch_get_repo_stats(self, repo_list: "list[str]") -> "dict[str, dict[str, Any]]":
|
70
|
+
"""Get statistics for multiple repositories in a batch"""
|
71
|
+
self.check_rate_limit()
|
72
|
+
|
73
|
+
results: "dict[str, dict[str, Any]]" = {}
|
74
|
+
for repo_url in repo_list:
|
75
|
+
# Extract owner/repo from URL
|
76
|
+
match = re.search(r"github\.com/([^/]+)/([^/\.]+)", repo_url)
|
77
|
+
if not match:
|
78
|
+
continue
|
79
|
+
|
80
|
+
owner, repo = match.groups()
|
81
|
+
repo = repo.split(".")[0] # Remove .git extension if present
|
82
|
+
|
83
|
+
# Get stats
|
84
|
+
stats = self.get_repo_stats(owner, repo)
|
85
|
+
results[repo_url] = stats
|
86
|
+
|
87
|
+
# Be nice to GitHub API
|
88
|
+
time.sleep(0.1)
|
89
|
+
return results
|
90
|
+
|
91
|
+
|
92
|
+
class CrateAPIClient:
|
93
|
+
def __init__(self, config: PipelineConfig) -> None:
|
94
|
+
self.config = config
|
95
|
+
# Simple session without dependency on HTTPClientUtils
|
96
|
+
self.session = requests.Session()
|
97
|
+
self.session.headers.update({"User-Agent": "SigilDERG-Data-Production/1.3.2"})
|
98
|
+
|
99
|
+
def fetch_crate_metadata(self, crate_name: str) -> "dict[str, Any] | None":
|
100
|
+
"""Fetch metadata with retry logic"""
|
101
|
+
for attempt in range(self.config.max_retries):
|
102
|
+
try:
|
103
|
+
return self._fetch_metadata(crate_name)
|
104
|
+
except Exception as e:
|
105
|
+
logging.warning(
|
106
|
+
f"Attempt {attempt + 1} failed for {crate_name}: {str(e)}"
|
107
|
+
)
|
108
|
+
wait = 2**attempt
|
109
|
+
time.sleep(wait)
|
110
|
+
return None
|
111
|
+
|
112
|
+
def _fetch_metadata(self, crate_name: str) -> "dict[str, Any] | None":
|
113
|
+
"""Enhanced metadata fetching that tries multiple sources"""
|
114
|
+
# First try crates.io (primary source)
|
115
|
+
try:
|
116
|
+
r = self.session.get(f"https://crates.io/api/v1/crates/{crate_name}")
|
117
|
+
if r.ok:
|
118
|
+
data = r.json()
|
119
|
+
crate_data = data["crate"]
|
120
|
+
latest = crate_data["newest_version"]
|
121
|
+
|
122
|
+
# Get readme
|
123
|
+
readme_response = self.session.get(
|
124
|
+
f"https://crates.io/api/v1/crates/{crate_name}/readme"
|
125
|
+
)
|
126
|
+
readme = readme_response.text if readme_response.ok else ""
|
127
|
+
|
128
|
+
# Get dependencies
|
129
|
+
deps_url = (
|
130
|
+
f"https://crates.io/api/v1/crates/{crate_name}/"
|
131
|
+
f"{latest}/dependencies"
|
132
|
+
)
|
133
|
+
deps_response = self.session.get(deps_url)
|
134
|
+
deps: list[dict[str, Any]] = (
|
135
|
+
deps_response.json().get("dependencies", [])
|
136
|
+
if deps_response.ok
|
137
|
+
else []
|
138
|
+
)
|
139
|
+
|
140
|
+
# Get features - using the versions endpoint
|
141
|
+
features = []
|
142
|
+
versions_response = self.session.get(
|
143
|
+
f"https://crates.io/api/v1/crates/{crate_name}/{latest}"
|
144
|
+
)
|
145
|
+
if versions_response.ok:
|
146
|
+
version_data = versions_response.json().get("version", {})
|
147
|
+
features_dict = version_data.get("features", {})
|
148
|
+
features = [
|
149
|
+
{"name": k, "dependencies": v} for k, v in features_dict.items()
|
150
|
+
]
|
151
|
+
|
152
|
+
# Repository info and GitHub stars
|
153
|
+
repo = crate_data.get("repository", "")
|
154
|
+
gh_stars = 0
|
155
|
+
|
156
|
+
# Check if it's a GitHub repo
|
157
|
+
if "github.com" in repo and self.config.github_token:
|
158
|
+
match = re.search(r"github.com/([^/]+)/([^/]+)", repo)
|
159
|
+
if match:
|
160
|
+
owner, repo_name = match.groups()
|
161
|
+
repo_name = repo_name.split(".")[0] # Handle .git extensions
|
162
|
+
gh_url = f"https://api.github.com/repos/{owner}/{repo_name}"
|
163
|
+
gh_headers: dict[str, str] = {}
|
164
|
+
if self.config.github_token:
|
165
|
+
gh_headers["Authorization"] = (
|
166
|
+
f"token {self.config.github_token}"
|
167
|
+
)
|
168
|
+
|
169
|
+
gh = self.session.get(gh_url, headers=gh_headers)
|
170
|
+
if gh.ok:
|
171
|
+
gh_data = gh.json()
|
172
|
+
gh_stars = gh_data.get("stargazers_count", 0)
|
173
|
+
|
174
|
+
# Check if it's hosted on lib.rs
|
175
|
+
lib_rs_data = {}
|
176
|
+
if "lib.rs" in repo:
|
177
|
+
lib_rs_url = f"https://lib.rs/crates/{crate_name}"
|
178
|
+
lib_rs_response = self.session.get(lib_rs_url)
|
179
|
+
if lib_rs_response.ok:
|
180
|
+
soup = BeautifulSoup(lib_rs_response.text, "html.parser")
|
181
|
+
# Get README from lib.rs if not already available
|
182
|
+
if not readme:
|
183
|
+
readme_div = soup.find("div", class_="readme")
|
184
|
+
if readme_div:
|
185
|
+
readme = readme_div.get_text(
|
186
|
+
strip=True
|
187
|
+
) # Get lib.rs specific stats
|
188
|
+
stats_div = soup.find("div", class_="crate-stats")
|
189
|
+
if isinstance(stats_div, Tag):
|
190
|
+
downloads_text = stats_div.find(
|
191
|
+
string=re.compile(r"[\d,]+ downloads")
|
192
|
+
)
|
193
|
+
if downloads_text:
|
194
|
+
lib_rs_data["librs_downloads"] = int(
|
195
|
+
re.sub(r"[^\d]", "", str(downloads_text))
|
196
|
+
)
|
197
|
+
|
198
|
+
# Extract code snippets and sections (simplified)
|
199
|
+
code_snippets: list[str] = (
|
200
|
+
[]
|
201
|
+
) # Simplified - would normally extract from readme
|
202
|
+
readme_sections: dict[str, str] = (
|
203
|
+
{}
|
204
|
+
) # Simplified - would normally parse sections
|
205
|
+
|
206
|
+
result: dict[str, Any] = {
|
207
|
+
"name": crate_name,
|
208
|
+
"version": latest,
|
209
|
+
"description": crate_data.get("description", ""),
|
210
|
+
"repository": repo,
|
211
|
+
"keywords": crate_data.get("keywords", []),
|
212
|
+
"categories": crate_data.get("categories", []),
|
213
|
+
"readme": readme,
|
214
|
+
"downloads": crate_data.get("downloads", 0),
|
215
|
+
"github_stars": gh_stars,
|
216
|
+
"dependencies": deps,
|
217
|
+
"code_snippets": code_snippets,
|
218
|
+
"features": features,
|
219
|
+
"readme_sections": readme_sections,
|
220
|
+
**lib_rs_data,
|
221
|
+
}
|
222
|
+
|
223
|
+
return result
|
224
|
+
|
225
|
+
except Exception as e:
|
226
|
+
logging.error(
|
227
|
+
f"Failed fetching metadata for {crate_name}: {str(e)}"
|
228
|
+
)
|
229
|
+
raise
|
230
|
+
|
231
|
+
# If crates.io fails, try lib.rs
|
232
|
+
try:
|
233
|
+
r = self.session.get(f"https://lib.rs/crates/{crate_name}")
|
234
|
+
if r.ok:
|
235
|
+
soup = BeautifulSoup(r.text, "html.parser")
|
236
|
+
|
237
|
+
# Extract metadata from lib.rs page
|
238
|
+
h1 = soup.select_one("h1")
|
239
|
+
name = h1.text.strip() if h1 else crate_name
|
240
|
+
|
241
|
+
# Find description
|
242
|
+
desc_elem = soup.select_one(".description")
|
243
|
+
description = desc_elem.text.strip() if desc_elem else ""
|
244
|
+
|
245
|
+
# Find repository link
|
246
|
+
repo_link: Union[str, None] = None
|
247
|
+
for a in soup.select("a"):
|
248
|
+
href = a.get("href")
|
249
|
+
if href and isinstance(href, str) and "github.com" in href:
|
250
|
+
repo_link = href
|
251
|
+
break
|
252
|
+
|
253
|
+
# Find keywords
|
254
|
+
keywords_elem = soup.select_one(".keywords")
|
255
|
+
keywords = (
|
256
|
+
[k.text.strip() for k in keywords_elem.find_all("a")]
|
257
|
+
if keywords_elem
|
258
|
+
else []
|
259
|
+
)
|
260
|
+
|
261
|
+
# Basic metadata from lib.rs
|
262
|
+
return {
|
263
|
+
"name": name,
|
264
|
+
"version": "latest", # lib.rs doesn't easily expose version
|
265
|
+
"description": description,
|
266
|
+
"repository": repo_link or "",
|
267
|
+
"keywords": keywords,
|
268
|
+
"categories": [],
|
269
|
+
"readme": "",
|
270
|
+
"downloads": 0,
|
271
|
+
"github_stars": 0,
|
272
|
+
"dependencies": [],
|
273
|
+
"code_snippets": [],
|
274
|
+
"features": [],
|
275
|
+
"readme_sections": {},
|
276
|
+
"source": "lib.rs",
|
277
|
+
}
|
278
|
+
except Exception:
|
279
|
+
pass
|
280
|
+
|
281
|
+
# Finally, try GitHub search
|
282
|
+
try:
|
283
|
+
# This is a simplification - GitHub's search API requires
|
284
|
+
# authentication
|
285
|
+
gh_search_headers: dict[str, str] = {}
|
286
|
+
if self.config.github_token:
|
287
|
+
gh_search_headers["Authorization"] = f"token {self.config.github_token}"
|
288
|
+
|
289
|
+
search_url = (
|
290
|
+
f"https://api.github.com/search/repositories?"
|
291
|
+
f"q={crate_name}+language:rust"
|
292
|
+
)
|
293
|
+
r = requests.get(search_url, headers=gh_search_headers)
|
294
|
+
|
295
|
+
if r.ok:
|
296
|
+
results = r.json().get("items", [])
|
297
|
+
if results:
|
298
|
+
repo = results[0] # Take first match
|
299
|
+
|
300
|
+
# Basic metadata from GitHub
|
301
|
+
return {
|
302
|
+
"name": crate_name,
|
303
|
+
"version": "unknown",
|
304
|
+
"description": repo.get("description", ""),
|
305
|
+
"repository": repo.get("html_url", ""),
|
306
|
+
"keywords": [],
|
307
|
+
"categories": [],
|
308
|
+
"readme": "",
|
309
|
+
"downloads": 0,
|
310
|
+
"github_stars": repo.get("stargazers_count", 0),
|
311
|
+
"dependencies": [],
|
312
|
+
"code_snippets": [],
|
313
|
+
"features": [],
|
314
|
+
"readme_sections": {},
|
315
|
+
"source": "github",
|
316
|
+
}
|
317
|
+
except Exception:
|
318
|
+
pass
|
319
|
+
|
320
|
+
# If all sources fail
|
321
|
+
return None
|
@@ -0,0 +1,334 @@
|
|
1
|
+
# progress_monitor.py
|
2
|
+
"""
|
3
|
+
Real-time progress monitoring for the Rust Crate Pipeline (CLI-only).
|
4
|
+
|
5
|
+
This module provides:
|
6
|
+
- Live progress bars with ETA
|
7
|
+
- Real-time statistics and metrics
|
8
|
+
- Status printouts
|
9
|
+
- Performance monitoring
|
10
|
+
- Error tracking and reporting
|
11
|
+
- Status JSON file for external tools/scripts
|
12
|
+
"""
|
13
|
+
|
14
|
+
import time
|
15
|
+
import threading
|
16
|
+
import json
|
17
|
+
import os
|
18
|
+
from datetime import datetime, timedelta
|
19
|
+
from typing import Dict, List, Optional, Any, Union
|
20
|
+
from dataclasses import dataclass, field
|
21
|
+
from collections import deque
|
22
|
+
import logging
|
23
|
+
|
24
|
+
try:
|
25
|
+
from tqdm import tqdm
|
26
|
+
TQDM_AVAILABLE = True
|
27
|
+
except ImportError:
|
28
|
+
TQDM_AVAILABLE = False
|
29
|
+
|
30
|
+
try:
|
31
|
+
import psutil
|
32
|
+
PSUTIL_AVAILABLE = True
|
33
|
+
except ImportError:
|
34
|
+
PSUTIL_AVAILABLE = False
|
35
|
+
|
36
|
+
|
37
|
+
@dataclass
|
38
|
+
class PipelineMetrics:
|
39
|
+
"""Real-time pipeline metrics and statistics."""
|
40
|
+
total_crates: int = 0
|
41
|
+
processed_crates: int = 0
|
42
|
+
successful_crates: int = 0
|
43
|
+
failed_crates: int = 0
|
44
|
+
skipped_crates: int = 0
|
45
|
+
current_batch: int = 0
|
46
|
+
total_batches: int = 0
|
47
|
+
start_time: Optional[datetime] = None
|
48
|
+
current_operation: str = "Initializing"
|
49
|
+
errors: List[Dict[str, Any]] = field(default_factory=list)
|
50
|
+
warnings: List[Dict[str, Any]] = field(default_factory=list)
|
51
|
+
performance_stats: Dict[str, Any] = field(default_factory=dict)
|
52
|
+
|
53
|
+
@property
|
54
|
+
def progress_percentage(self) -> float:
|
55
|
+
"""Calculate progress percentage."""
|
56
|
+
if self.total_crates == 0:
|
57
|
+
return 0.0
|
58
|
+
return (self.processed_crates / self.total_crates) * 100
|
59
|
+
|
60
|
+
@property
|
61
|
+
def success_rate(self) -> float:
|
62
|
+
"""Calculate success rate percentage."""
|
63
|
+
if self.processed_crates == 0:
|
64
|
+
return 0.0
|
65
|
+
return (self.successful_crates / self.processed_crates) * 100
|
66
|
+
|
67
|
+
@property
|
68
|
+
def elapsed_time(self) -> timedelta:
|
69
|
+
"""Calculate elapsed time."""
|
70
|
+
if not self.start_time:
|
71
|
+
return timedelta(0)
|
72
|
+
return datetime.now() - self.start_time
|
73
|
+
|
74
|
+
@property
|
75
|
+
def estimated_completion(self) -> Optional[datetime]:
|
76
|
+
"""Estimate completion time."""
|
77
|
+
if self.processed_crates == 0 or not self.start_time:
|
78
|
+
return None
|
79
|
+
|
80
|
+
avg_time_per_crate = self.elapsed_time / self.processed_crates
|
81
|
+
remaining_crates = self.total_crates - self.processed_crates
|
82
|
+
estimated_remaining = avg_time_per_crate * remaining_crates
|
83
|
+
|
84
|
+
return datetime.now() + estimated_remaining
|
85
|
+
|
86
|
+
|
87
|
+
class ProgressMonitor:
|
88
|
+
"""Real-time progress monitoring with live dashboard."""
|
89
|
+
|
90
|
+
def __init__(self, total_crates: int, output_dir: str = "output"):
|
91
|
+
self.metrics = PipelineMetrics(total_crates=total_crates)
|
92
|
+
self.output_dir = output_dir
|
93
|
+
self.logger = logging.getLogger(__name__)
|
94
|
+
|
95
|
+
# Performance tracking
|
96
|
+
self.crate_times: deque = deque(maxlen=100) # Last 100 crate processing times
|
97
|
+
self.batch_times: deque = deque(maxlen=50) # Last 50 batch processing times
|
98
|
+
|
99
|
+
# Status tracking
|
100
|
+
self.current_crate: Optional[str] = None
|
101
|
+
self.current_operation: str = "Initializing"
|
102
|
+
self.status_file = os.path.join(output_dir, "pipeline_status.json")
|
103
|
+
|
104
|
+
# Thread safety
|
105
|
+
self._lock = threading.Lock()
|
106
|
+
|
107
|
+
# Initialize
|
108
|
+
self.metrics.start_time = datetime.now()
|
109
|
+
self._save_status()
|
110
|
+
|
111
|
+
# Create output directory if it doesn't exist
|
112
|
+
os.makedirs(output_dir, exist_ok=True)
|
113
|
+
|
114
|
+
def start_crate(self, crate_name: str) -> None:
|
115
|
+
"""Mark the start of processing a crate."""
|
116
|
+
with self._lock:
|
117
|
+
self.current_crate = crate_name
|
118
|
+
self.current_operation = f"Processing {crate_name}"
|
119
|
+
self.metrics.current_operation = self.current_operation
|
120
|
+
self._save_status()
|
121
|
+
|
122
|
+
def complete_crate(self, crate_name: str, success: bool = True,
|
123
|
+
processing_time: Optional[float] = None) -> None:
|
124
|
+
"""Mark the completion of processing a crate."""
|
125
|
+
with self._lock:
|
126
|
+
self.metrics.processed_crates += 1
|
127
|
+
|
128
|
+
if success:
|
129
|
+
self.metrics.successful_crates += 1
|
130
|
+
else:
|
131
|
+
self.metrics.failed_crates += 1
|
132
|
+
|
133
|
+
if processing_time:
|
134
|
+
self.crate_times.append(processing_time)
|
135
|
+
|
136
|
+
self.current_crate = None
|
137
|
+
self.current_operation = "Waiting for next crate"
|
138
|
+
self.metrics.current_operation = self.current_operation
|
139
|
+
|
140
|
+
# Update performance stats
|
141
|
+
self._update_performance_stats()
|
142
|
+
self._save_status()
|
143
|
+
|
144
|
+
def skip_crate(self, crate_name: str, reason: str = "Unknown") -> None:
|
145
|
+
"""Mark a crate as skipped."""
|
146
|
+
with self._lock:
|
147
|
+
self.metrics.processed_crates += 1
|
148
|
+
self.metrics.skipped_crates += 1
|
149
|
+
|
150
|
+
self.metrics.warnings.append({
|
151
|
+
"crate": crate_name,
|
152
|
+
"reason": reason,
|
153
|
+
"timestamp": datetime.now().isoformat()
|
154
|
+
})
|
155
|
+
|
156
|
+
self._save_status()
|
157
|
+
|
158
|
+
def start_batch(self, batch_num: int, batch_size: int) -> None:
|
159
|
+
"""Mark the start of processing a batch."""
|
160
|
+
with self._lock:
|
161
|
+
self.metrics.current_batch = batch_num
|
162
|
+
self.current_operation = f"Processing batch {batch_num}"
|
163
|
+
self.metrics.current_operation = self.current_operation
|
164
|
+
self._save_status()
|
165
|
+
|
166
|
+
def complete_batch(self, batch_num: int, processing_time: Optional[float] = None) -> None:
|
167
|
+
"""Mark the completion of processing a batch."""
|
168
|
+
with self._lock:
|
169
|
+
if processing_time:
|
170
|
+
self.batch_times.append(processing_time)
|
171
|
+
|
172
|
+
self.current_operation = "Batch completed, preparing next batch"
|
173
|
+
self.metrics.current_operation = self.current_operation
|
174
|
+
self._save_status()
|
175
|
+
|
176
|
+
def add_error(self, crate_name: str, error: str, error_type: str = "Processing") -> None:
|
177
|
+
"""Add an error to the metrics."""
|
178
|
+
with self._lock:
|
179
|
+
self.metrics.errors.append({
|
180
|
+
"crate": crate_name,
|
181
|
+
"error": error,
|
182
|
+
"type": error_type,
|
183
|
+
"timestamp": datetime.now().isoformat()
|
184
|
+
})
|
185
|
+
self._save_status()
|
186
|
+
|
187
|
+
def add_warning(self, crate_name: str, warning: str) -> None:
|
188
|
+
"""Add a warning to the metrics."""
|
189
|
+
with self._lock:
|
190
|
+
self.metrics.warnings.append({
|
191
|
+
"crate": crate_name,
|
192
|
+
"warning": warning,
|
193
|
+
"timestamp": datetime.now().isoformat()
|
194
|
+
})
|
195
|
+
self._save_status()
|
196
|
+
|
197
|
+
def update_operation(self, operation: str) -> None:
|
198
|
+
"""Update the current operation description."""
|
199
|
+
with self._lock:
|
200
|
+
self.current_operation = operation
|
201
|
+
self.metrics.current_operation = operation
|
202
|
+
self._save_status()
|
203
|
+
|
204
|
+
def _update_performance_stats(self) -> None:
|
205
|
+
"""Update performance statistics."""
|
206
|
+
if self.crate_times:
|
207
|
+
self.metrics.performance_stats.update({
|
208
|
+
"avg_crate_time": sum(self.crate_times) / len(self.crate_times),
|
209
|
+
"min_crate_time": min(self.crate_times),
|
210
|
+
"max_crate_time": max(self.crate_times),
|
211
|
+
"crates_per_minute": len(self.crate_times) / (sum(self.crate_times) / 60)
|
212
|
+
})
|
213
|
+
|
214
|
+
if self.batch_times:
|
215
|
+
self.metrics.performance_stats.update({
|
216
|
+
"avg_batch_time": sum(self.batch_times) / len(self.batch_times),
|
217
|
+
"min_batch_time": min(self.batch_times),
|
218
|
+
"max_batch_time": max(self.batch_times)
|
219
|
+
})
|
220
|
+
|
221
|
+
# System stats if available
|
222
|
+
if PSUTIL_AVAILABLE:
|
223
|
+
try:
|
224
|
+
cpu_percent = psutil.cpu_percent()
|
225
|
+
memory = psutil.virtual_memory()
|
226
|
+
disk = psutil.disk_usage(self.output_dir)
|
227
|
+
|
228
|
+
self.metrics.performance_stats.update({
|
229
|
+
"system_cpu_percent": cpu_percent,
|
230
|
+
"system_memory_percent": memory.percent,
|
231
|
+
"system_disk_percent": disk.percent,
|
232
|
+
"system_memory_available": memory.available,
|
233
|
+
"system_disk_free": disk.free
|
234
|
+
})
|
235
|
+
except Exception as e:
|
236
|
+
self.logger.warning(f"Failed to get system stats: {e}")
|
237
|
+
|
238
|
+
def _save_status(self) -> None:
|
239
|
+
"""Save current status to file."""
|
240
|
+
try:
|
241
|
+
status_data = {
|
242
|
+
"metrics": {
|
243
|
+
"total_crates": self.metrics.total_crates,
|
244
|
+
"processed_crates": self.metrics.processed_crates,
|
245
|
+
"successful_crates": self.metrics.successful_crates,
|
246
|
+
"failed_crates": self.metrics.failed_crates,
|
247
|
+
"skipped_crates": self.metrics.skipped_crates,
|
248
|
+
"progress_percentage": self.metrics.progress_percentage,
|
249
|
+
"success_rate": self.metrics.success_rate,
|
250
|
+
"current_batch": self.metrics.current_batch,
|
251
|
+
"total_batches": self.metrics.total_batches,
|
252
|
+
"start_time": self.metrics.start_time.isoformat() if self.metrics.start_time else None,
|
253
|
+
"elapsed_time": str(self.metrics.elapsed_time),
|
254
|
+
"estimated_completion": self.metrics.estimated_completion.isoformat() if self.metrics.estimated_completion else None,
|
255
|
+
"current_operation": self.metrics.current_operation
|
256
|
+
},
|
257
|
+
"current_crate": self.current_crate,
|
258
|
+
"performance_stats": self.metrics.performance_stats,
|
259
|
+
"errors": self.metrics.errors[-10:], # Last 10 errors
|
260
|
+
"warnings": self.metrics.warnings[-10:], # Last 10 warnings
|
261
|
+
"last_updated": datetime.now().isoformat()
|
262
|
+
}
|
263
|
+
|
264
|
+
with open(self.status_file, 'w') as f:
|
265
|
+
json.dump(status_data, f, indent=2)
|
266
|
+
|
267
|
+
except Exception as e:
|
268
|
+
self.logger.error(f"Failed to save status: {e}")
|
269
|
+
|
270
|
+
def get_status_summary(self) -> Dict[str, Any]:
|
271
|
+
"""Get a summary of current status."""
|
272
|
+
with self._lock:
|
273
|
+
return {
|
274
|
+
"progress": f"{self.metrics.progress_percentage:.1f}%",
|
275
|
+
"processed": f"{self.metrics.processed_crates}/{self.metrics.total_crates}",
|
276
|
+
"success_rate": f"{self.metrics.success_rate:.1f}%",
|
277
|
+
"elapsed_time": str(self.metrics.elapsed_time),
|
278
|
+
"estimated_completion": self.metrics.estimated_completion.isoformat() if self.metrics.estimated_completion else None,
|
279
|
+
"current_operation": self.current_operation,
|
280
|
+
"current_crate": self.current_crate,
|
281
|
+
"errors_count": len(self.metrics.errors),
|
282
|
+
"warnings_count": len(self.metrics.warnings)
|
283
|
+
}
|
284
|
+
|
285
|
+
def print_status(self) -> None:
|
286
|
+
"""Print current status to console."""
|
287
|
+
summary = self.get_status_summary()
|
288
|
+
|
289
|
+
print("\n" + "="*80)
|
290
|
+
print("🚀 RUST CRATE PIPELINE - REAL-TIME STATUS")
|
291
|
+
print("="*80)
|
292
|
+
print(f"📊 Progress: {summary['progress']} ({summary['processed']} crates)")
|
293
|
+
print(f"✅ Success Rate: {summary['success_rate']}")
|
294
|
+
print(f"⏱️ Elapsed Time: {summary['elapsed_time']}")
|
295
|
+
if summary['estimated_completion']:
|
296
|
+
print(f"🎯 Estimated Completion: {summary['estimated_completion']}")
|
297
|
+
print(f"🔄 Current Operation: {summary['current_operation']}")
|
298
|
+
if summary['current_crate']:
|
299
|
+
print(f"📦 Current Crate: {summary['current_crate']}")
|
300
|
+
print(f"❌ Errors: {summary['errors_count']}")
|
301
|
+
print(f"⚠️ Warnings: {summary['warnings_count']}")
|
302
|
+
|
303
|
+
# Performance stats
|
304
|
+
if self.metrics.performance_stats:
|
305
|
+
stats = self.metrics.performance_stats
|
306
|
+
if 'avg_crate_time' in stats:
|
307
|
+
print(f"⚡ Avg Crate Time: {stats['avg_crate_time']:.2f}s")
|
308
|
+
if 'crates_per_minute' in stats:
|
309
|
+
print(f"🚀 Processing Rate: {stats['crates_per_minute']:.1f} crates/min")
|
310
|
+
if 'system_cpu_percent' in stats:
|
311
|
+
print(f"💻 System CPU: {stats['system_cpu_percent']:.1f}%")
|
312
|
+
if 'system_memory_percent' in stats:
|
313
|
+
print(f"🧠 System Memory: {stats['system_memory_percent']:.1f}%")
|
314
|
+
|
315
|
+
print("="*80)
|
316
|
+
|
317
|
+
def create_progress_bar(self, desc: str = "Processing crates") -> Optional[Any]:
|
318
|
+
"""Create a progress bar if tqdm is available."""
|
319
|
+
if not TQDM_AVAILABLE:
|
320
|
+
return None
|
321
|
+
|
322
|
+
return tqdm(
|
323
|
+
total=self.metrics.total_crates,
|
324
|
+
desc=desc,
|
325
|
+
unit="crate",
|
326
|
+
bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}]"
|
327
|
+
)
|
328
|
+
|
329
|
+
|
330
|
+
def create_monitor(total_crates: int, output_dir: str = "output") -> ProgressMonitor:
|
331
|
+
"""Create and configure a CLI-only progress monitor."""
|
332
|
+
monitor = ProgressMonitor(total_crates, output_dir)
|
333
|
+
print("✅ Real-time CLI progress monitoring enabled")
|
334
|
+
return monitor
|
rust_crate_pipeline/version.py
CHANGED
@@ -1,74 +1,74 @@
|
|
1
|
-
from typing import Dict, List, Tuple, Optional, Any
|
2
|
-
"""Version information for rust-crate-pipeline."""
|
3
|
-
|
4
|
-
__version__ = "1.3.
|
5
|
-
__version_info__ = tuple(int(x) for x in __version__.split("-")[0].split("."))
|
6
|
-
|
7
|
-
# Version history
|
8
|
-
# 1.2.5-dev.20250621 - Dev branch: experimental, not a formal
|
9
|
-
# release. Originated from v1.2.5.
|
10
|
-
# 1.2.5 - Last official release.
|
11
|
-
# 1.5.1 - Configuration Standardization Release: Model Path Consistency
|
12
|
-
# - Standardized all configuration to use GGUF model paths
|
13
|
-
# - Updated CLI defaults for --crawl4ai-model to
|
14
|
-
# ~/models/deepseek/deepseek-coder-6.7b-instruct.Q4_K_M.gguf
|
15
|
-
# - Enhanced Rule Zero alignment with transparent configuration practices
|
16
|
-
# - Updated all test files to use consistent GGUF model path references
|
17
|
-
# - Comprehensive documentation updates for proper model configuration
|
18
|
-
# - Removed inconsistent Ollama references in favor of llama-cpp-python
|
19
|
-
# - Ensured CLI help text and JSON examples reflect correct model paths
|
20
|
-
# 1.5.0 - Major Release: Enhanced Web Scraping with Crawl4AI Integration
|
21
|
-
# - Integrated Crawl4AI for advanced web scraping capabilities
|
22
|
-
# - Added JavaScript-rendered content extraction via Playwright
|
23
|
-
# - Enhanced README parsing with LLM-powered content analysis
|
24
|
-
# - New CLI options: --enable-crawl4ai, --disable-crawl4ai, --crawl4ai-model
|
25
|
-
# - Enhanced configuration with local GGUF model paths and crawl4ai_timeout
|
26
|
-
# - Comprehensive test coverage for all Crawl4AI features
|
27
|
-
# - Rule Zero compliant with full transparency and audit trails
|
28
|
-
# 1.4.0 - Major Release: Rule Zero Compliance Audit Complete
|
29
|
-
# - Completed comprehensive Rule Zero alignment audit
|
30
|
-
# - Eliminated all code redundancy and dead code
|
31
|
-
# - Achieved 100% test coverage (22/22 tests passing)
|
32
|
-
# - Refactored to pure asyncio architecture (thread-free)
|
33
|
-
# - Suppressed Pydantic deprecation warnings
|
34
|
-
# - Full production readiness with Docker support
|
35
|
-
# - Enhanced documentation with PyPI cross-references
|
36
|
-
# - Certified Rule Zero compliance across all four principles
|
37
|
-
# 1.3.1 - Bug Fix Release: Crawl4AI Integration Cleanup
|
38
|
-
# - Fixed CSS selector syntax errors in Crawl4AI integration
|
39
|
-
# - Cleaned up duplicate and obsolete test files
|
40
|
-
# - Resolved import conflicts between workspace and integration configs
|
41
|
-
# - Improved error handling in enhanced scraping module
|
42
|
-
# - Standardized on direct llama.cpp approach (removed Ollama dependencies)
|
43
|
-
# - Enhanced Rule Zero compliance with transparent cleanup process
|
44
|
-
# - Fixed type annotation compatibility issues
|
45
|
-
# - Fixed Python 3.9 compatibility for type annotations
|
46
|
-
# - Updated dict[str, Any] to "dict[str, Any]" format
|
47
|
-
# - Fixed Union type expressions in conditional imports
|
48
|
-
# - Resolved IDE linter errors in network.py, pipeline.py, and production_config.py
|
49
|
-
# - Improved code quality and maintainability
|
50
|
-
# 1.3.0 - Quality & Integration Release: Comprehensive code quality improvements
|
51
|
-
# - Fixed all critical PEP 8 violations (F821, F811, E114)
|
52
|
-
# - Enhanced error handling with graceful dependency fallbacks
|
53
|
-
# - Improved module integration and import path resolution
|
54
|
-
# - Added comprehensive test validation (21/21 tests passing)
|
55
|
-
# - Enhanced async support and Unicode handling
|
56
|
-
# - Production-ready CLI interfaces with robust error handling
|
57
|
-
# - Full Rule Zero compliance validation
|
58
|
-
# 1.2.0 - Major release: Production-ready, cleaned codebase
|
59
|
-
# - Unified documentation into single comprehensive README
|
60
|
-
# - Removed all non-essential development and test files
|
61
|
-
# - Optimized for PyPI distribution and Docker deployment
|
62
|
-
# - Enhanced GitHub token integration and setup
|
63
|
-
# 1.1.2 - Production release: Cleaned up non-essential files
|
64
|
-
# - Unified documentation into single README
|
65
|
-
# - Optimized for PyPI distribution
|
66
|
-
# 1.1.1 - Bug fix: Added missing python-dateutil dependency
|
67
|
-
# - Fixed relativedelta import error
|
68
|
-
# 1.1.0 - Updated author and contact information
|
69
|
-
# - Enhanced package configuration
|
70
|
-
# 0.1.0 - Initial release
|
71
|
-
# - Core pipeline functionality
|
72
|
-
# - AI-powered metadata enrichment
|
73
|
-
# - Dependency analysis
|
74
|
-
# - PyPI package setup
|
1
|
+
from typing import Dict, List, Tuple, Optional, Any
|
2
|
+
"""Version information for rust-crate-pipeline."""
|
3
|
+
|
4
|
+
__version__ = "1.3.4"
|
5
|
+
__version_info__ = tuple(int(x) for x in __version__.split("-")[0].split("."))
|
6
|
+
|
7
|
+
# Version history
|
8
|
+
# 1.2.5-dev.20250621 - Dev branch: experimental, not a formal
|
9
|
+
# release. Originated from v1.2.5.
|
10
|
+
# 1.2.5 - Last official release.
|
11
|
+
# 1.5.1 - Configuration Standardization Release: Model Path Consistency
|
12
|
+
# - Standardized all configuration to use GGUF model paths
|
13
|
+
# - Updated CLI defaults for --crawl4ai-model to
|
14
|
+
# ~/models/deepseek/deepseek-coder-6.7b-instruct.Q4_K_M.gguf
|
15
|
+
# - Enhanced Rule Zero alignment with transparent configuration practices
|
16
|
+
# - Updated all test files to use consistent GGUF model path references
|
17
|
+
# - Comprehensive documentation updates for proper model configuration
|
18
|
+
# - Removed inconsistent Ollama references in favor of llama-cpp-python
|
19
|
+
# - Ensured CLI help text and JSON examples reflect correct model paths
|
20
|
+
# 1.5.0 - Major Release: Enhanced Web Scraping with Crawl4AI Integration
|
21
|
+
# - Integrated Crawl4AI for advanced web scraping capabilities
|
22
|
+
# - Added JavaScript-rendered content extraction via Playwright
|
23
|
+
# - Enhanced README parsing with LLM-powered content analysis
|
24
|
+
# - New CLI options: --enable-crawl4ai, --disable-crawl4ai, --crawl4ai-model
|
25
|
+
# - Enhanced configuration with local GGUF model paths and crawl4ai_timeout
|
26
|
+
# - Comprehensive test coverage for all Crawl4AI features
|
27
|
+
# - Rule Zero compliant with full transparency and audit trails
|
28
|
+
# 1.4.0 - Major Release: Rule Zero Compliance Audit Complete
|
29
|
+
# - Completed comprehensive Rule Zero alignment audit
|
30
|
+
# - Eliminated all code redundancy and dead code
|
31
|
+
# - Achieved 100% test coverage (22/22 tests passing)
|
32
|
+
# - Refactored to pure asyncio architecture (thread-free)
|
33
|
+
# - Suppressed Pydantic deprecation warnings
|
34
|
+
# - Full production readiness with Docker support
|
35
|
+
# - Enhanced documentation with PyPI cross-references
|
36
|
+
# - Certified Rule Zero compliance across all four principles
|
37
|
+
# 1.3.1 - Bug Fix Release: Crawl4AI Integration Cleanup
|
38
|
+
# - Fixed CSS selector syntax errors in Crawl4AI integration
|
39
|
+
# - Cleaned up duplicate and obsolete test files
|
40
|
+
# - Resolved import conflicts between workspace and integration configs
|
41
|
+
# - Improved error handling in enhanced scraping module
|
42
|
+
# - Standardized on direct llama.cpp approach (removed Ollama dependencies)
|
43
|
+
# - Enhanced Rule Zero compliance with transparent cleanup process
|
44
|
+
# - Fixed type annotation compatibility issues
|
45
|
+
# - Fixed Python 3.9 compatibility for type annotations
|
46
|
+
# - Updated dict[str, Any] to "dict[str, Any]" format
|
47
|
+
# - Fixed Union type expressions in conditional imports
|
48
|
+
# - Resolved IDE linter errors in network.py, pipeline.py, and production_config.py
|
49
|
+
# - Improved code quality and maintainability
|
50
|
+
# 1.3.0 - Quality & Integration Release: Comprehensive code quality improvements
|
51
|
+
# - Fixed all critical PEP 8 violations (F821, F811, E114)
|
52
|
+
# - Enhanced error handling with graceful dependency fallbacks
|
53
|
+
# - Improved module integration and import path resolution
|
54
|
+
# - Added comprehensive test validation (21/21 tests passing)
|
55
|
+
# - Enhanced async support and Unicode handling
|
56
|
+
# - Production-ready CLI interfaces with robust error handling
|
57
|
+
# - Full Rule Zero compliance validation
|
58
|
+
# 1.2.0 - Major release: Production-ready, cleaned codebase
|
59
|
+
# - Unified documentation into single comprehensive README
|
60
|
+
# - Removed all non-essential development and test files
|
61
|
+
# - Optimized for PyPI distribution and Docker deployment
|
62
|
+
# - Enhanced GitHub token integration and setup
|
63
|
+
# 1.1.2 - Production release: Cleaned up non-essential files
|
64
|
+
# - Unified documentation into single README
|
65
|
+
# - Optimized for PyPI distribution
|
66
|
+
# 1.1.1 - Bug fix: Added missing python-dateutil dependency
|
67
|
+
# - Fixed relativedelta import error
|
68
|
+
# 1.1.0 - Updated author and contact information
|
69
|
+
# - Enhanced package configuration
|
70
|
+
# 0.1.0 - Initial release
|
71
|
+
# - Core pipeline functionality
|
72
|
+
# - AI-powered metadata enrichment
|
73
|
+
# - Dependency analysis
|
74
|
+
# - PyPI package setup
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: rust-crate-pipeline
|
3
|
-
Version: 1.3.
|
3
|
+
Version: 1.3.4
|
4
4
|
Summary: A comprehensive system for gathering, enriching, and analyzing metadata for Rust crates using AI-powered insights
|
5
5
|
Home-page: https://github.com/Superuser666-Sigil/SigilDERG-Data_Production
|
6
6
|
Author: SuperUser666-Sigil
|
@@ -8,12 +8,13 @@ rust_crate_pipeline/crate_analysis.py,sha256=GsoXemJ9VFyAbb4Sm5gY5ToTqNtOA4pI38A
|
|
8
8
|
rust_crate_pipeline/crate_list.txt,sha256=W3NxDtxvihyKp9SN85FYXX6p8Hh49IFih1M4-c-CynM,4334
|
9
9
|
rust_crate_pipeline/github_token_checker.py,sha256=COXXS9uoLV9WYIcT02C-bV5uH3fa9D9HJImc07vMjLs,3766
|
10
10
|
rust_crate_pipeline/main.py,sha256=iGYEAYvXkoFFvaA6DIVGiUL3wLhiCzatB6Fvf-Yrj2A,18858
|
11
|
-
rust_crate_pipeline/network.py,sha256=
|
11
|
+
rust_crate_pipeline/network.py,sha256=khyjfOplaDvMxLWGB-JbPQnc27ZfozKGYBFw2b3BScM,12834
|
12
12
|
rust_crate_pipeline/pipeline.py,sha256=YN6PEhg0Si_oo6-Wtm_PviytzWzpQupTPC2e4L1F7XE,16349
|
13
13
|
rust_crate_pipeline/production_config.py,sha256=uWylP9AIZZx7-9aT4sFmAKEEW9miJDxaiek8VE6WP-0,2372
|
14
|
+
rust_crate_pipeline/progress_monitor.py,sha256=5K9KP-Xggi1JEINfRmq2W-wGUHtNIBTcocpDtB1t8iM,13743
|
14
15
|
rust_crate_pipeline/unified_llm_processor.py,sha256=eo7KotNuqwc7_hgpFm18QLokFoufFslnvi8TnDsSYEg,25064
|
15
16
|
rust_crate_pipeline/unified_pipeline.py,sha256=2yglmXVlQfSkVq0HVTPonDee6VxWaQWZw0X2l4lLBGw,23704
|
16
|
-
rust_crate_pipeline/version.py,sha256=
|
17
|
+
rust_crate_pipeline/version.py,sha256=tEnYb8C6sG13xp0Nfedzjn8sJo94OXAq0bfReOnGYDY,4415
|
17
18
|
rust_crate_pipeline/core/__init__.py,sha256=Sq4HWdANGqoYln7JdCog7m3BsGeR3tHdseeflvNetoQ,509
|
18
19
|
rust_crate_pipeline/core/canon_registry.py,sha256=36tmt_wU6-kSyZnGfh53N64C7E3G-QR7GFbr9epj4zg,4700
|
19
20
|
rust_crate_pipeline/core/irl_engine.py,sha256=QRZUdkN24W9XutLkj8JDplEz6FmnquUrwKsl0s2zRr4,10491
|
@@ -22,9 +23,9 @@ rust_crate_pipeline/scraping/__init__.py,sha256=ySkTRg7nIxgcbHJQ3L1XzcrOo281NZu0
|
|
22
23
|
rust_crate_pipeline/scraping/unified_scraper.py,sha256=ZE2gkc0vQ3BOLdSX_IV-kMe8QAm2Av4M7VqpkxEKyT4,9965
|
23
24
|
rust_crate_pipeline/utils/file_utils.py,sha256=tMaCPy7ghs9x4Hxu_sviX8MXU2sBjNvohUrvt4MejoM,2853
|
24
25
|
rust_crate_pipeline/utils/logging_utils.py,sha256=e5jG0Yd6k3exgAdbVca46kWADJ_Qz8UJ3yEJzwTqPyI,2452
|
25
|
-
rust_crate_pipeline-1.3.
|
26
|
-
rust_crate_pipeline-1.3.
|
27
|
-
rust_crate_pipeline-1.3.
|
28
|
-
rust_crate_pipeline-1.3.
|
29
|
-
rust_crate_pipeline-1.3.
|
30
|
-
rust_crate_pipeline-1.3.
|
26
|
+
rust_crate_pipeline-1.3.4.dist-info/licenses/LICENSE,sha256=tpd4XNpbssrSx9-iErATOLrOh0ivNPfO2I5MAPUpats,1088
|
27
|
+
rust_crate_pipeline-1.3.4.dist-info/METADATA,sha256=HS2WqdbGdgq5XaNm2RlIXXk0qLsy-H7MwtiKJyz7hsc,11254
|
28
|
+
rust_crate_pipeline-1.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
29
|
+
rust_crate_pipeline-1.3.4.dist-info/entry_points.txt,sha256=9Rr_IRuFRIridXxUSdEJbB3ba0NnpEfKmknZXFdYRC0,70
|
30
|
+
rust_crate_pipeline-1.3.4.dist-info/top_level.txt,sha256=GUdB7RyxHLhijQxui_KTy3B8p_L2APui9C6RYa0FuaE,20
|
31
|
+
rust_crate_pipeline-1.3.4.dist-info/RECORD,,
|
File without changes
|
{rust_crate_pipeline-1.3.2.dist-info → rust_crate_pipeline-1.3.4.dist-info}/entry_points.txt
RENAMED
File without changes
|
{rust_crate_pipeline-1.3.2.dist-info → rust_crate_pipeline-1.3.4.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
File without changes
|