nene2-python 1.8.9__tar.gz → 1.8.11__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.
Files changed (240) hide show
  1. {nene2_python-1.8.9 → nene2_python-1.8.11}/.github/workflows/ci.yml +3 -1
  2. {nene2_python-1.8.9 → nene2_python-1.8.11}/CHANGELOG.md +21 -0
  3. {nene2_python-1.8.9 → nene2_python-1.8.11}/PKG-INFO +1 -1
  4. nene2_python-1.8.11/docs/field-trials/2026-05-field-trial-50.md +108 -0
  5. nene2_python-1.8.11/docs/field-trials/2026-05-field-trial-51.md +127 -0
  6. {nene2_python-1.8.9 → nene2_python-1.8.11}/pyproject.toml +1 -1
  7. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/http/problem_details.py +4 -0
  8. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/http/test_problem_details.py +15 -0
  9. {nene2_python-1.8.9 → nene2_python-1.8.11}/uv.lock +1 -1
  10. {nene2_python-1.8.9 → nene2_python-1.8.11}/.env.example +0 -0
  11. {nene2_python-1.8.9 → nene2_python-1.8.11}/.github/workflows/docs.yml +0 -0
  12. {nene2_python-1.8.9 → nene2_python-1.8.11}/.github/workflows/publish.yml +0 -0
  13. {nene2_python-1.8.9 → nene2_python-1.8.11}/.gitignore +0 -0
  14. {nene2_python-1.8.9 → nene2_python-1.8.11}/.vitepress/config.mts +0 -0
  15. {nene2_python-1.8.9 → nene2_python-1.8.11}/.vitepress/theme/custom.css +0 -0
  16. {nene2_python-1.8.9 → nene2_python-1.8.11}/.vitepress/theme/index.ts +0 -0
  17. {nene2_python-1.8.9 → nene2_python-1.8.11}/AGENTS.md +0 -0
  18. {nene2_python-1.8.9 → nene2_python-1.8.11}/CLAUDE.md +0 -0
  19. {nene2_python-1.8.9 → nene2_python-1.8.11}/Dockerfile +0 -0
  20. {nene2_python-1.8.9 → nene2_python-1.8.11}/LICENSE +0 -0
  21. {nene2_python-1.8.9 → nene2_python-1.8.11}/README.md +0 -0
  22. {nene2_python-1.8.9 → nene2_python-1.8.11}/alembic/README +0 -0
  23. {nene2_python-1.8.9 → nene2_python-1.8.11}/alembic/env.py +0 -0
  24. {nene2_python-1.8.9 → nene2_python-1.8.11}/alembic/script.py.mako +0 -0
  25. {nene2_python-1.8.9 → nene2_python-1.8.11}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  26. {nene2_python-1.8.9 → nene2_python-1.8.11}/alembic.ini +0 -0
  27. {nene2_python-1.8.9 → nene2_python-1.8.11}/compose.yaml +0 -0
  28. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0001-toolchain.md +0 -0
  29. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0002-clean-architecture.md +0 -0
  30. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0003-security-first.md +0 -0
  31. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0004-ai-first-design.md +0 -0
  32. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0005-logging.md +0 -0
  33. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0006-rate-limiting.md +0 -0
  34. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0009-mcp-design.md +0 -0
  35. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0010-async-use-case.md +0 -0
  36. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  37. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/de/index.md +0 -0
  38. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/de/tutorials/getting-started.md +0 -0
  39. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/explanation/architecture.md +0 -0
  40. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/explanation/design-philosophy.md +0 -0
  41. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  42. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  43. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  44. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  45. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  46. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  47. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  48. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  49. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  50. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  51. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  52. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  53. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  54. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  55. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  56. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  57. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  58. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  59. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  60. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  61. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  62. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  63. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  64. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  65. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  66. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  67. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  68. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  69. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  70. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  71. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  72. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  73. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  74. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  75. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  76. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  77. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  78. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  79. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  80. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  81. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  82. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  83. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  84. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  85. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  86. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  87. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  88. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  89. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  90. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/fr/index.md +0 -0
  91. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/fr/tutorials/getting-started.md +0 -0
  92. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/how-to/add-new-domain.md +0 -0
  93. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/how-to/async-use-case.md +0 -0
  94. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/how-to/configure-auth.md +0 -0
  95. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/how-to/new-project.md +0 -0
  96. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/how-to/problem-details.md +0 -0
  97. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/how-to/run-tests.md +0 -0
  98. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/how-to/sqlalchemy-repository.md +0 -0
  99. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/how-to/validation.md +0 -0
  100. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/howto/mcp-setup.md +0 -0
  101. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/index.md +0 -0
  102. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/explanation/architecture.md +0 -0
  103. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/explanation/design-philosophy.md +0 -0
  104. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/how-to/add-new-domain.md +0 -0
  105. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/how-to/configure-auth.md +0 -0
  106. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/how-to/new-project.md +0 -0
  107. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/how-to/run-tests.md +0 -0
  108. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  109. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/howto/mcp-setup.md +0 -0
  110. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/index.md +0 -0
  111. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/reference/api.md +0 -0
  112. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/reference/configuration.md +0 -0
  113. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/reference/framework-modules.md +0 -0
  114. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/tutorials/first-domain.md +0 -0
  115. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/ja/tutorials/getting-started.md +0 -0
  116. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/pt-br/index.md +0 -0
  117. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/pt-br/tutorials/getting-started.md +0 -0
  118. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/reference/api.md +0 -0
  119. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/reference/configuration.md +0 -0
  120. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/reference/framework-modules.md +0 -0
  121. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/roadmap.md +0 -0
  122. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/todo/current.md +0 -0
  123. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/tutorials/first-domain.md +0 -0
  124. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/tutorials/getting-started.md +0 -0
  125. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/zh/index.md +0 -0
  126. {nene2_python-1.8.9 → nene2_python-1.8.11}/docs/zh/tutorials/getting-started.md +0 -0
  127. {nene2_python-1.8.9 → nene2_python-1.8.11}/package-lock.json +0 -0
  128. {nene2_python-1.8.9 → nene2_python-1.8.11}/package.json +0 -0
  129. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/__init__.py +0 -0
  130. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/__main__.py +0 -0
  131. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/app.py +0 -0
  132. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/comment/__init__.py +0 -0
  133. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/comment/entity.py +0 -0
  134. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/comment/exceptions.py +0 -0
  135. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/comment/handler.py +0 -0
  136. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/comment/repository.py +0 -0
  137. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/comment/sqlalchemy_repository.py +0 -0
  138. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/comment/use_case.py +0 -0
  139. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/mcp.py +0 -0
  140. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/note/__init__.py +0 -0
  141. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/note/async_use_case.py +0 -0
  142. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/note/entity.py +0 -0
  143. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/note/exceptions.py +0 -0
  144. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/note/handler.py +0 -0
  145. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/note/repository.py +0 -0
  146. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/note/sqlalchemy_repository.py +0 -0
  147. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/note/use_case.py +0 -0
  148. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/schema.py +0 -0
  149. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/tag/__init__.py +0 -0
  150. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/tag/entity.py +0 -0
  151. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/tag/exceptions.py +0 -0
  152. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/tag/handler.py +0 -0
  153. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/tag/repository.py +0 -0
  154. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/tag/sqlalchemy_repository.py +0 -0
  155. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/example/tag/use_case.py +0 -0
  156. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/__init__.py +0 -0
  157. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/auth/__init__.py +0 -0
  158. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/auth/api_key.py +0 -0
  159. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/auth/bearer_token.py +0 -0
  160. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/auth/exceptions.py +0 -0
  161. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/auth/interfaces.py +0 -0
  162. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/auth/local_verifier.py +0 -0
  163. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/config/__init__.py +0 -0
  164. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/config/settings.py +0 -0
  165. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/database/__init__.py +0 -0
  166. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/database/exceptions.py +0 -0
  167. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/database/health.py +0 -0
  168. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/database/interfaces.py +0 -0
  169. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/database/sqlalchemy_executor.py +0 -0
  170. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/database/utils.py +0 -0
  171. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/http/__init__.py +0 -0
  172. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/http/health.py +0 -0
  173. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/http/pagination.py +0 -0
  174. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/log/__init__.py +0 -0
  175. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/log/setup.py +0 -0
  176. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/mcp/__init__.py +0 -0
  177. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/mcp/http_client.py +0 -0
  178. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/mcp/server.py +0 -0
  179. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/middleware/__init__.py +0 -0
  180. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/middleware/domain_exception.py +0 -0
  181. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/middleware/error_handler.py +0 -0
  182. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/middleware/request_id.py +0 -0
  183. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/middleware/request_logging.py +0 -0
  184. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/middleware/request_size_limit.py +0 -0
  185. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/middleware/security_headers.py +0 -0
  186. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/middleware/throttle.py +0 -0
  187. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/py.typed +0 -0
  188. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/use_case/__init__.py +0 -0
  189. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/use_case/protocols.py +0 -0
  190. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/validation/__init__.py +0 -0
  191. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/nene2/validation/exceptions.py +0 -0
  192. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/scripts/__init__.py +0 -0
  193. {nene2_python-1.8.9 → nene2_python-1.8.11}/src/scripts/export_openapi.py +0 -0
  194. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/__init__.py +0 -0
  195. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/__init__.py +0 -0
  196. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/comment/__init__.py +0 -0
  197. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/comment/test_comment_http.py +0 -0
  198. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/comment/test_comment_repository.py +0 -0
  199. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/comment/test_comment_use_case.py +0 -0
  200. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/conftest.py +0 -0
  201. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/note/__init__.py +0 -0
  202. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/note/test_async_note_use_case.py +0 -0
  203. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/note/test_list_notes.py +0 -0
  204. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/note/test_note_repository.py +0 -0
  205. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/tag/__init__.py +0 -0
  206. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/tag/test_tag_repository.py +0 -0
  207. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/tag/test_tags.py +0 -0
  208. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/test_cors.py +0 -0
  209. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/example/test_mcp.py +0 -0
  210. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/__init__.py +0 -0
  211. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/auth/__init__.py +0 -0
  212. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/auth/test_api_key.py +0 -0
  213. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/auth/test_bearer_token.py +0 -0
  214. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/auth/test_token_issuer.py +0 -0
  215. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/config/__init__.py +0 -0
  216. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/config/test_settings.py +0 -0
  217. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/database/__init__.py +0 -0
  218. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/database/test_transaction.py +0 -0
  219. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/database/test_utils.py +0 -0
  220. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/http/__init__.py +0 -0
  221. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/http/test_health.py +0 -0
  222. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/http/test_pagination.py +0 -0
  223. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/log/__init__.py +0 -0
  224. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/log/test_setup.py +0 -0
  225. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/mcp/__init__.py +0 -0
  226. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/mcp/test_http_client.py +0 -0
  227. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/middleware/__init__.py +0 -0
  228. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/middleware/test_error_handler.py +0 -0
  229. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/middleware/test_request_id.py +0 -0
  230. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/middleware/test_request_logging.py +0 -0
  231. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  232. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/middleware/test_security_headers.py +0 -0
  233. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  234. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/middleware/test_throttle.py +0 -0
  235. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/use_case/__init__.py +0 -0
  236. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/use_case/test_protocols.py +0 -0
  237. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/validation/__init__.py +0 -0
  238. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/nene2/validation/test_exceptions.py +0 -0
  239. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/scripts/__init__.py +0 -0
  240. {nene2_python-1.8.9 → nene2_python-1.8.11}/tests/scripts/test_export_openapi.py +0 -0
