exonware-xwapi 0.9.0.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 (155) hide show
  1. exonware_xwapi-0.9.0.2/.github/workflows/check-versions.yml +31 -0
  2. exonware_xwapi-0.9.0.2/.github/workflows/check_version_source_of_truth.py +147 -0
  3. exonware_xwapi-0.9.0.2/.github/workflows/compliance.yml +38 -0
  4. exonware_xwapi-0.9.0.2/.github/workflows/publish.yml +70 -0
  5. exonware_xwapi-0.9.0.2/.github/workflows/update_release_date_from_version_file.py +60 -0
  6. exonware_xwapi-0.9.0.2/.github/workflows/verify_tag_matches_version.py +70 -0
  7. exonware_xwapi-0.9.0.2/.github/workflows-config.yaml +8 -0
  8. exonware_xwapi-0.9.0.2/.gitignore +66 -0
  9. exonware_xwapi-0.9.0.2/LICENSE +22 -0
  10. exonware_xwapi-0.9.0.2/PKG-INFO +176 -0
  11. exonware_xwapi-0.9.0.2/README.md +87 -0
  12. exonware_xwapi-0.9.0.2/pyproject.toml +149 -0
  13. exonware_xwapi-0.9.0.2/src/exonware/__init__.py +1 -0
  14. exonware_xwapi-0.9.0.2/src/exonware/xwapi/__init__.py +227 -0
  15. exonware_xwapi-0.9.0.2/src/exonware/xwapi/action.py +46 -0
  16. exonware_xwapi-0.9.0.2/src/exonware/xwapi/base.py +357 -0
  17. exonware_xwapi-0.9.0.2/src/exonware/xwapi/client/__init__.py +12 -0
  18. exonware_xwapi-0.9.0.2/src/exonware/xwapi/client/base.py +16 -0
  19. exonware_xwapi-0.9.0.2/src/exonware/xwapi/client/engines/__init__.py +97 -0
  20. exonware_xwapi-0.9.0.2/src/exonware/xwapi/client/engines/base.py +47 -0
  21. exonware_xwapi-0.9.0.2/src/exonware/xwapi/client/engines/contracts.py +142 -0
  22. exonware_xwapi-0.9.0.2/src/exonware/xwapi/client/engines/native.py +118 -0
  23. exonware_xwapi-0.9.0.2/src/exonware/xwapi/client/xwclient.py +674 -0
  24. exonware_xwapi-0.9.0.2/src/exonware/xwapi/common/__init__.py +91 -0
  25. exonware_xwapi-0.9.0.2/src/exonware/xwapi/common/app.py +183 -0
  26. exonware_xwapi-0.9.0.2/src/exonware/xwapi/common/openapi.py +196 -0
  27. exonware_xwapi-0.9.0.2/src/exonware/xwapi/common/serialization.py +58 -0
  28. exonware_xwapi-0.9.0.2/src/exonware/xwapi/common/utils/__init__.py +11 -0
  29. exonware_xwapi-0.9.0.2/src/exonware/xwapi/common/utils/async_utils.py +99 -0
  30. exonware_xwapi-0.9.0.2/src/exonware/xwapi/config.py +43 -0
  31. exonware_xwapi-0.9.0.2/src/exonware/xwapi/contracts.py +206 -0
  32. exonware_xwapi-0.9.0.2/src/exonware/xwapi/defs.py +41 -0
  33. exonware_xwapi-0.9.0.2/src/exonware/xwapi/entity_store.py +109 -0
  34. exonware_xwapi-0.9.0.2/src/exonware/xwapi/errors.py +371 -0
  35. exonware_xwapi-0.9.0.2/src/exonware/xwapi/facade.py +551 -0
  36. exonware_xwapi-0.9.0.2/src/exonware/xwapi/providers.py +240 -0
  37. exonware_xwapi-0.9.0.2/src/exonware/xwapi/query.py +87 -0
  38. exonware_xwapi-0.9.0.2/src/exonware/xwapi/schema/__init__.py +25 -0
  39. exonware_xwapi-0.9.0.2/src/exonware/xwapi/schema/contracts.py +59 -0
  40. exonware_xwapi-0.9.0.2/src/exonware/xwapi/schema/generator.py +57 -0
  41. exonware_xwapi-0.9.0.2/src/exonware/xwapi/schema/graphql.py +67 -0
  42. exonware_xwapi-0.9.0.2/src/exonware/xwapi/schema/validator.py +69 -0
  43. exonware_xwapi-0.9.0.2/src/exonware/xwapi/serialization.py +22 -0
  44. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/__init__.py +21 -0
  45. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/admin/__init__.py +13 -0
  46. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/admin/router.py +459 -0
  47. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/base.py +16 -0
  48. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/__init__.py +199 -0
  49. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/base.py +94 -0
  50. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/contracts.py +214 -0
  51. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/email_store.py +220 -0
  52. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/fastapi.py +473 -0
  53. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/flask.py +338 -0
  54. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/graphql.py +319 -0
  55. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/grpc.py +329 -0
  56. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/http_base.py +156 -0
  57. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/imap.py +303 -0
  58. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/pop3.py +270 -0
  59. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/smtp.py +192 -0
  60. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/engines/websocket.py +215 -0
  61. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/governance/__init__.py +19 -0
  62. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/governance/lockfile.py +196 -0
  63. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/governance/registry.py +145 -0
  64. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/http/__init__.py +8 -0
  65. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/http/error_adapter.py +32 -0
  66. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/__init__.py +28 -0
  67. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/admin_auth.py +69 -0
  68. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/api_token.py +210 -0
  69. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/auth.py +149 -0
  70. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/observability.py +100 -0
  71. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/pause.py +47 -0
  72. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/ratelimit.py +140 -0
  73. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/tenant.py +70 -0
  74. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/middleware/trace.py +56 -0
  75. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/pipeline/__init__.py +18 -0
  76. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/pipeline/manager.py +57 -0
  77. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/pipeline/outbox.py +212 -0
  78. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/pipeline/worker.py +126 -0
  79. exonware_xwapi-0.9.0.2/src/exonware/xwapi/server/xwserver.py +1773 -0
  80. exonware_xwapi-0.9.0.2/src/exonware/xwapi/token_management.py +245 -0
  81. exonware_xwapi-0.9.0.2/src/exonware/xwapi/version.py +82 -0
  82. exonware_xwapi-0.9.0.2/src/xwapi.py +10 -0
  83. exonware_xwapi-0.9.0.2/tests/0.core/__init__.py +8 -0
  84. exonware_xwapi-0.9.0.2/tests/0.core/conftest.py +12 -0
  85. exonware_xwapi-0.9.0.2/tests/0.core/data/expected/.gitkeep +0 -0
  86. exonware_xwapi-0.9.0.2/tests/0.core/data/fixtures/.gitkeep +0 -0
  87. exonware_xwapi-0.9.0.2/tests/0.core/data/inputs/.gitkeep +0 -0
  88. exonware_xwapi-0.9.0.2/tests/0.core/runner.py +91 -0
  89. exonware_xwapi-0.9.0.2/tests/0.core/test_core_app.py +131 -0
  90. exonware_xwapi-0.9.0.2/tests/0.core/test_core_app_tough.py +478 -0
  91. exonware_xwapi-0.9.0.2/tests/0.core/test_core_errors.py +130 -0
  92. exonware_xwapi-0.9.0.2/tests/0.core/test_core_errors_tough.py +557 -0
  93. exonware_xwapi-0.9.0.2/tests/0.core/test_core_imports.py +51 -0
  94. exonware_xwapi-0.9.0.2/tests/0.core/test_core_serialization.py +110 -0
  95. exonware_xwapi-0.9.0.2/tests/0.core/test_core_server.py +44 -0
  96. exonware_xwapi-0.9.0.2/tests/1.unit/__init__.py +8 -0
  97. exonware_xwapi-0.9.0.2/tests/1.unit/action_tests/__init__.py +8 -0
  98. exonware_xwapi-0.9.0.2/tests/1.unit/action_tests/test_action_endpoint.py +46 -0
  99. exonware_xwapi-0.9.0.2/tests/1.unit/conftest.py +12 -0
  100. exonware_xwapi-0.9.0.2/tests/1.unit/engines_tests/__init__.py +8 -0
  101. exonware_xwapi-0.9.0.2/tests/1.unit/engines_tests/test_engine_agnostic_multi_protocol.py +84 -0
  102. exonware_xwapi-0.9.0.2/tests/1.unit/engines_tests/test_engine_registry.py +74 -0
  103. exonware_xwapi-0.9.0.2/tests/1.unit/engines_tests/test_fastapi_engine.py +94 -0
  104. exonware_xwapi-0.9.0.2/tests/1.unit/engines_tests/test_fastapi_engine_tough.py +555 -0
  105. exonware_xwapi-0.9.0.2/tests/1.unit/examples_tests/test_examples_runtime.py +53 -0
  106. exonware_xwapi-0.9.0.2/tests/1.unit/middleware_tests/__init__.py +8 -0
  107. exonware_xwapi-0.9.0.2/tests/1.unit/middleware_tests/test_api_token_helpers.py +63 -0
  108. exonware_xwapi-0.9.0.2/tests/1.unit/middleware_tests/test_middleware_tough.py +626 -0
  109. exonware_xwapi-0.9.0.2/tests/1.unit/middleware_tests/test_pause_middleware.py +73 -0
  110. exonware_xwapi-0.9.0.2/tests/1.unit/middleware_tests/test_trace_middleware.py +76 -0
  111. exonware_xwapi-0.9.0.2/tests/1.unit/openapi_tests/__init__.py +8 -0
  112. exonware_xwapi-0.9.0.2/tests/1.unit/openapi_tests/test_openapi_schema.py +70 -0
  113. exonware_xwapi-0.9.0.2/tests/1.unit/pipeline_tests/test_background_worker.py +49 -0
  114. exonware_xwapi-0.9.0.2/tests/1.unit/pipeline_tests/test_outbox_in_memory.py +50 -0
  115. exonware_xwapi-0.9.0.2/tests/1.unit/query_tests/__init__.py +8 -0
  116. exonware_xwapi-0.9.0.2/tests/1.unit/query_tests/test_query_params.py +54 -0
  117. exonware_xwapi-0.9.0.2/tests/1.unit/query_tests/test_query_tough.py +574 -0
  118. exonware_xwapi-0.9.0.2/tests/1.unit/runner.py +91 -0
  119. exonware_xwapi-0.9.0.2/tests/1.unit/serialization_tests/__init__.py +8 -0
  120. exonware_xwapi-0.9.0.2/tests/1.unit/serialization_tests/test_serialization.py +34 -0
  121. exonware_xwapi-0.9.0.2/tests/1.unit/server_tests/__init__.py +8 -0
  122. exonware_xwapi-0.9.0.2/tests/1.unit/server_tests/test_server.py +134 -0
  123. exonware_xwapi-0.9.0.2/tests/1.unit/server_tests/test_server_admin_security.py +24 -0
  124. exonware_xwapi-0.9.0.2/tests/1.unit/server_tests/test_server_pause_policy.py +39 -0
  125. exonware_xwapi-0.9.0.2/tests/1.unit/server_tests/test_server_pipeline.py +109 -0
  126. exonware_xwapi-0.9.0.2/tests/1.unit/server_tests/test_server_stop_flush.py +50 -0
  127. exonware_xwapi-0.9.0.2/tests/1.unit/server_tests/test_server_tough.py +590 -0
  128. exonware_xwapi-0.9.0.2/tests/1.unit/test_agent.py +172 -0
  129. exonware_xwapi-0.9.0.2/tests/1.unit/test_entity_store.py +64 -0
  130. exonware_xwapi-0.9.0.2/tests/1.unit/test_facade_entity_generation.py +107 -0
  131. exonware_xwapi-0.9.0.2/tests/1.unit/test_facade_lifecycle.py +57 -0
  132. exonware_xwapi-0.9.0.2/tests/1.unit/test_governance_lockfile.py +40 -0
  133. exonware_xwapi-0.9.0.2/tests/1.unit/test_governance_registry.py +45 -0
  134. exonware_xwapi-0.9.0.2/tests/1.unit/test_token_management.py +317 -0
  135. exonware_xwapi-0.9.0.2/tests/2.integration/__init__.py +8 -0
  136. exonware_xwapi-0.9.0.2/tests/2.integration/conftest.py +12 -0
  137. exonware_xwapi-0.9.0.2/tests/2.integration/runner.py +91 -0
  138. exonware_xwapi-0.9.0.2/tests/2.integration/test_agent_server.py +107 -0
  139. exonware_xwapi-0.9.0.2/tests/2.integration/test_integration_admin_auth.py +124 -0
  140. exonware_xwapi-0.9.0.2/tests/2.integration/test_integration_admin_router.py +101 -0
  141. exonware_xwapi-0.9.0.2/tests/2.integration/test_integration_api_token_middleware.py +317 -0
  142. exonware_xwapi-0.9.0.2/tests/2.integration/test_integration_app_flow.py +126 -0
  143. exonware_xwapi-0.9.0.2/tests/2.integration/test_integration_entity_crud.py +76 -0
  144. exonware_xwapi-0.9.0.2/tests/2.integration/test_integration_server_pause_resume.py +210 -0
  145. exonware_xwapi-0.9.0.2/tests/2.integration/test_integration_token_management.py +78 -0
  146. exonware_xwapi-0.9.0.2/tests/2.integration/test_integration_tough.py +573 -0
  147. exonware_xwapi-0.9.0.2/tests/3.advance/__init__.py +4 -0
  148. exonware_xwapi-0.9.0.2/tests/3.advance/conftest.py +5 -0
  149. exonware_xwapi-0.9.0.2/tests/3.advance/runner.py +82 -0
  150. exonware_xwapi-0.9.0.2/tests/3.advance/test_advance_smoke.py +6 -0
  151. exonware_xwapi-0.9.0.2/tests/3.advance/test_reliability_stress.py +81 -0
  152. exonware_xwapi-0.9.0.2/tests/__init__.py +8 -0
  153. exonware_xwapi-0.9.0.2/tests/conftest.py +73 -0
  154. exonware_xwapi-0.9.0.2/tests/runner.py +235 -0
  155. exonware_xwapi-0.9.0.2/tests/verify_installation.py +65 -0
