nene2-python 1.8.4__tar.gz → 1.8.5__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 (229) hide show
  1. {nene2_python-1.8.4 → nene2_python-1.8.5}/CHANGELOG.md +10 -0
  2. {nene2_python-1.8.4 → nene2_python-1.8.5}/PKG-INFO +1 -1
  3. nene2_python-1.8.5/docs/field-trials/2026-05-field-trial-37.md +77 -0
  4. nene2_python-1.8.5/docs/field-trials/2026-05-field-trial-38.md +85 -0
  5. nene2_python-1.8.5/docs/field-trials/2026-05-field-trial-39.md +95 -0
  6. nene2_python-1.8.5/docs/field-trials/2026-05-field-trial-40.md +87 -0
  7. {nene2_python-1.8.4 → nene2_python-1.8.5}/pyproject.toml +1 -1
  8. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/middleware/request_size_limit.py +25 -9
  9. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/middleware/test_request_size_limit.py +39 -0
  10. {nene2_python-1.8.4 → nene2_python-1.8.5}/uv.lock +1 -1
  11. {nene2_python-1.8.4 → nene2_python-1.8.5}/.env.example +0 -0
  12. {nene2_python-1.8.4 → nene2_python-1.8.5}/.github/workflows/ci.yml +0 -0
  13. {nene2_python-1.8.4 → nene2_python-1.8.5}/.github/workflows/docs.yml +0 -0
  14. {nene2_python-1.8.4 → nene2_python-1.8.5}/.github/workflows/publish.yml +0 -0
  15. {nene2_python-1.8.4 → nene2_python-1.8.5}/.gitignore +0 -0
  16. {nene2_python-1.8.4 → nene2_python-1.8.5}/.vitepress/config.mts +0 -0
  17. {nene2_python-1.8.4 → nene2_python-1.8.5}/.vitepress/theme/custom.css +0 -0
  18. {nene2_python-1.8.4 → nene2_python-1.8.5}/.vitepress/theme/index.ts +0 -0
  19. {nene2_python-1.8.4 → nene2_python-1.8.5}/AGENTS.md +0 -0
  20. {nene2_python-1.8.4 → nene2_python-1.8.5}/CLAUDE.md +0 -0
  21. {nene2_python-1.8.4 → nene2_python-1.8.5}/Dockerfile +0 -0
  22. {nene2_python-1.8.4 → nene2_python-1.8.5}/LICENSE +0 -0
  23. {nene2_python-1.8.4 → nene2_python-1.8.5}/README.md +0 -0
  24. {nene2_python-1.8.4 → nene2_python-1.8.5}/alembic/README +0 -0
  25. {nene2_python-1.8.4 → nene2_python-1.8.5}/alembic/env.py +0 -0
  26. {nene2_python-1.8.4 → nene2_python-1.8.5}/alembic/script.py.mako +0 -0
  27. {nene2_python-1.8.4 → nene2_python-1.8.5}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  28. {nene2_python-1.8.4 → nene2_python-1.8.5}/alembic.ini +0 -0
  29. {nene2_python-1.8.4 → nene2_python-1.8.5}/compose.yaml +0 -0
  30. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0001-toolchain.md +0 -0
  31. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0002-clean-architecture.md +0 -0
  32. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0003-security-first.md +0 -0
  33. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0004-ai-first-design.md +0 -0
  34. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0005-logging.md +0 -0
  35. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0006-rate-limiting.md +0 -0
  36. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0009-mcp-design.md +0 -0
  37. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0010-async-use-case.md +0 -0
  38. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  39. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/de/index.md +0 -0
  40. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/de/tutorials/getting-started.md +0 -0
  41. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/explanation/architecture.md +0 -0
  42. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/explanation/design-philosophy.md +0 -0
  43. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  44. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  45. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  46. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  47. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  48. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  49. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  50. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  51. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  52. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  53. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  54. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  55. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  56. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  57. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  58. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  59. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  60. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  61. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  62. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  63. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  64. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  65. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  66. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  67. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  68. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  69. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  70. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  71. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  72. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  73. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  74. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  75. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  76. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  77. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  78. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  79. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/fr/index.md +0 -0
  80. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/fr/tutorials/getting-started.md +0 -0
  81. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/how-to/add-new-domain.md +0 -0
  82. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/how-to/async-use-case.md +0 -0
  83. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/how-to/configure-auth.md +0 -0
  84. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/how-to/new-project.md +0 -0
  85. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/how-to/problem-details.md +0 -0
  86. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/how-to/run-tests.md +0 -0
  87. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/how-to/sqlalchemy-repository.md +0 -0
  88. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/how-to/validation.md +0 -0
  89. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/howto/mcp-setup.md +0 -0
  90. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/index.md +0 -0
  91. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/explanation/architecture.md +0 -0
  92. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/explanation/design-philosophy.md +0 -0
  93. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/how-to/add-new-domain.md +0 -0
  94. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/how-to/configure-auth.md +0 -0
  95. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/how-to/new-project.md +0 -0
  96. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/how-to/run-tests.md +0 -0
  97. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  98. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/howto/mcp-setup.md +0 -0
  99. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/index.md +0 -0
  100. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/reference/api.md +0 -0
  101. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/reference/configuration.md +0 -0
  102. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/reference/framework-modules.md +0 -0
  103. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/tutorials/first-domain.md +0 -0
  104. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/ja/tutorials/getting-started.md +0 -0
  105. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/pt-br/index.md +0 -0
  106. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/pt-br/tutorials/getting-started.md +0 -0
  107. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/reference/api.md +0 -0
  108. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/reference/configuration.md +0 -0
  109. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/reference/framework-modules.md +0 -0
  110. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/roadmap.md +0 -0
  111. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/todo/current.md +0 -0
  112. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/tutorials/first-domain.md +0 -0
  113. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/tutorials/getting-started.md +0 -0
  114. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/zh/index.md +0 -0
  115. {nene2_python-1.8.4 → nene2_python-1.8.5}/docs/zh/tutorials/getting-started.md +0 -0
  116. {nene2_python-1.8.4 → nene2_python-1.8.5}/package-lock.json +0 -0
  117. {nene2_python-1.8.4 → nene2_python-1.8.5}/package.json +0 -0
  118. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/__init__.py +0 -0
  119. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/__main__.py +0 -0
  120. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/app.py +0 -0
  121. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/comment/__init__.py +0 -0
  122. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/comment/entity.py +0 -0
  123. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/comment/exceptions.py +0 -0
  124. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/comment/handler.py +0 -0
  125. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/comment/repository.py +0 -0
  126. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/comment/sqlalchemy_repository.py +0 -0
  127. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/comment/use_case.py +0 -0
  128. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/mcp.py +0 -0
  129. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/note/__init__.py +0 -0
  130. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/note/async_use_case.py +0 -0
  131. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/note/entity.py +0 -0
  132. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/note/exceptions.py +0 -0
  133. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/note/handler.py +0 -0
  134. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/note/repository.py +0 -0
  135. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/note/sqlalchemy_repository.py +0 -0
  136. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/note/use_case.py +0 -0
  137. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/schema.py +0 -0
  138. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/tag/__init__.py +0 -0
  139. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/tag/entity.py +0 -0
  140. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/tag/exceptions.py +0 -0
  141. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/tag/handler.py +0 -0
  142. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/tag/repository.py +0 -0
  143. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/tag/sqlalchemy_repository.py +0 -0
  144. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/example/tag/use_case.py +0 -0
  145. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/__init__.py +0 -0
  146. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/auth/__init__.py +0 -0
  147. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/auth/api_key.py +0 -0
  148. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/auth/bearer_token.py +0 -0
  149. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/auth/exceptions.py +0 -0
  150. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/auth/interfaces.py +0 -0
  151. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/auth/local_verifier.py +0 -0
  152. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/config/__init__.py +0 -0
  153. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/config/settings.py +0 -0
  154. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/database/__init__.py +0 -0
  155. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/database/exceptions.py +0 -0
  156. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/database/health.py +0 -0
  157. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/database/interfaces.py +0 -0
  158. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/database/sqlalchemy_executor.py +0 -0
  159. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/database/utils.py +0 -0
  160. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/http/__init__.py +0 -0
  161. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/http/health.py +0 -0
  162. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/http/pagination.py +0 -0
  163. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/http/problem_details.py +0 -0
  164. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/log/__init__.py +0 -0
  165. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/log/setup.py +0 -0
  166. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/mcp/__init__.py +0 -0
  167. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/mcp/http_client.py +0 -0
  168. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/mcp/server.py +0 -0
  169. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/middleware/__init__.py +0 -0
  170. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/middleware/domain_exception.py +0 -0
  171. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/middleware/error_handler.py +0 -0
  172. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/middleware/request_id.py +0 -0
  173. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/middleware/request_logging.py +0 -0
  174. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/middleware/security_headers.py +0 -0
  175. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/middleware/throttle.py +0 -0
  176. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/py.typed +0 -0
  177. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/use_case/__init__.py +0 -0
  178. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/use_case/protocols.py +0 -0
  179. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/validation/__init__.py +0 -0
  180. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/nene2/validation/exceptions.py +0 -0
  181. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/scripts/__init__.py +0 -0
  182. {nene2_python-1.8.4 → nene2_python-1.8.5}/src/scripts/export_openapi.py +0 -0
  183. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/__init__.py +0 -0
  184. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/__init__.py +0 -0
  185. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/comment/__init__.py +0 -0
  186. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/comment/test_comment_http.py +0 -0
  187. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/comment/test_comment_repository.py +0 -0
  188. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/comment/test_comment_use_case.py +0 -0
  189. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/conftest.py +0 -0
  190. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/note/__init__.py +0 -0
  191. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/note/test_async_note_use_case.py +0 -0
  192. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/note/test_list_notes.py +0 -0
  193. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/note/test_note_repository.py +0 -0
  194. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/tag/__init__.py +0 -0
  195. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/tag/test_tag_repository.py +0 -0
  196. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/tag/test_tags.py +0 -0
  197. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/test_cors.py +0 -0
  198. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/example/test_mcp.py +0 -0
  199. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/__init__.py +0 -0
  200. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/auth/__init__.py +0 -0
  201. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/auth/test_api_key.py +0 -0
  202. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/auth/test_bearer_token.py +0 -0
  203. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/auth/test_token_issuer.py +0 -0
  204. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/config/__init__.py +0 -0
  205. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/config/test_settings.py +0 -0
  206. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/database/__init__.py +0 -0
  207. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/database/test_transaction.py +0 -0
  208. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/database/test_utils.py +0 -0
  209. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/http/__init__.py +0 -0
  210. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/http/test_health.py +0 -0
  211. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/http/test_pagination.py +0 -0
  212. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/http/test_problem_details.py +0 -0
  213. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/log/__init__.py +0 -0
  214. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/log/test_setup.py +0 -0
  215. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/mcp/__init__.py +0 -0
  216. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/mcp/test_http_client.py +0 -0
  217. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/middleware/__init__.py +0 -0
  218. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/middleware/test_error_handler.py +0 -0
  219. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/middleware/test_request_id.py +0 -0
  220. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/middleware/test_request_logging.py +0 -0
  221. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/middleware/test_security_headers.py +0 -0
  222. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  223. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/middleware/test_throttle.py +0 -0
  224. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/use_case/__init__.py +0 -0
  225. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/use_case/test_protocols.py +0 -0
  226. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/validation/__init__.py +0 -0
  227. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/nene2/validation/test_exceptions.py +0 -0
  228. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/scripts/__init__.py +0 -0
  229. {nene2_python-1.8.4 → nene2_python-1.8.5}/tests/scripts/test_export_openapi.py +0 -0
