medicafe 0.250810.5__tar.gz → 0.250810.7__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.
Files changed (74) hide show
  1. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot.bat +7 -1
  2. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_Crosswalk_Utils.py +75 -30
  3. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/__main__.py +21 -0
  4. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/core_utils.py +116 -12
  5. {medicafe-0.250810.5/medicafe.egg-info → medicafe-0.250810.7}/PKG-INFO +1 -1
  6. {medicafe-0.250810.5 → medicafe-0.250810.7/medicafe.egg-info}/PKG-INFO +1 -1
  7. {medicafe-0.250810.5 → medicafe-0.250810.7}/setup.py +1 -1
  8. {medicafe-0.250810.5 → medicafe-0.250810.7}/LICENSE +0 -0
  9. {medicafe-0.250810.5 → medicafe-0.250810.7}/MANIFEST.in +0 -0
  10. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot.py +0 -0
  11. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_Charges.py +0 -0
  12. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_Crosswalk_Library.py +0 -0
  13. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_Post.py +0 -0
  14. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_Preprocessor.py +0 -0
  15. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_Preprocessor_lib.py +0 -0
  16. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_UI.py +0 -0
  17. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_dataformat_library.py +0 -0
  18. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_docx_decoder.py +0 -0
  19. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/MediBot_smart_import.py +0 -0
  20. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/__init__.py +0 -0
  21. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/get_medicafe_version.py +0 -0
  22. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/update_json.py +0 -0
  23. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediBot/update_medicafe.py +0 -0
  24. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/MediLink_ConfigLoader.py +0 -0
  25. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/__init__.py +0 -0
  26. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/api_core.py +0 -0
  27. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/api_core_backup.py +0 -0
  28. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/api_factory.py +0 -0
  29. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/api_utils.py +0 -0
  30. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/graphql_utils.py +0 -0
  31. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/logging_config.py +0 -0
  32. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/logging_demo.py +0 -0
  33. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/migration_helpers.py +0 -0
  34. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediCafe/smart_import.py +0 -0
  35. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_837p_cob_library.py +0 -0
  36. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_837p_encoder.py +0 -0
  37. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_837p_encoder_library.py +0 -0
  38. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_837p_utilities.py +0 -0
  39. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_API_Generator.py +0 -0
  40. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Azure.py +0 -0
  41. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_ClaimStatus.py +0 -0
  42. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_DataMgmt.py +0 -0
  43. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Decoder.py +0 -0
  44. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Deductible.py +0 -0
  45. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Deductible_Validator.py +0 -0
  46. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Display_Utils.py +0 -0
  47. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Down.py +0 -0
  48. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Gmail.py +0 -0
  49. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Mailer.py +0 -0
  50. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Parser.py +0 -0
  51. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_PatientProcessor.py +0 -0
  52. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Scan.py +0 -0
  53. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Scheduler.py +0 -0
  54. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_UI.py +0 -0
  55. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_Up.py +0 -0
  56. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_insurance_utils.py +0 -0
  57. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_main.py +0 -0
  58. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/MediLink_smart_import.py +0 -0
  59. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/Soumit_api.py +0 -0
  60. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/__init__.py +0 -0
  61. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/insurance_type_integration_test.py +0 -0
  62. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/openssl.cnf +0 -0
  63. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/test.py +0 -0
  64. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/test_cob_library.py +0 -0
  65. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/test_timing.py +0 -0
  66. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/test_validation.py +0 -0
  67. {medicafe-0.250810.5 → medicafe-0.250810.7}/MediLink/webapp.html +0 -0
  68. {medicafe-0.250810.5 → medicafe-0.250810.7}/medicafe.egg-info/SOURCES.txt +0 -0
  69. {medicafe-0.250810.5 → medicafe-0.250810.7}/medicafe.egg-info/dependency_links.txt +0 -0
  70. {medicafe-0.250810.5 → medicafe-0.250810.7}/medicafe.egg-info/entry_points.txt +0 -0
  71. {medicafe-0.250810.5 → medicafe-0.250810.7}/medicafe.egg-info/not-zip-safe +0 -0
  72. {medicafe-0.250810.5 → medicafe-0.250810.7}/medicafe.egg-info/requires.txt +0 -0
  73. {medicafe-0.250810.5 → medicafe-0.250810.7}/medicafe.egg-info/top_level.txt +0 -0
  74. {medicafe-0.250810.5 → medicafe-0.250810.7}/setup.cfg +0 -0
@@ -25,6 +25,8 @@ echo ========================================
25
25
  echo DEBUG MODE (INTERACTIVE)
