nene2-python 1.8.18__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.18 → nene2_python-1.8.19}/CHANGELOG.md +13 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/PKG-INFO +1 -1
- nene2_python-1.8.19/docs/field-trials/2026-05-field-trial-68.md +67 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/pyproject.toml +1 -1
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/http/problem_details.py +13 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/middleware/domain_exception.py +7 -2
- {nene2_python-1.8.18 → nene2_python-1.8.19}/uv.lock +1 -1
- {nene2_python-1.8.18 → nene2_python-1.8.19}/.env.example +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/.gitignore +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/AGENTS.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/CLAUDE.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/Dockerfile +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/LICENSE +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/README.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/alembic/README +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/alembic/env.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/alembic.ini +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/compose.yaml +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/de/index.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/fr/index.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/index.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/index.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/reference/api.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/roadmap.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/todo/current.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/zh/index.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/package-lock.json +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/package.json +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/__main__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/app.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/mcp.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/schema.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.18 → nene2_python-1.8.19}/tests/scripts/test_export_openapi.py +0 -0
|
@@ -5,6 +5,19 @@ 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
|
+
|
|
8
21
|
## [1.8.18] — 2026-05-20
|
|
9
22
|
|
|
10
23
|
FT67 フィールドトライアル — SqlAlchemyTransactionManager 実運用検証。
|
|
@@ -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,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 に明記され、今後は混同を防げる。
|
|
@@ -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.18 → 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
|
|
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
|