exonware-xwlazy 0.1.0.22__tar.gz → 1.0.1.2__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 (101) hide show
  1. {exonware_xwlazy-0.1.0.22 → exonware_xwlazy-1.0.1.2}/.gitignore +4 -0
  2. {exonware_xwlazy-0.1.0.22 → exonware_xwlazy-1.0.1.2}/PKG-INFO +6 -6
  3. {exonware_xwlazy-0.1.0.22 → exonware_xwlazy-1.0.1.2}/README.md +4 -0
  4. {exonware_xwlazy-0.1.0.22 → exonware_xwlazy-1.0.1.2}/pyproject.toml +4 -5
  5. exonware_xwlazy-1.0.1.2/src/_old/exonware/__init__.py +42 -0
  6. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/__init__.py +14 -3
  7. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/__init__.py +8 -1
  8. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/base.py +11 -3
  9. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/cache.py +24 -24
  10. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/logger.py +5 -6
  11. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/services/__init__.py +0 -1
  12. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/services/dependency_mapper.py +31 -14
  13. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/services/install_async_utils.py +9 -3
  14. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/services/install_cache_utils.py +4 -5
  15. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/services/keyword_detection.py +0 -1
  16. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/services/spec_cache.py +2 -3
  17. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/services/state_manager.py +12 -12
  18. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/strategies/__init__.py +0 -1
  19. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/strategies/caching_dict.py +2 -3
  20. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/strategies/caching_installation.py +0 -1
  21. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/strategies/caching_lfu.py +2 -3
  22. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/strategies/caching_lru.py +0 -1
  23. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/strategies/caching_multitier.py +0 -1
  24. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/common/strategies/caching_ttl.py +2 -3
  25. exonware_xwlazy-1.0.1.2/src/_old/exonware/xwlazy/common/utils.py +141 -0
  26. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/config.py +1 -2
  27. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/contracts.py +162 -26
  28. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/defs.py +19 -18
  29. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/errors.py +0 -1
  30. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/facade.py +175 -29
  31. exonware_xwlazy-1.0.1.2/src/_old/exonware/xwlazy/host/__init__.py +7 -0
  32. exonware_xwlazy-1.0.1.2/src/_old/exonware/xwlazy/host/conf.py +15 -0
  33. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/base.py +103 -26
  34. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/data.py +0 -1
  35. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/facade.py +4 -5
  36. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/importer_engine.py +1017 -171
  37. exonware_xwlazy-1.0.1.2/src/_old/exonware/xwlazy/module/partial_module_detector.py +274 -0
  38. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/strategies/__init__.py +0 -1
  39. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/strategies/module_helper_lazy.py +3 -4
  40. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/strategies/module_helper_simple.py +0 -1
  41. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/strategies/module_manager_advanced.py +0 -1
  42. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/strategies/module_manager_simple.py +0 -1
  43. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/base.py +131 -53
  44. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/conf.py +6 -6
  45. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/data.py +0 -1
  46. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/facade.py +0 -1
  47. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/__init__.py +0 -1
  48. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/async_install_handle.py +0 -1
  49. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/config_manager.py +20 -17
  50. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/discovery.py +89 -51
  51. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/host_packages.py +41 -6
  52. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/install_async.py +16 -3
  53. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/install_cache.py +4 -5
  54. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/install_interactive.py +0 -1
  55. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/install_policy.py +14 -15
  56. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/install_registry.py +6 -5
  57. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/install_result.py +0 -1
  58. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/install_sbom.py +6 -10
  59. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/install_utils.py +0 -1
  60. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/installer_engine.py +3 -4
  61. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/lazy_installer.py +181 -19
  62. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/manifest.py +55 -53
  63. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/services/strategy_registry.py +152 -13
  64. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_discovery_file.py +2 -3
  65. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_discovery_hybrid.py +2 -3
  66. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_discovery_manifest.py +2 -3
  67. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_execution_async.py +3 -4
  68. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_execution_cached.py +2 -3
  69. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_execution_pip.py +2 -3
  70. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_execution_wheel.py +2 -3
  71. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_mapping_discovery_first.py +2 -3
  72. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_mapping_hybrid.py +2 -3
  73. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_mapping_manifest_first.py +2 -3
  74. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_policy_allow_list.py +4 -5
  75. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_policy_deny_list.py +4 -5
  76. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_policy_permissive.py +3 -4
  77. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_timing_clean.py +2 -3
  78. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_timing_full.py +2 -3
  79. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_timing_smart.py +2 -3
  80. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/package_timing_temporary.py +2 -3
  81. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/runtime/adaptive_learner.py +7 -8
  82. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/runtime/base.py +14 -15
  83. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/runtime/facade.py +7 -8
  84. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/runtime/intelligent_selector.py +6 -7
  85. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/runtime/metrics.py +6 -7
  86. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/runtime/performance.py +5 -6
  87. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/version.py +2 -2
  88. exonware_xwlazy-1.0.1.2/src/_old/xwlazy/__init__.py +13 -0
  89. exonware_xwlazy-1.0.1.2/src/_old/xwlazy/lazy.py +29 -0
  90. exonware_xwlazy-1.0.1.2/src/exonware/__init__.py +93 -0
  91. exonware_xwlazy-1.0.1.2/src/exonware/xwlazy/version.py +77 -0
  92. exonware_xwlazy-1.0.1.2/src/exonware/xwlazy.py +2546 -0
  93. exonware_xwlazy-1.0.1.2/src/exonware/xwlazy_external_libs.toml +716 -0
  94. {exonware_xwlazy-0.1.0.22 → exonware_xwlazy-1.0.1.2}/src/xwlazy.py +3 -16
  95. {exonware_xwlazy-0.1.0.22 → exonware_xwlazy-1.0.1.2}/src/xwlazy_wrapper.py +0 -1
  96. exonware_xwlazy-0.1.0.22/src/exonware/__init__.py +0 -23
  97. {exonware_xwlazy-0.1.0.22 → exonware_xwlazy-1.0.1.2}/LICENSE +0 -0
  98. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/module/__init__.py +0 -0
  99. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/__init__.py +0 -0
  100. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/package/strategies/__init__.py +0 -0
  101. {exonware_xwlazy-0.1.0.22/src → exonware_xwlazy-1.0.1.2/src/_old}/exonware/xwlazy/runtime/__init__.py +0 -0
