nene2-python 1.8.28__tar.gz → 1.8.29__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 (280) hide show
  1. {nene2_python-1.8.28 → nene2_python-1.8.29}/CHANGELOG.md +13 -0
  2. {nene2_python-1.8.28 → nene2_python-1.8.29}/PKG-INFO +1 -1
  3. nene2_python-1.8.29/docs/field-trials/2026-05-field-trial-84.md +151 -0
  4. {nene2_python-1.8.28 → nene2_python-1.8.29}/pyproject.toml +1 -1
  5. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/__init__.py +2 -0
  6. nene2_python-1.8.29/src/nene2/auth/deps.py +50 -0
  7. nene2_python-1.8.29/tests/nene2/auth/test_make_require_auth.py +99 -0
  8. {nene2_python-1.8.28 → nene2_python-1.8.29}/uv.lock +1 -1
  9. {nene2_python-1.8.28 → nene2_python-1.8.29}/.env.example +0 -0
  10. {nene2_python-1.8.28 → nene2_python-1.8.29}/.github/workflows/ci.yml +0 -0
  11. {nene2_python-1.8.28 → nene2_python-1.8.29}/.github/workflows/docs.yml +0 -0
  12. {nene2_python-1.8.28 → nene2_python-1.8.29}/.github/workflows/publish.yml +0 -0
  13. {nene2_python-1.8.28 → nene2_python-1.8.29}/.gitignore +0 -0
  14. {nene2_python-1.8.28 → nene2_python-1.8.29}/.vitepress/config.mts +0 -0
  15. {nene2_python-1.8.28 → nene2_python-1.8.29}/.vitepress/theme/custom.css +0 -0
  16. {nene2_python-1.8.28 → nene2_python-1.8.29}/.vitepress/theme/index.ts +0 -0
  17. {nene2_python-1.8.28 → nene2_python-1.8.29}/AGENTS.md +0 -0
  18. {nene2_python-1.8.28 → nene2_python-1.8.29}/CLAUDE.md +0 -0
  19. {nene2_python-1.8.28 → nene2_python-1.8.29}/Dockerfile +0 -0
  20. {nene2_python-1.8.28 → nene2_python-1.8.29}/LICENSE +0 -0
  21. {nene2_python-1.8.28 → nene2_python-1.8.29}/README.md +0 -0
  22. {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/README +0 -0
  23. {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/env.py +0 -0
  24. {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/script.py.mako +0 -0
  25. {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  26. {nene2_python-1.8.28 → nene2_python-1.8.29}/alembic.ini +0 -0
  27. {nene2_python-1.8.28 → nene2_python-1.8.29}/compose.yaml +0 -0
  28. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0001-toolchain.md +0 -0
  29. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0002-clean-architecture.md +0 -0
  30. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0003-security-first.md +0 -0
  31. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0004-ai-first-design.md +0 -0
  32. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0005-logging.md +0 -0
  33. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0006-rate-limiting.md +0 -0
  34. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0009-mcp-design.md +0 -0
  35. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0010-async-use-case.md +0 -0
  36. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  37. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/de/index.md +0 -0
  38. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/de/tutorials/getting-started.md +0 -0
  39. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/explanation/architecture.md +0 -0
  40. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/explanation/design-philosophy.md +0 -0
  41. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  42. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  43. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  44. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  45. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  46. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  47. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  48. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  49. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  50. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  51. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  52. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  53. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  54. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  55. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  56. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  57. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  58. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  59. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  60. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  61. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  62. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  63. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  64. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  65. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  66. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  67. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  68. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  69. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  70. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  71. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  72. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  73. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  74. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  75. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  76. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  77. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  78. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  79. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  80. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  81. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  82. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  83. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  84. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  85. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  86. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  87. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  88. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  89. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  90. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  91. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  92. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  93. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  94. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  95. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  96. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  97. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  98. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  99. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  100. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  101. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  102. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  103. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  104. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  105. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  106. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  107. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  108. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  109. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  110. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  111. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  112. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  113. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  114. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  115. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  116. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  117. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  118. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  119. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  120. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  121. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  122. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  123. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  124. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/fr/index.md +0 -0
  125. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/fr/tutorials/getting-started.md +0 -0
  126. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/add-new-domain.md +0 -0
  127. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/async-use-case.md +0 -0
  128. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/configure-auth.md +0 -0
  129. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/middleware-stack.md +0 -0
  130. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/new-project.md +0 -0
  131. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/problem-details.md +0 -0
  132. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/run-tests.md +0 -0
  133. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/sqlalchemy-repository.md +0 -0
  134. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/how-to/validation.md +0 -0
  135. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/howto/mcp-setup.md +0 -0
  136. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/index.md +0 -0
  137. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/explanation/architecture.md +0 -0
  138. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/explanation/design-philosophy.md +0 -0
  139. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/add-new-domain.md +0 -0
  140. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/configure-auth.md +0 -0
  141. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/new-project.md +0 -0
  142. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/run-tests.md +0 -0
  143. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  144. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/howto/mcp-setup.md +0 -0
  145. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/index.md +0 -0
  146. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/reference/api.md +0 -0
  147. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/reference/configuration.md +0 -0
  148. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/reference/framework-modules.md +0 -0
  149. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/tutorials/first-domain.md +0 -0
  150. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/ja/tutorials/getting-started.md +0 -0
  151. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/pt-br/index.md +0 -0
  152. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/pt-br/tutorials/getting-started.md +0 -0
  153. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/reference/api.md +0 -0
  154. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/reference/configuration.md +0 -0
  155. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/reference/framework-modules.md +0 -0
  156. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/roadmap.md +0 -0
  157. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/todo/current.md +0 -0
  158. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/tutorials/first-domain.md +0 -0
  159. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/tutorials/getting-started.md +0 -0
  160. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/zh/index.md +0 -0
  161. {nene2_python-1.8.28 → nene2_python-1.8.29}/docs/zh/tutorials/getting-started.md +0 -0
  162. {nene2_python-1.8.28 → nene2_python-1.8.29}/package-lock.json +0 -0
  163. {nene2_python-1.8.28 → nene2_python-1.8.29}/package.json +0 -0
  164. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/__init__.py +0 -0
  165. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/__main__.py +0 -0
  166. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/app.py +0 -0
  167. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/__init__.py +0 -0
  168. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/entity.py +0 -0
  169. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/exceptions.py +0 -0
  170. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/handler.py +0 -0
  171. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/repository.py +0 -0
  172. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/sqlalchemy_repository.py +0 -0
  173. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/comment/use_case.py +0 -0
  174. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/mcp.py +0 -0
  175. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/__init__.py +0 -0
  176. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/async_use_case.py +0 -0
  177. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/entity.py +0 -0
  178. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/exceptions.py +0 -0
  179. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/handler.py +0 -0
  180. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/repository.py +0 -0
  181. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/sqlalchemy_repository.py +0 -0
  182. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/note/use_case.py +0 -0
  183. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/schema.py +0 -0
  184. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/__init__.py +0 -0
  185. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/entity.py +0 -0
  186. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/exceptions.py +0 -0
  187. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/handler.py +0 -0
  188. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/repository.py +0 -0
  189. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/sqlalchemy_repository.py +0 -0
  190. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/example/tag/use_case.py +0 -0
  191. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/__init__.py +0 -0
  192. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/api_key.py +0 -0
  193. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/bearer_token.py +0 -0
  194. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/exceptions.py +0 -0
  195. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/interfaces.py +0 -0
  196. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/auth/local_verifier.py +0 -0
  197. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/config/__init__.py +0 -0
  198. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/config/settings.py +0 -0
  199. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/__init__.py +0 -0
  200. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/exceptions.py +0 -0
  201. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/health.py +0 -0
  202. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/interfaces.py +0 -0
  203. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/sqlalchemy_executor.py +0 -0
  204. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/database/utils.py +0 -0
  205. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/http/__init__.py +0 -0
  206. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/http/health.py +0 -0
  207. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/http/pagination.py +0 -0
  208. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/http/problem_details.py +0 -0
  209. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/log/__init__.py +0 -0
  210. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/log/setup.py +0 -0
  211. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/mcp/__init__.py +0 -0
  212. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/mcp/http_client.py +0 -0
  213. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/mcp/server.py +0 -0
  214. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/__init__.py +0 -0
  215. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/domain_exception.py +0 -0
  216. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/error_handler.py +0 -0
  217. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/request_id.py +0 -0
  218. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/request_logging.py +0 -0
  219. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/request_size_limit.py +0 -0
  220. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/security_headers.py +0 -0
  221. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/setup.py +0 -0
  222. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/middleware/throttle.py +0 -0
  223. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/py.typed +0 -0
  224. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/use_case/__init__.py +0 -0
  225. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/use_case/protocols.py +0 -0
  226. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/validation/__init__.py +0 -0
  227. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/nene2/validation/exceptions.py +0 -0
  228. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/scripts/__init__.py +0 -0
  229. {nene2_python-1.8.28 → nene2_python-1.8.29}/src/scripts/export_openapi.py +0 -0
  230. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/__init__.py +0 -0
  231. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/__init__.py +0 -0
  232. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/comment/__init__.py +0 -0
  233. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/comment/test_comment_http.py +0 -0
  234. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/comment/test_comment_repository.py +0 -0
  235. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/comment/test_comment_use_case.py +0 -0
  236. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/conftest.py +0 -0
  237. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/note/__init__.py +0 -0
  238. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/note/test_async_note_use_case.py +0 -0
  239. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/note/test_list_notes.py +0 -0
  240. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/note/test_note_repository.py +0 -0
  241. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/tag/__init__.py +0 -0
  242. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/tag/test_tag_repository.py +0 -0
  243. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/tag/test_tags.py +0 -0
  244. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/test_cors.py +0 -0
  245. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/example/test_mcp.py +0 -0
  246. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/__init__.py +0 -0
  247. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/auth/__init__.py +0 -0
  248. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/auth/test_api_key.py +0 -0
  249. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/auth/test_bearer_token.py +0 -0
  250. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/auth/test_token_issuer.py +0 -0
  251. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/config/__init__.py +0 -0
  252. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/config/test_settings.py +0 -0
  253. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/database/__init__.py +0 -0
  254. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/database/test_transaction.py +0 -0
  255. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/database/test_utils.py +0 -0
  256. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/http/__init__.py +0 -0
  257. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/http/test_health.py +0 -0
  258. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/http/test_pagination.py +0 -0
  259. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/http/test_problem_details.py +0 -0
  260. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/log/__init__.py +0 -0
  261. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/log/test_setup.py +0 -0
  262. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/mcp/__init__.py +0 -0
  263. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/mcp/test_http_client.py +0 -0
  264. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/mcp/test_server.py +0 -0
  265. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/__init__.py +0 -0
  266. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_error_handler.py +0 -0
  267. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_request_id.py +0 -0
  268. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_request_logging.py +0 -0
  269. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  270. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_security_headers.py +0 -0
  271. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  272. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  273. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/middleware/test_throttle.py +0 -0
  274. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/use_case/__init__.py +0 -0
  275. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/use_case/test_protocols.py +0 -0
  276. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  277. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/validation/__init__.py +0 -0
  278. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/nene2/validation/test_exceptions.py +0 -0
  279. {nene2_python-1.8.28 → nene2_python-1.8.29}/tests/scripts/__init__.py +0 -0
  280. {nene2_python-1.8.28 → nene2_python-1.8.29}/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.28] — 2026-05-20
9
+
10
+ FT83 フィールドトライアル — Depends() DI パターン検証と PaginationResponse / PaginationDep 改善。
11
+
12
+ ### Added
13
+ - `PaginationResponse.model_dump()` を `to_dict()` の Pydantic 互換エイリアスとして追加 (#355) (FT83)
14
+ — Pydantic v2 ユーザーが `model_dump()` を期待して AttributeError になる問題を解消
15
+ - `PaginationDep` 型エイリアスを `nene2.http` に追加 (#355) (FT83)
16
+ — `Annotated[PaginationQueryParser, Depends(PaginationQueryParser)]` の省略形
17
+ - Field trial report: `docs/field-trials/2026-05-field-trial-83.md` (FT83)
18
+
19
+ ---
20
+
8
21
  ## [1.8.27] — 2026-05-20
9
22
 
10
23
  FT81 フィールドトライアル — CORS 設定パターン検証と setup_middlewares() への CORS 統合。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.28
3
+ Version: 1.8.29
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,151 @@
1
+ # FT84: 認証 Depends ユーティリティ — CurrentUser / require_auth パターン検証
2
+
3
+ **日付**: 2026-05-20
4
+ **テーマ**: nene2.auth に認証 Depends ユーティリティがない摩擦点と make_require_auth 設計検証
5
+ **バージョン**: v1.8.28
6
+ **FTディレクトリ**: `/home/xi/docker/nene2-python-FT/ft84-auth-depends/`
7
+
8
+ ---
9
+
10
+ ## 概要
11
+
12
+ `LocalTokenVerifier` と `TokenVerifierProtocol` は実装されているが、
13
+ FastAPI の Depends パターンに接続するユーティリティがない。
14
+ ユーザーは `HTTPBearer` → `verify()` → 未認証 401 を毎プロジェクトで手組みする必要がある。
15
+ `make_require_auth(verifier)` ファクトリーを追加することで解消できる。
16
+
17
+ ---
18
+
19
+ ## 発見した問題
20
+
21
+ ### 問題1: 認証 Depends を毎回手組みする必要がある
22
+
23
+ ```python
24
+ # 現状: プロジェクトごとに以下のコードを書く必要がある (50+ 行のボイラープレート)
25
+
26
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
27
+ from nene2.auth import LocalTokenVerifier
28
+
29
+ security = HTTPBearer(auto_error=False)
30
+ _verifier = LocalTokenVerifier.from_env("BEARER_TOKENS")
31
+
32
+ def get_current_user(
33
+ credentials: HTTPAuthorizationCredentials | None = Depends(security),
34
+ ) -> str | None:
35
+ if credentials is None:
36
+ return None
37
+ if not _verifier.verify(credentials.credentials):
38
+ return None
39
+ return credentials.credentials
40
+
41
+ def require_auth(user: str | None = Depends(get_current_user)) -> str:
42
+ if user is None:
43
+ raise HTTPException(status_code=401)
44
+ return user
45
+
46
+ # ハンドラーで使う
47
+ @app.post("/items")
48
+ def create(body: ItemBody, user: str = Depends(require_auth)) -> JSONResponse: ...
49
+ ```
50
+
51
+ ### 問題2: 任意認証パターンが冗長
52
+
53
+ ```python
54
+ # 未認証でも通るが user を取得したいパターン
55
+ def optional_auth(user: str | None = Depends(get_current_user)) -> str | None:
56
+ return user # None = 未認証、str = ユーザーID
57
+
58
+ @app.get("/feed")
59
+ def get_feed(user: str | None = Depends(optional_auth)) -> JSONResponse:
60
+ if user:
61
+ return JSONResponse({"personalized": True})
62
+ return JSONResponse({"personalized": False})
63
+ ```
64
+
65
+ ### 問題3: TokenVerifierProtocol を Depends で直接使えない
66
+
67
+ ```python
68
+ # こういう書き方をしたくなるが Depends は callable を期待する
69
+ @app.post("/items")
70
+ def create(
71
+ body: ItemBody,
72
+ token: Annotated[str, Depends(LocalTokenVerifier.from_env("TOKENS"))], # ← 機能しない
73
+ ) -> JSONResponse: ...
74
+ ```
75
+
76
+ ---
77
+
78
+ ## テスト結果(全12件パス)
79
+
80
+ ```
81
+ test_public_endpoint_no_auth PASSED
82
+ test_create_item_with_valid_token PASSED
83
+ test_create_item_without_token_returns_401 PASSED
84
+ test_create_item_with_invalid_token_returns_401 PASSED
85
+ test_get_me_returns_user_info PASSED
86
+ test_delete_item_authenticated PASSED
87
+ test_delete_item_unauthenticated_returns_401 PASSED
88
+ test_request_id_present_on_401 PASSED # nene2 ミドルウェアと共存
89
+ test_security_headers_present_on_401 PASSED # nene2 ミドルウェアと共存
90
+ test_friction_boilerplate_for_auth_depends PASSED # 摩擦記録
91
+ test_friction_optional_auth_pattern_verbose PASSED # 摩擦記録
92
+ test_friction_verifier_not_injectable_as_depends PASSED # 摩擦記録
93
+ ```
94
+
95
+ ---
96
+
97
+ ## 摩擦ポイント一覧
98
+
99
+ | ID | 内容 | 深刻度 |
100
+ |---|---|---|
101
+ | F84-1 | 認証 Depends ボイラープレートを毎プロジェクトで手組みする必要がある | 中 |
102
+ | F84-2 | 任意認証(Optional User)パターンが冗長 | 低 |
103
+ | F84-3 | `TokenVerifierProtocol` を Depends で直接使えない | 低 |
104
+
105
+ ---
106
+
107
+ ## 使用感(主観評価)
108
+
109
+ ### 直感性 ★★☆☆☆
110
+
111
+ 「nene2 でトークン認証を追加する」という操作に必要なステップが多すぎる。
112
+ `BearerTokenMiddleware` は全経路に認証を入れるには便利だが、
113
+ 「このエンドポイントだけ認証を要求」パターンに Depends ユーティリティがない。
114
+ FastAPI の HTTPBearer を使う方法は FastAPI のドキュメントを参照しなければわからない。
115
+
116
+ ### 実害の深刻さ ★★★☆☆
117
+
118
+ 認証は多くのプロジェクトで必要。毎回 50 行のボイラープレートを書くのは
119
+ DRY 原則に反する。バグが入りやすく、テスト漏れにもつながる。
120
+
121
+ ### 修正のしやすさ ★★★★★
122
+
123
+ `make_require_auth(verifier)` 関数を `nene2.auth` に追加するだけ:
124
+
125
+ ```python
126
+ def make_require_auth(verifier: TokenVerifierProtocol) -> Callable[..., str]:
127
+ security = HTTPBearer(auto_error=False)
128
+ def get_current_user(credentials: ... = Depends(security)) -> str | None:
129
+ if credentials is None or not verifier.verify(credentials.credentials):
130
+ return None
131
+ return credentials.credentials
132
+ def require_auth(user: str | None = Depends(get_current_user)) -> str:
133
+ if user is None:
134
+ raise HTTPException(status_code=401)
135
+ return user
136
+ return require_auth
137
+ ```
138
+
139
+ ### 総合コメント
140
+
141
+ nene2 の認証機能は「ミドルウェアで全経路を保護する」ユースケースは
142
+ `BearerTokenMiddleware` でカバーされている。
143
+ 不足しているのは「特定エンドポイントのみ認証を要求する」ユースケース。
144
+ `make_require_auth()` を追加することで「nene2 だけで完結」できる。
145
+
146
+ ---
147
+
148
+ ## 推奨アクション
149
+
150
+ 1. **Issue #358**: `nene2.auth` に `make_require_auth(verifier)` ファクトリーを追加
151
+ — `require_auth = make_require_auth(LocalTokenVerifier.from_env("TOKENS"))` パターンを実現
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.28"
3
+ version = "1.8.29"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -2,6 +2,7 @@
2
2
 
3
3
  from .api_key import ApiKeyAuthMiddleware
4
4
  from .bearer_token import BearerTokenMiddleware
5
+ from .deps import make_require_auth
5
6
  from .exceptions import TokenVerificationException
6
7
  from .interfaces import TokenIssuerProtocol, TokenVerifierProtocol
7
8
  from .local_verifier import LocalTokenVerifier
@@ -13,4 +14,5 @@ __all__ = [
13
14
  "TokenIssuerProtocol",
14
15
  "TokenVerificationException",
15
16
  "TokenVerifierProtocol",
17
+ "make_require_auth",
16
18
  ]
@@ -0,0 +1,50 @@
1
+ """FastAPI Depends utilities for authentication.
2
+
3
+ Provides ``make_require_auth`` to wire :class:`TokenVerifierProtocol` into
4
+ FastAPI's dependency injection system without boilerplate.
5
+ """
6
+
7
+ from collections.abc import Callable
8
+ from typing import Annotated
9
+
10
+ from fastapi import Depends, HTTPException
11
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
12
+
13
+ from .interfaces import TokenVerifierProtocol
14
+
15
+ _security = HTTPBearer(auto_error=False)
16
+
17
+
18
+ def make_require_auth(verifier: TokenVerifierProtocol) -> Callable[..., str]:
19
+ """Return a FastAPI Depends-compatible callable that enforces token authentication.
20
+
21
+ Usage::
22
+
23
+ from nene2.auth import LocalTokenVerifier, make_require_auth
24
+
25
+ verifier = LocalTokenVerifier.from_env("BEARER_TOKENS")
26
+ require_auth = make_require_auth(verifier)
27
+
28
+
29
+ @app.post("/items")
30
+ def create_item(
31
+ body: ItemBody,
32
+ token: Annotated[str, Depends(require_auth)],
33
+ ) -> JSONResponse: ...
34
+
35
+ Args:
36
+ verifier: A :class:`TokenVerifierProtocol` implementation to validate tokens.
37
+
38
+ Returns:
39
+ A dependency function that returns the raw token string when authenticated,
40
+ or raises ``HTTP 401`` when the token is absent or invalid.
41
+ """
42
+
43
+ def _get_token(
44
+ credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(_security)],
45
+ ) -> str:
46
+ if credentials is None or not verifier.verify(credentials.credentials):
47
+ raise HTTPException(status_code=401, detail="Invalid or missing token")
48
+ return credentials.credentials
49
+
50
+ return _get_token
@@ -0,0 +1,99 @@
1
+ """Tests for make_require_auth() Depends factory."""
2
+
3
+ from typing import Annotated
4
+
5
+ from fastapi import Depends, FastAPI
6
+ from fastapi.responses import JSONResponse
7
+ from fastapi.testclient import TestClient
8
+
9
+ from nene2.auth import LocalTokenVerifier, make_require_auth
10
+
11
+
12
+ def _make_app(tokens: list[str]) -> FastAPI:
13
+ verifier = LocalTokenVerifier(tokens)
14
+ require_auth = make_require_auth(verifier)
15
+
16
+ app = FastAPI()
17
+
18
+ @app.get("/public")
19
+ def public() -> JSONResponse:
20
+ return JSONResponse({"public": True})
21
+
22
+ @app.get("/protected")
23
+ def protected(token: Annotated[str, Depends(require_auth)]) -> JSONResponse:
24
+ return JSONResponse({"token": token})
25
+
26
+ @app.post("/items")
27
+ def create(token: Annotated[str, Depends(require_auth)]) -> JSONResponse:
28
+ return JSONResponse({"created_by": token[:8]}, status_code=201)
29
+
30
+ return app
31
+
32
+
33
+ def test_valid_token_returns_200() -> None:
34
+ client = TestClient(_make_app(["secret-token"]), raise_server_exceptions=False)
35
+ r = client.get("/protected", headers={"Authorization": "Bearer secret-token"})
36
+ assert r.status_code == 200
37
+ assert r.json()["token"] == "secret-token" # noqa: S105
38
+
39
+
40
+ def test_missing_token_returns_401() -> None:
41
+ client = TestClient(_make_app(["secret-token"]), raise_server_exceptions=False)
42
+ r = client.get("/protected")
43
+ assert r.status_code == 401
44
+
45
+
46
+ def test_invalid_token_returns_401() -> None:
47
+ client = TestClient(_make_app(["secret-token"]), raise_server_exceptions=False)
48
+ r = client.get("/protected", headers={"Authorization": "Bearer wrong-token"})
49
+ assert r.status_code == 401
50
+
51
+
52
+ def test_public_endpoint_no_auth_required() -> None:
53
+ client = TestClient(_make_app([]), raise_server_exceptions=False)
54
+ r = client.get("/public")
55
+ assert r.status_code == 200
56
+
57
+
58
+ def test_post_with_valid_token_returns_201() -> None:
59
+ client = TestClient(_make_app(["my-api-key"]), raise_server_exceptions=False)
60
+ r = client.post("/items", headers={"Authorization": "Bearer my-api-key"})
61
+ assert r.status_code == 201
62
+
63
+
64
+ def test_multiple_valid_tokens() -> None:
65
+ client = TestClient(_make_app(["token-a", "token-b"]), raise_server_exceptions=False)
66
+ r_a = client.get("/protected", headers={"Authorization": "Bearer token-a"})
67
+ r_b = client.get("/protected", headers={"Authorization": "Bearer token-b"})
68
+ assert r_a.status_code == 200
69
+ assert r_b.status_code == 200
70
+
71
+
72
+ def test_empty_allowlist_rejects_all() -> None:
73
+ client = TestClient(_make_app([]), raise_server_exceptions=False)
74
+ r = client.get("/protected", headers={"Authorization": "Bearer any-token"})
75
+ assert r.status_code == 401
76
+
77
+
78
+ def test_different_verifiers_independent() -> None:
79
+ """2つの異なる verifier を同じアプリで使える。"""
80
+ verifier_a = LocalTokenVerifier(["token-a"])
81
+ verifier_b = LocalTokenVerifier(["token-b"])
82
+ require_a = make_require_auth(verifier_a)
83
+ require_b = make_require_auth(verifier_b)
84
+
85
+ app = FastAPI()
86
+
87
+ @app.get("/route-a")
88
+ def route_a(token: Annotated[str, Depends(require_a)]) -> JSONResponse:
89
+ return JSONResponse({"token": token})
90
+
91
+ @app.get("/route-b")
92
+ def route_b(token: Annotated[str, Depends(require_b)]) -> JSONResponse:
93
+ return JSONResponse({"token": token})
94
+
95
+ client = TestClient(app, raise_server_exceptions=False)
96
+ assert client.get("/route-a", headers={"Authorization": "Bearer token-a"}).status_code == 200
97
+ assert client.get("/route-a", headers={"Authorization": "Bearer token-b"}).status_code == 401
98
+ assert client.get("/route-b", headers={"Authorization": "Bearer token-b"}).status_code == 200
99
+ assert client.get("/route-b", headers={"Authorization": "Bearer token-a"}).status_code == 401
@@ -925,7 +925,7 @@ wheels = [
925
925
 
926
926
  [[package]]
927
927
  name = "nene2-python"
928
- version = "1.8.28"
928
+ version = "1.8.29"
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