storepy 4.2.0__tar.gz → 4.2.1__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 (76) hide show
  1. {storepy-4.2.0/src/storepy.egg-info → storepy-4.2.1}/PKG-INFO +1 -1
  2. {storepy-4.2.0 → storepy-4.2.1}/pyproject.toml +1 -1
  3. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/resource/__init__.py +4 -2
  4. {storepy-4.2.0 → storepy-4.2.1/src/storepy.egg-info}/PKG-INFO +1 -1
  5. {storepy-4.2.0 → storepy-4.2.1}/tests/test_resource.py +41 -0
  6. {storepy-4.2.0 → storepy-4.2.1}/LICENSE +0 -0
  7. {storepy-4.2.0 → storepy-4.2.1}/README.md +0 -0
  8. {storepy-4.2.0 → storepy-4.2.1}/setup.cfg +0 -0
  9. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/__init__.py +0 -0
  10. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/ask.py +0 -0
  11. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/ask_knowledge.md +0 -0
  12. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/bootstrap.py +0 -0
  13. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/cache.py +0 -0
  14. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/core.py +0 -0
  15. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/crud/__init__.py +0 -0
  16. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/crud/exec.py +0 -0
  17. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/crud/id.py +0 -0
  18. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/crud/mutation.py +0 -0
  19. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/crud/query.py +0 -0
  20. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/crud/write.py +0 -0
  21. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/datasource.py +0 -0
  22. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/ddl.py +0 -0
  23. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/executors/__init__.py +0 -0
  24. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/executors/_values.py +0 -0
  25. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/executors/mongo.py +0 -0
  26. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/executors/mysql.py +0 -0
  27. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/executors/postgres.py +0 -0
  28. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/executors/sqlite.py +0 -0
  29. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/feedback.py +0 -0
  30. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/introspect/__init__.py +0 -0
  31. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/introspect/mysql.py +0 -0
  32. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/introspect/postgres.py +0 -0
  33. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/introspect/sqlite.py +0 -0
  34. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/llm.py +0 -0
  35. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/load.py +0 -0
  36. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/metadef.py +0 -0
  37. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/naming.py +0 -0
  38. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/permission.py +0 -0
  39. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/resource/providers/__init__.py +0 -0
  40. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/resource/providers/local.py +0 -0
  41. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/resource/providers/s3.py +0 -0
  42. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/schema.py +0 -0
  43. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/sync.py +0 -0
  44. {storepy-4.2.0 → storepy-4.2.1}/src/py_store/workflow.py +0 -0
  45. {storepy-4.2.0 → storepy-4.2.1}/src/storepy.egg-info/SOURCES.txt +0 -0
  46. {storepy-4.2.0 → storepy-4.2.1}/src/storepy.egg-info/dependency_links.txt +0 -0
  47. {storepy-4.2.0 → storepy-4.2.1}/src/storepy.egg-info/requires.txt +0 -0
  48. {storepy-4.2.0 → storepy-4.2.1}/src/storepy.egg-info/top_level.txt +0 -0
  49. {storepy-4.2.0 → storepy-4.2.1}/tests/test_ask.py +0 -0
  50. {storepy-4.2.0 → storepy-4.2.1}/tests/test_cache.py +0 -0
  51. {storepy-4.2.0 → storepy-4.2.1}/tests/test_coverage_margin.py +0 -0
  52. {storepy-4.2.0 → storepy-4.2.1}/tests/test_federation_e2e.py +0 -0
  53. {storepy-4.2.0 → storepy-4.2.1}/tests/test_feedback_flush.py +0 -0
  54. {storepy-4.2.0 → storepy-4.2.1}/tests/test_fns_binding.py +0 -0
  55. {storepy-4.2.0 → storepy-4.2.1}/tests/test_host_contract.py +0 -0
  56. {storepy-4.2.0 → storepy-4.2.1}/tests/test_host_paths.py +0 -0
  57. {storepy-4.2.0 → storepy-4.2.1}/tests/test_load.py +0 -0
  58. {storepy-4.2.0 → storepy-4.2.1}/tests/test_metadef.py +0 -0
  59. {storepy-4.2.0 → storepy-4.2.1}/tests/test_migration.py +0 -0
  60. {storepy-4.2.0 → storepy-4.2.1}/tests/test_mongo_executor.py +0 -0
  61. {storepy-4.2.0 → storepy-4.2.1}/tests/test_mongo_session.py +0 -0
  62. {storepy-4.2.0 → storepy-4.2.1}/tests/test_multi_datasource.py +0 -0
  63. {storepy-4.2.0 → storepy-4.2.1}/tests/test_parity_deny.py +0 -0
  64. {storepy-4.2.0 → storepy-4.2.1}/tests/test_profile.py +0 -0
  65. {storepy-4.2.0 → storepy-4.2.1}/tests/test_py_store.py +0 -0
  66. {storepy-4.2.0 → storepy-4.2.1}/tests/test_raw_sql_and_ddl.py +0 -0
  67. {storepy-4.2.0 → storepy-4.2.1}/tests/test_rbac_host.py +0 -0
  68. {storepy-4.2.0 → storepy-4.2.1}/tests/test_real_backends_e2e.py +0 -0
  69. {storepy-4.2.0 → storepy-4.2.1}/tests/test_require_context.py +0 -0
  70. {storepy-4.2.0 → storepy-4.2.1}/tests/test_resource_e2e.py +0 -0
  71. {storepy-4.2.0 → storepy-4.2.1}/tests/test_resource_units.py +0 -0
  72. {storepy-4.2.0 → storepy-4.2.1}/tests/test_scenario_course_platform.py +0 -0
  73. {storepy-4.2.0 → storepy-4.2.1}/tests/test_scenario_manager_transaction.py +0 -0
  74. {storepy-4.2.0 → storepy-4.2.1}/tests/test_session.py +0 -0
  75. {storepy-4.2.0 → storepy-4.2.1}/tests/test_sql_commit_and_registry.py +0 -0
  76. {storepy-4.2.0 → storepy-4.2.1}/tests/test_workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: storepy