@@ -5,6 +5,10 @@
5
5
  # CI/CD Tools (keep private)
6
6
  .ci/
7
7
 
8
+ # Migration/legacy directories (may contain paths too long for Windows)
9
+ MIGRAT/
10
+ MIGRATE/
11
+
8
12
  # ========================================
9
13
  # Python
10
14
  # ========================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: exonware-xwlazy
3
- Version: 0.1.0.22
3
+ Version: 1.0.1.2
4
4
  Summary: Marker package that enables lazy install features across the eXonware suite.
5
5
  Project-URL: Homepage, https://exonware.com
6
6
  Project-URL: Repository, https://github.com/exonware/xwlazy
@@ -15,13 +15,9 @@ Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.8
19
- Classifier: Programming Language :: Python :: 3.9
20
- Classifier: Programming Language :: Python :: 3.10
21
- Classifier: Programming Language :: Python :: 3.11
22
18
  Classifier: Programming Language :: Python :: 3.12
23
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
- Requires-Python: >=3.8
20
+ Requires-Python: >=3.12
25
21
  Requires-Dist: tomli>=2.0.1; python_version < '3.11'
26
22
  Description-Content-Type: text/markdown
27
23
 
@@ -178,6 +174,10 @@ After `pip install -e .`, xwlazy automatically enables lazy loading for your pac
178
174
  ## 📖 Documentation
179
175
 
180
176
  - **[Architecture Reference](docs/REF_ARCH.md)** - System design, patterns, and structure
177
+ - **[Integration Guide](docs/INTEGRATION_GUIDE.md)** - Integration with xw libraries
178
+ - **[Best Practices](docs/BEST_PRACTICES.md)** - Usage guidelines and recommendations
179
+ - **[Troubleshooting Guide](docs/TROUBLESHOOTING.md)** - Common issues and solutions
180
+ - **[Production Deployment](docs/PRODUCTION_DEPLOYMENT.md)** - Production deployment guide
181
181
  - **[Keyword Detection Guide](docs/KEYWORD_DETECTION.md)** - Zero-code integration
182
182
  - **[Competition Benchmarks](benchmarks/competition_tests/)** - Performance comparisons
183
183
  - **[Performance Analysis](benchmarks/competition_tests/PERFORMANCE_ANALYSIS.md)** - Optimization recommendations