@@ -0,0 +1,31 @@
1
+ ## NOTE: AUTO-GENERATED FILE
2
+ ## This workflow was generated by tools/ci/python_scripts/generate_workflows.py.
3
+ ## Do NOT edit this file manually - changes will be overwritten.
4
+
5
+ name: Check for Hardcoded Versions
6
+
7
+ on:
8
+ pull_request:
9
+ paths:
10
+ - 'src/**/*.py'
11
+ - 'tests/**/*.py'
12
+ - 'docs/**/*.md'
13
+ - '*.md'
14
+ - '*.txt'
15
+ push:
16
+ branches: [main, develop]
17
+
18
+ jobs:
19
+ check-versions:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v3
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v4
26
+ with:
27
+ python-version: '3.12'
28
+
29
+ - name: Verify version.py is the source of truth
30
+ run: |
31
+ python .github/workflows/check_version_source_of_truth.py
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Verify that the version configured in pyproject.toml ultimately comes from a
4
+ single source-of-truth version location (file or attribute) and print it.
5
+
6
+ This is extracted from the GitHub Actions check-versions workflow so it can be
7
+ tested and reused outside of CI.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import importlib
13
+ import re
14
+ import sys
15
+ from pathlib import Path
16
+
17
+
18
+ def _read_pyproject() -> str:
19
+ path = Path("pyproject.toml")
20
+ if not path.exists():
21
+ print("No pyproject.toml", file=sys.stderr)
22
+ sys.exit(2)
23
+ return path.read_text(encoding="utf-8")
24
+
25
+
26
+ def _read_attr_from_module_source(
27
+ project_root: Path,
28
+ module_name: str,
29
+ attr_name: str,
30
+ ) -> str | None:
31
+ """
32
+ Resolve module source under ./src and read a simple string assignment
33
+ like: __version__ = "1.2.3"
34
+ This avoids import side effects from package-level dependencies.
35
+ """
36
+ module_rel = Path(*module_name.split("."))
37
+ candidates = [
38
+ project_root / "src" / (str(module_rel) + ".py"),
39
+ project_root / "src" / module_rel / "__init__.py",
40
+ ]
41
+ pattern = re.compile(
42
+ rf"^\s*{re.escape(attr_name)}\s*=\s*['\"]([^'\"]+)['\"]\s*$",
43
+ re.MULTILINE,
44
+ )
45
+ for candidate in candidates:
46
+ if not candidate.exists():
47
+ continue
48
+ text = candidate.read_text(encoding="utf-8")
49
+ m = pattern.search(text)
50
+ if m:
51
+ return m.group(1)
52
+ return None
53
+
54
+
55
+ def main() -> None:
56
+ text = _read_pyproject()
57
+
58
+ # 1) Hatchling: [tool.hatch.version] path = ".../version.py"
59
+ m = re.search(
60
+ r"\[tool\.hatch\.version\][^\n]*\n\s*path\s*=\s*[\"']([^\"']+)[\"']",
61
+ text,
62
+ re.DOTALL,
63
+ )
64
+ if m:
65
+ version_file = Path(m.group(1).strip())
66
+ if not version_file.exists():
67
+ print(f"Version file not found: {version_file}", file=sys.stderr)
68
+ sys.exit(2)
69
+ ns: dict[str, object] = {}
70
+ exec(version_file.read_text(encoding="utf-8"), ns)
71
+ version = ns.get("__version__")
72
+ if not isinstance(version, str) or not version:
73
+ print(f"__version__ missing or empty in {version_file}", file=sys.stderr)
74
+ sys.exit(2)
75
+ print("Current version:", version)
76
+ sys.exit(0)
77
+
78
+ # 2) Setuptools dynamic inline table (file=...) -> open version file and exec
79
+ m = re.search(
80
+ r'version\s*=\s*\{\s*file\s*=\s*["\']([^"\']+)["\']',
81
+ text,
82
+ re.DOTALL,
83
+ )
84
+ if not m:
85
+ # 2b) Setuptools dynamic table style:
86
+ # [tool.setuptools.dynamic.version]
87
+ # attr = "pkg.module.__version__" OR file = "..."
88
+ m = re.search(
89
+ r"\[tool\.setuptools\.dynamic\.version\][^\n]*\n\s*file\s*=\s*[\"']([^\"']+)[\"']",
90
+ text,
91
+ re.DOTALL,
92
+ )
93
+ if m:
94
+ version_file = Path(m.group(1).strip())
95
+ if not version_file.exists():
96
+ print(f"Version file not found: {version_file}", file=sys.stderr)
97
+ sys.exit(2)
98
+ ns: dict[str, object] = {}
99
+ exec(version_file.read_text(encoding="utf-8"), ns)
100
+ version = ns.get("__version__")
101
+ if not isinstance(version, str) or not version:
102
+ print(f"__version__ missing or empty in {version_file}", file=sys.stderr)
103
+ sys.exit(2)
104
+ print("Current version:", version)
105
+ sys.exit(0)
106
+
107
+ # 3) Setuptools dynamic inline table (attr="pkg.module.__version__")
108
+ m = re.search(
109
+ r'version\s*=\s*\{\s*attr\s*=\s*["\']([^"\']+)["\']',
110
+ text,
111
+ re.DOTALL,
112
+ )
113
+ if not m:
114
+ # 3b) Setuptools dynamic table style:
115
+ # [tool.setuptools.dynamic.version]
116
+ # attr = "pkg.module.__version__"
117
+ m = re.search(
118
+ r"\[tool\.setuptools\.dynamic\.version\][^\n]*\n\s*attr\s*=\s*[\"']([^\"']+)[\"']",
119
+ text,
120
+ re.DOTALL,
121
+ )
122
+ if m:
123
+ attr_path = m.group(1).strip()
124
+ module_name, _, attr = attr_path.rpartition(".")
125
+ if not module_name or not attr:
126
+ sys.exit(2)
127
+ version = _read_attr_from_module_source(Path("."), module_name, attr)
128
+ if version is None:
129
+ # Fallback to import for unusual dynamic cases.
130
+ mod = importlib.import_module(module_name)
131
+ version = getattr(mod, attr, None)
132
+ if not isinstance(version, str) or not version:
133
+ print(
134
+ f"Attribute {attr_path} is missing or empty",
135
+ file=sys.stderr,
136
+ )
137
+ sys.exit(2)
138
+ print("Current version:", version)
139
+ sys.exit(0)
140
+
141
+ # If none of the patterns matched, exit with a non-success status expected by CI.
142
+ sys.exit(2)
143
+
144
+
145
+ if __name__ == "__main__":
146
+ main()
147
+
@@ -0,0 +1,38 @@
1
+ name: xwapi Compliance
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - "xwapi/**"
7
+ - "tools/ci/python_scripts/**"
8
+ push:
9
+ branches: ["main"]
10
+ paths:
11
+ - "xwapi/**"
12
+ - "tools/ci/python_scripts/**"
13
+
14
+ jobs:
15
+ compliance:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+
25
+ - name: Install dev dependencies
26
+ run: |
27
+ python -m pip install --upgrade pip
28
+ pip install -r xwapi/requirements.txt
29
+
30
+ - name: Format & type checks
31
+ run: |
32
+ black --check xwapi/src xwapi/tests
33
+ isort --check-only xwapi/src xwapi/tests
34
+ mypy xwapi/src
35
+
36
+ - name: Test suite
37
+ run: |
38
+ python -m pytest xwapi/tests
@@ -0,0 +1,70 @@
1
+ # NOTE: AUTO-GENERATED FILE
2
+ # This workflow was generated by tools/ci/python_scripts/generate_workflows.py.
3
+ # Do NOT edit this file manually - changes will be overwritten.
4
+
5
+ name: Dual PyPI Publish - exonware-xwapi & xwapi
6
+
7
+ on:
8
+ push:
9
+ tags:
10
+ - "v*"
11
+ workflow_dispatch:
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python
21
+ uses: actions/setup-python@v4
22
+ with:
23
+ python-version: "3.12"
24
+
25
+ - name: Verify tag matches version.py
26
+ run: |
27
+ python .github/workflows/verify_tag_matches_version.py
28
+
29
+ - name: Install dependencies
30
+ run: |
31
+ python -m pip install --upgrade pip
32
+ pip install build twine
33
+
34
+ - name: Set release date to today
35
+ run: |
36
+ python .github/workflows/update_release_date_from_version_file.py
37
+
38
+ - name: Build exonware-xwapi package
39
+ run: |
40
+ echo "Building exonware-xwapi package..."
41
+ python -m build
42
+
43
+ - name: Build xwapi package
44
+ run: |
45
+ echo "Building xwapi package..."
46
+ cp pyproject.toml pyproject.backup.toml
47
+ cp pyproject.xwapi.toml pyproject.toml
48
+ python -m build
49
+ mv pyproject.backup.toml pyproject.toml
50
+
51
+ - name: List built packages
52
+ run: |
53
+ echo "Built packages:"
54
+ find dist -name "*.whl" -o -name "*.tar.gz" | sort
55
+
56
+ - name: Publish exonware-xwapi to PyPI
57
+ env:
58
+ TWINE_USERNAME: __token__
59
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
60
+ run: |
61
+ echo "Publishing exonware-xwapi to PyPI..."
62
+ twine upload dist/exonware_xwapi-*.whl dist/exonware_xwapi-*.tar.gz
63
+
64
+ - name: Publish xwapi to PyPI
65
+ env:
66
+ TWINE_USERNAME: __token__
67
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
68
+ run: |
69
+ echo "Publishing xwapi to PyPI..."
70
+ twine upload dist/xwapi-*.whl dist/xwapi-*.tar.gz
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Update a __date__ literal in the version file referenced from pyproject.toml
4
+ to today's ISO date (YYYY-MM-DD), when present as a string literal.
5
+
6
+ This is extracted from the GitHub Actions publish workflow so it can be reused
7
+ and tested outside of CI.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ import sys
14
+ from datetime import date
15
+ from pathlib import Path
16
+
17
+
18
+ def main() -> None:
19
+ pyproject_path = Path("pyproject.toml")
20
+ if not pyproject_path.exists():
21
+ print("No pyproject.toml; skipping __date__ update")
22
+ sys.exit(0)
23
+
24
+ pyproject = pyproject_path.read_text(encoding="utf-8")
25
+ m = re.search(
26
+ r"\[tool\.hatch\.version\][^\n]*\n\s*path\s*=\s*[\"']([^\"']+)[\"']",
27
+ pyproject,
28
+ re.DOTALL,
29
+ )
30
+ if not m:
31
+ print("No [tool.hatch.version] path; skipping __date__ update")
32
+ sys.exit(0)
33
+
34
+ version_file = Path(m.group(1).strip())
35
+ if not version_file.exists():
36
+ print(f"Version file not found: {version_file}; skipping __date__ update")
37
+ sys.exit(0)
38
+
39
+ text = version_file.read_text(encoding="utf-8")
40
+ literal_match = re.search(
41
+ r"__date__\s*=\s*[\"'][^\"']*[\"']",
42
+ text,
43
+ )
44
+ if not literal_match:
45
+ print("__date__ is dynamic or missing; no update needed")
46
+ sys.exit(0)
47
+
48
+ today = date.today().isoformat()
49
+ new_text = re.sub(
50
+ r"(__date__\s*=\s*)[\"'][^\"']*[\"']",
51
+ r'\1"' + today + '"',
52
+ text,
53
+ )
54
+ version_file.write_text(new_text, encoding="utf-8")
55
+ print("Set __date__ to", today, "in", version_file)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ main()
60
+
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Verify that the current Git reference (tag) matches the library version defined
4
+ in the version file referenced from pyproject.toml.
5
+
6
+ This script is extracted from the GitHub Actions publish workflow so it can be
7
+ reused and tested outside of CI.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ import re
14
+ import sys
15
+ from pathlib import Path
16
+
17
+
18
+ def _load_version_from_pyproject() -> str | None:
19
+ pyproject_path = Path("pyproject.toml")
20
+ if not pyproject_path.exists():
21
+ print("No pyproject.toml; skipping", file=sys.stderr)
22
+ return None
23
+
24
+ text = pyproject_path.read_text(encoding="utf-8")
25
+ m = re.search(
26
+ r"\[tool\.hatch\.version\][^\n]*\n\s*path\s*=\s*[\"']([^\"']+)[\"']",
27
+ text,
28
+ re.DOTALL,
29
+ )
30
+ if not m:
31
+ print("No [tool.hatch.version] path; skipping", file=sys.stderr)
32
+ return None
33
+
34
+ version_file = Path(m.group(1).strip())
35
+ if not version_file.exists():
36
+ print(f"Version file not found: {version_file}", file=sys.stderr)
37
+ sys.exit(1)
38
+
39
+ ns: dict[str, object] = {}
40
+ exec(version_file.read_text(encoding="utf-8"), ns)
41
+ version = ns.get("__version__")
42
+ if not isinstance(version, str) or not version:
43
+ print(f"__version__ missing or empty in {version_file}", file=sys.stderr)
44
+ sys.exit(1)
45
+ return version
46
+
47
+
48
+ def main() -> None:
49
+ file_ver = _load_version_from_pyproject()
50
+ if not file_ver:
51
+ sys.exit(0)
52
+
53
+ ref = os.environ.get("GITHUB_REF", "")
54
+ if not ref.startswith("refs/tags/"):
55
+ print(f"Not a tag build; version: {file_ver}")
56
+ sys.exit(0)
57
+
58
+ tag_name = ref.removeprefix("refs/tags/")
59
+ tag_ver = tag_name[1:] if tag_name.startswith("v") else tag_name
60
+
61
+ if tag_ver != file_ver:
62
+ print(f"ERROR: Tag {tag_ver} != version.py {file_ver}", file=sys.stderr)
63
+ sys.exit(1)
64
+
65
+ print("OK: tag and version match:", file_ver)
66
+ sys.exit(0)
67
+
68
+
69
+ if __name__ == "__main__":
70
+ main()
@@ -0,0 +1,8 @@
1
+ projects:
2
+ xwapi:
3
+ repo: xwapi
4
+ package_main: exonware-xwapi
5
+ package_wrapper: xwapi
6
+ python_version: "3.12"
7
+ has_wrapper: true
8
+ uses_check_versions: true
@@ -0,0 +1,66 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ ENV/
26
+ env/
27
+ .venv
28
+
29
+ # IDE
30
+ .vscode/
31
+ .idea/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # Testing
37
+ .pytest_cache/
38
+ .coverage
39
+ htmlcov/
40
+ .tox/
41
+
42
+ # Documentation
43
+ docs/_build/
44
+
45
+ # OS
46
+ .DS_Store
47
+ Thumbs.db
48
+
49
+ # xwlazy generated files (should be in ~/.xwlazy/ but ignore in case of old behavior)
50
+ xwlazy.lock.toml
51
+ xwlazy_sbom.toml
52
+
53
+ # Local archive folders
54
+ .archieve/
55
+
56
+ # Local secrets file
57
+ .secrets
58
+
59
+ .examples/
60
+
61
+ # Local private folders
62
+ .secrets
63
+ .references
64
+ .venv
65
+ .archive
66
+
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 eXonware.com - eXonware Backend Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: exonware-xwapi
3
+ Version: 0.9.0.2
4
+ Summary: Entity-to-Web-API conversion library: Python framework that turns internal configuration, service functions, and entities into production-ready APIs using FastAPI, with minimal boilerplate
5
+ Project-URL: Homepage, https://exonware.com
6
+ Project-URL: Repository, https://github.com/exonware/xwapi
7
+ Project-URL: Documentation, https://github.com/exonware/xwapi#readme
8
+ Project-URL: Subtree, https://github.com/exonware/xwapi.git
9
+ Author-email: eXonware Backend Team <connect@exonware.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: api,exonware,fastapi,graphql,oauth2,openapi,rest
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Information Technology
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Internet :: WWW/HTTP
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.12
23
+ Requires-Dist: exonware-xwaction
24
+ Requires-Dist: exonware-xwdata
25
+ Requires-Dist: exonware-xwentity
26
+ Requires-Dist: exonware-xwschema
27
+ Requires-Dist: exonware-xwsystem
28
+ Requires-Dist: fastapi>=0.104.0
29
+ Requires-Dist: requests>=2.31.0
30
+ Requires-Dist: uvicorn[standard]>=0.24.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: black>=23.0.0; extra == 'dev'
33
+ Requires-Dist: exonware-xwlazy; extra == 'dev'
34
+ Requires-Dist: isort>=5.12.0; extra == 'dev'
35
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
36
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
37
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
38
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
39
+ Provides-Extra: full
40
+ Requires-Dist: a2wsgi; extra == 'full'
41
+ Requires-Dist: aiodns; extra == 'full'
42
+ Requires-Dist: aiohttp; extra == 'full'
43
+ Requires-Dist: atomicwrites; extra == 'full'
44
+ Requires-Dist: attrs; extra == 'full'
45
+ Requires-Dist: bottleneck; extra == 'full'
46
+ Requires-Dist: brotli; extra == 'full'
47
+ Requires-Dist: brotlicffi; extra == 'full'
48
+ Requires-Dist: cachebox; extra == 'full'
49
+ Requires-Dist: cachetools; extra == 'full'
50
+ Requires-Dist: cbor2; extra == 'full'
51
+ Requires-Dist: chardet; extra == 'full'
52
+ Requires-Dist: cloudpickle; extra == 'full'
53
+ Requires-Dist: cython; extra == 'full'
54
+ Requires-Dist: dicttoxml; extra == 'full'
55
+ Requires-Dist: httpx; extra == 'full'
56
+ Requires-Dist: hyperlight-hyperjson; extra == 'full'
57
+ Requires-Dist: interegular; extra == 'full'
58
+ Requires-Dist: json5; extra == 'full'
59
+ Requires-Dist: lark; extra == 'full'
60
+ Requires-Dist: lz4; extra == 'full'
61
+ Requires-Dist: msgpack; extra == 'full'
62
+ Requires-Dist: msgspec; extra == 'full'
63
+ Requires-Dist: numexpr; extra == 'full'
64
+ Requires-Dist: numpy; extra == 'full'
65
+ Requires-Dist: orjson; extra == 'full'
66
+ Requires-Dist: pandas; extra == 'full'
67
+ Requires-Dist: pyarrow; extra == 'full'
68
+ Requires-Dist: pyjwt; extra == 'full'
69
+ Requires-Dist: pylru; extra == 'full'
70
+ Requires-Dist: pymongo; extra == 'full'
71
+ Requires-Dist: python-multipart; extra == 'full'
72
+ Requires-Dist: pytz; extra == 'full'
73
+ Requires-Dist: regex; extra == 'full'
74
+ Requires-Dist: rich; extra == 'full'
75
+ Requires-Dist: scikit-learn; extra == 'full'
76
+ Requires-Dist: simplejson; extra == 'full'
77
+ Requires-Dist: socks; extra == 'full'
78
+ Requires-Dist: tomli; extra == 'full'
79
+ Requires-Dist: tomli-w; extra == 'full'
80
+ Requires-Dist: uarray; extra == 'full'
81
+ Requires-Dist: xmltodict; extra == 'full'
82
+ Requires-Dist: zstandard; extra == 'full'
83
+ Provides-Extra: lazy
84
+ Requires-Dist: exonware-xwlazy; extra == 'lazy'
85
+ Requires-Dist: exonware-xwsystem[lazy]; extra == 'lazy'
86
+ Provides-Extra: storage
87
+ Requires-Dist: exonware-xwstorage; extra == 'storage'
88
+ Description-Content-Type: text/markdown
89
+
90
+ # xwapi
91
+
92
+ Engine-agnostic API framework for the eXonware stack. `xwapi` exposes `xwentity` and `xwaction` as HTTP endpoints, standardizes error contracts, supports production middleware, and now includes a durable action pipeline plus API token lifecycle/metering.
93
+
94
+ *Longer guide: [README_LONG.md](README_LONG.md).*
95
+
96
+ **Company:** eXonware.com · **Author:** eXonware Backend Team · **Email:** connect@exonware.com
97
+
98
+ [![Status](https://img.shields.io/badge/status-beta-blue.svg)](https://exonware.com)
99
+ [![Python](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org)
100
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
101
+
102
+ ---
103
+
104
+ ## Install
105
+
106
+ | Install | When to use |
107
+ |---------|-------------|
108
+ | `pip install exonware-xwapi` | Core runtime |
109
+ | `pip install exonware-xwapi[lazy]` | Lazy dependency loading |
110
+ | `pip install exonware-xwapi[full]` | Full production dependency set |
111
+
112
+ ---
113
+
114
+ ## Quick start
115
+
116
+ ```python
117
+ from exonware.xwapi import XWAPI
118
+ from exonware.xwentity import XWEntity
119
+
120
+
121
+ class User(XWEntity):
122
+ name: str
123
+ email: str
124
+ age: int
125
+
126
+
127
+ api = XWAPI(entities=[User], title="My API", version="1.0.0")
128
+ app = api.create_app(engine="fastapi")
129
+ ```
130
+
131
+ ---
132
+
133
+ ## New production features
134
+
135
+ - **Engine-agnostic error contract:** `xwapi_error_to_http_parts` plus adapters keeps `XWAPIError` transport-neutral.
136
+ - **Outbox + singleton worker pipeline:** `ActionPipelineManager`, `AOutboxStore`/`InMemoryOutboxStore`, and `BackgroundWorker`.
137
+ - **API token lifecycle:** create/list/revoke tokens, usage tracking, balance/recharge, idempotent metering.
138
+ - **Provider abstractions:** `IAuthProvider`, `IStorageProvider`, `IPaymentProvider` with in-memory and library adapters.
139
+ - **API token middleware:** bearer verification, optional scope enforcement, deny-unmapped policy, usage metering via `Idempotency-Key`.
140
+ - **Admin/operations endpoints:** server status/health/pipeline controls and token admin endpoints.
141
+ - **Production guardrails:** environment-based admin token enforcement and admin read-protection support.
142
+
143
+ ---
144
+
145
+ ## eXonware integration
146
+
147
+ `xwapi` now explicitly integrates with:
148
+
149
+ - `xwsystem` (serialization, logging/utilities)
150
+ - `xwaction` (action registration and execution)
151
+ - `xwentity` (entity-driven API surfaces)
152
+ - `xwschema` (schema validation/generation integration points)
153
+ - `xwdata` (data/serialization integration paths)
154
+
155
+ ---
156
+
157
+ ## Docs and tests
158
+
159
+ - Start at [docs/INDEX.md](docs/INDEX.md).
160
+ - API and architecture references: [docs/REF_15_API.md](docs/REF_15_API.md), [docs/REF_13_ARCH.md](docs/REF_13_ARCH.md).
161
+ - Test layers: `0.core`, `1.unit`, `2.integration`, `3.advance`.
162
+ - Full run: `python tests/runner.py`
163
+
164
+ ---
165
+
166
+ ## Async support
167
+
168
+ - Core runtime includes async methods across facade, token manager, middleware paths, and server actions.
169
+ - Async APIs are recommended for I/O-heavy and concurrent workloads.
170
+
171
+ ---
172
+
173
+ MIT - see [LICENSE](LICENSE). Homepage: https://exonware.com
174
+ Version: 0.9.0.2 | Updated: 01-Apr-2026
175
+
176
+ *Built with ❤️ by eXonware.com - Revolutionizing Python Development Since 2025*