26
26
  echo ========================================
27
27
  echo Running full diagnostic suite...
28
+ set "MEDICAFE_IMPORT_DEBUG=1"
29
+ set "MEDICAFE_IMPORT_STRICT=0"
28
30
  echo.
29
31
  set "SKIP_CLS_AFTER_DEBUG=1"
30
32
  call "%~dp0full_debug_suite.bat" /interactive
@@ -37,6 +39,8 @@ echo ========================================
37
39
  echo DEBUG MODE (NON-INTERACTIVE)
38
40
  echo ========================================
39
41
  echo Running full diagnostic suite...
42
+ set "MEDICAFE_IMPORT_DEBUG=1"
43
+ set "MEDICAFE_IMPORT_STRICT=0"
40
44
  echo.
41
45
  set "SKIP_CLS_AFTER_DEBUG=1"
42
46
  call "%~dp0full_debug_suite.bat"
@@ -45,6 +49,8 @@ goto normal_mode
45
49
 
46
50
  :start_normal_mode
47
51
  echo Starting Normal Mode...
52
+ set "MEDICAFE_IMPORT_DEBUG=0"
53
+ set "MEDICAFE_IMPORT_STRICT=1"
48
54
  goto normal_mode
49
55
 
50
56
  :normal_mode
@@ -329,7 +335,7 @@ if exist "%upgrade_medicafe_local%" (
329
335
  cls
330
336
  echo Version: %medicafe_version%
331
337
  echo --------------------------------------------------------------
332
- echo .//* Welcome to MediBot *\\.
338
+ echo .//* Welcome to MediCafe *\\.
333
339
  echo --------------------------------------------------------------
334
340
  echo.
335
341
 
@@ -83,15 +83,44 @@ def check_crosswalk_health(crosswalk):
83
83
 
84
84
  def prompt_user_for_api_calls(crosswalk, config):
85
85
  """
86
- Prompts user with a 3-second timeout to skip API calls if crosswalk looks healthy.
87
- Windows XP compatible version using threading instead of select.
88
-
86
+ Prompt user with a short timeout to optionally run API validation when the crosswalk looks healthy.
87
+
88
+ Implementation notes and rationale
89
+ ----------------------------------
90
+ A previous implementation attempted to capture a quick ENTER press using a background thread that
91
+ called ``input()`` and then ``join(timeout=...)``. When the timeout elapsed, the thread often
92
+ remained blocked inside ``input()``. That stray, still-waiting ``input()`` subsequently consumed
93
+ the next ENTER the user typed at a later prompt (e.g., the Medicare question), producing a blank,
94
+ unlabeled input step before the real prompt. This manifested as “press Enter once to get the next
95
+ prompt,” which is undesirable and confusing.
96
+
97
+ To avoid leaving a pending console read, this implementation uses Windows' non-blocking console
98
+ polling (``msvcrt.kbhit()/getwch()``) for up to ~2 seconds to detect an ENTER press without ever
99
+ invoking ``input()``. Non-ENTER keys are discarded to avoid leaking keystrokes into subsequent
100
+ prompts. If ENTER is not pressed during the window, we skip API validation with no lingering input
101
+ state.
102
+
103
+ If threading must be used in future
104
+ -----------------------------------
105
+ Do not call ``input()`` in a background thread and leave it running after a timeout. Instead:
106
+ - Use a worker thread that performs non-blocking polling (e.g., ``msvcrt.kbhit()/getwch()``) and
107
+ sets a threading.Event when ENTER is detected.
108
+ - The main thread waits on the Event with a timeout. On timeout, signal the worker to stop.
109
+ - Before returning, drain any buffered keystrokes with a short loop while ``msvcrt.kbhit()`` to
110
+ ensure no characters (including ENTER) carry over to the next prompt.
111
+ - This avoids dangling reads and preserves a clean console state.
112
+
113
+ Compatibility
114
+ -------------
115
+ - Targets Python 3.4.4 / Windows XP constraints; avoids ``selectors``/``select`` on stdin and
116
+ avoids advanced console APIs. Falls back to a non-interactive skip on non-Windows platforms.
117
+
89
118
  Args:
90
119
  crosswalk (dict): The crosswalk dictionary to check.
91
120
  config (dict): Configuration settings for logging.
92
-
121
+
93
122
  Returns:
94
- bool: True if should proceed with API calls, False if should skip
123
+ bool: True to proceed with API calls; False to skip
95
124
  """
96
125
 
97
126
  is_healthy, missing_names, missing_medisoft_ids, missing_names_list, missing_medisoft_ids_list = check_crosswalk_health(crosswalk)
@@ -103,31 +132,47 @@ def prompt_user_for_api_calls(crosswalk, config):
103
132
  print(" - All payers have names")
104
133
  print(" - All payers have medisoft IDs")
105
134
  print("\nPress ENTER to run API validation, or wait 2 seconds to skip...")
106
-
107
- # Use threading for timeout on Windows
108
- user_input = [None] # Use list to store result from thread
109
-
110
- def get_input():
111
- try:
112
- user_input[0] = input()
113
- except (EOFError, KeyboardInterrupt):
114
- user_input[0] = ""
115
-
116
- # Start input thread
117
- input_thread = threading.Thread(target=get_input)
118
- input_thread.daemon = True
119
- input_thread.start()
120
-
121
- # Wait for 2 seconds or until input is received
122
- input_thread.join(timeout=2.0)
123
-
124
- if user_input[0] is not None:
125
- print("Running API validation calls...")
126
- MediLink_ConfigLoader.log("User pressed ENTER - proceeding with API calls", config, level="INFO")
127
- return True
128
- else:
129
- print("Timed out - skipping API calls")
130
- MediLink_ConfigLoader.log("Timeout - skipping API calls", config, level="INFO")
135
+
136
+ # Windows-safe, non-blocking ENTER detection without leaving a stray input() pending
137
+ try:
138
+ import os, time
139
+ msvcrt = None
140
+ if os.name == 'nt':
141
+ try:
142
+ import msvcrt # Windows-only
143
+ except ImportError:
144
+ msvcrt = None
145
+
146
+ if msvcrt is not None:
147
+ start_time = time.time()
148
+ pressed_enter = False
149
+ # Consume keys for up to 2 seconds, act only if ENTER is hit
150
+ while time.time() - start_time < 2.0:
151
+ if msvcrt.kbhit():
152
+ ch = msvcrt.getwch()
153
+ if ch in ('\r', '\n'):
154
+ pressed_enter = True
155
+ break
156
+ # Discard any other keys so they don't leak into later prompts
157
+ time.sleep(0.01)
158
+
159
+ if pressed_enter:
160
+ print("Running API validation calls...")
161
+ MediLink_ConfigLoader.log("User pressed ENTER - proceeding with API calls", config, level="INFO")
162
+ return True
163
+ else:
164
+ print("Timed out - skipping API calls")
165
+ MediLink_ConfigLoader.log("Timeout - skipping API calls", config, level="INFO")
166
+ return False
167
+ else:
168
+ # Fallback for non-Windows: avoid interactive wait to prevent stdin conflicts
169
+ print("(Skipping interactive API validation prompt on this platform)")
170
+ MediLink_ConfigLoader.log("Skipped interactive API validation prompt (unsupported platform)", config, level="INFO")
171
+ return False
172
+ except Exception:
173
+ # On any error, default to skipping to avoid dangling input states
174
+ print("(Error during prompt; skipping API validation)")
175
+ MediLink_ConfigLoader.log("Error during API validation prompt; skipping", config, level="WARNING")
131
176
  return False
132
177
  else:
133
178
  print("\nCrosswalk needs attention:")
@@ -25,6 +25,7 @@ import sys
25
25
  import os
26
26
  import argparse
27
27
  import subprocess
28
+ from MediCafe.core_utils import import_medilink_module, require_functions, print_import_diagnostics
28
29
 
29
30
  # Set up module paths for proper imports
30
31
  def setup_entry_point_paths():
@@ -44,6 +45,21 @@ def setup_entry_point_paths():
44
45
  while pkg_dir in sys.path:
45
46
  sys.path.remove(pkg_dir)
46
47
 
48
+ def validate_critical_imports():
49
+ """Validate key modules and capabilities before launching subprocesses.
50
+ - Ensures we resolve the in-repo MediLink_DataMgmt and it exposes required functions.
51
+ Behavior depends on env flags handled inside core_utils.require_functions.
52
+ """
53
+ try:
54
+ datamgmt = import_medilink_module('MediLink_DataMgmt')
55
+ if datamgmt is not None:
56
+ print_import_diagnostics('MediLink_DataMgmt', datamgmt)
57
+ # These are used by both MediBot and MediLink flows
58
+ require_functions(datamgmt, ['read_general_fixed_width_data', 'read_fixed_width_data', 'parse_fixed_width_data'])
59
+ except Exception as e:
60
+ # Fail fast only if strict mode is enabled (exception originates from require_functions)
61
+ print("Import validation warning: {}".format(e))
62
+
47
63
  def run_medibot(config_file=None):
48
64
  """Run MediBot application"""
49
65
  try:
@@ -58,6 +74,9 @@ def run_medibot(config_file=None):
58
74
  print("Error: MediBot.py not found at {}".format(medibot_path))
59
75
  return 1
60
76
 
77
+ # Pre-flight import validation (no-op unless strict/debug enabled)
78
+ validate_critical_imports()
79
+
61
80
  # Build subprocess arguments
62
81
  args = [sys.executable, medibot_path]
63
82
  if config_file:
@@ -83,6 +102,8 @@ def run_medilink():
83
102
  """Run MediLink application"""
84
103
  try:
85
104
  print("Starting MediLink...")
105
+ # Pre-flight import validation (no-op unless strict/debug enabled)
106
+ validate_critical_imports()
86
107
  # Use subprocess.call for Python 3.4.4 compatibility
87
108
 
88
109
  # Get the path to MediLink_main.py
@@ -29,6 +29,62 @@ for _p in list(sys.path):
29
29
  DEFAULT_CONFIG_PATH = os.path.join(project_dir, 'json', 'config.json')
30
30
  DEFAULT_CROSSWALK_PATH = os.path.join(project_dir, 'json', 'crosswalk.json')
31
31
 
32
+ # Environment flags controlling import behavior
33
+ _STRICT_IMPORT = os.environ.get('MEDICAFE_IMPORT_STRICT', '0').strip().lower() in ('1', 'true', 'yes', 'y')
34
+ _DEBUG_IMPORT = os.environ.get('MEDICAFE_IMPORT_DEBUG', '0').strip().lower() in ('1', 'true', 'yes', 'y')
35
+
36
+ # Simple memoization cache for resolved imports
37
+ _IMPORT_CACHE = {}
38
+
39
+ def _cache_get(key):
40
+ try:
41
+ return _IMPORT_CACHE.get(key)
42
+ except Exception:
43
+ return None
44
+
45
+ def _cache_set(key, value):
46
+ try:
47
+ _IMPORT_CACHE[key] = value
48
+ except Exception:
49
+ pass
50
+
51
+ def _module_provenance_ok(module):
52
+ """Return True if module appears to come from this workspace."""
53
+ try:
54
+ module_file = getattr(module, '__file__', '') or ''
55
+ return os.path.abspath(project_dir) in os.path.abspath(module_file)
56
+ except Exception:
57
+ return False
58
+
59
+ def require_functions(module, names):
60
+ """Ensure the module exposes all required attributes; raise RuntimeError on failure in strict mode; return bool otherwise."""
61
+ missing = [name for name in names if not hasattr(module, name)]
62
+ if missing:
63
+ message = "Missing required symbols {} in module {}".format(missing, getattr(module, '__name__', 'unknown'))
64
+ if _STRICT_IMPORT:
65
+ raise RuntimeError(message)
66
+ if _DEBUG_IMPORT:
67
+ try:
68
+ print("[IMPORT DIAG] {} (file: {})".format(message, getattr(module, '__file__', 'unknown')))
69
+ except Exception:
70
+ pass
71
+ return False
72
+ return True
73
+
74
+ def print_import_diagnostics(label, module):
75
+ """Print a single-line diagnostic about a resolved module (debug only)."""
76
+ if not _DEBUG_IMPORT:
77
+ return
78
+ try:
79
+ print("[IMPORT DIAG] {} -> name={} file={} workspace_provenance={}".format(
80
+ label,
81
+ getattr(module, '__name__', 'unknown'),
82
+ getattr(module, '__file__', 'unknown'),
83
+ _module_provenance_ok(module)
84
+ ))
85
+ except Exception:
86
+ pass
87
+
32
88
  def setup_project_path(file_path=None):
33
89
  """
34
90
  Standard project path setup function used by all entry points.
@@ -122,12 +178,26 @@ def smart_import(import_specs, default_value=None):
122
178
  try:
123
179
  if isinstance(spec, str):
124
180
  # Simple string - direct import
125
- return __import__(spec)
181
+ # If dotted path, ensure we get the submodule, not just the top-level package
182
+ cached = _cache_get(('str', spec))
183
+ if cached is not None:
184
+ return cached
185
+ if '.' in spec:
186
+ result = __import__(spec, fromlist=['*'])
187
+ else:
188
+ result = __import__(spec)
189
+ _cache_set(('str', spec), result)
190
+ return result
126
191
  elif isinstance(spec, tuple):
127
192
  # Tuple - (path, function_name)
128
193
  path, function_name = spec
194
+ cached = _cache_get(('tuple', path, function_name))
195
+ if cached is not None:
196
+ return cached
129
197
  module = __import__(path, fromlist=[function_name])
130
- return getattr(module, function_name)
198
+ result = getattr(module, function_name)
199
+ _cache_set(('tuple', path, function_name), result)
200
+ return result
131
201
  elif isinstance(spec, dict):
132
202
  # Dict with fallback
133
203
  path = spec['path']
@@ -135,19 +205,38 @@ def smart_import(import_specs, default_value=None):
135
205
  fallback = spec.get('fallback')
136
206
 
137
207
  try:
208
+ cache_key = ('dict', path, function_name)
209
+ cached = _cache_get(cache_key)
210
+ if cached is not None:
211
+ return cached
138
212
  if function_name:
139
213
  module = __import__(path, fromlist=[function_name])
140
- return getattr(module, function_name)
214
+ result = getattr(module, function_name)
141
215
  else:
142
- return __import__(path)
216
+ # If dotted, use fromlist to return the submodule
217
+ if '.' in path:
218
+ result = __import__(path, fromlist=['*'])
219
+ else:
220
+ result = __import__(path)
221
+ _cache_set(cache_key, result)
222
+ return result
143
223
  except ImportError:
144
224
  if fallback:
145
225
  try:
226
+ cache_key = ('dict_fallback', fallback, function_name)
227
+ cached = _cache_get(cache_key)
228
+ if cached is not None:
229
+ return cached
146
230
  if function_name:
147
231
  module = __import__(fallback, fromlist=[function_name])
148
- return getattr(module, function_name)
232
+ result = getattr(module, function_name)
149
233
  else:
150
- return __import__(fallback)
234
+ if '.' in fallback:
235
+ result = __import__(fallback, fromlist=['*'])
236
+ else:
237
+ result = __import__(fallback)
238
+ _cache_set(cache_key, result)
239
+ return result
151
240
  except ImportError:
152
241
  continue
153
242
  continue
@@ -252,14 +341,16 @@ def import_medilink_module(module_name, function_name=None):
252
341
  Returns:
253
342
  The imported module/function or None
254
343
  """
344
+ # Prefer importing from the in-repo MediLink package first to avoid
345
+ # accidentally picking up a similarly named top-level/site-packages module.
255
346
  import_specs = [
256
- # Direct import first
257
- module_name,
258
- # Then try with MediLink prefix
347
+ # Prefer package-qualified import first (local codebase)
259
348
  'MediLink.{}'.format(module_name),
349
+ # Then try direct import (legacy/installed module)
350
+ module_name,
260
351
  # Then try relative import
261
352
  '.{}'.format(module_name),
262
- # Finally try as a submodule
353
+ # Finally try as a submodule with fallback
263
354
  {'path': 'MediLink.{}'.format(module_name), 'fallback': module_name}
264
355
  ]
265
356
 
@@ -275,9 +366,22 @@ def import_medilink_module(module_name, function_name=None):
275
366
  'function': function_name,
276
367
  'fallback': spec.get('fallback')
277
368
  })