@@ -151,6 +151,10 @@ After `pip install -e .`, xwlazy automatically enables lazy loading for your pac
151
151
  ## 📖 Documentation
152
152
 
153
153
  - **[Architecture Reference](docs/REF_ARCH.md)** - System design, patterns, and structure
154
+ - **[Integration Guide](docs/INTEGRATION_GUIDE.md)** - Integration with xw libraries
155
+ - **[Best Practices](docs/BEST_PRACTICES.md)** - Usage guidelines and recommendations
156
+ - **[Troubleshooting Guide](docs/TROUBLESHOOTING.md)** - Common issues and solutions
157
+ - **[Production Deployment](docs/PRODUCTION_DEPLOYMENT.md)** - Production deployment guide
154
158
  - **[Keyword Detection Guide](docs/KEYWORD_DETECTION.md)** - Zero-code integration
155
159
  - **[Competition Benchmarks](benchmarks/competition_tests/)** - Performance comparisons
156
160
  - **[Performance Analysis](benchmarks/competition_tests/PERFORMANCE_ANALYSIS.md)** - Optimization recommendations
@@ -7,7 +7,7 @@ name = "exonware-xwlazy"
7
7
  dynamic = ["version"]
8
8
  description = "Marker package that enables lazy install features across the eXonware suite."
9
9
  readme = "README.md"
10
- requires-python = ">=3.8"
10
+ requires-python = ">=3.12"
11
11
  keywords = ["lazy", "exonware", "xwlazy"]
