storepy 2.7.0__tar.gz → 3.0.0__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.
- {storepy-2.7.0/src/storepy.egg-info → storepy-3.0.0}/PKG-INFO +2 -2
- {storepy-2.7.0 → storepy-3.0.0}/pyproject.toml +2 -2
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/__init__.py +7 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/permission.py +19 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/schema.py +15 -0
- {storepy-2.7.0 → storepy-3.0.0/src/storepy.egg-info}/PKG-INFO +2 -2
- {storepy-2.7.0 → storepy-3.0.0}/src/storepy.egg-info/requires.txt +1 -1
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_ask.py +20 -14
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_coverage_margin.py +6 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_py_store.py +5 -1
- {storepy-2.7.0 → storepy-3.0.0}/LICENSE +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/README.md +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/setup.cfg +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/ask.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/ask_knowledge.md +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/core.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/crud/__init__.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/crud/exec.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/crud/id.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/crud/mutation.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/crud/query.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/crud/write.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/datasource.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/ddl.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/executors/__init__.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/executors/_values.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/executors/mongo.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/executors/mysql.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/executors/postgres.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/executors/sqlite.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/feedback.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/introspect/__init__.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/introspect/mysql.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/introspect/postgres.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/introspect/sqlite.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/llm.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/sync.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/py_store/workflow.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/storepy.egg-info/SOURCES.txt +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/storepy.egg-info/dependency_links.txt +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/src/storepy.egg-info/top_level.txt +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_federation_e2e.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_host_contract.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_host_paths.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_migration.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_mongo_executor.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_mongo_session.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_multi_datasource.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_profile.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_raw_sql_and_ddl.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_rbac_host.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_real_backends_e2e.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_require_context.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_scenario_course_platform.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_scenario_manager_transaction.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_session.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_sql_commit_and_registry.py +0 -0
- {storepy-2.7.0 → storepy-3.0.0}/tests/test_workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: storepy
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
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
|
|
@@ -24,7 +24,7 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Requires-Dist: pymongo>=4.9
|
|
27
|
-
Requires-Dist: rust-store-py<
|
|
27
|
+
Requires-Dist: rust-store-py<4.0.0,>=3.0.0
|
|
28
28
|
Provides-Extra: mysql
|
|
29
29
|
Requires-Dist: asyncmy>=0.2.9; extra == "mysql"
|
|
30
30
|
Provides-Extra: postgres
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "storepy"
|
|
7
|
-
version = "
|
|
7
|
+
version = "3.0.0"
|
|
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"
|
|
@@ -25,7 +25,7 @@ authors = [
|
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
27
|
"pymongo>=4.9",
|
|
28
|
-
"rust-store-py>=
|
|
28
|
+
"rust-store-py>=3.0.0,<4.0.0",
|
|
29
29
|
]
|
|
30
30
|
classifiers = [
|
|
31
31
|
"Development Status :: 4 - Beta",
|
|
@@ -281,6 +281,13 @@ class Store:
|
|
|
281
281
|
rbac_writable_fields = staticmethod(permission.rbac_writable_fields)
|
|
282
282
|
rbacRowCondition = staticmethod(permission.rbac_row_condition)
|
|
283
283
|
rbac_row_condition = staticmethod(permission.rbac_row_condition)
|
|
284
|
+
# 角色清单与未配置姿态(清单化语义,判决唯一在 core;本层仅透传配置)
|
|
285
|
+
setExemptRoles = staticmethod(permission.set_exempt_roles)
|
|
286
|
+
set_exempt_roles = staticmethod(permission.set_exempt_roles)
|
|
287
|
+
setDenyWriteRoles = staticmethod(permission.set_deny_write_roles)
|
|
288
|
+
set_deny_write_roles = staticmethod(permission.set_deny_write_roles)
|
|
289
|
+
setUnconfiguredPolicy = staticmethod(permission.set_unconfigured_policy)
|
|
290
|
+
set_unconfigured_policy = staticmethod(permission.set_unconfigured_policy)
|
|
284
291
|
# 查询档位(判决唯一在 core):standard 默认放开 / text2query 功能收缩
|
|
285
292
|
# 进入档即等效强制 ctx;未知档由 core 抛 ValueError 上抛(禁静默回落默认档)
|
|
286
293
|
setProfile = staticmethod(schema.set_profile)
|
|
@@ -153,6 +153,25 @@ def rbac_row_condition(model, action, ctx=None):
|
|
|
153
153
|
return core.rbac_row_condition(_model(model), action, ctx)
|
|
154
154
|
|
|
155
155
|
|
|
156
|
+
def set_exempt_roles(roles):
|
|
157
|
+
"""豁免角色清单(命中者在一切判决环节直接放行)。
|
|
158
|
+
|
|
159
|
+
判决唯一在 core:本层仅透传配置。默认空——无豁免(清单化语义,
|
|
160
|
+
super_admin/admin 不再默认放行,迁移见迁移指南)。
|
|
161
|
+
"""
|
|
162
|
+
return core.set_exempt_roles(roles)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def set_deny_write_roles(roles):
|
|
166
|
+
"""拒写角色清单(命中者一切写路径拒绝,读不受影响)。默认空——无拒写。"""
|
|
167
|
+
return core.set_deny_write_roles(roles)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def set_unconfigured_policy(policy):
|
|
171
|
+
"""schema 白名单缺失/为空时的默认姿态:"open"(默认,放行)| "closed"(全拒)。"""
|
|
172
|
+
return core.set_unconfigured_policy(policy)
|
|
173
|
+
|
|
174
|
+
|
|
156
175
|
# ─── 自定义错误 ──────────────────────────────────────────────
|
|
157
176
|
|
|
158
177
|
|
|
@@ -213,6 +213,21 @@ def set_require_context(require=True):
|
|
|
213
213
|
core.set_require_context(bool(require))
|
|
214
214
|
|
|
215
215
|
|
|
216
|
+
def set_exempt_roles(roles):
|
|
217
|
+
"""豁免角色清单(命中者在一切判决环节直接放行)。默认空——无豁免(清单化语义)"""
|
|
218
|
+
core.set_exempt_roles(roles)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def set_deny_write_roles(roles):
|
|
222
|
+
"""拒写角色清单(命中者一切写路径拒绝,读不受影响)。默认空——无拒写"""
|
|
223
|
+
core.set_deny_write_roles(roles)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def set_unconfigured_policy(policy):
|
|
227
|
+
"""schema 白名单缺失/为空时的默认姿态:"open"(默认,放行)| "closed"(全拒)"""
|
|
228
|
+
core.set_unconfigured_policy(policy)
|
|
229
|
+
|
|
230
|
+
|
|
216
231
|
def require_context():
|
|
217
232
|
"""「上下文强制」开关当前值"""
|
|
218
233
|
return core.require_context()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: storepy
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
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
|
|
@@ -24,7 +24,7 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Requires-Dist: pymongo>=4.9
|
|
27
|
-
Requires-Dist: rust-store-py<
|
|
27
|
+
Requires-Dist: rust-store-py<4.0.0,>=3.0.0
|
|
28
28
|
Provides-Extra: mysql
|
|
29
29
|
Requires-Dist: asyncmy>=0.2.9; extra == "mysql"
|
|
30
30
|
Provides-Extra: postgres
|
|
@@ -354,20 +354,26 @@ def test_t8_summary_without_ctx_names_only():
|
|
|
354
354
|
def test_t8_admin_summary_and_compute_skip_warning():
|
|
355
355
|
"""admin 视角:role 进摘要;read 白名单计算列无论角色一律收窄并告警(同签名去重)"""
|
|
356
356
|
_ask_mod._COMPUTE_SKIP_SIGS.clear() # 告警去重为进程级,单测内复位以保证可断言
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
357
|
+
# 清单化语义(设计 §11.5):AkUser.read 白名单不含 admin,admin 视角需显式豁免
|
|
358
|
+
from py_store import permission as _perm
|
|
359
|
+
_perm.set_exempt_roles(['admin'])
|
|
360
|
+
try:
|
|
361
|
+
events = []
|
|
362
|
+
feedback.set_sink(events.append)
|
|
363
|
+
s = store.describe_for_ai(CTX_ADMIN)
|
|
364
|
+
aku = next(m for m in s if m['name'] == 'AkUser')
|
|
365
|
+
assert aku['fields']['role'] == 'string'
|
|
366
|
+
assert 'displayName' not in aku['computes']
|
|
367
|
+
assert 'upperName' in aku['computes']
|
|
368
|
+
assert 'itemCount' in next(m for m in s if m['name'] == 'AkOrder')['computes']
|
|
369
|
+
warns = [e for e in events if e['type'] == 'ask_summary_compute_skipped']
|
|
370
|
+
assert len(warns) == 1
|
|
371
|
+
assert warns[0]['model'] == 'AkUser' and warns[0]['compute'] == 'displayName'
|
|
372
|
+
# 同签名只告警一次(再次 describe 不重复)
|
|
373
|
+
store.describe_for_ai(CTX_ADMIN)
|
|
374
|
+
assert len([e for e in events if e['type'] == 'ask_summary_compute_skipped']) == 1
|
|
375
|
+
finally:
|
|
376
|
+
_perm.set_exempt_roles([])
|
|
371
377
|
|
|
372
378
|
|
|
373
379
|
# ── T9 无 ctx 直接拒绝(fail-secure,A4) ──────────────────────
|
|
@@ -320,8 +320,14 @@ def test_permission_wrappers_and_internal_sync_return():
|
|
|
320
320
|
})
|
|
321
321
|
assert permission.should_inject_owner_condition(
|
|
322
322
|
'CovOwner', {'userId': 'u1', 'roles': ['user']}) is True
|
|
323
|
+
# 清单化语义(设计 §11.5):admin 不再默认豁免——creator-only 下同样注入 owner 条件;
|
|
324
|
+
# 显式豁免后恢复「不注入」
|
|
325
|
+
assert permission.should_inject_owner_condition(
|
|
326
|
+
'CovOwner', {'userId': 'u1', 'roles': ['admin']}) is True
|
|
327
|
+
permission.set_exempt_roles(['admin'])
|
|
323
328
|
assert permission.should_inject_owner_condition(
|
|
324
329
|
'CovOwner', {'userId': 'u1', 'roles': ['admin']}) is False
|
|
330
|
+
permission.set_exempt_roles([])
|
|
325
331
|
assert permission.get_readable_relations('CovOwner', None) is None, '无上下文不裁剪'
|
|
326
332
|
assert _run(permission.run_as_internal(lambda: 42)) == 42, '同步返回值须原样返回'
|
|
327
333
|
|
|
@@ -98,10 +98,14 @@ def test_perm_scoped_roles_and_run_as_internal():
|
|
|
98
98
|
def test_perm_schema_read_write():
|
|
99
99
|
s = _sc.get('PermModel')
|
|
100
100
|
assert perm.can_read_schema(s, {'roles': ['seller']}) is True
|
|
101
|
-
|
|
101
|
+
# 清单化语义(设计 §11.5):admin 不再默认放行——不在白名单即拒,显式豁免后直通
|
|
102
|
+
assert perm.can_read_schema(s, {'roles': ['admin']}) is False
|
|
102
103
|
assert perm.can_read_schema(s, {'roles': ['guest']}) is False
|
|
103
104
|
assert perm.can_write_schema(s, {'roles': ['seller']}) is True
|
|
104
105
|
assert perm.can_write_schema(s, {'roles': ['guest']}) is False
|
|
106
|
+
perm.set_exempt_roles(['admin'])
|
|
107
|
+
assert perm.can_read_schema(s, {'roles': ['admin']}) is True
|
|
108
|
+
perm.set_exempt_roles([])
|
|
105
109
|
|
|
106
110
|
|
|
107
111
|
def test_perm_owner_condition():
|
|
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
|
|
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
|