278
- return smart_import(function_specs)
369
+ result = smart_import(function_specs)
370
+ if result is not None and _DEBUG_IMPORT:
371
+ try:
372
+ print_import_diagnostics('import_medilink_module:{}:function'.format(module_name), result)
373
+ except Exception:
374
+ pass
375
+ return result
279
376
  else:
280
- return smart_import(import_specs)
377
+ result = smart_import(import_specs)
378
+ if result is not None:
379
+ # In strict mode, verify provenance and capabilities if demanded by callers
380
+ if _STRICT_IMPORT and not _module_provenance_ok(result):
381
+ raise RuntimeError("Imported module '{}' from outside workspace: {}".format(
382
+ module_name, getattr(result, '__file__', 'unknown')))
383
+ print_import_diagnostics('import_medilink_module:{}'.format(module_name), result)
384
+ return result
281
385
 
282
386
  def get_shared_config_loader():
283
387
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250810.5
3
+ Version: 0.250810.7
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2
6
6
  Author: Daniel Vidaud
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250810.5
3
+ Version: 0.250810.7
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2
6
6
  Author: Daniel Vidaud
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='medicafe',
5
- version="0.250810.5",
5
+ version="0.250810.7",
6
6
  description='MediCafe',
7
7
  long_description="""
8
8
  # Project Overview: MediCafe
File without changes
File without changes
File without changes