cloudoll 4.0.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.
- cloudoll-4.2.1/AGENTS.md +5 -0
- cloudoll-4.2.1/AI.md +32 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/CHANGELOG.md +47 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/MANIFEST.in +2 -0
- {cloudoll-4.0.0/cloudoll.egg-info → cloudoll-4.2.1}/PKG-INFO +78 -7
- {cloudoll-4.0.0 → cloudoll-4.2.1}/README.md +76 -6
- {cloudoll-4.0.0 → cloudoll-4.2.1}/README.zh-CN.md +184 -9
- {cloudoll-4.0.0 → cloudoll-4.2.1}/SECURITY.md +1 -1
- cloudoll-4.2.1/cloudoll/__init__.py +1 -0
- cloudoll-4.2.1/cloudoll/ai/AGENTS.md +7 -0
- cloudoll-4.2.1/cloudoll/ai/__init__.py +1 -0
- cloudoll-4.2.1/cloudoll/ai/commands.py +106 -0
- cloudoll-4.2.1/cloudoll/ai/docs/ai.md +61 -0
- cloudoll-4.2.1/cloudoll/ai/docs/app-start.md +81 -0
- cloudoll-4.2.1/cloudoll/ai/docs/changelog.md +193 -0
- cloudoll-4.2.1/cloudoll/ai/docs/config.md +150 -0
- cloudoll-4.2.1/cloudoll/ai/docs/cookie-and-session.md +124 -0
- cloudoll-4.2.1/cloudoll/ai/docs/database.md +340 -0
- cloudoll-4.2.1/cloudoll/ai/docs/deployment.md +131 -0
- cloudoll-4.2.1/cloudoll/ai/docs/event-source.md +62 -0
- cloudoll-4.2.1/cloudoll/ai/docs/exception.md +68 -0
- cloudoll-4.2.1/cloudoll/ai/docs/file-uploads.md +88 -0
- cloudoll-4.2.1/cloudoll/ai/docs/http-client.md +70 -0
- cloudoll-4.2.1/cloudoll/ai/docs/jwt.md +88 -0
- cloudoll-4.2.1/cloudoll/ai/docs/logs.md +191 -0
- cloudoll-4.2.1/cloudoll/ai/docs/middleware.md +63 -0
- cloudoll-4.2.1/cloudoll/ai/docs/migration.md +85 -0
- cloudoll-4.2.1/cloudoll/ai/docs/router.md +115 -0
- cloudoll-4.2.1/cloudoll/ai/docs/start.md +93 -0
- cloudoll-4.2.1/cloudoll/ai/docs/structure.md +65 -0
- cloudoll-4.2.1/cloudoll/ai/docs/validation.md +121 -0
- cloudoll-4.2.1/cloudoll/ai/docs/view.md +63 -0
- cloudoll-4.2.1/cloudoll/ai/docs/websockets.md +67 -0
- cloudoll-4.2.1/cloudoll/ai/manifest.json +110 -0
- cloudoll-4.2.1/cloudoll/ai/skills/cloudoll/SKILL.md +46 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/cli.py +4 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/clitool/cli_main.py +15 -2
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/clitool/watch.py +17 -2
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/__init__.py +2 -1
- cloudoll-4.2.1/cloudoll/orm/datasources.py +88 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/model.py +55 -23
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/README.md +6 -1
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/config/conf.local.yaml +4 -0
- cloudoll-4.2.1/cloudoll/template/requirements.txt +1 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/__init__.py +6 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/configuration.py +12 -1
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/core.py +48 -21
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/jwt.py +2 -1
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/lifecycle.py +11 -3
- cloudoll-4.2.1/cloudoll/web/request_data.py +150 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/resources.py +37 -1
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/sessions.py +5 -4
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/types.py +2 -1
- cloudoll-4.2.1/cloudoll/web/validation.py +100 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1/cloudoll.egg-info}/PKG-INFO +78 -7
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll.egg-info/SOURCES.txt +38 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll.egg-info/requires.txt +1 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/docs/architecture.md +1 -1
- {cloudoll-4.0.0 → cloudoll-4.2.1}/docs/maintenance.md +33 -2
- {cloudoll-4.0.0 → cloudoll-4.2.1}/docs/reliability.md +1 -1
- {cloudoll-4.0.0 → cloudoll-4.2.1}/pyproject.toml +2 -0
- cloudoll-4.2.1/scripts/sync_ai_docs.py +83 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/check_wheel.py +8 -0
- cloudoll-4.2.1/tests/test_ai.py +89 -0
- cloudoll-4.2.1/tests/test_datasources.py +250 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_dev_lifecycle.py +51 -2
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_production_contracts.py +1 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_regressions.py +3 -3
- cloudoll-4.2.1/tests/test_release_workflow.py +47 -0
- cloudoll-4.2.1/tests/test_request_contract.py +296 -0
- cloudoll-4.2.1/tests/test_startup_logging.py +91 -0
- cloudoll-4.2.1/tests/test_validation.py +244 -0
- cloudoll-4.2.1/tests/typing/middleware_contract.py +30 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/typing/query_contract.py +2 -0
- cloudoll-4.2.1/tests/typing/request_contract.py +21 -0
- cloudoll-4.0.0/cloudoll/__init__.py +0 -1
- cloudoll-4.0.0/cloudoll/template/requirements.txt +0 -1
- cloudoll-4.0.0/cloudoll/web/request_data.py +0 -63
- cloudoll-4.0.0/tests/test_request_contract.py +0 -144
- {cloudoll-4.0.0 → cloudoll-4.2.1}/LICENSE +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/__main__.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/clitool/__init__.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/clitool/m2d.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/clitool/process.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/clitool/schema.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/clitool/spinner.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/logging/__init__.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/mail/__init__.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/mail/smtp.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/observability.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/aws_engine.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/awsmysql.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/awspostgres.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/base.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/compiler.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/dialects.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/engine.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/field.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/mysql.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/parse.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/postgres.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/protocols.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/query.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/savepoints.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/streaming.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/test_sql.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/orm/values.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/py.typed +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/__init__.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/controllers/admin/test.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/controllers/api/index.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/controllers/es/es.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/controllers/home/index.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/controllers/upload/upload.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/controllers/ws/ws.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/middlewares/auth.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/static/css/index.css +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/static/img/cat.avif +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/static/js/index.js +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/template/templates/index.html +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/utils/__init__.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/utils/async_tools.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/utils/common.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/utils/password.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/utils/snowflake.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/context.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/exception.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/html.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/requests.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/routing.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll/web/settings.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll.egg-info/dependency_links.txt +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll.egg-info/entry_points.txt +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/cloudoll.egg-info/top_level.txt +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/docs/aurora.md +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/docs/production.md +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/docs/schema-generation.md +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/docs/streaming-and-types.md +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/setup.cfg +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/setup.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/audit_dependencies.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/check_release.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/conftest.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/integration/test_aws_failover.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/integration/test_databases.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/integration/test_savepoints.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/integration/test_schema.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/integration/test_streaming.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/run_local_databases.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_architecture.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_field_types.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_retry_and_query_regressions.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_review_fixes.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_save_snapshots_and_cleanup.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_savepoint_unit.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_schema_generation.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_sql_echo.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_streaming_unit.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_transactions.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/test_typing_regressions.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/typing/engine_contract.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/typing/field_contract.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/typing/production_contract.py +0 -0
- {cloudoll-4.0.0 → cloudoll-4.2.1}/tests/typing/web_contract.py +0 -0
cloudoll-4.2.1/AGENTS.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Cloudoll library maintenance
|
|
2
|
+
|
|
3
|
+
This repository implements Cloudoll; it is not a generated application. Read `AI.md` before changing AI assets. Public API behavior must be verified against source and tests, not inferred from other frameworks. The application-development skill is `cloudoll/ai/skills/cloudoll/SKILL.md`; read it when creating application examples.
|
|
4
|
+
|
|
5
|
+
Official documentation is maintained in the sibling `cloudoll-py-docs` repository, under `src/views`. Bundled copies in `cloudoll/ai/docs` are generated: edit the original docs, then run the sync command in `AI.md`. Update docs and tests together when changing public behavior. Preserve Python 3.9 compatibility.
|
cloudoll-4.2.1/AI.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# AI-assisted Cloudoll development
|
|
2
|
+
|
|
3
|
+
For application developers, `cloudoll create PROJECT` installs an `AGENTS.md` entry and `.agents/skills/cloudoll/SKILL.md`. Existing projects can run `python -m cloudoll.cli ai init .`. Existing user instructions are preserved; a different/customized skill is never silently replaced.
|
|
4
|
+
|
|
5
|
+
No MCP server is required:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
python -m cloudoll.cli ai list
|
|
9
|
+
python -m cloudoll.cli ai read structure
|
|
10
|
+
python -m cloudoll.cli ai read database
|
|
11
|
+
python -m cloudoll.cli ai search Body
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Use the project's Python environment. Ask your coding agent to read the project `AGENTS.md` and Cloudoll skill before coding. Tools that do not discover these files automatically can be given the paths explicitly. These instructions improve discoverability; they cannot guarantee that every model obeys them.
|
|
15
|
+
|
|
16
|
+
## Maintaining the assets
|
|
17
|
+
|
|
18
|
+
`cloudoll/ai/docs` contains verbatim snapshots of the official site's Markdown, not hand-maintained API summaries. `manifest.json` records the library version, source path, public URL, and SHA-256 for every topic. Relative site links in these snapshots refer to https://cloudoll.chuchur.com.
|
|
19
|
+
|
|
20
|
+
From the library root, with the documentation repository available:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
python scripts/sync_ai_docs.py --docs-root ../cloudoll-py-docs
|
|
24
|
+
python scripts/sync_ai_docs.py --docs-root ../cloudoll-py-docs --check
|
|
25
|
+
python -m pytest tests/test_ai.py
|
|
26
|
+
python -m build
|
|
27
|
+
python tests/check_wheel.py dist/cloudoll-VERSION-py3-none-any.whl
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The sync also generates the site's `public/llms.txt` and `public/llms-full.txt`. Commit both repositories' changes. These files are discovery aids, not a replacement for version-matched local docs. When upgrading Cloudoll in an existing application, review its installed skill against the packaged skill and merge intentional changes; initialization refuses a differing skill to avoid overwriting custom instructions.
|
|
31
|
+
|
|
32
|
+
Before release, run the cross-repository `--check` command. Tests validate the packaged snapshot integrity and version; they cannot detect uncommitted changes in an unavailable documentation repository.
|
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.2.1
|
|
4
|
+
|
|
5
|
+
- Correct the AI initialization regression test to compare the original bytes
|
|
6
|
+
rather than assume LF newlines on Windows. Keep preservation and idempotence
|
|
7
|
+
assertions enabled on every platform.
|
|
8
|
+
- Normalize the generated full documentation's trailing newline.
|
|
9
|
+
- Include all 4.2.0 features. The 4.2.0 tag was blocked by Windows CI and was
|
|
10
|
+
not published to PyPI; 4.2.1 is the replacement release.
|
|
11
|
+
|
|
12
|
+
## 4.2.0
|
|
13
|
+
|
|
14
|
+
Backward-compatible AI-assisted development release. Python 3.9+ remains supported.
|
|
15
|
+
|
|
16
|
+
- Add offline `cloudoll ai list`, `read`, and `search` commands for bundled official
|
|
17
|
+
documentation, with source URLs, version metadata and content integrity checks.
|
|
18
|
+
- Add `cloudoll ai init` for existing projects. New scaffolds automatically include
|
|
19
|
+
an AGENTS.md entry and a project-local Cloudoll skill covering documented project
|
|
20
|
+
structure, validation, ORM, resource lifecycle, security and verification.
|
|
21
|
+
- Preserve existing project instructions and refuse to overwrite customized skills
|
|
22
|
+
or write through symbolic links. Initialization is repeatable.
|
|
23
|
+
- Add documentation synchronization tooling and website LLM discovery assets.
|
|
24
|
+
Test both instruction initialization and AI resources in the built wheel.
|
|
25
|
+
|
|
26
|
+
## 4.1.0
|
|
27
|
+
|
|
28
|
+
Backward-compatible feature release. Pydantic 2 (`>=2.0,<3`) is now a core
|
|
29
|
+
runtime dependency. Existing handlers and explicit ORM engine bindings remain
|
|
30
|
+
supported. Python 3.9+ remains supported by the CI matrix.
|
|
31
|
+
|
|
32
|
+
Publication date is recorded by the release tag/PyPI; preparing these files does
|
|
33
|
+
not publish the package.
|
|
34
|
+
|
|
35
|
+
- Correct lifecycle callback and JWT payload type declarations for strict
|
|
36
|
+
checking with current dependency annotations.
|
|
37
|
+
- Include HTTP method and actual request path in exception logs; emit 4xx access
|
|
38
|
+
logs at WARNING and 5xx at ERROR so failed requests remain visible above INFO.
|
|
39
|
+
|
|
40
|
+
- Add Pydantic 2 request validation with typed `Body`, `Query`, `Form`, and
|
|
41
|
+
`Path` bindings, repeated collection parameters and structured HTTP 400 errors.
|
|
42
|
+
Preserve legacy request and streaming multipart handlers.
|
|
43
|
+
|
|
44
|
+
- Add application-local named ORM datasources via `orm.default` and model
|
|
45
|
+
`__datasource__`, independent class-level queries, `Model.query()` and
|
|
46
|
+
`Model.transaction()`. Reuse existing task-owned engine transactions.
|
|
47
|
+
- Add `datasource_context` for scoped standalone/test engine bindings. Keep
|
|
48
|
+
explicit `.use(engine)`, offline SQL compilation and bound records compatible.
|
|
49
|
+
|
|
3
50
|
## 4.0.0
|
|
4
51
|
|
|
5
52
|
Release notes for 4.0.0. Publication date is recorded by the release tag/PyPI;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudoll
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.1
|
|
4
4
|
Summary: Assist in quickly creating distributable microservices based on aiohttp, with a focus on simplicity and ease of use.
|
|
5
5
|
Author-email: Qiu <smallerqiu@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -17,6 +17,7 @@ License-File: LICENSE
|
|
|
17
17
|
Requires-Dist: watchfiles
|
|
18
18
|
Requires-Dist: colorlog
|
|
19
19
|
Requires-Dist: aiohttp
|
|
20
|
+
Requires-Dist: pydantic<3,>=2.0
|
|
20
21
|
Requires-Dist: jinja2
|
|
21
22
|
Requires-Dist: pyjwt
|
|
22
23
|
Requires-Dist: aiohttp_session[secure]
|
|
@@ -53,16 +54,25 @@ Dynamic: license-file
|
|
|
53
54
|
|
|
54
55
|
English | [简体中文](README.zh-CN.md)
|
|
55
56
|
|
|
56
|
-
Cloudoll 4.
|
|
57
|
+
Cloudoll 4.2.1 is an aiohttp-based Python library for routing, middleware,
|
|
57
58
|
templates, sessions, JWT, database access and application scaffolding.
|
|
58
59
|
|
|
60
|
+
## AI-assisted development
|
|
61
|
+
|
|
62
|
+
New scaffolds include `AGENTS.md` and a project-local Cloudoll skill. For existing
|
|
63
|
+
projects, run `cloudoll ai init .` in the project's Python environment. Query the
|
|
64
|
+
bundled official documentation offline with `cloudoll ai list`,
|
|
65
|
+
`cloudoll ai read database`, or `cloudoll ai search Body`. No MCP is required.
|
|
66
|
+
See [AI.md](AI.md) for version matching and maintenance. If your installed release
|
|
67
|
+
does not have `cloudoll ai --help`, consult its matching README and source first.
|
|
68
|
+
|
|
59
69
|
## Install
|
|
60
70
|
|
|
61
71
|
Python 3.9+ is required. Install only the optional drivers you use:
|
|
62
72
|
|
|
63
73
|
```sh
|
|
64
|
-
python -m pip install 'cloudoll==4.
|
|
65
|
-
python -m pip install 'cloudoll[mysql,postgres,cache]==4.
|
|
74
|
+
python -m pip install 'cloudoll==4.2.1'
|
|
75
|
+
python -m pip install 'cloudoll[mysql,postgres,cache]==4.2.1'
|
|
66
76
|
```
|
|
67
77
|
|
|
68
78
|
The AWS extra requires Python 3.10+. Aurora remains untested; see
|
|
@@ -77,7 +87,7 @@ python -m pip install -r requirements.txt
|
|
|
77
87
|
cloudoll start -n myapp
|
|
78
88
|
```
|
|
79
89
|
|
|
80
|
-
The generated project targets 4.
|
|
90
|
+
The generated project targets 4.2.1 and receives a random JWT secret. Controllers
|
|
81
91
|
and authentication middleware are examples: replace demo credentials and supply
|
|
82
92
|
your own authorization and upload policies before deployment.
|
|
83
93
|
|
|
@@ -162,8 +172,8 @@ python -m ruff check cloudoll tests
|
|
|
162
172
|
python -m mypy
|
|
163
173
|
python -m pytest -q -m 'not integration' -k 'not aws'
|
|
164
174
|
python -m build
|
|
165
|
-
python tests/check_wheel.py dist/cloudoll-4.
|
|
166
|
-
python tests/check_release.py dist/cloudoll-4.
|
|
175
|
+
python tests/check_wheel.py dist/cloudoll-4.2.1-py3-none-any.whl
|
|
176
|
+
python tests/check_release.py dist/cloudoll-4.2.1-py3-none-any.whl
|
|
167
177
|
```
|
|
168
178
|
|
|
169
179
|
Native integration tests require explicitly configured disposable databases.
|
|
@@ -178,3 +188,64 @@ from ordinary CI. Passing CI is not a substitute for workload-specific testing.
|
|
|
178
188
|
- [Compatibility and release policy](docs/maintenance.md)
|
|
179
189
|
- [Security reporting](SECURITY.md)
|
|
180
190
|
- [MIT license](LICENSE)
|
|
191
|
+
|
|
192
|
+
## Named ORM datasources
|
|
193
|
+
|
|
194
|
+
Configure `orm: {default: blog}` with a matching `database.blog` connection.
|
|
195
|
+
Models may set `__datasource__ = "analytics"` to select another named connection.
|
|
196
|
+
Within an active application, `await User.select().where(User.id > 0).all()`
|
|
197
|
+
creates a fresh query using that engine. `User.query()` retains `Query[User]`
|
|
198
|
+
typing. Existing `User.use(engine)` and offline `User.use(None)` are unchanged.
|
|
199
|
+
|
|
200
|
+
`async with User.transaction():` delegates to the engine's task-owned transaction
|
|
201
|
+
and nested-savepoint implementation. Queries using the same engine participate
|
|
202
|
+
in that transaction; separate datasources are not a distributed transaction.
|
|
203
|
+
Loaded records keep their original engine. Each JOIN executes on its Query's
|
|
204
|
+
engine, not across independently bound model datasources.
|
|
205
|
+
|
|
206
|
+
Scripts/tests can use `with datasource_context({"blog": engine}, default="blog"):`
|
|
207
|
+
(imported from `cloudoll.orm`). This scopes existing engines without managing
|
|
208
|
+
engine lifetime. Missing contexts/defaults/uninitialized engines fail explicitly.
|
|
209
|
+
See the [configuration and examples](README.zh-CN.md#默认数据源与模型绑定).
|
|
210
|
+
|
|
211
|
+
## Request validation
|
|
212
|
+
|
|
213
|
+
Pydantic 2 is a core dependency. Define module-level input models and explicitly
|
|
214
|
+
bind sources with `Body[T]`, `Query[T]`, `Form[T]`, or `Path[T]`. Handlers receive
|
|
215
|
+
validated model instances; the aliases also preserve editor/type-checker support.
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
219
|
+
from cloudoll.web import Query
|
|
220
|
+
|
|
221
|
+
class ArticleQuery(BaseModel):
|
|
222
|
+
model_config = ConfigDict(extra="forbid")
|
|
223
|
+
page: int = Field(default=1, ge=1)
|
|
224
|
+
size: int = Field(default=20, ge=1, le=100)
|
|
225
|
+
tags: list[str] = Field(default_factory=list)
|
|
226
|
+
|
|
227
|
+
@app.get("/articles")
|
|
228
|
+
async def list_articles(query: Query[ArticleQuery]):
|
|
229
|
+
return query.model_dump()
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`?page=2&tags=python&tags=life` produces an integer and a list. Repeated
|
|
233
|
+
Query/Form values are retained for list/set/tuple fields (including string aliases);
|
|
234
|
+
scalar fields use the first value. Comma-separated values and bracket-style nested
|
|
235
|
+
query syntax are not expanded. Use JSON Body for nested objects.
|
|
236
|
+
|
|
237
|
+
Combine Query and Path with one Body **or** Form, optionally preceded by the raw
|
|
238
|
+
request parameter. Body accepts JSON/`+json`; Form accepts URL-encoded and multipart
|
|
239
|
+
forms. Validation happens before the handler and always returns JSON HTTP 400 with
|
|
240
|
+
`error.status`, `message`, `request_id`, and `details`. Each detail contains `source`,
|
|
241
|
+
`loc` (including nested field/index locations), `code`, and `message`. Raw inputs
|
|
242
|
+
and exception context are omitted; custom validator errors use `Invalid value`.
|
|
243
|
+
Middleware can catch the exported `RequestValidationError` to customize this response.
|
|
244
|
+
Malformed JSON (400) and unsupported media types (415) retain the existing
|
|
245
|
+
`server.json_errors` behavior.
|
|
246
|
+
|
|
247
|
+
Legacy handlers and streaming `(request, field)` uploads remain supported. Typed
|
|
248
|
+
handlers only parse declared sources. Unknown fields follow model configuration
|
|
249
|
+
(Pydantic defaults to ignoring them); use `extra="forbid"` for strict write inputs.
|
|
250
|
+
Separate create/update models and use `model_dump(exclude_unset=True)` for partial
|
|
251
|
+
updates. Validation does not replace authorization or database constraints.
|
|
@@ -2,16 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
Cloudoll 4.
|
|
5
|
+
Cloudoll 4.2.1 is an aiohttp-based Python library for routing, middleware,
|
|
6
6
|
templates, sessions, JWT, database access and application scaffolding.
|
|
7
7
|
|
|
8
|
+
## AI-assisted development
|
|
9
|
+
|
|
10
|
+
New scaffolds include `AGENTS.md` and a project-local Cloudoll skill. For existing
|
|
11
|
+
projects, run `cloudoll ai init .` in the project's Python environment. Query the
|
|
12
|
+
bundled official documentation offline with `cloudoll ai list`,
|
|
13
|
+
`cloudoll ai read database`, or `cloudoll ai search Body`. No MCP is required.
|
|
14
|
+
See [AI.md](AI.md) for version matching and maintenance. If your installed release
|
|
15
|
+
does not have `cloudoll ai --help`, consult its matching README and source first.
|
|
16
|
+
|
|
8
17
|
## Install
|
|
9
18
|
|
|
10
19
|
Python 3.9+ is required. Install only the optional drivers you use:
|
|
11
20
|
|
|
12
21
|
```sh
|
|
13
|
-
python -m pip install 'cloudoll==4.
|
|
14
|
-
python -m pip install 'cloudoll[mysql,postgres,cache]==4.
|
|
22
|
+
python -m pip install 'cloudoll==4.2.1'
|
|
23
|
+
python -m pip install 'cloudoll[mysql,postgres,cache]==4.2.1'
|
|
15
24
|
```
|
|
16
25
|
|
|
17
26
|
The AWS extra requires Python 3.10+. Aurora remains untested; see
|
|
@@ -26,7 +35,7 @@ python -m pip install -r requirements.txt
|
|
|
26
35
|
cloudoll start -n myapp
|
|
27
36
|
```
|
|
28
37
|
|
|
29
|
-
The generated project targets 4.
|
|
38
|
+
The generated project targets 4.2.1 and receives a random JWT secret. Controllers
|
|
30
39
|
and authentication middleware are examples: replace demo credentials and supply
|
|
31
40
|
your own authorization and upload policies before deployment.
|
|
32
41
|
|
|
@@ -111,8 +120,8 @@ python -m ruff check cloudoll tests
|
|
|
111
120
|
python -m mypy
|
|
112
121
|
python -m pytest -q -m 'not integration' -k 'not aws'
|
|
113
122
|
python -m build
|
|
114
|
-
python tests/check_wheel.py dist/cloudoll-4.
|
|
115
|
-
python tests/check_release.py dist/cloudoll-4.
|
|
123
|
+
python tests/check_wheel.py dist/cloudoll-4.2.1-py3-none-any.whl
|
|
124
|
+
python tests/check_release.py dist/cloudoll-4.2.1-py3-none-any.whl
|
|
116
125
|
```
|
|
117
126
|
|
|
118
127
|
Native integration tests require explicitly configured disposable databases.
|
|
@@ -127,3 +136,64 @@ from ordinary CI. Passing CI is not a substitute for workload-specific testing.
|
|
|
127
136
|
- [Compatibility and release policy](docs/maintenance.md)
|
|
128
137
|
- [Security reporting](SECURITY.md)
|
|
129
138
|
- [MIT license](LICENSE)
|
|
139
|
+
|
|
140
|
+
## Named ORM datasources
|
|
141
|
+
|
|
142
|
+
Configure `orm: {default: blog}` with a matching `database.blog` connection.
|
|
143
|
+
Models may set `__datasource__ = "analytics"` to select another named connection.
|
|
144
|
+
Within an active application, `await User.select().where(User.id > 0).all()`
|
|
145
|
+
creates a fresh query using that engine. `User.query()` retains `Query[User]`
|
|
146
|
+
typing. Existing `User.use(engine)` and offline `User.use(None)` are unchanged.
|
|
147
|
+
|
|
148
|
+
`async with User.transaction():` delegates to the engine's task-owned transaction
|
|
149
|
+
and nested-savepoint implementation. Queries using the same engine participate
|
|
150
|
+
in that transaction; separate datasources are not a distributed transaction.
|
|
151
|
+
Loaded records keep their original engine. Each JOIN executes on its Query's
|
|
152
|
+
engine, not across independently bound model datasources.
|
|
153
|
+
|
|
154
|
+
Scripts/tests can use `with datasource_context({"blog": engine}, default="blog"):`
|
|
155
|
+
(imported from `cloudoll.orm`). This scopes existing engines without managing
|
|
156
|
+
engine lifetime. Missing contexts/defaults/uninitialized engines fail explicitly.
|
|
157
|
+
See the [configuration and examples](README.zh-CN.md#默认数据源与模型绑定).
|
|
158
|
+
|
|
159
|
+
## Request validation
|
|
160
|
+
|
|
161
|
+
Pydantic 2 is a core dependency. Define module-level input models and explicitly
|
|
162
|
+
bind sources with `Body[T]`, `Query[T]`, `Form[T]`, or `Path[T]`. Handlers receive
|
|
163
|
+
validated model instances; the aliases also preserve editor/type-checker support.
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
167
|
+
from cloudoll.web import Query
|
|
168
|
+
|
|
169
|
+
class ArticleQuery(BaseModel):
|
|
170
|
+
model_config = ConfigDict(extra="forbid")
|
|
171
|
+
page: int = Field(default=1, ge=1)
|
|
172
|
+
size: int = Field(default=20, ge=1, le=100)
|
|
173
|
+
tags: list[str] = Field(default_factory=list)
|
|
174
|
+
|
|
175
|
+
@app.get("/articles")
|
|
176
|
+
async def list_articles(query: Query[ArticleQuery]):
|
|
177
|
+
return query.model_dump()
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`?page=2&tags=python&tags=life` produces an integer and a list. Repeated
|
|
181
|
+
Query/Form values are retained for list/set/tuple fields (including string aliases);
|
|
182
|
+
scalar fields use the first value. Comma-separated values and bracket-style nested
|
|
183
|
+
query syntax are not expanded. Use JSON Body for nested objects.
|
|
184
|
+
|
|
185
|
+
Combine Query and Path with one Body **or** Form, optionally preceded by the raw
|
|
186
|
+
request parameter. Body accepts JSON/`+json`; Form accepts URL-encoded and multipart
|
|
187
|
+
forms. Validation happens before the handler and always returns JSON HTTP 400 with
|
|
188
|
+
`error.status`, `message`, `request_id`, and `details`. Each detail contains `source`,
|
|
189
|
+
`loc` (including nested field/index locations), `code`, and `message`. Raw inputs
|
|
190
|
+
and exception context are omitted; custom validator errors use `Invalid value`.
|
|
191
|
+
Middleware can catch the exported `RequestValidationError` to customize this response.
|
|
192
|
+
Malformed JSON (400) and unsupported media types (415) retain the existing
|
|
193
|
+
`server.json_errors` behavior.
|
|
194
|
+
|
|
195
|
+
Legacy handlers and streaming `(request, field)` uploads remain supported. Typed
|
|
196
|
+
handlers only parse declared sources. Unknown fields follow model configuration
|
|
197
|
+
(Pydantic defaults to ignoring them); use `extra="forbid"` for strict write inputs.
|
|
198
|
+
Separate create/update models and use `model_dump(exclude_unset=True)` for partial
|
|
199
|
+
updates. Validation does not replace authorization or database constraints.
|
|
@@ -6,7 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
Cloudoll 是一个基于 aiohttp 的 Python Web 开发库,提供路由自动注册、中间件、模板渲染、会话管理、JWT、数据库访问及命令行脚手架,帮助快速构建 Web 应用和微服务。
|
|
8
8
|
|
|
9
|
-
本文适用于 Cloudoll 4.
|
|
9
|
+
本文适用于 Cloudoll 4.2.1。从 3.x 升级前请阅读下方迁移说明。
|
|
10
|
+
|
|
11
|
+
## AI 辅助开发
|
|
12
|
+
|
|
13
|
+
新建项目会自带 `AGENTS.md` 和项目级 Cloudoll Skill;已有项目可以在其 Python
|
|
14
|
+
环境中运行 `cloudoll ai init .` 补装。通过 `cloudoll ai list`、
|
|
15
|
+
`cloudoll ai read database` 和 `cloudoll ai search Body` 离线查询随包分发的官方文档,
|
|
16
|
+
不需要 MCP。请让 AI 先读项目指令,再按文档设计实现。
|
|
17
|
+
版本匹配及维护方式见 [AI.md](AI.md)。如果安装版本还不支持 `cloudoll ai --help`,
|
|
18
|
+
请先查阅该版本的 README 和源码。
|
|
10
19
|
|
|
11
20
|
## 环境要求
|
|
12
21
|
|
|
@@ -16,10 +25,10 @@ Cloudoll 是一个基于 aiohttp 的 Python Web 开发库,提供路由自动
|
|
|
16
25
|
|
|
17
26
|
## 安装
|
|
18
27
|
|
|
19
|
-
安装 4.
|
|
28
|
+
安装 4.2.1:
|
|
20
29
|
|
|
21
30
|
```sh
|
|
22
|
-
python -m pip install 'cloudoll==4.
|
|
31
|
+
python -m pip install 'cloudoll==4.2.1'
|
|
23
32
|
```
|
|
24
33
|
|
|
25
34
|
在当前仓库目录安装源码:
|
|
@@ -32,16 +41,16 @@ python -m pip install -e .
|
|
|
32
41
|
|
|
33
42
|
```sh
|
|
34
43
|
# MySQL
|
|
35
|
-
python -m pip install 'cloudoll[mysql]==4.
|
|
44
|
+
python -m pip install 'cloudoll[mysql]==4.2.1'
|
|
36
45
|
|
|
37
46
|
# PostgreSQL
|
|
38
|
-
python -m pip install 'cloudoll[postgres]==4.
|
|
47
|
+
python -m pip install 'cloudoll[postgres]==4.2.1'
|
|
39
48
|
|
|
40
49
|
# AWS 数据库驱动
|
|
41
|
-
python -m pip install 'cloudoll[aws]==4.
|
|
50
|
+
python -m pip install 'cloudoll[aws]==4.2.1'
|
|
42
51
|
|
|
43
52
|
# Redis / Memcached 会话或 Redis 数据库
|
|
44
|
-
python -m pip install 'cloudoll[cache]==4.
|
|
53
|
+
python -m pip install 'cloudoll[cache]==4.2.1'
|
|
45
54
|
```
|
|
46
55
|
|
|
47
56
|
源码开发时可用 `python -m pip install -e '.[mysql,postgres,cache,dev]'` 安装。
|
|
@@ -128,6 +137,114 @@ async def create_user(request):
|
|
|
128
137
|
|
|
129
138
|
单参数处理函数通过 `request.params` 获取路径参数、`request.qs` 获取查询参数、`request.body` 获取已解析的 JSON 或表单数据。路由装饰器还包括 `put`、`delete`,类视图可使用 `View` 和 `routes`。
|
|
130
139
|
|
|
140
|
+
### 控制台与请求错误日志
|
|
141
|
+
|
|
142
|
+
通过 `cloudoll` CLI 启动时,默认开启控制台和文件日志。直接在 Python 中创建应用时,
|
|
143
|
+
在启动入口显式配置(导入 Cloudoll 本身不会配置日志):
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import logging
|
|
147
|
+
from cloudoll.logging import configure_logging
|
|
148
|
+
|
|
149
|
+
configure_logging(level=logging.INFO, console=True, files=True)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`files=False` 可关闭文件输出;`level=logging.WARNING` 只看警告和错误。
|
|
153
|
+
每条请求日志包含请求方法、实际路径、HTTP 状态码、耗时和 request ID。
|
|
154
|
+
2xx/3xx 使用 INFO,4xx 使用 WARNING,5xx 使用 ERROR;未捕获异常同时记录请求方法、
|
|
155
|
+
路径和完整堆栈。路径不包含查询参数。响应头 `X-Request-ID` 可用于关联同一次请求的日志。
|
|
156
|
+
|
|
157
|
+
文件默认位于 macOS/Linux 的 `~/.cloudoll/logs/`,按日期写入 `YYYY-MM-DD-all.log`
|
|
158
|
+
和 `YYYY-MM-DD-error.log`;可用 `CLOUDOLL_LOG_DIR` 环境变量指定目录。控制台输出使用
|
|
159
|
+
stderr,服务由 systemd 托管时应查看该服务的 journal。`server.json_errors` 只控制
|
|
160
|
+
HTTP 错误响应格式,不是日志开关。
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
启动阶段会分别记录 `Initializing database '名称'`、`Initialized database '名称' (耗时ms)`,
|
|
164
|
+
以及 Session 的开始与完成时间。数据库连接仍并行初始化,各项耗时不可直接相加。
|
|
165
|
+
如果停在某项 Initializing,可据此定位尚未完成的资源;失败或取消会分别记录
|
|
166
|
+
Initialization failed / cancelled,并继续传播异常及清理已创建资源。
|
|
167
|
+
日志只标识配置中的连接名称,不打印连接字符串、密码或完整配置。
|
|
168
|
+
可选生命周期入口缺失只在 DEBUG 下提示一次;静态文件的反向代理建议也降为 DEBUG。
|
|
169
|
+
|
|
170
|
+
### 声明式参数校验
|
|
171
|
+
|
|
172
|
+
使用 Pydantic 2 模型声明输入,通过 `Body[T]`(JSON)、`Query[T]`(查询参数)、
|
|
173
|
+
`Form[T]`(表单)和 `Path[T]`(路径参数)指定来源。Pydantic 已作为基础依赖安装。
|
|
174
|
+
模型应定义在模块级,尤其是启用 `from __future__ import annotations` 时。
|
|
175
|
+
这些注解保留模型的静态类型,函数收到的是校验后的模型实例。
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
179
|
+
from cloudoll.web import Application, Body, Query, Path, Form
|
|
180
|
+
|
|
181
|
+
app = Application()
|
|
182
|
+
|
|
183
|
+
class ArticleQuery(BaseModel):
|
|
184
|
+
model_config = ConfigDict(extra="forbid")
|
|
185
|
+
page: int = Field(default=1, ge=1)
|
|
186
|
+
size: int = Field(default=20, ge=1, le=100)
|
|
187
|
+
tags: list[str] = Field(default_factory=list)
|
|
188
|
+
|
|
189
|
+
class ArticlePath(BaseModel):
|
|
190
|
+
id: int = Field(gt=0)
|
|
191
|
+
|
|
192
|
+
class ArticleInput(BaseModel):
|
|
193
|
+
model_config = ConfigDict(extra="forbid")
|
|
194
|
+
title: str = Field(min_length=1, max_length=200)
|
|
195
|
+
content: str = Field(min_length=1)
|
|
196
|
+
|
|
197
|
+
@app.get("/articles")
|
|
198
|
+
async def list_articles(query: Query[ArticleQuery]):
|
|
199
|
+
return query.model_dump()
|
|
200
|
+
|
|
201
|
+
@app.post("/articles/{id}")
|
|
202
|
+
async def save_article(request, path: Path[ArticlePath], body: Body[ArticleInput]):
|
|
203
|
+
return {"id": path.id, "article": body.model_dump()}
|
|
204
|
+
|
|
205
|
+
@app.post("/article-form")
|
|
206
|
+
async def submit_article(data: Form[ArticleInput]):
|
|
207
|
+
return data.model_dump()
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`GET /articles?page=2&tags=python&tags=生活` 得到整数 `page=2` 和列表
|
|
211
|
+
`tags=["python", "生活"]`。未传字段使用模型默认值;必填字段缺失、类型或范围不符
|
|
212
|
+
均在执行接口函数前返回 HTTP 400。Query/Form 的列表、集合和元组字段保留重复值
|
|
213
|
+
(支持字符串字段别名);标量字段沿用原接口取第一个值的行为,不自动拆分逗号。
|
|
214
|
+
嵌套对象建议通过 JSON Body 传递,不解析 `filter[name]` 这类查询字符串语法。
|
|
215
|
+
|
|
216
|
+
校验失败始终返回 JSON,无需启用 `server.json_errors`:
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"error": {
|
|
221
|
+
"status": 400,
|
|
222
|
+
"message": "Request validation failed",
|
|
223
|
+
"request_id": "...",
|
|
224
|
+
"details": [
|
|
225
|
+
{"source": "query", "loc": ["page"], "code": "greater_than_equal", "message": "Input should be greater than or equal to 1"}
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`loc` 保留嵌套字段和列表下标,例如 `["items", 0, "title"]`。错误不附带原始输入、
|
|
232
|
+
异常上下文或文档 URL;自定义校验器错误使用通用 `Invalid value` 提示,前端可按
|
|
233
|
+
`code` 提供文案。可通过中间件捕获公开的 `RequestValidationError` 定制响应。
|
|
234
|
+
格式错误的 JSON 返回 400,不支持的 Content-Type 返回 415,这两类解析错误仍遵循
|
|
235
|
+
已有的 `server.json_errors` 设置。
|
|
236
|
+
|
|
237
|
+
可以组合 Query、Path 与一个 Body 或 Form;Body 和 Form 不能同时使用。Body 接受
|
|
238
|
+
`application/json` 和 `+json` 类型,Form 接受 URL 编码与 multipart 表单。
|
|
239
|
+
可选的原始 request 必须是第一个参数,其后每个参数须声明来源。
|
|
240
|
+
旧的 `handler()`、`handler(request)`、`handler(request, field)` 文件流上传接口保持兼容。
|
|
241
|
+
声明来源的接口只解析所声明的数据;需要流式上传文件时继续使用原来的上传接口。
|
|
242
|
+
|
|
243
|
+
未知字段策略由模型决定:Pydantic 默认忽略,写接口建议使用上例的
|
|
244
|
+
`ConfigDict(extra="forbid")`。新增与更新定义独立输入模型;局部更新用
|
|
245
|
+
`model_dump(exclude_unset=True)` 区分“没传字段”和“显式传 null”。字段校验不替代
|
|
246
|
+
权限检查、数据库唯一约束等业务规则。
|
|
247
|
+
|
|
131
248
|
## 模板与静态文件
|
|
132
249
|
|
|
133
250
|
模板引擎使用 Jinja2。创建 `templates/index.html`:
|
|
@@ -273,6 +390,7 @@ db = await create_engine(
|
|
|
273
390
|
- 未配置会话密钥时,会使用应用实例内的随机密钥,重启后旧会话失效。
|
|
274
391
|
- 通过 HTTPS 提供服务时,将 `session.secure` 设置为 `true`。本地 HTTP 调试可保持 `false`。
|
|
275
392
|
- `request.session` 可以读写会话数据;`request.app.jwt_encode(payload)` 和 `request.app.jwt_decode(token)` 使用应用中的 JWT 配置。
|
|
393
|
+
- Session 中间件在错误处理完成后将会话保存到最终响应;返回 `render_json`、`render_error` 或由业务中间件替换错误响应时,会话修改仍会保存。渲染函数每次创建新响应,手动设置在其他响应上的 Cookie 仍需显式复制;框架的 `json_errors` 转换会保留 HTTP 异常上的 Cookie 和可重复响应头。
|
|
276
394
|
- 会话也支持 Redis 或 Memcached。配置 Redis 时,可使用 `session.redis.url`,或在 `session.redis` 下设置 `host`、`port`、`password` 和 `db`。
|
|
277
395
|
|
|
278
396
|
生成随机密钥:
|
|
@@ -305,6 +423,63 @@ async def list_users(request):
|
|
|
305
423
|
|
|
306
424
|
`Model.use(pool)` 返回绑定到指定连接的查询对象,不会修改其他查询或模型类的数据库绑定。每次独立查询应创建自己的查询对象。
|
|
307
425
|
|
|
426
|
+
### 默认数据源与模型绑定
|
|
427
|
+
|
|
428
|
+
`orm.default` 指向 `database` 中的连接名称,既不是驱动类型,也不是实际库名。
|
|
429
|
+
每个数据源独立配置服务器、库名和连接池:
|
|
430
|
+
|
|
431
|
+
```yaml
|
|
432
|
+
database:
|
|
433
|
+
blog:
|
|
434
|
+
url: mysql://username:password@host:3306/db1
|
|
435
|
+
analytics:
|
|
436
|
+
url: postgres://username:password@pg2:5432/db3
|
|
437
|
+
orm:
|
|
438
|
+
default: blog
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
class AccessLog(Model):
|
|
443
|
+
__table__ = "access_logs"
|
|
444
|
+
__datasource__ = "analytics"
|
|
445
|
+
id = models.IntegerField(primary_key=True)
|
|
446
|
+
|
|
447
|
+
# 在路由或已初始化数据库的应用生命周期内:
|
|
448
|
+
users = await User.select().where(User.id > 0).all()
|
|
449
|
+
logs = await AccessLog.where(AccessLog.id > 0).all()
|
|
450
|
+
|
|
451
|
+
# 每次 Model.query() 返回独立、带 Query[User] 类型的查询构造器。
|
|
452
|
+
query = User.query().select()
|
|
453
|
+
|
|
454
|
+
# 事务内,同一数据源的隐式查询自动复用事务连接。
|
|
455
|
+
async with User.transaction():
|
|
456
|
+
await User.where(User.id == 1).update(name="Alice")
|
|
457
|
+
await User.where(User.id == 2).update(name="Bob")
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
数据源解析优先级:显式 `.use(engine)` → `datasource_context` 中的映射或当前应用映射。
|
|
461
|
+
在映射内优先使用模型的 `__datasource__`,否则使用默认名称;模型绑定可继承。
|
|
462
|
+
映射中缺少指定名称时直接报错,不会退回其他数据库。
|
|
463
|
+
每次类级查询都创建新的 Query,不在模型类上缓存连接或查询条件。
|
|
464
|
+
已有记录仍使用读取它时绑定的引擎;`.use(None)` 仍可用于离线 SQL 编译。
|
|
465
|
+
|
|
466
|
+
`User.transaction()` 复用引擎现有事务实现,嵌套事务使用保存点。直接使用
|
|
467
|
+
`async with engine.transaction()` 也能让同一引擎的隐式查询参与事务。
|
|
468
|
+
多个数据源不构成分布式事务,不能保证一起提交或回滚。事务连接不能在子任务间共享。
|
|
469
|
+
不同数据源的模型不能通过一次 JOIN 自动跨实例查询;JOIN 始终在当前 Query 的数据源执行。
|
|
470
|
+
|
|
471
|
+
独立脚本、测试或没有应用上下文的任务,可绑定已创建的引擎:
|
|
472
|
+
|
|
473
|
+
```python
|
|
474
|
+
from cloudoll.orm import datasource_context
|
|
475
|
+
|
|
476
|
+
with datasource_context({"blog": engine}, default="blog"):
|
|
477
|
+
users = await User.select().all()
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
此上下文不创建或关闭引擎,退出时恢复上层绑定。没有活动应用/显式上下文、未配置默认值、
|
|
481
|
+
或者数据库尚未初始化时,隐式查询明确报错。现有 `.use(engine)` 用法不需要增加配置。
|
|
482
|
+
|
|
308
483
|
### 从数据库生成模型
|
|
309
484
|
|
|
310
485
|
```sh
|
|
@@ -371,8 +546,8 @@ cloudoll restart -n myapp
|
|
|
371
546
|
python -m pip install -e '.[mysql,postgres,cache,dev]'
|
|
372
547
|
python -m pytest -q -m 'not integration' -k 'not aws'
|
|
373
548
|
python -m build
|
|
374
|
-
python tests/check_wheel.py dist/cloudoll-4.
|
|
375
|
-
python tests/check_release.py dist/cloudoll-4.
|
|
549
|
+
python tests/check_wheel.py dist/cloudoll-4.2.1-py3-none-any.whl
|
|
550
|
+
python tests/check_release.py dist/cloudoll-4.2.1-py3-none-any.whl
|
|
376
551
|
```
|
|
377
552
|
|
|
378
553
|
最后一条命令用于验证构建出的 wheel,版本号变化后请调整文件名。测试使用模拟数据库连接及本机 HTTP 服务;真实数据库连接和 AWS 故障切换需要单独的集成环境。
|
|
@@ -15,7 +15,7 @@ reproduction, impact and any mitigation. No response-time SLA is promised.
|
|
|
15
15
|
|
|
16
16
|
Security fixes target the latest stable release. Older release branches have no
|
|
17
17
|
guaranteed backports; upgrade or contact the maintainer about a specific issue.
|
|
18
|
-
This policy accompanies Cloudoll 4.
|
|
18
|
+
This policy accompanies Cloudoll 4.1.0; published versions are identified by PyPI
|
|
19
19
|
and repository release tags, not by documentation edits alone.
|
|
20
20
|
CI compatibility with a Python version is not an extension of that runtime's
|
|
21
21
|
upstream security support. Deploy on a runtime still supported by its vendor.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.2.1"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
## Cloudoll development
|
|
2
|
+
|
|
3
|
+
Before building or changing a Cloudoll application, read `.agents/skills/cloudoll/SKILL.md` completely and follow its documentation workflow. Use the project's Python environment, not a globally installed version.
|
|
4
|
+
|
|
5
|
+
Official documentation is bundled with the installed library: `python -m cloudoll.cli ai list`, `ai read <topic>`, and `ai search <query>`. These commands work offline without MCP. Read `structure`, `config`, `router`, and `validation` before designing an application; read `database` before database work. Consult the installed source when resolving a signature discrepancy; report the discrepancy rather than inventing an API.
|
|
6
|
+
|
|
7
|
+
Prefer the documented scaffold, auto-discovered controllers/middlewares, YAML configuration, framework-owned database lifecycle, typed request validation, and ORM for ordinary CRUD. Low-level alternatives require a task-specific reason. Never silently replace Cloudoll's architecture with patterns from another framework.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Versioned documentation and project instructions for AI-assisted development."""
|