@@ -5,6 +5,16 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ---
7
7
 
8
+ ## [1.8.5] — 2026-05-20
9
+
10
+ FT37〜FT40 フィールドトライアル — RequestSizeLimitMiddleware パスごとサイズ制限とドキュメント改善。
11
+
12
+ ### Added
13
+ - `RequestSizeLimitMiddleware` に `path_limits: dict[str, int] | None = None` パラメータを追加 — パスごとに異なるリクエストボディサイズ制限を設定可能に (FT39)
14
+ - Field trial reports: `docs/field-trials/2026-05-field-trial-37.md` 〜 `docs/field-trials/2026-05-field-trial-40.md`
15
+
16
+ ---
17
+
8
18
  ## [1.8.4] — 2026-05-20
9
19
 
10
20
  FT33〜FT36 フィールドトライアル — バリデーション・DB整合性・混合認証・非同期ヘルスチェック改善。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.4
3
+ Version: 1.8.5
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,77 @@
1
+ # Field Trial 37: PaginationResponse + PaginationQueryParser 実運用検証
2
+
3
+ **日付**: 2026-05-20
4
+ **バージョン**: v1.8.4 時点
5
+ **テーマ**: `PaginationQueryParser` の `Depends()` パターンと `PaginationResponse.to_dict()` を使ったページネーションパイプラインの実運用確認
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ 50 件の `Product` データを `PaginationQueryParser` で limit/offset 制御し、
12
+ `PaginationResponse.to_dict()` で slotted dataclass を自動シリアライズして `JSONResponse` に変換するパイプラインを検証した。
13
+
14
+ ---
15
+
16
+ ## 実装内容
17
+
18
+ `/home/xi/docker/nene2-python-FT/ft37-pagination/` に以下を作成:
19
+
20
+ - **`app.py`** — `Annotated[PaginationQueryParser, Depends()]` パターンの `/api/products` エンドポイント
21
+ - **`test_app.py`** — デフォルト・カスタム・最終ページ・無効値・Depends 動作確認 (9 件)
22
+ - **`test_friction.py`** — 摩擦点の確認テスト (4 件)
23
+
24
+ **テスト結果**: 13 件全通過 ✅
25
+
26
+ ---
27
+
28
+ ## 摩擦点
29
+
30
+ ### FP37-1: `Annotated[PaginationQueryParser, Depends()]` と書く必要がある
31
+
32
+ **分類**: 軽微な摩擦(FastAPI 標準パターン)
33
+
34
+ `def list(pagination: PaginationQueryParser = Depends())` ではなく
35
+ `def list(pagination: Annotated[PaginationQueryParser, Depends()])` が推奨パターン。
36
+ FastAPI の仕様に従っているが、初見では迷いやすい。
37
+
38
+ **判断**: `docs/how-to/validation.md` および新規作成する how-to への記載で対応。
39
+
40
+ ---
41
+
42
+ ### FP37-2: `to_dict()` は dataclass のみ自動シリアライズ
43
+
44
+ **分類**: 既知の制約(軽微)
45
+
46
+ `PaginationResponse.to_dict()` は `dataclasses.is_dataclass()` で判定する。
47
+ Pydantic モデルの items は `.model_dump()` を個別に呼ぶ必要がある。
48
+ 通常の dict はそのまま通過する。
49
+
50
+ **判断**: dataclass 向け最適化という設計意図通り。
51
+ Pydantic モデルとの混在ユースケースは how-to ドキュメントに記載する。
52
+
53
+ ---
54
+
55
+ ### FP37-3: `total` 省略時に `"total"` キーが消える(設計通り)
56
+
57
+ **分類**: 設計通り(摩擦なし)
58
+
59
+ `PaginationResponse(items=[], limit=10, offset=0)` のように `total` を渡さないと
60
+ `to_dict()` の結果に `"total"` キーが含まれない。
61
+ `total=None` を明示しても同じ結果。
62
+
63
+ **判断**: 全件数を取得しない効率的なページネーションをサポートする意図通り。
64
+
65
+ ---
66
+
67
+ ## フレームワーク変更
68
+
69
+ なし(全て設計通りの挙動)
70
+
71
+ ---
72
+
73
+ ## 関連
74
+
75
+ - `nene2.http.PaginationQueryParser`
76
+ - `nene2.http.PaginationResponse`
77
+ - FT10 (PaginationResponse.to_dict() スロット対応, v1.3.0)
@@ -0,0 +1,85 @@
1
+ # Field Trial 38: SqlAlchemyTransactionManager.transactional() 実運用検証
2
+
3
+ **日付**: 2026-05-20
4
+ **バージョン**: v1.8.4 時点
5
+ **テーマ**: 注文作成(在庫更新 + 注文レコード作成)をトランザクションで包み、途中での例外発生時のロールバック動作を確認する
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ `SqlAlchemyTransactionManager.transactional(callback)` を使って複数の DB 書き込みを一つのトランザクションに包み、途中で例外が発生した場合のロールバック動作を検証した。
12
+
13
+ ---
14
+
15
+ ## 実装内容
16
+
17
+ `/home/xi/docker/nene2-python-FT/ft38-transactions/` に以下を作成:
18
+
19
+ - **`app.py`** — 在庫チェック + 在庫減少 + 注文作成を `transactional()` で包んだ注文 API
20
+ - **`test_app.py`** — 正常注文・在庫不足・ロールバック確認・連続注文 (6 件)
21
+ - **`test_friction.py`** — 摩擦点の確認テスト (3 件)
22
+
23
+ **テスト結果**: 9 件全通過 ✅
24
+
25
+ ---
26
+
27
+ ## 摩擦点
28
+
29
+ ### FP38-1: コールバック内で型アノテーションに `DatabaseQueryExecutorInterface` が必要
30
+
31
+ **分類**: 軽微な摩擦(mypy --strict 使用時)
32
+
33
+ ```python
34
+ def _do_create(db: object) -> int:
35
+ ...
36
+ ```
37
+
38
+ `db` を `object` 型にするとメソッド呼び出しに mypy エラーが出る。
39
+ `DatabaseQueryExecutorInterface` をインポートして型アノテーションする必要がある。
40
+
41
+ **判断**: mypy --strict の設計上当然の制約。インポートはやや冗長だが許容範囲。
42
+ `db: DatabaseQueryExecutorInterface` が推奨パターンとしてドキュメントに記載されている。
43
+
44
+ ---
45
+
46
+ ### FP38-2: savepoint(ネストトランザクション)はサポートしない
47
+
48
+ **分類**: 既知の制約(設計通り)
49
+
50
+ `DatabaseQueryExecutorInterface` が持つのは `fetch_all` / `fetch_one` / `write` のみ。
51
+ SQLAlchemy の savepoint 機能(`SAVEPOINT` / `ROLLBACK TO SAVEPOINT`)は使えない。
52
+
53
+ **判断**: フレームワークの抽象化レイヤーが Pure SQL テキストベースの設計を採用しているため、
54
+ ORM/Connection 固有の高度な機能は意図的に除外されている。
55
+ savepoint が必要な場合は SQLAlchemy を直接使う Repository を実装するのが正しいパターン。
56
+
57
+ ---
58
+
59
+ ### FP38-3: トランザクション途中の例外はすべてロールバックされる(設計通り)
60
+
61
+ **分類**: 設計通り(摩擦なし)
62
+
63
+ ```python
64
+ def _callback(db: DatabaseQueryExecutorInterface) -> None:
65
+ db.write("INSERT ...") # ← この変更も
66
+ raise ValueError("oops") # ← ここで例外 → 全部ロールバック
67
+ ```
68
+
69
+ 例外の種類に関わらず(`DatabaseIntegrityException` 以外でも)全変更がロールバックされる。
70
+ これは `SqlAlchemyTransactionManager.transactional()` の期待される動作。
71
+
72
+ ---
73
+
74
+ ## フレームワーク変更
75
+
76
+ なし(全て設計通りの挙動)
77
+
78
+ ---
79
+
80
+ ## 関連
81
+
82
+ - `nene2.database.SqlAlchemyTransactionManager`
83
+ - `nene2.database.DatabaseQueryExecutorInterface`
84
+ - FT16 (DatabaseIntegrityException, v1.7.0)
85
+ - FT34 (DatabaseIntegrityException 実運用, v1.8.3)
@@ -0,0 +1,95 @@
1
+ # Field Trial 39: RequestSizeLimitMiddleware 実運用検証
2
+
3
+ **日付**: 2026-05-20
4
+ **バージョン**: v1.8.4 時点
5
+ **テーマ**: `RequestSizeLimitMiddleware` で JSON ボディとバイナリアップロードのサイズを制限するパターンと、パスごとに異なる制限を設定したいユースケースの確認
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ 小さな `max_bytes` 制限(200 bytes)を設定したアプリで、正常なリクエスト・超過リクエスト・除外パス・境界値を検証した。
12
+ 主な発見: パスごとに異なる制限(例: 通常 API は 1 MiB、アップロードエンドポイントは 10 MiB)が設定できない摩擦を発見。`path_limits` パラメータを追加して対応。
13
+
14
+ ---
15
+
16
+ ## 実装内容
17
+
18
+ `/home/xi/docker/nene2-python-FT/ft39-request-size/` に以下を作成:
19
+
20
+ - **`app.py`** — `RequestSizeLimitMiddleware(max_bytes=200, exclude_paths=[...])` の動作確認アプリ
21
+ - **`test_app.py`** — 正常・超過・除外パス・境界値の動作確認 (8 件)
22
+ - **`test_friction.py`** — 摩擦点の確認テスト (3 件)
23
+
24
+ **テスト結果**: 11 件全通過 ✅
25
+
26
+ ---
27
+
28
+ ## 摩擦点
29
+
30
+ ### FP39-1: パスごとに異なる max_bytes を設定できない
31
+
32
+ **分類**: 摩擦あり → **実装で対応**
33
+
34
+ `RequestSizeLimitMiddleware` は全体で一つの `max_bytes` のみ設定できる。
35
+ アップロードエンドポイント(例: 10 MiB)と通常 API(例: 1 MiB)で異なる制限を設定したい場合、
36
+ `exclude_paths` でアップロードを除外してハンドラー内で手動チェックするしかなかった。
37
+
38
+ **対応**: `ThrottleMiddleware.path_limits` と同様のパターンで `path_limits: dict[str, int] | None` を追加 (#259)。
39
+
40
+ ```python
41
+ app.add_middleware(
42
+ RequestSizeLimitMiddleware,
43
+ max_bytes=1_048_576, # デフォルト: 1 MiB
44
+ path_limits={
45
+ "/upload/file": 10_485_760, # /upload/file: 10 MiB
46
+ "/api/import": 5_242_880, # /api/import: 5 MiB
47
+ },
48
+ )
49
+ ```
50
+
51
+ 413 レスポンスの `max_bytes` フィールドはパス固有の制限値を返す。
52
+
53
+ ---
54
+
55
+ ### FP39-2: max_bytes 構造化フィールドが 413 レスポンスに含まれる(FT23 改善の確認)
56
+
57
+ **分類**: 良い設計の確認
58
+
59
+ ```json
60
+ {
61
+ "status": 413,
62
+ "type": ".../payload-too-large",
63
+ "title": "Payload Too Large",
64
+ "max_bytes": 200
65
+ }
66
+ ```
67
+
68
+ `max_bytes` フィールドがあることでクライアントがプログラムで制限値を知れる。
69
+ FT23 で追加された改善が実際に役立つことを確認。
70
+
71
+ ---
72
+
73
+ ### FP39-3: Content-Length ヘッダーによる早期拒否が機能する
74
+
75
+ **分類**: 設計通り(摩擦なし)
76
+
77
+ Content-Length ヘッダーが `max_bytes` を超える場合、ボディを読む前に 413 を返す。
78
+ メモリ効率が良い設計。
79
+
80
+ ---
81
+
82
+ ## フレームワーク変更
83
+
84
+ - `RequestSizeLimitMiddleware` に `path_limits: dict[str, int] | None = None` を追加 (FP39-1)
85
+ - テスト 2 件追加
86
+
87
+ ---
88
+
89
+ ## 関連
90
+
91
+ - `nene2.middleware.RequestSizeLimitMiddleware`
92
+ - FT12 (RequestSizeLimitMiddleware exclude_paths, v1.5.0)
93
+ - FT23 (413 レスポンスに max_bytes 追加, v1.8.0)
94
+ - FT28 (ThrottleMiddleware path_limits, v1.8.1)
95
+ - Issue #259
@@ -0,0 +1,87 @@
1
+ # Field Trial 40: 多ドメイン連携(Article + Tag 紐付け)実運用検証
2
+
3
+ **日付**: 2026-05-20
4
+ **バージョン**: v1.8.4 時点
5
+ **テーマ**: 1 つの UseCase が 2 つのリポジトリ(Article + Tag)を横断するパターンの実運用確認
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ `TagArticleUseCase` が `InMemoryArticleRepository` と `InMemoryTagRepository` を
12
+ コンストラクタインジェクションで受け取り、記事へのタグ付けを管理するパターンを実装した。
13
+ 複数の無効 TagId がある場合に `ValidationException` で全エラーを一度に収集するパターンも確認。
14
+
15
+ ---
16
+
17
+ ## 実装内容
18
+
19
+ `/home/xi/docker/nene2-python-FT/ft40-multi-domain/` に以下を作成:
20
+
21
+ - **`app.py`** — Article + Tag の 2 ドメインを跨ぐ `TagArticleUseCase` と FastAPI エンドポイント
22
+ - **`test_app.py`** — 正常タグ付け・不正タグ・404・複数バリデーションエラー (7 件)
23
+ - **`test_friction.py`** — 摩擦点の確認テスト (4 件)
24
+
25
+ **テスト結果**: 11 件全通過 ✅
26
+
27
+ ---
28
+
29
+ ## 摩擦点
30
+
31
+ ### FP40-1: 複数リポジトリを受け取る UseCase は自然に書ける(良い設計)
32
+
33
+ **分類**: 摩擦なし(設計の確認)
34
+
35
+ フレームワークは UseCase 構造に制約を課さないため、
36
+ 複数のリポジトリを `__init__` で受け取るパターンが素直に実装できる。
37
+ CLAUDE.md の「UseCase は他の UseCase を呼ばない」ルールに従い、
38
+ 各リポジトリを直接呼ぶことで依存関係がシンプルに保たれる。
39
+
40
+ ---
41
+
42
+ ### FP40-2: カスタムドメイン例外は SimpleDomainHandler 登録が必要
43
+
44
+ **分類**: 軽微な摩擦(設計通り・注意喚起)
45
+
46
+ `ValidationException` は `ErrorHandlerMiddleware` が自動で 422 に変換するが、
47
+ `ArticleNotFoundException` のようなカスタムドメイン例外は
48
+ `SimpleDomainHandler` を明示的に登録しないと 500 になる。
49
+ 登録を忘れると意図しない 500 レスポンスになるため注意が必要。
50
+
51
+ ```python
52
+ # 忘れると 500 になる
53
+ handlers = [
54
+ SimpleDomainHandler(ArticleNotFoundException, "article-not-found", "Not Found", 404),
55
+ ]
56
+ app.add_middleware(ErrorHandlerMiddleware, domain_handlers=handlers)
57
+ ```
58
+
59
+ **判断**: ドメイン例外の HTTP マッピングを明示的にする設計は正しい。
60
+ `configure-auth.md` と同様に how-to ドキュメントでパターンを示す価値がある。
61
+
62
+ ---
63
+
64
+ ### FP40-3: 複数ドメイン横断バリデーションエラーを一括収集できる
65
+
66
+ **分類**: 良い設計の確認
67
+
68
+ `ValidationException` リストにエラーを積み上げてから raise するパターンで、
69
+ 複数の無効 TagId を一度のリクエストでまとめてクライアントに通知できる。
70
+ FT33 で確認した `ValidationCode(StrEnum)` パターンと組み合わせると更に型安全になる。
71
+
72
+ ---
73
+
74
+ ## フレームワーク変更
75
+
76
+ なし(全て設計通りの挙動)
77
+
78
+ ---
79
+
80
+ ## 関連
81
+
82
+ - `nene2.use_case.UseCaseProtocol`
83
+ - `nene2.validation.ValidationException`
84
+ - `nene2.middleware.SimpleDomainHandler`
85
+ - FT13 (ValidationException 実運用, v1.6.0)
86
+ - FT21 (SimpleDomainHandler 実装, v1.8.0)
87
+ - FT33 (ValidationCode StrEnum パターン, v1.8.3)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.4"
3
+ version = "1.8.5"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -23,7 +23,18 @@ class RequestSizeLimitMiddleware(BaseHTTPMiddleware):
23
23
  then reads the actual body to catch chunked-transfer requests that
24
24
  omit Content-Length entirely.
25
25
 
26
- Use ``exclude_paths`` to bypass the size limit for specific endpoints::
26
+ Use ``path_limits`` to set per-path limits that override the default::
27
+
28
+ app.add_middleware(
29
+ RequestSizeLimitMiddleware,
30
+ max_bytes=1_048_576, # default: 1 MiB
31
+ path_limits={
32
+ "/upload/file": 10_485_760, # /upload/file: 10 MiB
33
+ "/api/import": 5_242_880, # /api/import: 5 MiB
34
+ },
35
+ )
36
+
37
+ Use ``exclude_paths`` to bypass the size limit entirely::
27
38
 
28
39
  app.add_middleware(
29
40
  RequestSizeLimitMiddleware,
@@ -37,35 +48,40 @@ class RequestSizeLimitMiddleware(BaseHTTPMiddleware):
37
48
  app: object,
38
49
  *,
39
50
  max_bytes: int = _DEFAULT_MAX_BYTES,
51
+ path_limits: dict[str, int] | None = None,
40
52
  exclude_paths: list[str] | None = None,
41
53
  ) -> None:
42
54
  super().__init__(app) # type: ignore[arg-type]
43
55
  self._max_bytes = max_bytes
56
+ self._path_limits: dict[str, int] = path_limits or {}
44
57
  self._exclude_paths = set(exclude_paths or [])
45
58
 
46
59
  async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
47
- if request.url.path in self._exclude_paths:
60
+ path = request.url.path
61
+ if path in self._exclude_paths:
48
62
  return await call_next(request)
49
63
 
64
+ effective_limit = self._path_limits.get(path, self._max_bytes)
65
+
50
66
  content_length = request.headers.get("Content-Length")
51
67
  if content_length is not None:
52
68
  try:
53
- if int(content_length) > self._max_bytes:
54
- return self._too_large()
69
+ if int(content_length) > effective_limit:
70
+ return self._too_large(effective_limit)
55
71
  except ValueError:
56
72
  pass
57
73
 
58
74
  body = await request.body()
59
- if len(body) > self._max_bytes:
60
- return self._too_large()
75
+ if len(body) > effective_limit:
76
+ return self._too_large(effective_limit)
61
77
 
62
78
  return await call_next(request)
63
79
 
64
- def _too_large(self) -> Response:
80
+ def _too_large(self, limit: int) -> Response:
65
81
  return problem_details_response(
66
82
  "payload-too-large",
67
83
  "Payload Too Large",
68
84
  413,
69
- _TOO_LARGE.format(limit=self._max_bytes),
70
- extra={"max_bytes": self._max_bytes},
85
+ _TOO_LARGE.format(limit=limit),
86
+ extra={"max_bytes": limit},
71
87
  )
@@ -69,6 +69,45 @@ def test_malformed_content_length_is_tolerated() -> None:
69
69
  assert response.status_code == 200
70
70
 
71
71
 
72
+ def test_path_limits_overrides_default_for_specific_paths() -> None:
73
+ app = FastAPI()
74
+ app.add_middleware(
75
+ RequestSizeLimitMiddleware,
76
+ max_bytes=100,
77
+ path_limits={"/upload/large": 5000},
78
+ )
79
+
80
+ @app.post("/upload")
81
+ async def upload() -> JSONResponse:
82
+ return JSONResponse({"ok": True})
83
+
84
+ @app.post("/upload/large")
85
+ async def upload_large() -> JSONResponse:
86
+ return JSONResponse({"ok": True})
87
+
88
+ client = TestClient(app)
89
+ assert client.post("/upload", content=b"x" * 200).status_code == 413
90
+ assert client.post("/upload/large", content=b"x" * 200).status_code == 200
91
+
92
+
93
+ def test_path_limits_413_response_shows_path_limit_not_default() -> None:
94
+ app = FastAPI()
95
+ app.add_middleware(
96
+ RequestSizeLimitMiddleware,
97
+ max_bytes=1000,
98
+ path_limits={"/strict": 50},
99
+ )
100
+
101
+ @app.post("/strict")
102
+ async def strict() -> JSONResponse:
103
+ return JSONResponse({"ok": True})
104
+
105
+ client = TestClient(app)
106
+ r = client.post("/strict", content=b"x" * 100)
107
+ assert r.status_code == 413
108
+ assert r.json()["max_bytes"] == 50
109
+
110
+
72
111
  def test_exclude_paths_bypasses_size_limit() -> None:
73
112
  app = FastAPI()
74
113
  app.add_middleware(
@@ -925,7 +925,7 @@ wheels = [
925
925
 
926
926
  [[package]]
927
927
  name = "nene2-python"
928
- version = "1.8.4"
928
+ version = "1.8.5"
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