@@ -47,4 +47,6 @@ jobs:
47
47
  run: uv run ruff format --check src/ tests/
48
48
 
49
49
  - name: pip-audit
50
- run: uv run pip-audit
50
+ # PYSEC-2025-183: pyjwt weak-key-length — disputed by supplier, no fix version available.
51
+ # Transitive via mcp>=1.0. Re-evaluate when pyjwt releases a fix. (#280)
52
+ run: uv run pip-audit --ignore-vuln PYSEC-2025-183
@@ -5,6 +5,27 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ---
7
7
 
8
+ ## [1.8.11] — 2026-05-20
9
+
10
+ FT51 フィールドトライアル — SimpleDomainHandler 実運用検証 + problem_details_response バグ修正。
11
+
12
+ ### Fixed
13
+ - `problem_details_response()` — `extra` に RFC 9457 予約済みフィールド (`type`, `title`, `status`, `detail`) が含まれる場合に `ValueError` を raise するよう修正 (#282) (FT51)
14
+
15
+ ### Added
16
+ - Field trial report: `docs/field-trials/2026-05-field-trial-51.md`
17
+
18
+ ---
19
+
20
+ ## [1.8.10] — 2026-05-20
21
+
22
+ FT50 フィールドトライアル — ValidationException + ValidationCode(StrEnum) 実運用検証。
23
+
24
+ ### Added
25
+ - Field trial report: `docs/field-trials/2026-05-field-trial-50.md`
26
+
27
+ ---
28
+
8
29
  ## [1.8.9] — 2026-05-20
9
30
 
10
31
  FT46〜FT49 フィールドトライアル — ドキュメント改善。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.9
3
+ Version: 1.8.11
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,108 @@
1
+ # Field Trial 50: ValidationException + ValidationCode(StrEnum) 実運用検証
2
+
3
+ **Date**: 2026-05-20
4
+ **Theme**: `ValidationException` + `ValidationCode(StrEnum)` の実運用パターン検証
5
+ **Version under test**: v1.8.9
6
+ **FT App**: `/home/xi/docker/nene2-python-FT/ft50-validation/`
7
+
8
+ ---
9
+
10
+ ## 概要
11
+
12
+ 複数フィールドバリデーション (`ValidationException` + `ValidationError` リスト) と
13
+ `ValidationCode(StrEnum)` による型安全なエラーコードの組み合わせを、
14
+ 商品作成 API で実運用した。
15
+
16
+ ---
17
+
18
+ ## 実装内容
19
+
20
+ ### `ProductCode(StrEnum)` によるエラーコード定義
21
+
22
+ ```python
23
+ class ProductCode(StrEnum):
24
+ required = "required"
25
+ out_of_range = "out_of_range"
26
+ invalid_format = "invalid_format"
27
+ duplicate = "duplicate"
28
+ ```
29
+
30
+ `StrEnum` を継承することで、エラーコードが静的解析で型チェックされる。
31
+ レスポンスの `code` フィールドには文字列値(`"required"` 等)がそのまま出力される。
32
+
33
+ ### 複数フィールドバリデーション
34
+
35
+ ```python
36
+ def validate_product(name: str, price: int, sku: str, stock: int) -> None:
37
+ errors: list[ValidationError] = []
38
+ if not name.strip():
39
+ errors.append(ValidationError("name", "Name is required.", ProductCode.required))
40
+ if price < 0:
41
+ errors.append(ValidationError("price", "Price must be non-negative.", ProductCode.out_of_range))
42
+ # ...
43
+ if errors:
44
+ raise ValidationException(errors)
45
+ ```
46
+
47
+ エラーを収集してから一括で `raise ValidationException(errors)` するパターン。
48
+
49
+ ### 単一フィールド専用例外
50
+
51
+ ```python
52
+ if body.sku in _existing_skus:
53
+ raise ValidationException.single("sku", f"SKU '{body.sku}' already exists.", ProductCode.duplicate)
54
+ ```
55
+
56
+ `ValidationException.single()` classmethod で 1 行で単一エラーを raise できる。
57
+
58
+ ---
59
+
60
+ ## テスト結果
61
+
62
+ 12 tests, all passed.
63
+
64
+ | テスト | 結果 |
65
+ |---|---|
66
+ | 正常系: 商品作成成功 | ✅ |
67
+ | 単一フィールドエラー (price < 0) | ✅ |
68
+ | 複数フィールドエラー | ✅ |
69
+ | price 上限チェック (> 1,000,000) | ✅ |
70
+ | SKU フォーマット検証 | ✅ |
71
+ | 重複 SKU (ValidationException.single) | ✅ |
72
+ | ValidationCode 値が文字列として返る | ✅ |
73
+ | 422 Problem Details 構造確認 | ✅ |
74
+ | StrEnum 値がレスポンスで文字列になる | ✅ |
75
+ | message フィールドがレスポンスに含まれる | ✅ |
76
+ | 同一フィールド複数エラー | ✅ |
77
+ | エラーリストの順序が検証順と一致 | ✅ |
78
+
79
+ ---
80
+
81
+ ## 摩擦ポイント
82
+
83
+ 摩擦なし。以下の点がすべて期待通りに動作した:
84
+
85
+ - **FP50-1 (確認済み)**: `ValidationCode(StrEnum)` の値はレスポンスの `code` フィールドに文字列として正しく出力される。`StrEnum` は `str` のサブクラスなので JSON シリアライズ時に文字列化される。
86
+ - **FP50-2 (確認済み)**: `ValidationError` の `message` フィールドがレスポンスの各エラーオブジェクトに含まれる。
87
+ - **FP50-3 (確認済み)**: 同一フィールドへの複数エラー収集は設計上サポートされており、正常に動作する。
88
+ - **FP50-4 (確認済み)**: `ValidationException(errors)` はエラーリストの順序を保持する。
89
+
90
+ ---
91
+
92
+ ## フレームワーク変更
93
+
94
+ なし。
95
+
96
+ ---
97
+
98
+ ## 結論
99
+
100
+ `ValidationException` + `ValidationCode(StrEnum)` の組み合わせは実運用で摩擦なく使える。
101
+ エラーコードを `StrEnum` で定義することで:
102
+
103
+ 1. IDE/mypy による型補完・チェックが得られる
104
+ 2. レスポンスには文字列値がそのまま出力される(追加設定不要)
105
+ 3. `validate_product()` 関数でエラーを収集して一括 raise するパターンが自然に書ける
106
+ 4. 単一フィールドの場合は `ValidationException.single()` で 1 行で済む
107
+
108
+ API クライアントは `code` 文字列でエラー種別を判定できる。
@@ -0,0 +1,127 @@
1
+ # Field Trial 51: SimpleDomainHandler 実運用検証
2
+
3
+ **Date**: 2026-05-20
4
+ **Theme**: `SimpleDomainHandler` + `detail_factory` / `extra_factory` の実運用パターン検証
5
+ **Version under test**: v1.8.10
6
+ **FT App**: `/home/xi/docker/nene2-python-FT/ft51-domain-handler/`
7
+
8
+ ---
9
+
10
+ ## 概要
11
+
12
+ 複数のドメイン例外を `SimpleDomainHandler` で Problem Details に変換するパターンを
13
+ 記事 API で実運用した。404 / 403 / 409 の 3 種類のエラーを検証。
14
+
15
+ ---
16
+
17
+ ## 実装内容
18
+
19
+ ### ドメイン例外クラス
20
+
21
+ ```python
22
+ class ArticleNotFoundError(Exception):
23
+ def __init__(self, article_id: int) -> None:
24
+ self.article_id = article_id
25
+
26
+ class ArticleAccessDeniedError(Exception):
27
+ def __init__(self, article_id: int, user_id: str) -> None:
28
+ self.article_id = article_id
29
+ self.user_id = user_id
30
+
31
+ class ArticleTitleConflictError(Exception):
32
+ def __init__(self, title: str) -> None:
33
+ self.title = title
34
+ ```
35
+
36
+ ### SimpleDomainHandler による登録
37
+
38
+ ```python
39
+ handlers = [
40
+ SimpleDomainHandler(
41
+ ArticleNotFoundError,
42
+ "article-not-found",
43
+ "Article Not Found",
44
+ 404,
45
+ detail_factory=lambda exc: str(exc),
46
+ extra_factory=lambda exc: {"article_id": exc.article_id},
47
+ ),
48
+ SimpleDomainHandler(
49
+ ArticleAccessDeniedError,
50
+ "article-access-denied",
51
+ "Access Denied",
52
+ 403,
53
+ detail_factory=lambda exc: str(exc),
54
+ extra_factory=lambda exc: {"article_id": exc.article_id, "user_id": exc.user_id},
55
+ ),
56
+ SimpleDomainHandler(
57
+ ArticleTitleConflictError,
58
+ "article-title-conflict",
59
+ "Article Title Conflict",
60
+ 409,
61
+ detail_factory=lambda exc: str(exc),
62
+ extra_factory=lambda exc: {"article_title": exc.title}, # ← "title" ではなく "article_title"
63
+ ),
64
+ ]
65
+ app.add_middleware(ErrorHandlerMiddleware, domain_handlers=handlers)
66
+ ```
67
+
68
+ ---
69
+
70
+ ## テスト結果
71
+
72
+ 6 tests, all passed (after fixing FP51-1).
73
+
74
+ ---
75
+
76
+ ## 摩擦ポイント
77
+
78
+ ### FP51-1: `extra_factory` に `title` キーを返すと Problem Details の `title` が上書きされる
79
+
80
+ **状況**: `ArticleTitleConflictError` の `extra_factory` で `{"title": exc.title}` を返したところ、
81
+ Problem Details レスポンスの `title` フィールド(`"Article Title Conflict"`)が `exc.title`(`"Dup"`)に
82
+ 上書きされた。
83
+
84
+ ```json
85
+ {
86
+ "type": "...",
87
+ "title": "Dup", // ← "Article Title Conflict" のはずが上書きされた
88
+ "status": 409,
89
+ "detail": "Article with title 'Dup' already exists"
90
+ }
91
+ ```
92
+
93
+ **原因**: `problem_details_response()` が `body.update(extra)` で `extra` を後から適用するため、
94
+ RFC 9457 の予約済みフィールド (`type`, `title`, `status`, `detail`) を含む `extra` が
95
+ 意図せずフィールドを上書きする。
96
+
97
+ **修正**: `problem_details_response()` が `extra` に予約済みフィールドが含まれている場合に
98
+ `ValueError` を raise するよう修正 (#282)。
99
+
100
+ ```python
101
+ # 修正後のコード
102
+ _RESERVED_FIELDS = frozenset({"type", "title", "status", "detail"})
103
+ if extra:
104
+ overlap = _RESERVED_FIELDS & extra.keys()
105
+ if overlap:
106
+ raise ValueError(f"extra contains reserved Problem Details fields: {sorted(overlap)}")
107
+ body.update(extra)
108
+ ```
109
+
110
+ **ワークアラウンド(修正前)**: `extra` に予約済みキーと衝突しない名前を使う(例: `"article_title"`)。
111
+
112
+ ---
113
+
114
+ ## フレームワーク変更
115
+
116
+ ### `nene2.http.problem_details_response()` — extra reserved fields 保護 (#282)
117
+
118
+ `extra` に RFC 9457 予約済みフィールド (`type`, `title`, `status`, `detail`) が含まれる場合に
119
+ `ValueError` を raise するようになった。
120
+
121
+ ---
122
+
123
+ ## 結論
124
+
125
+ `SimpleDomainHandler` + `detail_factory` / `extra_factory` の組み合わせは実運用で使いやすい。
126
+ ただし `extra_factory` の返り値に RFC 9457 予約済みフィールドと同名のキーを入れると
127
+ サイレントに上書きされる危険があった。`ValueError` による早期検知で問題を防止できる。
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.9"
3
+ version = "1.8.11"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -8,6 +8,7 @@ from typing import Any
8
8
  from fastapi.responses import JSONResponse
9
9
 
10
10
  PROBLEM_DETAILS_BASE_URL = "https://nene2.dev/problems/"
11
+ _RESERVED_FIELDS = frozenset({"type", "title", "status", "detail"})
11
12
 
12
13
  _configured_base_url: str | None = None
13
14
 
@@ -73,6 +74,9 @@ def problem_details_response(
73
74
  if detail:
74
75
  body["detail"] = detail
75
76
  if extra:
77
+ overlap = _RESERVED_FIELDS & extra.keys()
78
+ if overlap:
79
+ raise ValueError(f"extra contains reserved Problem Details fields: {sorted(overlap)}")
76
80
  body.update(extra)
77
81
 
78
82
  return JSONResponse(
@@ -74,3 +74,18 @@ def test_reset_problem_details_is_idempotent() -> None:
74
74
  reset_problem_details()
75
75
  r = problem_details_response("not-found", "Not Found", 404)
76
76
  assert b"nene2.dev/problems/not-found" in r.body
77
+
78
+
79
+ def test_extra_with_reserved_field_raises_value_error() -> None:
80
+ with pytest.raises(ValueError, match="reserved Problem Details fields"):
81
+ problem_details_response("x", "X", 400, extra={"title": "bad"})
82
+
83
+
84
+ def test_extra_with_type_reserved_raises_value_error() -> None:
85
+ with pytest.raises(ValueError, match="reserved Problem Details fields"):
86
+ problem_details_response("x", "X", 400, extra={"type": "overridden"})
87
+
88
+
89
+ def test_extra_with_status_reserved_raises_value_error() -> None:
90
+ with pytest.raises(ValueError, match="reserved Problem Details fields"):
91
+ problem_details_response("x", "X", 400, extra={"status": 500})
@@ -925,7 +925,7 @@ wheels = [
925
925
 
926
926
  [[package]]
927
927
  name = "nene2-python"
928
- version = "1.8.9"
928
+ version = "1.8.11"
929
929
  source = { editable = "." }
930
930
  dependencies = [
931
931
  { name = "alembic" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes