labull-framework 0.1.24__tar.gz → 0.1.26__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.
- {labull_framework-0.1.24/labull_framework.egg-info → labull_framework-0.1.26}/PKG-INFO +1 -1
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/export.py +10 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/service.py +25 -1
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/validate.py +55 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/views.py +27 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/urls.py +3 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26/labull_framework.egg-info}/PKG-INFO +1 -1
- {labull_framework-0.1.24 → labull_framework-0.1.26}/pyproject.toml +1 -1
- {labull_framework-0.1.24 → labull_framework-0.1.26}/LICENSE +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/MANIFEST.in +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/README-pypi.md +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/__init__.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/admin.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/apps.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/auth/__init__.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/auth/backend.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/auth/bootstrap.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/auth/sso.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/auth/views.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/__init__.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/data.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/gitinfo.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/static/canvas.css +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/static/canvas.js +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/static/page.html +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/views.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/env.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/jsonio.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/__init__.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/blockpackage.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/deepcheck.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/devkit.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/dispatch.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/health.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/parallel.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/registry.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/sdk.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/management/__init__.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/management/commands/__init__.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/management/commands/labull_contract.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/migrations/0001_initial.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/migrations/0002_remove_magicblock_prev_files_and_more.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/migrations/0003_magicblock_tags.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/migrations/0004_magicblock_detail.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/migrations/__init__.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/models.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/settings.py +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework.egg-info/SOURCES.txt +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework.egg-info/dependency_links.txt +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework.egg-info/requires.txt +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework.egg-info/top_level.txt +0 -0
- {labull_framework-0.1.24 → labull_framework-0.1.26}/setup.cfg +0 -0
|
@@ -264,6 +264,16 @@ def package_files(rows: Iterable[Any], tables: list[dict[str, Any]] | None = Non
|
|
|
264
264
|
# 🔴 没有清单就补一份:id 用**这个块自己的 uuid**,绝不编一个看起来合法的 ——
|
|
265
265
|
# 那会让下次"读回改动"报一条用户无法理解的错。
|
|
266
266
|
package[BLOCK_MANIFEST] = _starter_manifest(row)
|
|
267
|
+
else:
|
|
268
|
+
# 🔴 库里那一列 `detail` **要写进导出给你那份清单**(作者就是在那儿改它的):
|
|
269
|
+
# 否则"控制台看得见详情、yaml 里却没有这个键",人会以为这个字段根本不存在
|
|
270
|
+
# (用户 0.1.24 报的就是这条)。只动 `detail:` 那一行,作者写的注释一个字不动。
|
|
271
|
+
# ⚠️ 库里是空的就**一个字都不动**(`set_manifest_value` 里那条:这是"没写",
|
|
272
|
+
# 不是"要删掉")—— 导出铺下去的那一份必须与库里那份逐字一致,否则
|
|
273
|
+
# "导出 → 读回"每次都多一条"清单改了",一保存还给每个块白记一版。
|
|
274
|
+
detail = str(getattr(row, 'detail', '') or '').strip()
|
|
275
|
+
if detail:
|
|
276
|
+
package[BLOCK_MANIFEST] = validate.set_manifest_value(package[BLOCK_MANIFEST], 'detail', detail)
|
|
267
277
|
# 🔴 **每个块自带一个 `excluded/` 说明**(用户要求:"每个 magic block 自带一个文件夹
|
|
268
278
|
# excluded …… 可以随意在这里面开发,最终被保存进库,但执行的时候是被忽略的")。
|
|
269
279
|
# ⚠️ 只在**缺的时候**补:作者自己改过 / 删过的那一份不该被每次导出悄悄覆盖回来
|
|
@@ -410,8 +410,32 @@ class MagicBlockService:
|
|
|
410
410
|
raise SaveRejected('写完标签之后读不回来,请检查数据库连接')
|
|
411
411
|
return saved
|
|
412
412
|
|
|
413
|
-
|
|
413
|
+
def set_detail(self, block_id: str, detail: Any) -> Any:
|
|
414
|
+
"""**只改详情介绍**(清单里那个可选的 `detail`),一行业务数据都不碰。
|
|
415
|
+
|
|
416
|
+
🔴 为什么不走 `save()`:那是"整包替换 + 记一版 + 重跑体检(子进程深检)"。
|
|
417
|
+
详情是**给人读的一段话** —— 与标签同一个性质(组织与说明,不是代码),
|
|
418
|
+
为它重跑一遍深检、在版本史里留一版**内容一模一样**的版本,两件都不该发生。
|
|
419
|
+
🔴 空串是合法输入(= 清掉那段说明),所以这里**不接受 `None`**(那说明调用方搞错了语义)。
|
|
420
|
+
🔴 改完**下一次导出**就会把它写进那份 `magic-block.open.yaml`(见 `export.package_files`),
|
|
421
|
+
于是"控制台里写的"与"yaml 里写的"从此是同一份 —— 作者在两边改都算数。
|
|
422
|
+
"""
|
|
423
|
+
if detail is None:
|
|
424
|
+
raise SaveRejected('要改详情就给一个字符串(哪怕是空的);一个都不传说明调用方搞错了')
|
|
425
|
+
model = self._model()
|
|
426
|
+
with transaction.atomic():
|
|
427
|
+
row = model.objects.select_for_update().filter(id=block_id).first()
|
|
428
|
+
if row is None:
|
|
429
|
+
raise BlockNotFound(f'没有这个 magic block:{block_id}')
|
|
430
|
+
# 🔴 换行压成空格:清单是"一行一个键",而导出会把这一段原样写进 yaml。
|
|
431
|
+
row.detail = ' '.join(str(detail).split())
|
|
432
|
+
row.save(update_fields=['detail', 'updated_at'])
|
|
433
|
+
saved = self.get(block_id)
|
|
434
|
+
if saved is None:
|
|
435
|
+
raise SaveRejected('写完详情之后读不回来,请检查数据库连接')
|
|
436
|
+
return saved
|
|
414
437
|
|
|
438
|
+
# ──────────────────────────── 版本史 ────────────────────────────
|
|
415
439
|
def versions(self, block_id: str) -> list[dict]:
|
|
416
440
|
"""这一块的版本列表(**新的在前**):`number / files 数 / 谁传的 / 什么时候 / 是不是当前**。
|
|
417
441
|
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/validate.py
RENAMED
|
@@ -263,6 +263,61 @@ def read_manifest(files: dict[str, str]) -> tuple[dict[str, str] | None, list[st
|
|
|
263
263
|
return manifest, []
|
|
264
264
|
|
|
265
265
|
|
|
266
|
+
#: 清单里 `detail` 没写时,导出会在清单里留一行注释提示 —— **不做这件事**(2026-10-06 撤掉)。
|
|
267
|
+
#:
|
|
268
|
+
#: 🔴 为什么撤:`detail` 是可选键,加上一行注释确实能让作者看见这个字段,但**代价太大** ——
|
|
269
|
+
#: 导出铺下去的那一份必须与库里那份**逐字一致**,否则"导出 → 读回"每次都多一条
|
|
270
|
+
#: "`magic-block.open.yaml` 改了",而且一保存就给每个块白记一版(`sdk-check-py` 里那条
|
|
271
|
+
#: "读回改动只有平台自带的那一份说明"的判据当场红)。
|
|
272
|
+
#: ⇒ 可选键"怎么用"写在 `README.md` / `GUIDE.lock.md` 与开发包示例里,**不往作者的文件里塞东西**。
|
|
273
|
+
|
|
274
|
+
def set_manifest_value(text: str, key: str, value: str) -> str:
|
|
275
|
+
"""把清单里 `key:` 那一行改成 `value`(没有这一行就插进去);**其余内容逐字保留**。
|
|
276
|
+
|
|
277
|
+
🔴 它只给**导出**用:导出给你的那份 `magic-block.open.yaml` 必须带上库里那一列
|
|
278
|
+
(`detail` 就是靠它才看得见、改得动的),否则"库里存着、yaml 里没有"这件事
|
|
279
|
+
会让作者以为字段根本不存在(用户 0.1.24 报的正是这条)。
|
|
280
|
+
🔴 **只动那一行**:注释、空行、键的先后顺序全是作者写的,重排一遍就等于把他的文件改没了。
|
|
281
|
+
🔴 `value` 为空 = "库里没写" ⇒ **一个字都不动**(作者那一行原样留着、缺这一行也不补):
|
|
282
|
+
那是"没写",不是"要删掉"、也不是"要有这么一行"。
|
|
283
|
+
⚠️ 这条不只是省事:导出铺下去的那一份得与库里那份**逐字一致** —— 否则"导出 → 读回"
|
|
284
|
+
每次都多一条"清单改了"(还会给每个块白记一版)。可选键的"怎么用"写在
|
|
285
|
+
`README.md` / `GUIDE.lock.md` 与开发包示例里,**不往作者的文件里塞注释**。
|
|
286
|
+
🔴 值的写法与 `export._starter_manifest` **同一套**:外面套一对双引号(清单的解析器对
|
|
287
|
+
引号里的 `#` 当正文),换行与控制字符一律压成空格 —— 清单是"一行一个键"。
|
|
288
|
+
⚠️ 找不到那一行时插在 `brief` 后面(没有 `brief` 就插在 `name` 后面,都没有就追加到末尾):
|
|
289
|
+
贴着它上面那几个"给人看的键",作者一眼看得到。
|
|
290
|
+
"""
|
|
291
|
+
clean = ' '.join(str(value or '').split()) # 换行 / 制表 / 连续空白 ⇒ 一个空格
|
|
292
|
+
lines = str(text or '').split('\n')
|
|
293
|
+
if not clean:
|
|
294
|
+
return '\n'.join(lines)
|
|
295
|
+
line = f'{key}: "{clean}"'
|
|
296
|
+
|
|
297
|
+
def key_of(raw: str) -> str:
|
|
298
|
+
body = _strip_comment(raw).strip()
|
|
299
|
+
if not body or body.startswith('-') or body.startswith('#'):
|
|
300
|
+
return ''
|
|
301
|
+
at = _separator_at(body)
|
|
302
|
+
return body[:at].strip() if at >= 0 else ''
|
|
303
|
+
|
|
304
|
+
for index, raw in enumerate(lines):
|
|
305
|
+
if key_of(raw) == key:
|
|
306
|
+
lines[index] = line
|
|
307
|
+
return '\n'.join(lines)
|
|
308
|
+
for anchor in ('brief', 'name'):
|
|
309
|
+
for index, raw in enumerate(lines):
|
|
310
|
+
if key_of(raw) == anchor:
|
|
311
|
+
lines.insert(index + 1, line)
|
|
312
|
+
return '\n'.join(lines)
|
|
313
|
+
# 一行都没有(空文件 / 全是注释):插在**最后一个非空行之后**,末尾的空行留着(那是文件的收尾)。
|
|
314
|
+
cut = len(lines)
|
|
315
|
+
while cut > 0 and not lines[cut - 1].strip():
|
|
316
|
+
cut -= 1
|
|
317
|
+
lines.insert(cut, line)
|
|
318
|
+
return '\n'.join(lines)
|
|
319
|
+
|
|
320
|
+
|
|
266
321
|
def _valid_file_name(name: str) -> bool:
|
|
267
322
|
"""包内文件名合法吗(形状 + 不许 `..` 段)。"""
|
|
268
323
|
return bool(FILE_NAME_PATTERN.match(name)) and '..' not in str(name).split('/')
|
|
@@ -258,6 +258,33 @@ def tags_view(request: HttpRequest, block_id: str) -> HttpResponse:
|
|
|
258
258
|
return _json(_row_view(row))
|
|
259
259
|
|
|
260
260
|
|
|
261
|
+
@staff_only
|
|
262
|
+
def detail_field_view(request: HttpRequest, block_id: str) -> HttpResponse:
|
|
263
|
+
"""`/magic-blocks/<id>/detail` —— **只改详情介绍**(`PUT`/`POST`),别的一律不碰。
|
|
264
|
+
|
|
265
|
+
请求体:`{"detail": "一段给人读的说明"}`(**空串 = 清掉**)。
|
|
266
|
+
|
|
267
|
+
🔴 为什么要有这条口(而不是复用 `POST /magic-blocks`):那是"整包替换 + 记一版 + 重跑体检",
|
|
268
|
+
为了写一段说明去跑一遍深检(子进程加载块代码)既慢又重,还会在版本史里留一版
|
|
269
|
+
**内容一模一样**的版本;而且控制台改详情时**手里没有那包代码**(抽屉里不摆正文)。
|
|
270
|
+
详情与标签同一个性质:说明与组织,给它一条轻的路。
|
|
271
|
+
🔴 与 `detail_view()`(那一行的 GET / DELETE)**不是一回事**,名字故意取得不一样:
|
|
272
|
+
那个管的是"这一块那一行",这个只管"详情这一个字段"。
|
|
273
|
+
"""
|
|
274
|
+
if request.method not in ('POST', 'PUT', 'PATCH'):
|
|
275
|
+
return problem('这条接口只认 POST / PUT / PATCH', 405)
|
|
276
|
+
body = _body(request)
|
|
277
|
+
if 'detail' not in body:
|
|
278
|
+
return problem('要改详情就带上 detail 字段(空串表示清掉这段说明)', 400)
|
|
279
|
+
try:
|
|
280
|
+
row = MagicBlockService().set_detail(block_id, body.get('detail'))
|
|
281
|
+
except BlockNotFound as exc:
|
|
282
|
+
return problem(exc.message, 404)
|
|
283
|
+
except SaveRejected as exc:
|
|
284
|
+
return _reject(exc)
|
|
285
|
+
return _json(_row_view(row))
|
|
286
|
+
|
|
287
|
+
|
|
261
288
|
@staff_only
|
|
262
289
|
def versions_view(request: HttpRequest, block_id: str, number: str | None = None) -> HttpResponse:
|
|
263
290
|
"""`/magic-blocks/<id>/versions[/<number>]` —— 版本史的读、切、删、下。
|
|
@@ -76,6 +76,9 @@ urlpatterns: list[Any] = [
|
|
|
76
76
|
path('magic-blocks', block_views.list_view, name='labull-blocks-list'),
|
|
77
77
|
# 🔴 只改标签那一条(轻路:不重跑体检、不记版本)—— 与 `POST /magic-blocks`(整包替换)分开。
|
|
78
78
|
path('magic-blocks/<str:block_id>/tags', block_views.tags_view, name='labull-blocks-tags'),
|
|
79
|
+
# 🔴 只改详情介绍那一条,同一个理由(详情是"给人读的一段话",不是代码)。
|
|
80
|
+
# ⚠️ 与 `labull-blocks-detail`(`magic-blocks/<block_id>` 那一行本身)不是一回事。
|
|
81
|
+
path('magic-blocks/<str:block_id>/detail', block_views.detail_field_view, name='labull-blocks-detail-field'),
|
|
79
82
|
# 🔴 版本史那一条排在 `<block_id>` **之前**(同一个理由:路径前缀更长的那条要先匹配)。
|
|
80
83
|
# `GET/POST/DELETE …/versions[/<n>]` 五个动作都在同一个视图里分流(见 `versions_view`)。
|
|
81
84
|
re_path(r'^magic-blocks/(?P<block_id>[^/]+)/versions(?:/(?P<number>\d+))?$',
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "labull-framework"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.26"
|
|
8
8
|
description = "Labull Framework: a pluggable Django app with Authing OIDC login and magic blocks"
|
|
9
9
|
readme = "README-pypi.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
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
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/static/canvas.css
RENAMED
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/static/canvas.js
RENAMED
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/canvas/static/page.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/__init__.py
RENAMED
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/blockpackage.py
RENAMED
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/deepcheck.py
RENAMED
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/dispatch.py
RENAMED
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/parallel.py
RENAMED
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/magic_block/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/management/commands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework/migrations/0001_initial.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.24 → labull_framework-0.1.26}/labull_framework.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|