nene2-python 1.8.17__tar.gz → 1.8.19__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.
- {nene2_python-1.8.17 → nene2_python-1.8.19}/CHANGELOG.md +25 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/PKG-INFO +1 -1
- nene2_python-1.8.19/docs/field-trials/2026-05-field-trial-65.md +54 -0
- nene2_python-1.8.19/docs/field-trials/2026-05-field-trial-66.md +52 -0
- nene2_python-1.8.19/docs/field-trials/2026-05-field-trial-67.md +75 -0
- nene2_python-1.8.19/docs/field-trials/2026-05-field-trial-68.md +67 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/pyproject.toml +1 -1
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/database/sqlalchemy_executor.py +17 -1
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/http/problem_details.py +13 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/middleware/domain_exception.py +7 -2
- {nene2_python-1.8.17 → nene2_python-1.8.19}/uv.lock +1 -1
- {nene2_python-1.8.17 → nene2_python-1.8.19}/.env.example +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/.gitignore +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/AGENTS.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/CLAUDE.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/Dockerfile +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/LICENSE +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/README.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/alembic/README +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/alembic/env.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/alembic.ini +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/compose.yaml +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/de/index.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/fr/index.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/index.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/index.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/reference/api.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/roadmap.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/todo/current.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/zh/index.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/package-lock.json +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/package.json +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/__main__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/app.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/mcp.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/schema.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.17 → nene2_python-1.8.19}/tests/scripts/test_export_openapi.py +0 -0
|
@@ -5,6 +5,31 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [1.8.19] — 2026-05-20
|
|
9
|
+
|
|
10
|
+
FT68 フィールドトライアル — SimpleDomainHandler + extra_factory 実運用検証。
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- `problem_details_response` の docstring に `extra` がトップレベルにフラットマージされることを明記 (#308) (FT68)
|
|
14
|
+
- `SimpleDomainHandler` の docstring に `extra_factory` のフラットマージ動作を例示 (#308) (FT68)
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Field trial report: `docs/field-trials/2026-05-field-trial-68.md`
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## [1.8.18] — 2026-05-20
|
|
22
|
+
|
|
23
|
+
FT67 フィールドトライアル — SqlAlchemyTransactionManager 実運用検証。
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- `SqlAlchemyQueryExecutor` の docstring に SQLite `:memory:` + `StaticPool` の注意書きを追加 (#305) (FT67)
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- Field trial reports: `docs/field-trials/2026-05-field-trial-65.md` 〜 `2026-05-field-trial-67.md` (FT65〜FT67)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
8
33
|
## [1.8.17] — 2026-05-20
|
|
9
34
|
|
|
10
35
|
FT64 フィールドトライアル — ValidationException 複数エラー実運用検証。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nene2-python
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.19
|
|
4
4
|
Summary: NENE2 Python — minimal API framework following NENE2's design philosophy
|
|
5
5
|
Project-URL: Homepage, https://github.com/hideyukiMORI/nene2-python
|
|
6
6
|
Project-URL: Repository, https://github.com/hideyukiMORI/nene2-python
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# FT65: DatabaseHealthCheck 実運用検証
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-20
|
|
4
|
+
**テーマ**: DB接続ヘルスチェック (`DatabaseHealthCheck`) と `CompositeHealthCheck` の組み合わせ実運用確認
|
|
5
|
+
**バージョン**: v1.8.17
|
|
6
|
+
**FTディレクトリ**: `/home/xi/docker/nene2-python-FT/ft65-db-health/`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 概要
|
|
11
|
+
|
|
12
|
+
`nene2.database.DatabaseHealthCheck` を `SqlAlchemyQueryExecutor` と組み合わせ、
|
|
13
|
+
`CompositeHealthCheck` に渡して FastAPI の `/health` エンドポイントで利用するパターンを検証した。
|
|
14
|
+
SQLite in-memory DB(正常)と存在しない DB(異常)の両方を確認。
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 実装内容
|
|
19
|
+
|
|
20
|
+
- `SqlAlchemyQueryExecutor(create_engine("sqlite:///:memory:"))`: SQLite in-memory DB
|
|
21
|
+
- `DatabaseHealthCheck(executor)`: `SELECT 1` で接続確認
|
|
22
|
+
- `CompositeHealthCheck([db_health])`: 集約して `/health` で返却
|
|
23
|
+
- 存在しない DB パスで 503 になることも確認
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## テスト結果
|
|
28
|
+
|
|
29
|
+
**4/4 passed**
|
|
30
|
+
|
|
31
|
+
| テスト | 結果 |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `test_healthy_db_returns_200` | PASSED |
|
|
34
|
+
| `test_broken_db_returns_503` | PASSED |
|
|
35
|
+
| `test_direct_database_health_check` | PASSED |
|
|
36
|
+
| `test_in_memory_db_composite_check` | PASSED |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Friction Points
|
|
41
|
+
|
|
42
|
+
なし。`DatabaseHealthCheck` → `CompositeHealthCheck` → FastAPI の流れは直感的で問題なし。
|
|
43
|
+
|
|
44
|
+
**特筆点**:
|
|
45
|
+
- `DatabaseHealthCheck` の `SELECT 1` は軽量で本番運用に適している
|
|
46
|
+
- DB 接続失敗時は例外をキャッチして `status="error"` を返す設計で、
|
|
47
|
+
ヘルスエンドポイント自体が 500 になることがない
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 結論
|
|
52
|
+
|
|
53
|
+
`DatabaseHealthCheck` は実運用で問題なく使用できる。
|
|
54
|
+
`SqlAlchemyQueryExecutor` と `CompositeHealthCheck` の組み合わせが自然に機能する。
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# FT66: AppSettings 実運用検証
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-20
|
|
4
|
+
**テーマ**: 型付き設定オブジェクト (`AppSettings`) の実運用確認
|
|
5
|
+
**バージョン**: v1.8.17
|
|
6
|
+
**FTディレクトリ**: `/home/xi/docker/nene2-python-FT/ft66-app-settings/`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 概要
|
|
11
|
+
|
|
12
|
+
`nene2.config.AppSettings` を直接インスタンス化し、環境変数によるオーバーライド・
|
|
13
|
+
バリデーション・`db_url` プロパティ・`SecretStr` の動作を検証した。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装内容
|
|
18
|
+
|
|
19
|
+
- `AppSettings()`: デフォルト値確認
|
|
20
|
+
- `monkeypatch.setenv()`: 環境変数でのオーバーライド
|
|
21
|
+
- 不正値(`APP_ENV=staging`、`LOG_LEVEL=VERBOSE`)でのバリデーション確認
|
|
22
|
+
- `db_url` プロパティで SQLAlchemy URL 生成を確認
|
|
23
|
+
- `CORS_ORIGINS` の JSON リスト形式パース確認
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## テスト結果
|
|
28
|
+
|
|
29
|
+
**10/10 passed**
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Friction Points
|
|
34
|
+
|
|
35
|
+
### FP-1 (軽微): `str(SecretStr(""))` が `""` を返す
|
|
36
|
+
|
|
37
|
+
**発生箇所**: `assert str(settings.db_password) != ""` テストが失敗
|
|
38
|
+
|
|
39
|
+
**症状**: 空の `SecretStr("")` を `str()` に渡すと `""` が返る(マスクされない)。
|
|
40
|
+
非空 `SecretStr("secret")` は `**********` が返る。
|
|
41
|
+
|
|
42
|
+
**原因**: Pydantic の設計上の挙動で、空文字列は空文字列のまま。
|
|
43
|
+
`repr()` では `SecretStr('')` と表示される。
|
|
44
|
+
|
|
45
|
+
**対応**: テストを `get_secret_value() == ""` で修正。フレームワーク側は変更不要。
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 結論
|
|
50
|
+
|
|
51
|
+
`AppSettings` は実運用で問題なく使用できる。
|
|
52
|
+
環境変数からの自動パース(bool・int・list[str] の JSON 形式)が便利。
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# FT67: SqlAlchemyTransactionManager 実運用検証
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-20
|
|
4
|
+
**テーマ**: トランザクション管理 (`SqlAlchemyTransactionManager`) の実運用確認
|
|
5
|
+
**バージョン**: v1.8.17 → v1.8.18 (ドキュメント追加)
|
|
6
|
+
**FTディレクトリ**: `/home/xi/docker/nene2-python-FT/ft67-transaction-manager/`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 概要
|
|
11
|
+
|
|
12
|
+
`nene2.database.SqlAlchemyTransactionManager` を使って口座振替アプリを実装し、
|
|
13
|
+
`transactional()` コールバック API・ロールバック動作・コミット確認を検証した。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装内容
|
|
18
|
+
|
|
19
|
+
- SQLite in-memory DB に `accounts` テーブルを作成
|
|
20
|
+
- `transactional(callback)`: 振替処理を 1 トランザクションで実行
|
|
21
|
+
- 残高不足時は `ValueError` を raise → ロールバック
|
|
22
|
+
- GET `/accounts/{name}` と POST `/transfers` エンドポイント
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## テスト結果
|
|
27
|
+
|
|
28
|
+
**7/7 passed** (StaticPool 修正後)
|
|
29
|
+
|
|
30
|
+
| テスト | 結果 |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `test_get_existing_account` | PASSED |
|
|
33
|
+
| `test_get_nonexistent_account_returns_404` | PASSED |
|
|
34
|
+
| `test_successful_transfer` | PASSED |
|
|
35
|
+
| `test_insufficient_balance_returns_422` | PASSED |
|
|
36
|
+
| `test_transaction_rollback_on_error` | PASSED |
|
|
37
|
+
| `test_transfer_to_nonexistent_account` | PASSED |
|
|
38
|
+
| `test_transactional_commits_on_success` | PASSED |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Friction Points
|
|
43
|
+
|
|
44
|
+
### FP-1: SQLite `:memory:` と `SqlAlchemyQueryExecutor` の接続分離問題
|
|
45
|
+
|
|
46
|
+
**発生箇所**: `setup_db()` でテーブル作成後、`executor.fetch_one()` が `no such table: accounts` エラー
|
|
47
|
+
|
|
48
|
+
**症状**:
|
|
49
|
+
```
|
|
50
|
+
DatabaseConnectionException: (sqlite3.OperationalError) no such table: accounts
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**原因**: SQLAlchemy のデフォルトコネクションプールでは `sqlite:///:memory:` への接続ごとに
|
|
54
|
+
新しいインメモリDBが生成される。`setup_db()` と `executor.fetch_one()` が別DBを参照する。
|
|
55
|
+
|
|
56
|
+
**修正**:
|
|
57
|
+
```python
|
|
58
|
+
from sqlalchemy.pool import StaticPool
|
|
59
|
+
|
|
60
|
+
engine = create_engine(
|
|
61
|
+
"sqlite:///:memory:",
|
|
62
|
+
connect_args={"check_same_thread": False},
|
|
63
|
+
poolclass=StaticPool,
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`SqlAlchemyQueryExecutor` の docstring に注意書きを追加 (Issue #305, v1.8.18)。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 結論
|
|
72
|
+
|
|
73
|
+
`SqlAlchemyTransactionManager.transactional()` は実運用で問題なく使用できる。
|
|
74
|
+
コールバック内の例外でロールバックが正しく機能することも確認。
|
|
75
|
+
SQLite in-memory DB 使用時の `StaticPool` 要件はドキュメント化済み。
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# FT68: SimpleDomainHandler + extra_factory 実運用検証
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-20
|
|
4
|
+
**テーマ**: ドメイン例外ハンドラー (`SimpleDomainHandler`) + `extra_factory` の実運用確認
|
|
5
|
+
**バージョン**: v1.8.18 → v1.8.19 (ドキュメント追加)
|
|
6
|
+
**FTディレクトリ**: `/home/xi/docker/nene2-python-FT/ft68-domain-handler/`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 概要
|
|
11
|
+
|
|
12
|
+
`nene2.middleware.SimpleDomainHandler` を使って複数のドメイン例外クラスをそれぞれの
|
|
13
|
+
HTTP レスポンスにマッピングし、`extra_factory` による動的フィールドを検証した。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装内容
|
|
18
|
+
|
|
19
|
+
- `ArticleNotFoundError`, `ArticleAccessDeniedError`, `ArticleTitleConflictError`: 独自例外クラス
|
|
20
|
+
- 各例外に `SimpleDomainHandler` + `detail_factory` + `extra_factory` を設定
|
|
21
|
+
- `ErrorHandlerMiddleware(domain_handlers=[...])` に渡して自動ハンドリング
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## テスト結果
|
|
26
|
+
|
|
27
|
+
**7/7 passed** (テスト修正後)
|
|
28
|
+
|
|
29
|
+
| テスト | 結果 |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `test_existing_article_returns_200` | PASSED |
|
|
32
|
+
| `test_not_found_returns_404_with_article_id` | PASSED |
|
|
33
|
+
| `test_access_denied_returns_403` | PASSED |
|
|
34
|
+
| `test_title_conflict_returns_409` | PASSED |
|
|
35
|
+
| `test_detail_factory_populates_detail_field` | PASSED |
|
|
36
|
+
| `test_problem_details_format_compliant` | PASSED |
|
|
37
|
+
| `test_successful_create_returns_201` | PASSED |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Friction Points
|
|
42
|
+
|
|
43
|
+
### FP-1: `extra_factory` のフィールドがトップレベルにフラットマージされることが不明瞭
|
|
44
|
+
|
|
45
|
+
**発生箇所**: テストで `data["extra"]["article_id"]` とアクセスして `KeyError: 'extra'`
|
|
46
|
+
|
|
47
|
+
**症状**:
|
|
48
|
+
```python
|
|
49
|
+
# 期待していた構造
|
|
50
|
+
assert data["extra"]["article_id"] == 999 # → KeyError: 'extra'
|
|
51
|
+
|
|
52
|
+
# 実際の構造(RFC 9457 extension members = トップレベル)
|
|
53
|
+
assert data["article_id"] == 999 # ← 正しいアクセス方法
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**原因**: `extra` という引数名が「ネストされた辞書」を連想させるが、
|
|
57
|
+
実際は RFC 9457 仕様の extension members としてトップレベルにフラットマージされる。
|
|
58
|
+
|
|
59
|
+
**修正**: `problem_details_response` と `SimpleDomainHandler` の docstring に
|
|
60
|
+
フラットマージである旨とRFC 9457 extension members との関係を明記 (Issue #308, v1.8.19)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 結論
|
|
65
|
+
|
|
66
|
+
`SimpleDomainHandler` + `extra_factory` は実運用で問題なく使用できる。
|
|
67
|
+
extra フィールドのフラットマージ動作が docstring に明記され、今後は混同を防げる。
|
|
@@ -14,7 +14,23 @@ from .interfaces import DatabaseQueryExecutorInterface, DatabaseTransactionManag
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class SqlAlchemyQueryExecutor(DatabaseQueryExecutorInterface):
|
|
17
|
-
"""Execute queries using SQLAlchemy Core (connection-per-call, no ORM).
|
|
17
|
+
"""Execute queries using SQLAlchemy Core (connection-per-call, no ORM).
|
|
18
|
+
|
|
19
|
+
.. note:: SQLite in-memory databases
|
|
20
|
+
|
|
21
|
+
When using ``sqlite:///:memory:`` in tests, each new connection receives
|
|
22
|
+
a separate empty database. To share one in-memory DB across all
|
|
23
|
+
connections (including ``setup_db`` / schema creation) use
|
|
24
|
+
``StaticPool``::
|
|
25
|
+
|
|
26
|
+
from sqlalchemy.pool import StaticPool
|
|
27
|
+
|
|
28
|
+
engine = create_engine(
|
|
29
|
+
"sqlite:///:memory:",
|
|
30
|
+
connect_args={"check_same_thread": False},
|
|
31
|
+
poolclass=StaticPool,
|
|
32
|
+
)
|
|
33
|
+
"""
|
|
18
34
|
|
|
19
35
|
def __init__(self, engine: Engine) -> None:
|
|
20
36
|
self._engine = engine
|
|
@@ -60,6 +60,19 @@ def problem_details_response(
|
|
|
60
60
|
) -> JSONResponse:
|
|
61
61
|
"""Build an RFC 9457 Problem Details JSON response.
|
|
62
62
|
|
|
63
|
+
Args:
|
|
64
|
+
problem_type: Short identifier appended to ``base_url`` for the ``type`` URI.
|
|
65
|
+
title: Human-readable summary of the error.
|
|
66
|
+
status: HTTP status code.
|
|
67
|
+
detail: Optional human-readable explanation.
|
|
68
|
+
extra: Additional fields merged **at the top level** of the response body
|
|
69
|
+
(RFC 9457 extension members). They are NOT nested under an
|
|
70
|
+
``"extra"`` key. For example, ``extra={"item_id": 42}`` produces
|
|
71
|
+
``{"type": "...", "status": 404, "item_id": 42}``.
|
|
72
|
+
Raises ``ValueError`` if any key shadows a reserved field
|
|
73
|
+
(``type``, ``title``, ``status``, ``detail``).
|
|
74
|
+
base_url: Override the base URL for this call only.
|
|
75
|
+
|
|
63
76
|
``base_url`` resolution order:
|
|
64
77
|
1. Explicit ``base_url`` argument
|
|
65
78
|
2. Value set by :func:`configure_problem_details`
|
|
@@ -35,8 +35,11 @@ class SimpleDomainHandler:
|
|
|
35
35
|
]
|
|
36
36
|
app.add_middleware(ErrorHandlerMiddleware, domain_handlers=handlers)
|
|
37
37
|
|
|
38
|
-
When you need a dynamic ``detail`` or
|
|
39
|
-
pass an ``extra_factory`` callable
|
|
38
|
+
When you need a dynamic ``detail`` or extra fields derived from the exception,
|
|
39
|
+
pass an ``extra_factory`` callable. The dict returned by ``extra_factory`` is merged
|
|
40
|
+
**at the top level** of the Problem Details response (RFC 9457 extension members) —
|
|
41
|
+
the keys appear directly alongside ``type``, ``title``, etc., NOT nested under
|
|
42
|
+
an ``"extra"`` key::
|
|
40
43
|
|
|
41
44
|
SimpleDomainHandler(
|
|
42
45
|
PostNotFoundError,
|
|
@@ -46,6 +49,8 @@ class SimpleDomainHandler:
|
|
|
46
49
|
detail_factory=lambda exc: str(exc),
|
|
47
50
|
extra_factory=lambda exc: {"post_id": exc.post_id},
|
|
48
51
|
)
|
|
52
|
+
# Response: {"type": "...", "status": 404, ..., "post_id": 123}
|
|
53
|
+
# NOT: {"type": "...", ..., "extra": {"post_id": 123}}
|
|
49
54
|
"""
|
|
50
55
|
|
|
51
56
|
def __init__(
|
|
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
|
{nene2_python-1.8.17 → nene2_python-1.8.19}/alembic/versions/001_create_notes_and_tags_tables.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
|
|
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
|
|
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
|