3
- Version: 4.2.0
3
+ Version: 4.2.1
4
4
  Summary: Multi-backend data layer for Python asyncio (MongoDB, MySQL, SQLite, PostgreSQL): pure JSON schemas, GQL tree queries compiled to a single native query, GROUP BY/HAVING aggregation, computed columns, soft-delete and role-based access control
5
5
  Author-email: leo <coen_ddt@qq.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "storepy"
7
- version = "4.2.0"
7
+ version = "4.2.1"
8
8
  description = "Multi-backend data layer for Python asyncio (MongoDB, MySQL, SQLite, PostgreSQL): pure JSON schemas, GQL tree queries compiled to a single native query, GROUP BY/HAVING aggregation, computed columns, soft-delete and role-based access control"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -130,8 +130,10 @@ async def open(resource_id, *, order=None):
130
130
  "message": f'资源副本读取失败,降级到下一副本(backend={loc["backend"]}, id={resource_id}):{e}',
131
131
  "hint": "检查该 provider 可用性;该副本可能需要重建"})
132
132
  if last_err is not None:
133
- raise last_err
134
- raise FileNotFoundError(f"资源不存在或无可读副本: {resource_id}")
133
+ raise last_err # 有副本行但读取失败:原样重抛(500 透传),禁改语义
134
+ # core 稳定前缀(与 ERR_PERMISSION: / ERR_GQL_PARSE: 同构):适配层按前缀判定 → 404,
135
+ # 禁按中文文案匹配。仅「按 resource_id 查到零 ResourceLocation 行」时抛出。
136
+ raise FileNotFoundError(f"ERR_RESOURCE_NOT_FOUND:资源不存在或无可读副本: {resource_id}")
135
137
 
136
138
 
137
139
  async def remove(resource_id):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: storepy
3
- Version: 4.2.0
3
+ Version: 4.2.1
4
4
  Summary: Multi-backend data layer for Python asyncio (MongoDB, MySQL, SQLite, PostgreSQL): pure JSON schemas, GQL tree queries compiled to a single native query, GROUP BY/HAVING aggregation, computed columns, soft-delete and role-based access control
5
5
  Author-email: leo <coen_ddt@qq.com>
6
6
  License-Expression: MIT
@@ -10,6 +10,8 @@ import asyncio
10
10
  import re
11
11
  import tempfile
12
12
 
13
+ import pytest
14
+
13
15
  from py_store import feedback, resource
14
16
 
15
17
 
@@ -156,6 +158,45 @@ def test_open_degrade_on_provider_failure():
156
158
  _run(scenario())
157
159
 
158
160
 
161
+ def test_open_missing_resource_raises_prefixed_error():
162
+ """零副本行 → 抛 FileNotFoundError 且带 ERR_RESOURCE_NOT_FOUND: 稳定前缀(spec/03 判定顺序第 4 层)。"""
163
+ async def scenario():
164
+ store = _FakeStore()
165
+ resource.register_provider('memory', _MemoryProvider)
166
+ resource.configure({'store': store, 'providers': [{'kind': 'memory'}], 'url': {}})
167
+
168
+ rid = '0' * 40
169
+ prefix = 'ERR_RESOURCE_NOT_FOUND:'
170
+ with pytest.raises(FileNotFoundError) as ei:
171
+ await resource.open(rid)
172
+ assert str(ei.value).startswith(prefix), str(ei.value)
173
+ assert str(ei.value)[len(prefix):] == f'资源不存在或无可读副本: {rid}'
174
+
175
+ _run(scenario())
176
+
177
+
178
+ def test_open_provider_failure_reraises_without_prefix():
179
+ """有副本行但 provider 读取失败 → 原样重抛、不带前缀(保持 500 语义,禁伪装成「不存在」)。"""
180
+ async def scenario():
181
+ store = _FakeStore()
182
+ prev = feedback.get_sink()
183
+ feedback.set_sink(lambda e: None)
184
+ try:
185
+ resource.register_provider('badget', _BrokenGetProvider)
186
+ resource.configure({'store': store, 'providers': [{'kind': 'badget'}], 'url': {}})
187
+ store.rows.append({'_schema': 'ResourceLocation', 'resourceId': 'a' * 40,
188
+ 'backend': 'badget', 'key': 'k', 'status': 'ok', 'priority': 0})
189
+
190
+ with pytest.raises(RuntimeError) as ei:
191
+ await resource.open('a' * 40)
192
+ assert str(ei.value) == 'disk boom'
193
+ assert not str(ei.value).startswith('ERR_RESOURCE_NOT_FOUND:')
194
+ finally:
195
+ feedback.set_sink(prev)
196
+
197
+ _run(scenario())
198
+
199
+
159
200
  def test_partial_write_failure_persists_failed_and_feedback():
160
201
  async def scenario():
161
202
  store = _FakeStore()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes