medicafe 0.250816.0__py3-none-any.whl → 0.250818.0__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.
- MediBot/MediBot.py +5 -3
- MediBot/MediBot_Preprocessor_lib.py +31 -38
- MediBot/MediBot_UI.py +51 -28
- MediBot/__init__.py +1 -1
- MediBot/update_medicafe.py +202 -727
- MediCafe/__init__.py +1 -1
- MediLink/__init__.py +1 -1
- {medicafe-0.250816.0.dist-info → medicafe-0.250818.0.dist-info}/METADATA +1 -1
- {medicafe-0.250816.0.dist-info → medicafe-0.250818.0.dist-info}/RECORD +13 -13
- {medicafe-0.250816.0.dist-info → medicafe-0.250818.0.dist-info}/LICENSE +0 -0
- {medicafe-0.250816.0.dist-info → medicafe-0.250818.0.dist-info}/WHEEL +0 -0
- {medicafe-0.250816.0.dist-info → medicafe-0.250818.0.dist-info}/entry_points.txt +0 -0
- {medicafe-0.250816.0.dist-info → medicafe-0.250818.0.dist-info}/top_level.txt +0 -0
MediBot/update_medicafe.py
CHANGED
@@ -1,770 +1,245 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
# 2. IMPROVED CACHE CLEARING: Added aggressive pkg_resources cache clearing for
|
8
|
-
# XP SP3 + Python 3.4.4 compatibility
|
9
|
-
# 3. FIXED VERIFICATION: Improved version comparison logic to properly detect
|
10
|
-
# upgrade success/failure instead of considering mismatched versions as "successful"
|
11
|
-
# 4. XP TIMING: Increased delays from 2-3s to 3-5s to accommodate XP's slower
|
12
|
-
# file system operations
|
13
|
-
# 5. ERROR HANDLING: Standardized error messages and exit codes throughout
|
14
|
-
#
|
15
|
-
# DEPLOYMENT NOTES:
|
16
|
-
# - This file exists in two locations for legacy compatibility:
|
17
|
-
# * MediBot/update_medicafe.py (primary)
|
18
|
-
# * MediBot/MediBot/update_medicafe.py (legacy fallback)
|
19
|
-
# - Both files must remain identical - the batch script copies between them
|
20
|
-
# - Designed for Windows XP SP3 + Python 3.4.4 + ASCII-only environments
|
21
|
-
#
|
22
|
-
import subprocess, sys, time, platform, os, shutil, random
|
23
|
-
|
24
|
-
# Safe import for pkg_resources with fallback
|
25
|
-
try:
|
26
|
-
import pkg_resources
|
27
|
-
except ImportError:
|
28
|
-
pkg_resources = None
|
29
|
-
print("Warning: pkg_resources not available. Some functionality may be limited.")
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# update_medicafe.py
|
3
|
+
# Script Version: 2.0.0 (clean 3-try updater)
|
4
|
+
# Target environment: Windows XP SP3 + Python 3.4.4 (ASCII-only)
|
5
|
+
|
6
|
+
import sys, os, time, subprocess, platform
|
30
7
|
|
31
|
-
# Safe import for requests with fallback
|
32
8
|
try:
|
33
9
|
import requests
|
34
|
-
except
|
10
|
+
except Exception:
|
35
11
|
requests = None
|
36
|
-
print("Warning: requests module not available. Some functionality may be limited.")
|
37
12
|
|
38
|
-
# Safe tqdm import with fallback
|
39
13
|
try:
|
40
|
-
|
41
|
-
def tqdm(iterable, **kwargs):
|
42
|
-
return _real_tqdm(iterable, **kwargs)
|
43
|
-
TQDM_AVAILABLE = True
|
14
|
+
import pkg_resources
|
44
15
|
except Exception:
|
45
|
-
|
46
|
-
def tqdm(iterable, **kwargs):
|
47
|
-
return iterable
|
48
|
-
|
49
|
-
# Initialize console output
|
50
|
-
print("="*60)
|
51
|
-
print("MediCafe Update Started")
|
52
|
-
print("Timestamp: {}".format(time.strftime("%Y-%m-%d %H:%M:%S")))
|
53
|
-
print("Python Version: {}".format(sys.version))
|
54
|
-
print("Platform: {}".format(platform.platform()))
|
55
|
-
print("="*60)
|
56
|
-
|
57
|
-
# Global debug mode toggle (defaults to streamlined mode)
|
58
|
-
DEBUG_MODE = False
|
59
|
-
|
60
|
-
def debug_step(step_number, step_title, message=""):
|
61
|
-
"""Print step information. Only show debug output in debug mode."""
|
62
|
-
if DEBUG_MODE:
|
63
|
-
print("\n" + "="*60)
|
64
|
-
print("STEP {}: {}".format(step_number, step_title))
|
65
|
-
print("="*60)
|
66
|
-
if message:
|
67
|
-
print(message)
|
68
|
-
|
69
|
-
# In debug mode, optionally pause for key steps
|
70
|
-
if step_number in [1, 7, 8]:
|
71
|
-
print("\nPress Enter to continue...")
|
72
|
-
try:
|
73
|
-
input()
|
74
|
-
except:
|
75
|
-
pass
|
76
|
-
else:
|
77
|
-
print("\nContinuing...")
|
78
|
-
else:
|
79
|
-
# Streamlined mode: no debug output, no pauses
|
80
|
-
pass
|
16
|
+
pkg_resources = None
|
81
17
|
|
82
|
-
def print_status(message, status_type="INFO"):
|
83
|
-
"""Print formatted status messages with ASCII-only visual indicators."""
|
84
|
-
if status_type == "SUCCESS":
|
85
|
-
print("\n" + "="*60)
|
86
|
-
print("[SUCCESS] {}".format(message))
|
87
|
-
print("="*60)
|
88
|
-
elif status_type == "ERROR":
|
89
|
-
print("\n" + "="*60)
|
90
|
-
print("[ERROR] {}".format(message))
|
91
|
-
print("="*60)
|
92
|
-
elif status_type == "WARNING":
|
93
|
-
print("\n" + "-"*60)
|
94
|
-
print("[WARNING] {}".format(message))
|
95
|
-
print("-"*60)
|
96
|
-
elif status_type == "INFO":
|
97
|
-
print("\n" + "-"*60)
|
98
|
-
print("[INFO] {}".format(message))
|
99
|
-
print("-"*60)
|
100
|
-
else:
|
101
|
-
print(message)
|
102
18
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
print("Final Status: {}".format(message))
|
111
|
-
|
112
|
-
print("\nExiting in 5 seconds...")
|
113
|
-
time.sleep(5)
|
114
|
-
sys.exit(0 if success else 1)
|
115
|
-
|
116
|
-
def clear_pkg_resources_cache():
|
117
|
-
"""
|
118
|
-
Aggressively clear pkg_resources cache for XP compatibility.
|
119
|
-
|
120
|
-
CRITICAL FIX (2025): On XP SP3 + Python 3.4.4, pkg_resources caching is particularly
|
121
|
-
problematic and can cause version detection to fail after package updates. This function
|
122
|
-
aggressively clears all known cache locations to ensure reliable version detection.
|
123
|
-
|
124
|
-
Called before every version check to prevent stale cache issues.
|
125
|
-
"""
|
126
|
-
if not pkg_resources:
|
127
|
-
return False
|
128
|
-
|
19
|
+
SCRIPT_NAME = "update_medicafe.py"
|
20
|
+
SCRIPT_VERSION = "2.0.0"
|
21
|
+
PACKAGE_NAME = "medicafe"
|
22
|
+
|
23
|
+
|
24
|
+
# ---------- UI helpers (ASCII-only) ----------
|
25
|
+
def _line(char, width):
|
129
26
|
try:
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
27
|
+
return char * width
|
28
|
+
except Exception:
|
29
|
+
return char * 60
|
30
|
+
|
31
|
+
|
32
|
+
def print_banner(title):
|
33
|
+
width = 60
|
34
|
+
print(_line("=", width))
|
35
|
+
print(title)
|
36
|
+
print(_line("=", width))
|
37
|
+
|
38
|
+
|
39
|
+
def print_section(title):
|
40
|
+
width = 60
|
41
|
+
print("\n" + _line("-", width))
|
42
|
+
print(title)
|
43
|
+
print(_line("-", width))
|
44
|
+
|
45
|
+
|
46
|
+
def print_status(kind, message):
|
47
|
+
label = "[{}]".format(kind)
|
48
|
+
print("{} {}".format(label, message))
|
49
|
+
|
50
|
+
|
51
|
+
# ---------- Version utilities ----------
|
52
|
+
def compare_versions(version1, version2):
|
53
|
+
try:
|
54
|
+
v1_parts = list(map(int, version1.split(".")))
|
55
|
+
v2_parts = list(map(int, version2.split(".")))
|
56
|
+
return (v1_parts > v2_parts) - (v1_parts < v2_parts)
|
57
|
+
except Exception:
|
58
|
+
# Fall back to string compare if unexpected formats
|
59
|
+
return (version1 > version2) - (version1 < version2)
|
60
|
+
|
148
61
|
|
149
62
|
def get_installed_version(package):
|
150
|
-
"""Get installed version with improved cache clearing for XP."""
|
151
63
|
try:
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
try:
|
158
|
-
version = pkg_resources.get_distribution(package).version
|
159
|
-
return version
|
160
|
-
except pkg_resources.DistributionNotFound:
|
161
|
-
return None
|
162
|
-
except Exception as e:
|
163
|
-
print("Warning: pkg_resources failed: {}".format(e))
|
164
|
-
# Fall through to pip method
|
165
|
-
|
166
|
-
# Fallback to pip show (may fail on Python 3.4 due to packaging issues)
|
167
|
-
process = subprocess.Popen(
|
168
|
-
[sys.executable, '-m', 'pip', 'show', package],
|
169
|
-
stdout=subprocess.PIPE,
|
170
|
-
stderr=subprocess.PIPE
|
171
|
-
)
|
172
|
-
stdout, stderr = process.communicate()
|
173
|
-
if process.returncode == 0:
|
174
|
-
for line in stdout.decode().splitlines():
|
64
|
+
proc = subprocess.Popen([sys.executable, '-m', 'pip', 'show', package],
|
65
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
66
|
+
out, err = proc.communicate()
|
67
|
+
if proc.returncode == 0:
|
68
|
+
for line in out.decode().splitlines():
|
175
69
|
if line.startswith("Version:"):
|
176
70
|
return line.split(":", 1)[1].strip()
|
177
|
-
|
178
|
-
|
179
|
-
print("Error retrieving installed version: {}".format(e))
|
180
|
-
return None
|
71
|
+
except Exception:
|
72
|
+
pass
|
181
73
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
Now simply returns the latest version immediately without double-checking.
|
193
|
-
"""
|
74
|
+
if pkg_resources:
|
75
|
+
try:
|
76
|
+
return pkg_resources.get_distribution(package).version
|
77
|
+
except Exception:
|
78
|
+
return None
|
79
|
+
return None
|
80
|
+
|
81
|
+
|
82
|
+
def get_latest_version(package, retries):
|
194
83
|
if not requests:
|
195
|
-
print("Error: requests module not available. Cannot fetch latest version.")
|
196
84
|
return None
|
197
|
-
|
85
|
+
|
86
|
+
headers = {
|
87
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
88
|
+
'Pragma': 'no-cache',
|
89
|
+
'Expires': '0',
|
90
|
+
'User-Agent': 'MediCafe-Updater/2.0.0'
|
91
|
+
}
|
92
|
+
|
93
|
+
last = None
|
198
94
|
for attempt in range(1, retries + 1):
|
199
95
|
try:
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
96
|
+
url = "https://pypi.org/pypi/{}/json?t={}".format(package, int(time.time()))
|
97
|
+
resp = requests.get(url, headers=headers, timeout=10)
|
98
|
+
resp.raise_for_status()
|
99
|
+
data = resp.json()
|
100
|
+
latest = data.get('info', {}).get('version')
|
101
|
+
if not latest:
|
102
|
+
raise Exception("Malformed PyPI response")
|
103
|
+
|
104
|
+
# Pragmatic double-fetch-if-equal to mitigate CDN staleness
|
105
|
+
if last and latest == last:
|
106
|
+
return latest
|
107
|
+
last = latest
|
108
|
+
if attempt == retries:
|
109
|
+
return latest
|
110
|
+
# If we just fetched same as before and it's equal to current installed, refetch once more quickly
|
111
|
+
time.sleep(1)
|
112
|
+
except Exception:
|
113
|
+
if attempt == retries:
|
114
|
+
return None
|
115
|
+
time.sleep(1)
|
116
|
+
|
117
|
+
return last
|
118
|
+
|
219
119
|
|
220
120
|
def check_internet_connection():
|
121
|
+
if not requests:
|
122
|
+
return False
|
221
123
|
try:
|
222
124
|
requests.get("http://www.google.com", timeout=5)
|
223
125
|
return True
|
224
|
-
except
|
126
|
+
except Exception:
|
225
127
|
return False
|
226
128
|
|
227
|
-
def clear_python_cache(workspace_path=None):
|
228
|
-
"""
|
229
|
-
Clear Python bytecode cache files to prevent import issues after updates.
|
230
|
-
|
231
|
-
Args:
|
232
|
-
workspace_path (str, optional): Path to the workspace root. If None,
|
233
|
-
will attempt to detect automatically.
|
234
|
-
|
235
|
-
Returns:
|
236
|
-
bool: True if cache was cleared successfully, False otherwise
|
237
|
-
"""
|
238
|
-
try:
|
239
|
-
print_status("Clearing Python bytecode cache...", "INFO")
|
240
|
-
|
241
|
-
# If no workspace path provided, try to detect it
|
242
|
-
if not workspace_path:
|
243
|
-
# Try to find the MediCafe workspace by looking for common directories
|
244
|
-
current_dir = os.getcwd()
|
245
|
-
potential_paths = [
|
246
|
-
current_dir,
|
247
|
-
os.path.dirname(current_dir),
|
248
|
-
os.path.join(current_dir, '..'),
|
249
|
-
os.path.join(current_dir, '..', '..')
|
250
|
-
]
|
251
|
-
|
252
|
-
for path in potential_paths:
|
253
|
-
if os.path.exists(os.path.join(path, 'MediCafe')) and \
|
254
|
-
os.path.exists(os.path.join(path, 'MediBot')) and \
|
255
|
-
os.path.exists(os.path.join(path, 'MediLink')):
|
256
|
-
workspace_path = path
|
257
|
-
break
|
258
|
-
|
259
|
-
if not workspace_path:
|
260
|
-
print_status("Could not detect workspace path. Cache clearing skipped.", "WARNING")
|
261
|
-
return False
|
262
|
-
|
263
|
-
print("Workspace path: {}".format(workspace_path))
|
264
|
-
|
265
|
-
# Directories to clear cache from
|
266
|
-
cache_dirs = [
|
267
|
-
os.path.join(workspace_path, 'MediCafe'),
|
268
|
-
os.path.join(workspace_path, 'MediBot'),
|
269
|
-
os.path.join(workspace_path, 'MediLink'),
|
270
|
-
workspace_path # Root workspace
|
271
|
-
]
|
272
|
-
|
273
|
-
cleared_count = 0
|
274
|
-
# First, remove __pycache__ directories (these are few, so prints are acceptable)
|
275
|
-
for cache_dir in cache_dirs:
|
276
|
-
if os.path.exists(cache_dir):
|
277
|
-
pycache_path = os.path.join(cache_dir, '__pycache__')
|
278
|
-
if os.path.exists(pycache_path):
|
279
|
-
try:
|
280
|
-
shutil.rmtree(pycache_path)
|
281
|
-
print("Cleared cache: {}".format(pycache_path))
|
282
|
-
cleared_count += 1
|
283
|
-
except Exception as e:
|
284
|
-
print("Warning: Could not clear cache at {}: {}".format(pycache_path, e))
|
285
|
-
|
286
|
-
# Next, collect all .pyc files to provide a clean progress indicator
|
287
|
-
pyc_files = []
|
288
|
-
for cache_dir in cache_dirs:
|
289
|
-
if os.path.exists(cache_dir):
|
290
|
-
for root, dirs, files in os.walk(cache_dir):
|
291
|
-
for file in files:
|
292
|
-
if file.endswith('.pyc'):
|
293
|
-
pyc_files.append(os.path.join(root, file))
|
294
|
-
|
295
|
-
# Remove .pyc files with a progress bar (tqdm if available, otherwise a single-line counter)
|
296
|
-
if pyc_files:
|
297
|
-
total_files = len(pyc_files)
|
298
|
-
removed_files = 0
|
299
|
-
|
300
|
-
if TQDM_AVAILABLE:
|
301
|
-
for file_path in tqdm(pyc_files, desc="Removing .pyc files", unit="file"):
|
302
|
-
try:
|
303
|
-
os.remove(file_path)
|
304
|
-
cleared_count += 1
|
305
|
-
removed_files += 1
|
306
|
-
except Exception as e:
|
307
|
-
print("Warning: Could not remove .pyc file {}: {}".format(os.path.basename(file_path), e))
|
308
|
-
else:
|
309
|
-
# Minimal, XP-safe single-line progress indicator
|
310
|
-
for file_path in pyc_files:
|
311
|
-
try:
|
312
|
-
os.remove(file_path)
|
313
|
-
cleared_count += 1
|
314
|
-
removed_files += 1
|
315
|
-
except Exception as e:
|
316
|
-
print("Warning: Could not remove .pyc file {}: {}".format(os.path.basename(file_path), e))
|
317
|
-
# Update progress on one line
|
318
|
-
try:
|
319
|
-
sys.stdout.write("\rRemoving .pyc files: {}/{}".format(removed_files, total_files))
|
320
|
-
sys.stdout.flush()
|
321
|
-
except Exception:
|
322
|
-
pass
|
323
|
-
# Finish the line after completion
|
324
|
-
try:
|
325
|
-
sys.stdout.write("\n")
|
326
|
-
sys.stdout.flush()
|
327
|
-
except Exception:
|
328
|
-
pass
|
329
|
-
|
330
|
-
if cleared_count > 0:
|
331
|
-
print_status("Successfully cleared {} cache items".format(cleared_count), "SUCCESS")
|
332
|
-
return True
|
333
|
-
else:
|
334
|
-
print_status("No cache files found to clear", "INFO")
|
335
|
-
return True
|
336
|
-
|
337
|
-
except Exception as e:
|
338
|
-
print_status("Error clearing cache: {}".format(e), "ERROR")
|
339
|
-
return False
|
340
129
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
def upgrade_package(package, retries=4, delay=2, target_version=None):
|
347
|
-
"""
|
348
|
-
Attempts to upgrade the package multiple times with escalating techniques.
|
349
|
-
|
350
|
-
CRITICAL FIX (2025): Improved version verification logic to properly detect upgrade
|
351
|
-
success/failure. The original code would consider an upgrade "successful" even when
|
352
|
-
there was a version mismatch, leading to inconsistent behavior.
|
353
|
-
|
354
|
-
Now properly compares expected vs actual versions and only returns True if the
|
355
|
-
upgrade actually succeeded with the correct version.
|
356
|
-
"""
|
357
|
-
if not check_internet_connection():
|
358
|
-
print_status("No internet connection detected. Please check your internet connection and try again.", "ERROR")
|
359
|
-
print_final_result(False, "No internet connection available")
|
360
|
-
|
361
|
-
# Light verbosity: show pinned target once
|
362
|
-
if target_version:
|
363
|
-
print("Pinned target version: {}".format(target_version))
|
364
|
-
|
365
|
-
def get_installed_version_fresh(package):
|
366
|
-
"""Get installed version using a fresh subprocess to avoid pkg_resources cache issues."""
|
367
|
-
try:
|
368
|
-
# Clear cache first
|
369
|
-
clear_pkg_resources_cache()
|
370
|
-
|
371
|
-
# First try pip show
|
372
|
-
process = subprocess.Popen(
|
373
|
-
[sys.executable, '-m', 'pip', 'show', package],
|
374
|
-
stdout=subprocess.PIPE,
|
375
|
-
stderr=subprocess.PIPE
|
376
|
-
)
|
377
|
-
stdout, stderr = process.communicate()
|
378
|
-
if process.returncode == 0:
|
379
|
-
for line in stdout.decode().splitlines():
|
380
|
-
if line.startswith("Version:"):
|
381
|
-
return line.split(":", 1)[1].strip()
|
382
|
-
|
383
|
-
# If pip show fails, try pkg_resources in a fresh subprocess
|
384
|
-
try:
|
385
|
-
import subprocess
|
386
|
-
process = subprocess.Popen(
|
387
|
-
[sys.executable, '-c', 'import pkg_resources; print(pkg_resources.get_distribution("{}").version)'.format(package)],
|
388
|
-
stdout=subprocess.PIPE,
|
389
|
-
stderr=subprocess.PIPE
|
390
|
-
)
|
391
|
-
stdout, stderr = process.communicate()
|
392
|
-
if process.returncode == 0:
|
393
|
-
return stdout.decode().strip()
|
394
|
-
except Exception:
|
395
|
-
pass
|
396
|
-
|
397
|
-
return None
|
398
|
-
except Exception as e:
|
399
|
-
print("Warning: Could not get fresh version: {}".format(e))
|
400
|
-
return None
|
130
|
+
# ---------- Upgrade logic (3 attempts, minimal delays) ----------
|
131
|
+
def run_pip_install(args):
|
132
|
+
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
133
|
+
out, err = proc.communicate()
|
134
|
+
return proc.returncode, out.decode(), err.decode()
|
401
135
|
|
402
|
-
def try_upgrade_with_strategy(attempt, strategy_name, cmd_args):
|
403
|
-
"""Try upgrade with specific strategy and return success status."""
|
404
|
-
print("Attempt {}/{}: Using {} strategy...".format(attempt, retries, strategy_name))
|
405
|
-
|
406
|
-
pkg_spec = package
|
407
|
-
if target_version:
|
408
|
-
pkg_spec = "{}=={}".format(package, target_version)
|
409
|
-
|
410
|
-
cmd = [sys.executable, '-m', 'pip', 'install'] + cmd_args + [pkg_spec]
|
411
|
-
|
412
|
-
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
413
|
-
stdout, stderr = process.communicate()
|
414
|
-
|
415
|
-
if process.returncode == 0:
|
416
|
-
print(stdout.decode().strip())
|
417
|
-
# Add longer delay to allow file system and package metadata to settle
|
418
|
-
# FIXED: Increased delay for XP compatibility
|
419
|
-
print("Waiting for package metadata to update...")
|
420
|
-
time.sleep(5) # Increased from 3 to 5 seconds
|
421
|
-
|
422
|
-
# Try multiple times to get the new version with increasing delays
|
423
|
-
new_version = None
|
424
|
-
for retry in range(3):
|
425
|
-
# Clear pkg_resources cache before each attempt
|
426
|
-
clear_pkg_resources_cache()
|
427
|
-
|
428
|
-
new_version = get_installed_version_fresh(package)
|
429
|
-
if new_version:
|
430
|
-
print("Detected new version: {}".format(new_version))
|
431
|
-
break
|
432
|
-
print("Version detection attempt {} failed, retrying...".format(retry + 1))
|
433
|
-
time.sleep(3) # Increased from 2 to 3 seconds
|
434
|
-
|
435
|
-
expected_version = target_version or get_latest_version(package)
|
436
|
-
|
437
|
-
# FIXED: Improved version verification logic
|
438
|
-
if expected_version and new_version:
|
439
|
-
version_comparison = compare_versions(new_version, expected_version)
|
440
|
-
if version_comparison >= 0:
|
441
|
-
print_status("Attempt {}: Upgrade succeeded with {}!".format(attempt, strategy_name), "SUCCESS")
|
442
|
-
return True
|
443
|
-
else:
|
444
|
-
print_status("Upgrade failed: Version mismatch. Current: {} Expected: {}".format(
|
445
|
-
new_version, expected_version), "ERROR")
|
446
|
-
return False
|
447
|
-
elif new_version:
|
448
|
-
# If we got a new version but can't verify expected, still consider success
|
449
|
-
print_status("Upgrade succeeded but version verification unclear. New version: {}".format(new_version), "WARNING")
|
450
|
-
return True
|
451
|
-
else:
|
452
|
-
print_status("Upgrade incomplete. Could not detect new version.", "ERROR")
|
453
|
-
return False
|
454
|
-
else:
|
455
|
-
print(stderr.decode().strip())
|
456
|
-
print_status("Attempt {}: Upgrade failed with {}.".format(attempt, strategy_name), "WARNING")
|
457
|
-
return False
|
458
|
-
|
459
|
-
# Define escalation strategies for each attempt
|
460
|
-
strategies = {
|
461
|
-
1: [
|
462
|
-
("Gentle Upgrade", ['--upgrade', '--no-deps', '--no-cache-dir', '--disable-pip-version-check', '-q']),
|
463
|
-
("Force Reinstall", ['--upgrade', '--force-reinstall', '--no-cache-dir', '--disable-pip-version-check', '-q'])
|
464
|
-
],
|
465
|
-
2: [
|
466
|
-
("Clean Install", ['--upgrade', '--force-reinstall', '--no-cache-dir', '--disable-pip-version-check', '--ignore-installed', '-q']),
|
467
|
-
("User Install", ['--upgrade', '--user', '--no-cache-dir', '--disable-pip-version-check', '-q'])
|
468
|
-
],
|
469
|
-
3: [
|
470
|
-
("Aggressive Clean", ['--upgrade', '--force-reinstall', '--no-cache-dir', '--disable-pip-version-check', '--ignore-installed', '--no-deps', '-q']),
|
471
|
-
("Pre-download", ['--upgrade', '--force-reinstall', '--no-cache-dir', '--disable-pip-version-check', '--pre', '-q'])
|
472
|
-
],
|
473
|
-
4: [
|
474
|
-
("Nuclear Option", ['--upgrade', '--force-reinstall', '--no-cache-dir', '--disable-pip-version-check', '--ignore-installed', '--no-deps', '--pre', '-q']),
|
475
|
-
("Last Resort", ['--upgrade', '--force-reinstall', '--no-cache-dir', '--disable-pip-version-check', '--ignore-installed', '--no-deps', '--pre', '--user', '-q'])
|
476
|
-
]
|
477
|
-
}
|
478
136
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
current_delay = delay + (attempt - 1)
|
492
|
-
print("All strategies failed for attempt {}. Retrying in {} seconds...".format(attempt, current_delay))
|
493
|
-
try:
|
494
|
-
time.sleep(current_delay + (random.random() * 1.0))
|
495
|
-
except Exception:
|
496
|
-
time.sleep(current_delay)
|
497
|
-
|
498
|
-
print_status("All upgrade attempts failed.", "ERROR")
|
499
|
-
return False
|
137
|
+
def verify_post_install(package, expected_version):
|
138
|
+
# Try quick reads with minimal backoff to avoid unnecessary slowness
|
139
|
+
for _ in range(3):
|
140
|
+
installed = get_installed_version(package)
|
141
|
+
if installed:
|
142
|
+
# Re-fetch latest once to avoid stale latest
|
143
|
+
latest_again = get_latest_version(package, retries=1) or expected_version
|
144
|
+
if compare_versions(installed, latest_again) >= 0:
|
145
|
+
return True, installed
|
146
|
+
time.sleep(1)
|
147
|
+
return False, get_installed_version(package)
|
148
|
+
|
500
149
|
|
501
|
-
def
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
'requests==2.21.0',
|
507
|
-
'argparse==1.4.0',
|
508
|
-
'tqdm==4.14.0',
|
509
|
-
'python-docx==0.8.11',
|
510
|
-
'PyYAML==5.2',
|
511
|
-
'chardet==3.0.4',
|
512
|
-
'msal==1.26.0'
|
150
|
+
def upgrade_package(package):
|
151
|
+
strategies = [
|
152
|
+
['install', '--upgrade', package, '--no-cache-dir', '--disable-pip-version-check'],
|
153
|
+
['install', '--upgrade', '--force-reinstall', package, '--no-cache-dir', '--disable-pip-version-check'],
|
154
|
+
['install', '--upgrade', '--force-reinstall', '--ignore-installed', '--user', package, '--no-cache-dir', '--disable-pip-version-check']
|
513
155
|
]
|
514
156
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
if is_windows_py34:
|
520
|
-
print_status("Detected Windows with Python 3.4", "INFO")
|
521
|
-
print("Please ensure the following packages are installed manually:")
|
522
|
-
for pkg in problematic_packages:
|
523
|
-
package_name, version = pkg.split('==')
|
524
|
-
try:
|
525
|
-
installed_version = pkg_resources.get_distribution(package_name).version
|
526
|
-
print("{} {} is already installed".format(package_name, installed_version))
|
527
|
-
if installed_version != version:
|
528
|
-
print("Note: Installed version ({}) differs from required ({})".format(installed_version, version))
|
529
|
-
print("If you experience issues, consider installing version {} manually".format(version))
|
530
|
-
except pkg_resources.DistributionNotFound:
|
531
|
-
print("{} is not installed".format(package_name))
|
532
|
-
print("Please install {}=={} manually using a pre-compiled wheel".format(package_name, version))
|
533
|
-
print("Download from: https://www.lfd.uci.edu/~gohlke/pythonlibs/")
|
534
|
-
print("Then run: pip install path\\to\\{}-{}-cp34-cp34m-win32.whl".format(package_name, version))
|
535
|
-
print("\nContinuing with other dependencies...")
|
536
|
-
else:
|
537
|
-
# Add problematic packages to the list for non-Windows XP environments
|
538
|
-
required_packages.extend(problematic_packages)
|
157
|
+
latest_before = get_latest_version(package, retries=2)
|
158
|
+
if not latest_before:
|
159
|
+
print_status('ERROR', 'Unable to determine latest version from PyPI')
|
160
|
+
return False
|
539
161
|
|
540
|
-
for
|
541
|
-
|
542
|
-
|
162
|
+
for idx, parts in enumerate(strategies):
|
163
|
+
attempt = idx + 1
|
164
|
+
print_section("Attempt {}/3".format(attempt))
|
165
|
+
cmd = [sys.executable, '-m', 'pip'] + parts
|
166
|
+
print_status('INFO', 'Running: {} -m pip {}'.format(sys.executable, ' '.join(parts)))
|
167
|
+
code, out, err = run_pip_install(cmd)
|
168
|
+
if code == 0:
|
169
|
+
ok, installed = verify_post_install(package, latest_before)
|
170
|
+
if ok:
|
171
|
+
print_status('SUCCESS', 'Installed version: {}'.format(installed))
|
172
|
+
return True
|
173
|
+
else:
|
174
|
+
print_status('WARNING', 'Install returned success but version not updated yet{}'.format(
|
175
|
+
'' if not installed else ' (detected {})'.format(installed)))
|
543
176
|
else:
|
544
|
-
|
545
|
-
|
177
|
+
# Show error output concisely
|
178
|
+
if err:
|
179
|
+
print(err.strip())
|
180
|
+
print_status('WARNING', 'pip returned non-zero exit code ({})'.format(code))
|
181
|
+
|
182
|
+
return False
|
546
183
|
|
547
|
-
# Skip problematic packages on Windows XP Python 3.4
|
548
|
-
if is_windows_py34 and any(package_name in p for p in problematic_packages):
|
549
|
-
continue
|
550
184
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
if not upgrade_package(package_name): # Attempt to upgrade/downgrade to the required version
|
558
|
-
print_status("Failed to upgrade/downgrade {} to version {}.".format(package_name, version), "WARNING")
|
559
|
-
time.sleep(2) # Pause for 2 seconds after failure message
|
560
|
-
elif version and installed_version == version: # Check if installed version matches required version
|
561
|
-
print("All versions match for {}. No changes needed.".format(package_name))
|
562
|
-
time.sleep(1) # Pause for 2 seconds to allow user to read the output
|
563
|
-
elif not version: # If no specific version is required, check for the latest version
|
564
|
-
latest_version = get_latest_version(package_name)
|
565
|
-
if latest_version and installed_version != latest_version:
|
566
|
-
print("Current version of {}: {}".format(package_name, installed_version))
|
567
|
-
print("Latest version of {}: {}".format(package_name, latest_version))
|
568
|
-
time.sleep(2) # Pause for 2 seconds to allow user to read the output
|
569
|
-
if not upgrade_package(package_name):
|
570
|
-
print_status("Failed to upgrade {}.".format(package_name), "WARNING")
|
571
|
-
time.sleep(2) # Pause for 2 seconds after failure message
|
572
|
-
except pkg_resources.DistributionNotFound:
|
573
|
-
print("Package {} is not installed. Attempting to install...".format(package_name))
|
574
|
-
time.sleep(2) # Pause for 2 seconds before attempting installation
|
575
|
-
if not upgrade_package(package_name):
|
576
|
-
print_status("Failed to install {}.".format(package_name), "WARNING")
|
577
|
-
time.sleep(2) # Pause for 2 seconds after failure message
|
578
|
-
|
579
|
-
def check_for_updates_only():
|
580
|
-
"""
|
581
|
-
Check if a new version is available without performing the upgrade.
|
582
|
-
Returns a simple status message for batch script consumption.
|
583
|
-
"""
|
185
|
+
# ---------- Main ----------
|
186
|
+
def main():
|
187
|
+
print_banner("MediCafe Updater ({} v{})".format(SCRIPT_NAME, SCRIPT_VERSION))
|
188
|
+
print_status('INFO', 'Python: {}'.format(sys.version.split(" ")[0]))
|
189
|
+
print_status('INFO', 'Platform: {}'.format(platform.platform()))
|
190
|
+
|
584
191
|
if not check_internet_connection():
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
latest_version = get_latest_version(package)
|
595
|
-
if not latest_version:
|
596
|
-
print("ERROR")
|
597
|
-
return
|
598
|
-
|
599
|
-
if compare_versions(latest_version, current_version) > 0:
|
600
|
-
print("UPDATE_AVAILABLE:" + latest_version)
|
192
|
+
print_section('Network check')
|
193
|
+
print_status('ERROR', 'No internet connection detected')
|
194
|
+
sys.exit(1)
|
195
|
+
|
196
|
+
print_section('Environment')
|
197
|
+
current = get_installed_version(PACKAGE_NAME)
|
198
|
+
if current:
|
199
|
+
print_status('INFO', 'Installed {}: {}'.format(PACKAGE_NAME, current))
|
601
200
|
else:
|
602
|
-
|
201
|
+
print_status('WARNING', '{} is not currently installed'.format(PACKAGE_NAME))
|
603
202
|
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
print_status("MediCafe Update Utility v1.1.0", "INFO")
|
610
|
-
print("Starting update process...")
|
611
|
-
|
612
|
-
# STEP 1: Environment Information
|
613
|
-
debug_step(1, "Environment Information",
|
614
|
-
"Python version: {}\n"
|
615
|
-
"Platform: {}\n"
|
616
|
-
"Current working directory: {}\n"
|
617
|
-
"Script location: {}\n"
|
618
|
-
"sys.executable: {}".format(
|
619
|
-
sys.version, platform.platform(), os.getcwd(),
|
620
|
-
__file__, sys.executable))
|
621
|
-
|
622
|
-
# STEP 2: Check Python and pip
|
623
|
-
debug_step(2, "Python and pip Verification")
|
624
|
-
print("Checking Python installation...")
|
625
|
-
try:
|
626
|
-
process = subprocess.Popen([sys.executable, '--version'],
|
627
|
-
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
628
|
-
stdout, stderr = process.communicate()
|
629
|
-
if process.returncode == 0:
|
630
|
-
print("Python version: {}".format(stdout.decode().strip()))
|
631
|
-
else:
|
632
|
-
print("Error checking Python: {}".format(stderr.decode().strip()))
|
633
|
-
except Exception as e:
|
634
|
-
print("Error checking Python: {}".format(e))
|
203
|
+
latest = get_latest_version(PACKAGE_NAME, retries=3)
|
204
|
+
if not latest:
|
205
|
+
print_status('ERROR', 'Could not fetch latest version information from PyPI')
|
206
|
+
sys.exit(1)
|
207
|
+
print_status('INFO', 'Latest {} on PyPI: {}'.format(PACKAGE_NAME, latest))
|
635
208
|
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
stdout, stderr = process.communicate()
|
641
|
-
if process.returncode == 0:
|
642
|
-
print("pip version: {}".format(stdout.decode().strip()))
|
643
|
-
else:
|
644
|
-
print("Error checking pip: {}".format(stderr.decode().strip()))
|
645
|
-
except Exception as e:
|
646
|
-
print("Error checking pip: {}".format(e))
|
647
|
-
|
648
|
-
# STEP 3: Check MediCafe package
|
649
|
-
debug_step(3, "MediCafe Package Check")
|
650
|
-
package = "medicafe"
|
651
|
-
current_version = get_installed_version(package)
|
652
|
-
if current_version:
|
653
|
-
print("Current MediCafe version: {}".format(current_version))
|
654
|
-
else:
|
655
|
-
print("MediCafe package not found or not accessible")
|
209
|
+
if current and compare_versions(latest, current) <= 0:
|
210
|
+
print_section('Status')
|
211
|
+
print_status('SUCCESS', 'Already up to date')
|
212
|
+
sys.exit(0)
|
656
213
|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
else:
|
668
|
-
print("Internet connection: FAILED")
|
669
|
-
print_final_result(False, "No internet connection available")
|
670
|
-
|
671
|
-
# STEP 5: Check for updates
|
672
|
-
debug_step(5, "Version Comparison")
|
673
|
-
latest_version = get_latest_version(package)
|
674
|
-
if latest_version:
|
675
|
-
print("Latest available version: {}".format(latest_version))
|
676
|
-
if current_version:
|
677
|
-
comparison = compare_versions(latest_version, current_version)
|
678
|
-
if comparison > 0:
|
679
|
-
print("Update needed: Current ({}) < Latest ({})".format(current_version, latest_version))
|
680
|
-
elif comparison == 0:
|
681
|
-
print("Already up to date: Current ({}) = Latest ({})".format(current_version, latest_version))
|
682
|
-
else:
|
683
|
-
print("Version mismatch: Current ({}) > Latest ({})".format(current_version, latest_version))
|
684
|
-
else:
|
685
|
-
print("Cannot compare versions - current version not available")
|
686
|
-
else:
|
687
|
-
print("Could not retrieve latest version information")
|
688
|
-
print_final_result(False, "Unable to fetch latest version")
|
689
|
-
|
690
|
-
# STEP 6: Dependencies check (skipped by default in streamlined mode)
|
691
|
-
debug_step(6, "Dependencies Check")
|
692
|
-
if DEBUG_MODE:
|
693
|
-
response = input("Do you want to check dependencies? (yes/no, default/enter is no): ").strip().lower()
|
694
|
-
if response in ['yes', 'y']:
|
695
|
-
ensure_dependencies()
|
696
|
-
else:
|
697
|
-
print_status("Skipping dependency check.", "INFO")
|
698
|
-
else:
|
699
|
-
print_status("Skipping dependency check (streamlined mode).", "INFO")
|
700
|
-
|
701
|
-
# STEP 7: Perform update
|
702
|
-
debug_step(7, "Update Execution")
|
703
|
-
if current_version and latest_version and compare_versions(latest_version, current_version) > 0:
|
704
|
-
print_status("A newer version is available. Proceeding with upgrade.", "INFO")
|
705
|
-
print("Current version: {}".format(current_version))
|
706
|
-
print("Target version: {}".format(latest_version))
|
707
|
-
|
708
|
-
if upgrade_package(package, target_version=latest_version):
|
709
|
-
# STEP 8: Verify upgrade
|
710
|
-
debug_step(8, "Upgrade Verification")
|
711
|
-
|
712
|
-
# Clear cache and wait for package metadata to settle
|
713
|
-
clear_pkg_resources_cache()
|
714
|
-
|
715
|
-
# FIXED: Increased wait time for XP compatibility
|
716
|
-
time.sleep(3) # Increased from 2 to 3 seconds
|
717
|
-
new_version = get_installed_version(package)
|
718
|
-
print("New installed version: {}".format(new_version))
|
719
|
-
|
720
|
-
if new_version and compare_versions(new_version, latest_version) >= 0:
|
721
|
-
print_status("Upgrade successful. New version: {}".format(new_version), "SUCCESS")
|
722
|
-
|
723
|
-
# DEBUG STEP 9: Clear cache
|
724
|
-
debug_step(9, "Cache Clearing")
|
725
|
-
print_status("Clearing Python cache to prevent import issues...", "INFO")
|
726
|
-
if clear_python_cache():
|
727
|
-
print_status("Cache cleared successfully. Update complete.", "SUCCESS")
|
728
|
-
else:
|
729
|
-
print_status("Cache clearing failed, but update was successful.", "WARNING")
|
730
|
-
|
731
|
-
print_final_result(True, "Successfully upgraded to version {}".format(new_version))
|
732
|
-
elif new_version:
|
733
|
-
print_status("Upgrade completed but version verification unclear. New version: {}".format(new_version), "WARNING")
|
734
|
-
print_status("The package was updated, but version comparison failed. This may be due to caching issues.", "WARNING")
|
735
|
-
|
736
|
-
# Still clear cache and exit successfully
|
737
|
-
debug_step(9, "Cache Clearing")
|
738
|
-
print_status("Clearing Python cache to prevent import issues...", "INFO")
|
739
|
-
if clear_python_cache():
|
740
|
-
print_status("Cache cleared successfully. Update complete.", "SUCCESS")
|
741
|
-
else:
|
742
|
-
print_status("Cache clearing failed, but update was successful.", "WARNING")
|
743
|
-
|
744
|
-
print_final_result(True, "Package updated (version verification unclear)")
|
745
|
-
else:
|
746
|
-
print_status("Upgrade verification failed. Could not detect new version.", "ERROR")
|
747
|
-
print_final_result(False, "Upgrade verification failed")
|
748
|
-
else:
|
749
|
-
print_final_result(False, "Upgrade process failed")
|
750
|
-
else:
|
751
|
-
print_status("You already have the latest version installed.", "SUCCESS")
|
752
|
-
print_final_result(True, "Already running latest version")
|
753
|
-
|
754
|
-
if __name__ == "__main__":
|
755
|
-
if len(sys.argv) > 1:
|
756
|
-
if sys.argv[1] == "--check-only":
|
757
|
-
check_for_updates_only()
|
758
|
-
sys.exit(0)
|
759
|
-
elif sys.argv[1] == "--clear-cache":
|
760
|
-
# Standalone cache clearing mode
|
761
|
-
print_status("MediCafe Cache Clearing Utility", "INFO")
|
762
|
-
workspace_path = sys.argv[2] if len(sys.argv) > 2 else None
|
763
|
-
if clear_python_cache(workspace_path):
|
764
|
-
print_status("Cache clearing completed successfully", "SUCCESS")
|
765
|
-
sys.exit(0)
|
766
|
-
else:
|
767
|
-
print_status("Cache clearing failed", "ERROR")
|
768
|
-
sys.exit(1)
|
214
|
+
print_section('Upgrade')
|
215
|
+
print_status('INFO', 'Upgrading {} to {} (up to 3 attempts)'.format(PACKAGE_NAME, latest))
|
216
|
+
success = upgrade_package(PACKAGE_NAME)
|
217
|
+
|
218
|
+
print_section('Result')
|
219
|
+
final_version = get_installed_version(PACKAGE_NAME)
|
220
|
+
if success:
|
221
|
+
print_status('SUCCESS', 'Update completed. {} is now at {}'.format(PACKAGE_NAME, final_version or '(unknown)'))
|
222
|
+
print_status('INFO', 'This updater script: v{}'.format(SCRIPT_VERSION))
|
223
|
+
sys.exit(0)
|
769
224
|
else:
|
770
|
-
|
225
|
+
print_status('ERROR', 'Update failed.')
|
226
|
+
if final_version and current and compare_versions(final_version, current) > 0:
|
227
|
+
print_status('WARNING', 'Partial success: detected {} after failures'.format(final_version))
|
228
|
+
print_status('INFO', 'This updater script: v{}'.format(SCRIPT_VERSION))
|
229
|
+
sys.exit(1)
|
230
|
+
|
231
|
+
|
232
|
+
if __name__ == '__main__':
|
233
|
+
# Optional quick mode: --check-only prints machine-friendly status
|
234
|
+
if len(sys.argv) > 1 and sys.argv[1] == '--check-only':
|
235
|
+
if not check_internet_connection():
|
236
|
+
print('ERROR')
|
237
|
+
sys.exit(1)
|
238
|
+
cur = get_installed_version(PACKAGE_NAME)
|
239
|
+
lat = get_latest_version(PACKAGE_NAME, retries=2)
|
240
|
+
if not cur or not lat:
|
241
|
+
print('ERROR')
|
242
|
+
sys.exit(1)
|
243
|
+
print('UPDATE_AVAILABLE:' + lat if compare_versions(lat, cur) > 0 else 'UP_TO_DATE')
|
244
|
+
sys.exit(0)
|
245
|
+
main()
|