12
12
  classifiers = [
13
13
  "Development Status :: 4 - Beta",
@@ -15,10 +15,6 @@ classifiers = [
15
15
  "License :: OSI Approved :: MIT License",
16
16
  "Operating System :: OS Independent",
17
17
  "Programming Language :: Python :: 3",
18
- "Programming Language :: Python :: 3.8",
19
- "Programming Language :: Python :: 3.9",
20
- "Programming Language :: Python :: 3.10",
21
- "Programming Language :: Python :: 3.11",
22
18
  "Programming Language :: Python :: 3.12",
23
19
  "Topic :: Software Development :: Libraries :: Python Modules",
24
20
  ]
@@ -42,6 +38,9 @@ Subtree = "https://github.com/Exonware/XWLazy.git"
42
38
  [tool.hatch.version]
43
39
  path = "src/exonware/xwlazy/version.py"
44
40
 
41
+ # Fallback: use static version if version.py doesn't exist
42
+ # __version__ = "1.0.1"
43
+
45
44
  [tool.hatch.build.targets.wheel]
46
45
  packages = ["src/exonware", "src/xwlazy"]
47
46
 
@@ -0,0 +1,42 @@
1
+ """
2
+ exonware package - Enterprise-grade Python framework ecosystem
3
+
4
+ Company: eXonware.com
5
+ Author: Eng. Muhammad AlShehri
6
+ Email: connect@exonware.com
7
+ Generation Date: 2025-01-03
8
+
9
+ This is a namespace package allowing multiple exonware subpackages
10
+ to coexist (xwsystem, xwnode, xwdata, xwlazy, etc.)
11
+ """
12
+
13
+ # Make this a namespace package FIRST
14
+ # This allows both exonware.xwsystem and exonware.xwlazy to coexist
15
+ __path__ = __import__('pkgutil').extend_path(__path__, __name__)
16
+
17
+ # Import version from xwlazy - required, no fallback
18
+ from exonware.xwlazy.version import __version__
19
+
20
+ __author__ = 'Eng. Muhammad AlShehri'
21
+ __email__ = 'connect@exonware.com'
22
+ __company__ = 'eXonware.com'
23
+
24
+ # NOW enable lazy mode (after namespace package is set up)
25
+ import sys
26
+ import importlib
27
+ try:
28
+ # Use importlib to import after namespace is ready
29
+ if 'exonware.xwlazy' not in sys.modules:
30
+ xwlazy_module = importlib.import_module('exonware.xwlazy')
31
+ auto_enable_lazy = getattr(xwlazy_module, 'auto_enable_lazy', None)
32
+ if auto_enable_lazy:
33
+ auto_enable_lazy("xwsystem", mode="smart")
34
+ print("✅ Lazy mode enabled for xwsystem")
35
+ else:
36
+ # Module already loaded, use it directly
37
+ from exonware.xwlazy import auto_enable_lazy
38
+ auto_enable_lazy("xwsystem", mode="smart")
39
+ print("✅ Lazy mode enabled for xwsystem")
40
+ except (ImportError, AttributeError):
41
+ print("❌ Lazy mode not enabled for xwsystem (xwlazy not installed)")
42
+ pass # xwlazy not installed - silently continue
@@ -63,6 +63,10 @@ from .facade import (
63
63
  configure_lazy_mode,
64
64
  preload_modules,
65
65
  optimize_lazy_mode,
66
+ # One-line activation API
67
+ auto_enable_lazy,
68
+ # Lazy-loader compatible API
69
+ attach,
66
70
  # Public API functions
67
71
  enable_lazy_install,
68
72
  disable_lazy_install,
@@ -167,6 +171,10 @@ def __getattr__(name: str) -> Any:
167
171
  elif name == "XWRuntimeHelper":
168
172
  from .runtime import XWRuntimeHelper
169
173
  return XWRuntimeHelper
174
+ elif name == "manifest":
175
+ # Import manifest module for lazy access
176
+ from .package.services import manifest
177
+ return manifest
170
178
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
171
179
 
172
180
  # Import core classes for advanced usage
@@ -210,7 +218,7 @@ from .module.importer_engine import (
210
218
  _mark_import_started,
211
219
  _mark_import_finished,
212
220
  _lazy_aware_import_module,
213
- _patch_import_module,
221
+ # _patch_import_module removed - deprecated, use sys.meta_path hooks instead
214
222
  _unpatch_import_module,
215
223
  )
216
224
 
@@ -245,6 +253,10 @@ __all__ = [
245
253
  "configure_lazy_mode",
246
254
  "preload_modules",
247
255
  "optimize_lazy_mode",
256
+ # One-line activation API
257
+ "auto_enable_lazy",
258
+ # Lazy-loader compatible API
259
+ "attach",
248
260
  # Public API functions
249
261
  "enable_lazy_install",
250
262
  "disable_lazy_install",
@@ -361,7 +373,6 @@ __all__ = [
361
373
  "_mark_import_started",
362
374
  "_mark_import_finished",
363
375
  "_lazy_aware_import_module",
364
- "_patch_import_module",
376
+ # "_patch_import_module", # Removed - deprecated, use sys.meta_path hooks instead
365
377
  "_unpatch_import_module",
366
378
  ]
367
-
@@ -28,6 +28,11 @@ from .cache import (
28
28
  InstallationCache,
29
29
  )
30
30
 
31
+ from .utils import (
32
+ find_project_root,
33
+ find_config_file,
34
+ )
35
+
31
36
  __all__ = [
32
37
  # Logger
33
38
  'get_logger',
@@ -43,5 +48,7 @@ __all__ = [
43
48
  'MultiTierCache',
44
49
  'BytecodeCache',
45
50
  'InstallationCache',
51
+ # Utils
52
+ 'find_project_root',
53
+ 'find_config_file',
46
54
  ]
47
-
@@ -12,13 +12,21 @@ Abstract base classes for shared/common strategies.
12
12
 
13
13
  from abc import ABC, abstractmethod
14
14
  from typing import Optional, Any
15
- from ..contracts import ICachingStrategy
15
+ from ..contracts import ICachingStrategy, ICacheStrategy
16
16
 
17
17
  # =============================================================================
18
18
  # ABSTRACT CACHING STRATEGY
19
19
  # =============================================================================
20
20
 
21
21
  class ACachingStrategy(ICachingStrategy, ABC):
22
+ """
23
+ Abstract base class for caching strategies (legacy name).
24
+
25
+ Note: Use ACacheStrategy for new code (ICacheStrategy interface).
26
+ """
27
+ pass
28
+
29
+ class ACacheStrategy(ICacheStrategy, ABC):
22
30
  """
23
31
  Abstract base class for caching strategies.
24
32
 
@@ -51,6 +59,6 @@ class ACachingStrategy(ICachingStrategy, ABC):
51
59
  # =============================================================================
52
60
 
53
61
  __all__ = [
54
- 'ACachingStrategy',
62
+ 'ACachingStrategy', # Legacy name
63
+ 'ACacheStrategy', # New name for ICacheStrategy interface
55
64
  ]
56
-
@@ -15,7 +15,6 @@ All cache code is centralized here to avoid duplication.
15
15
 
16
16
  import os
17
17
  import sys
18
- import json
19
18
  import time
20
19
  import pickle
21
20
  import struct
@@ -23,10 +22,11 @@ import importlib
23
22
  import importlib.util
24
23
  import threading
25
24
  from pathlib import Path
26
- from typing import Dict, List, Optional, Tuple, Any, Set
25
+ from typing import Optional, Any
27
26
  from collections import OrderedDict
28
27
  from queue import Queue
29
28
 
29
+ from exonware.xwsystem.io.serialization.auto_serializer import AutoSerializer
30
30
  from .logger import get_logger
31
31
 
32
32
  logger = get_logger("xwlazy.cache")
@@ -59,7 +59,7 @@ class MultiTierCache:
59
59
  self._l2_dir = l2_dir or Path.home() / ".xwlazy_cache"
60
60
  self._l2_dir.mkdir(parents=True, exist_ok=True)
61
61
  self._enable_l3 = enable_l3
62
- self._l3_patterns: Dict[str, Tuple[int, float]] = {}
62
+ self._l3_patterns: dict[str, tuple[int, float]] = {}
63
63
  self._lock = threading.RLock()
64
64
 
65
65
  self._l2_write_queue: Queue = Queue()
@@ -192,7 +192,7 @@ class MultiTierCache:
192
192
  for old_key, _ in sorted_patterns[:1000]:
193
193
  del self._l3_patterns[old_key]
194
194
 
195
- def get_predictive_keys(self, limit: int = 10) -> List[str]:
195
+ def get_predictive_keys(self, limit: int = 10) -> list[str]:
196
196
  """
197
197
  Get keys likely to be accessed soon (for preloading).
198
198
 
@@ -370,7 +370,7 @@ class InstallationCache:
370
370
 
371
371
  self._cache_file = cache_file
372
372
  self._lock = threading.RLock()
373
- self._cache: Dict[str, Dict[str, Any]] = {}
373
+ self._cache: dict[str, dict[str, Any]] = {}
374
374
  self._dirty = False
375
375
 
376
376
  # Load cache on init
@@ -384,15 +384,15 @@ class InstallationCache:
384
384
 
385
385
  try:
386
386
  with self._lock:
387
- with open(self._cache_file, 'r', encoding='utf-8') as f:
388
- data = json.load(f)
389
- # Validate format
390
- if isinstance(data, dict):
391
- self._cache = {k: v for k, v in data.items()
392
- if isinstance(v, dict) and 'installed' in v}
393
- else:
394
- self._cache = {}
395
- except (json.JSONDecodeError, IOError, OSError) as e:
387
+ auto_serializer = AutoSerializer()
388
+ data = auto_serializer.auto_load_file(self._cache_file, format_hint='json')
389
+ # Validate format
390
+ if isinstance(data, dict):
391
+ self._cache = {k: v for k, v in data.items()
392
+ if isinstance(v, dict) and 'installed' in v}
393
+ else:
394
+ self._cache = {}
395
+ except Exception as e:
396
396
  logger.debug(f"Failed to load installation cache: {e}")
397
397
  self._cache = {}
398
398
 
@@ -406,15 +406,16 @@ class InstallationCache:
406
406
  # Create parent directory if needed
407
407
  self._cache_file.parent.mkdir(parents=True, exist_ok=True)
408
408
 
409
- # Write atomically using temp file
410
- temp_file = self._cache_file.with_suffix('.tmp')
411
- with open(temp_file, 'w', encoding='utf-8') as f:
412
- json.dump(self._cache, f, indent=2, sort_keys=True)
413
-
414
- # Atomic rename
415
- temp_file.replace(self._cache_file)
409
+ auto_serializer = AutoSerializer()
410
+ auto_serializer.auto_save_file(
411
+ self._cache,
412
+ self._cache_file,
413
+ format_hint='json',
414
+ indent=2,
415
+ sort_keys=True
416
+ )
416
417
  self._dirty = False
417
- except (IOError, OSError) as e:
418
+ except Exception as e:
418
419
  logger.warning(f"Failed to save installation cache: {e}")
419
420
 
420
421
  def is_installed(self, package_name: str) -> bool:
@@ -487,7 +488,7 @@ class InstallationCache:
487
488
  self._dirty = True
488
489
  self._save_cache()
489
490
 
490
- def get_all_installed(self) -> Set[str]:
491
+ def get_all_installed(self) -> set[str]:
491
492
  """
492
493
  Get set of all packages marked as installed.
493
494
 
@@ -501,4 +502,3 @@ class InstallationCache:
501
502
  def __len__(self) -> int:
502
503
  """Return number of cached packages."""
503
504
  return len(self._cache)
504
-
@@ -17,7 +17,7 @@ import os
17
17
  import sys
18
18
  import logging
19
19
  import io
20
- from typing import Dict, Optional
20
+ from typing import Optional
21
21
  from datetime import datetime
22
22
 
23
23
  # =============================================================================
@@ -37,7 +37,7 @@ _EMOJI_MAP = {
37
37
  }
38
38
 
39
39
  # Default log category states
40
- _CATEGORY_DEFAULTS: Dict[str, bool] = {
40
+ _CATEGORY_DEFAULTS: dict[str, bool] = {
41
41
  "install": True,
42
42
  "hook": False,
43
43
  "enhance": False,
@@ -52,7 +52,7 @@ _CATEGORY_DEFAULTS: Dict[str, bool] = {
52
52
  # =============================================================================
53
53
 
54
54
  _configured = False
55
- _category_overrides: Dict[str, bool] = {}
55
+ _category_overrides: dict[str, bool] = {}
56
56
 
57
57
  # =============================================================================
58
58
  # HELPER FUNCTIONS
@@ -186,7 +186,7 @@ def set_log_category(category: str, enabled: bool) -> None:
186
186
  """
187
187
  _category_overrides[_normalize_category(category)] = bool(enabled)
188
188
 
189
- def set_log_categories(overrides: Dict[str, bool]) -> None:
189
+ def set_log_categories(overrides: dict[str, bool]) -> None:
190
190
  """
191
191
  Bulk update multiple log categories.
192
192
 
@@ -196,7 +196,7 @@ def set_log_categories(overrides: Dict[str, bool]) -> None:
196
196
  for category, enabled in overrides.items():
197
197
  set_log_category(category, enabled)
198
198
 
199
- def get_log_categories() -> Dict[str, bool]:
199
+ def get_log_categories() -> dict[str, bool]:
200
200
  """
201
201
  Return the effective state for each built-in log category.
202
202
 
@@ -254,4 +254,3 @@ def print_formatted(flag: str, message: str, same_line: bool = False) -> None:
254
254
  sys.stdout.flush()
255
255
  else:
256
256
  print(formatted)
257
-
@@ -69,4 +69,3 @@ __all__ = [
69
69
  'async_install_package',
70
70
  'async_uninstall_package',
71
71
  ]
72
-
@@ -5,7 +5,7 @@ This module contains DependencyMapper class extracted from lazy_core.py Section
5
5
  """
6
6
 
7
7
  import threading
8
- from typing import Dict, List, Optional, Set, Tuple
8
+ from typing import Optional
9
9
 
10
10
  # Lazy import to avoid circular dependency
11
11
  def _get_logger():
@@ -59,14 +59,14 @@ class DependencyMapper:
59
59
  def __init__(self, package_name: str = 'default'):
60
60
  """Initialize dependency mapper."""
61
61
  self._discovery = None # Lazy init to avoid circular imports
62
- self._package_import_mapping: Dict[str, List[str]] = {}
63
- self._import_package_mapping: Dict[str, str] = {}
62
+ self._package_import_mapping: dict[str, list[str]] = {}
63
+ self._import_package_mapping: dict[str, str] = {}
64
64
  self._cached = False
65
65
  self._lock = threading.RLock()
66
66
  self._package_name = package_name
67
67
  self._manifest_generation = -1
68
- self._manifest_dependencies: Dict[str, str] = {}
69
- self._manifest_signature: Optional[Tuple[str, float, float]] = None
68
+ self._manifest_dependencies: dict[str, str] = {}
69
+ self._manifest_signature: Optional[tuple[str, float, float]] = None
70
70
  self._manifest_empty = False
71
71
 
72
72
  def set_package_name(self, package_name: str) -> None:
@@ -115,7 +115,7 @@ class DependencyMapper:
115
115
  manifest = loader.get_manifest(self._package_name)
116
116
  current_generation = loader.generation
117
117
 
118
- dependencies: Dict[str, str] = {}
118
+ dependencies: dict[str, str] = {}
119
119
  manifest_empty = True
120
120
  if manifest and manifest.dependencies:
121
121
  dependencies = {
@@ -143,7 +143,7 @@ class DependencyMapper:
143
143
  _cache_spec_if_missing(module_name)
144
144
  return needs_cache
145
145
 
146
- DENY_LIST: Set[str] = {
146
+ DENY_LIST: set[str] = {
147
147
  # POSIX-only modules that don't exist on Windows but try to auto-install
148
148
  "pwd",
149
149
  "grp",
@@ -191,7 +191,9 @@ class DependencyMapper:
191
191
  1. Skip checks (stdlib, deny list)
192
192
  2. Manifest dependencies (explicit user configuration - highest priority)
193
193
  3. Spec cache (module already exists - skip auto-install)
194
- 4. Discovery mappings (automatic discovery)
194
+ 4. Discovery mappings (automatic discovery from project configs)
195
+ 5. Common mappings (quick access list - works without project configs)
196
+ 6. Fallback to import_name itself
195
197
  """
196
198
  if self._should_skip_auto_install(import_name):
197
199
  return None
@@ -206,27 +208,42 @@ class DependencyMapper:
206
208
  if manifest_hit:
207
209
  return manifest_hit
208
210
 
209
- # Check spec cache - if module already exists, skip auto-install
211
+ # Check common mappings BEFORE spec cache (common mappings are reliable)
212
+ # This is important for exonware projects to work immediately
213
+ # Common mappings take precedence over spec cache because spec cache can be stale
214
+ discovery = self._get_discovery()
215
+ common_mappings = getattr(discovery, 'COMMON_MAPPINGS', {})
216
+ common_hit = common_mappings.get(import_name)
217
+ if common_hit:
218
+ return common_hit
219
+
220
+ # Check spec cache - if module already exists AND we don't have a common mapping, skip auto-install
221
+ # Note: We check this AFTER common mappings because spec cache can be stale after uninstallation
210
222
  if _spec_cache_get(import_name):
211
223
  return None
212
224
 
225
+ # Try discovery mappings (from project configs)
213
226
  self._ensure_mappings_cached()
214
- return self._import_package_mapping.get(import_name, import_name)
227
+ discovery_hit = self._import_package_mapping.get(import_name)
228
+ if discovery_hit:
229
+ return discovery_hit
230
+
231
+ # Fallback: assume import name matches package name
232
+ return import_name
215
233
 
216
- def get_import_names(self, package_name: str) -> List[str]:
234
+ def get_import_names(self, package_name: str) -> list[str]:
217
235
  """Get all possible import names for a package."""
218
236
  self._ensure_mappings_cached()
219
237
  return self._package_import_mapping.get(package_name, [package_name])
220
238
 
221
- def get_package_import_mapping(self) -> Dict[str, List[str]]:
239
+ def get_package_import_mapping(self) -> dict[str, list[str]]:
222
240
  """Get complete package to import names mapping."""
223
241
  self._ensure_mappings_cached()
224
242
  return self._package_import_mapping.copy()
225
243
 
226
- def get_import_package_mapping(self) -> Dict[str, str]:
244
+ def get_import_package_mapping(self) -> dict[str, str]:
227
245
  """Get complete import to package name mapping."""
228
246
  self._ensure_mappings_cached()
229
247
  return self._import_package_mapping.copy()
230
248
 
231
249
  __all__ = ['DependencyMapper']
232
-
@@ -13,11 +13,12 @@ Used by both execution strategies and LazyInstaller.
13
13
 
14
14
  import os
15
15
  import sys
16
- import json
17
16
  import asyncio
18
17
  import subprocess
19
18
  from typing import Optional
20
19
 
20
+ from exonware.xwsystem.io.serialization.auto_serializer import AutoSerializer
21
+
21
22
  # Lazy imports
22
23
  def _get_logger():
23
24
  """Get logger (lazy import to avoid circular dependency)."""
@@ -31,6 +32,11 @@ def _ensure_logging_initialized():
31
32
  global logger
32
33
  if logger is None:
33
34
  logger = _get_logger()
35
+ # Fallback if get_logger returns None (should not happen but safety first)
36
+ if logger is None:
37
+ import logging
38
+ logger = logging.getLogger("xwlazy.install_async_utils.fallback")
39
+ logger.addHandler(logging.NullHandler())
34
40
 
35
41
  async def get_package_size_mb(package_name: str) -> Optional[float]:
36
42
  """
@@ -73,7 +79,8 @@ async def get_package_size_mb(package_name: str) -> Optional[float]:
73
79
  import urllib.request
74
80
  url = f"https://pypi.org/pypi/{package_name}/json"
75
81
  with urllib.request.urlopen(url, timeout=5) as response:
76
- data = json.loads(response.read())
82
+ auto_serializer = AutoSerializer()
83
+ data = auto_serializer.detect_and_deserialize(response.read(), format_hint='json')
77
84
  if 'urls' in data and data['urls']:
78
85
  latest = data['urls'][0]
79
86
  if 'size' in latest:
@@ -162,4 +169,3 @@ __all__ = [
162
169
  'async_install_package',
163
170
  'async_uninstall_package',
164
171
  ]
165
-
@@ -19,7 +19,7 @@ import tempfile
19
19
  import subprocess
20
20
  import zipfile
21
21
  from pathlib import Path
22
- from typing import Optional, List
22
+ from typing import Optional
23
23
  from contextlib import suppress
24
24
 
25
25
  # Lazy imports
@@ -77,7 +77,7 @@ def get_site_packages_dir() -> Path:
77
77
  path.mkdir(parents=True, exist_ok=True)
78
78
  return path
79
79
 
80
- def pip_install_from_path(wheel_path: Path, policy_args: Optional[List[str]] = None) -> bool:
80
+ def pip_install_from_path(wheel_path: Path, policy_args: Optional[list[str]] = None) -> bool:
81
81
  """Install a wheel file using pip."""
82
82
  try:
83
83
  pip_args = [
@@ -104,7 +104,7 @@ def pip_install_from_path(wheel_path: Path, policy_args: Optional[List[str]] = N
104
104
 
105
105
  def ensure_cached_wheel(
106
106
  package_name: str,
107
- policy_args: Optional[List[str]] = None,
107
+ policy_args: Optional[list[str]] = None,
108
108
  cache_dir: Optional[Path] = None
109
109
  ) -> Optional[Path]:
110
110
  """Ensure a wheel is cached, downloading it if necessary."""
@@ -220,7 +220,7 @@ def has_cached_install_tree(
220
220
 
221
221
  def install_from_cached_wheel(
222
222
  package_name: str,
223
- policy_args: Optional[List[str]] = None,
223
+ policy_args: Optional[list[str]] = None,
224
224
  cache_dir: Optional[Path] = None
225
225
  ) -> bool:
226
226
  """Install from a cached wheel file."""
@@ -242,4 +242,3 @@ __all__ = [
242
242
  'has_cached_install_tree',
243
243
  'install_from_cached_wheel',
244
244
  ]
245
-
@@ -280,4 +280,3 @@ def _detect_lazy_installation(package_name: str) -> bool:
280
280
  _lazy_detection_cache[package_name] = detected
281
281
 
282
282
  return detected
283
-
@@ -21,7 +21,7 @@ import importlib
21
21
  import importlib.machinery
22
22
  import importlib.util
23
23
  from pathlib import Path
24
- from typing import Optional, Tuple
24
+ from typing import Optional
25
25
  from collections import OrderedDict
26
26
  from functools import lru_cache
27
27
 
@@ -31,7 +31,7 @@ _SPEC_CACHE_TTL = float(os.environ.get("XWLAZY_SPEC_CACHE_TTL", "60") or 60.0)
31
31
 
32
32
  # Cache storage
33
33
  _spec_cache_lock = threading.RLock()
34
- _spec_cache: OrderedDict[str, Tuple[importlib.machinery.ModuleSpec, float]] = OrderedDict()
34
+ _spec_cache: OrderedDict[str, tuple[importlib.machinery.ModuleSpec, float]] = OrderedDict()
35
35
 
36
36
  # Multi-level cache: L1 (in-memory) + L2 (disk)
37
37
  _CACHE_L2_DIR = Path(
@@ -162,4 +162,3 @@ __all__ = [
162
162
  '_cache_spec_if_missing',
163
163
  'get_stdlib_module_set',
164
164
  ]
165
-