nene2-python 1.8.37__tar.gz → 1.8.39__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nene2_python-1.8.37 → nene2_python-1.8.39}/PKG-INFO +1 -1
- nene2_python-1.8.39/docs/field-trials/2026-05-field-trial-167.md +239 -0
- nene2_python-1.8.39/docs/field-trials/2026-05-field-trial-168.md +473 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/pyproject.toml +1 -1
- {nene2_python-1.8.37 → nene2_python-1.8.39}/uv.lock +1 -1
- {nene2_python-1.8.37 → nene2_python-1.8.39}/.env.example +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/.gitignore +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/AGENTS.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/CHANGELOG.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/CLAUDE.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/Dockerfile +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/LICENSE +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/README.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic/README +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic/env.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic.ini +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/compose.yaml +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/de/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-100.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-101.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-102.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-103.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-104.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-105.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-106.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-107.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-108.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-109.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-110.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-111.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-112.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-113.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-114.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-115.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-116.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-117.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-118.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-119.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-120.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-121.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-122.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-123.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-124.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-125.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-126.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-127.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-128.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-129.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-130.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-131.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-132.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-133.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-134.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-135.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-136.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-137.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-138.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-139.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-140.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-141.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-142.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-143.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-144.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-145.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-146.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-147.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-148.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-149.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-150.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-151.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-152.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-153.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-154.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-155.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-156.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-157.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-158.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-159.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-160.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-161.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-162.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-163.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-164.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-165.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-166.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-68.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-69.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-70.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-71.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-72.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-73.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-74.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-75.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-76.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-77.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-78.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-79.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-80.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-81.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-82.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-83.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-84.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-85.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-86.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-87.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-88.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-89.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-90.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-91.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-92.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-93.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-94.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-95.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-96.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-97.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-98.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-99.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/fr/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/api-versioning.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/background-tasks.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/cors.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/custom-auth-middleware.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/dependency-injection.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/domain-events.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/file-upload.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/lifespan-and-app-state.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/middleware-stack.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/response-patterns.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/soft-delete.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/streaming.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/structured-logging.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/webhook.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/reference/api.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/roadmap.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/templates/field-trial-report.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/todo/current.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/zh/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/package-lock.json +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/package.json +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/__main__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/app.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/mcp.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/schema.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/deps.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/cache/ttl.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/etag.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/setup.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/security/webhook.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/conftest.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/test_make_require_auth.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/cache/test_ttl.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/test_etag.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/mcp/test_server.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/security/test_webhook.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/scripts/test_export_openapi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nene2-python
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.39
|
|
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,239 @@
|
|
|
1
|
+
# FT167: enum モジュール
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-21
|
|
4
|
+
**テーマ**: `enum` モジュール — `Enum`・`StrEnum`・`IntEnum`・`Flag`・`auto()`・状態遷移・権限管理
|
|
5
|
+
**セキュリティ診断**: なし(167 % 3 = 2)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 概要
|
|
10
|
+
|
|
11
|
+
Python 標準ライブラリの `enum` モジュールを nene2-python フレームワーク上で検証した。
|
|
12
|
+
`enum` はドメインモデルの状態・権限・分類を型安全に表現するための中核モジュール。
|
|
13
|
+
`StrEnum`(Python 3.11+)は Pydantic v2 と組み合わせて HTTP 境界での型検証に直接使える。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装したサンプルアプリ
|
|
18
|
+
|
|
19
|
+
**場所**: `/home/xi/docker/nene2-python-FT/ft167-enum/`
|
|
20
|
+
|
|
21
|
+
### 主要機能
|
|
22
|
+
|
|
23
|
+
| クラス/関数 | 概要 |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `Color(Enum)` | 基本 Enum。値で参照・イテレーション |
|
|
26
|
+
| `TaskStatus(StrEnum)` | `auto()` で名前を小文字化。文字列として使用可能 |
|
|
27
|
+
| `Priority(IntEnum)` | 比較・算術演算が可能な整数 Enum |
|
|
28
|
+
| `Permission(Flag)` | ビットフラグで権限を組み合わせ(READ / WRITE / DELETE / ADMIN) |
|
|
29
|
+
| `HttpMethod(StrEnum)` | カスタムプロパティ(`is_safe`, `has_body`)付き |
|
|
30
|
+
| `Task` dataclass | `TaskStatus` / `Priority` / `Permission` を持つドメインオブジェクト |
|
|
31
|
+
| `transition_task()` | 許可された状態遷移のみ受け付ける型安全な状態機械 |
|
|
32
|
+
| `describe_permission()` | `Flag` の組み合わせを人間可読な辞書に変換 |
|
|
33
|
+
|
|
34
|
+
### HTTP エンドポイント
|
|
35
|
+
|
|
36
|
+
| メソッド | パス | 概要 |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| GET | `/enum/colors` | 全 Color 一覧 |
|
|
39
|
+
| GET | `/enum/task-status` | TaskStatus 一覧・StrEnum 確認 |
|
|
40
|
+
| POST | `/enum/transition` | 状態遷移バリデーション |
|
|
41
|
+
| GET | `/enum/priority` | Priority 比較 |
|
|
42
|
+
| POST | `/enum/permission` | Permission フラグ組み合わせ |
|
|
43
|
+
| GET | `/enum/http-method` | HttpMethod プロパティ |
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## テスト結果
|
|
48
|
+
|
|
49
|
+
**32 passed(摩擦ゼロ)**
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
32 passed in 1.00s
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 摩擦ポイント
|
|
58
|
+
|
|
59
|
+
**今回の FT では実装上の摩擦はゼロだった。**
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 観察点
|
|
64
|
+
|
|
65
|
+
### 観察1: `StrEnum` + `auto()` で名前を自動的に小文字化
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
class TaskStatus(enum.StrEnum):
|
|
69
|
+
PENDING = enum.auto() # → "pending"
|
|
70
|
+
RUNNING = enum.auto() # → "running"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`StrEnum` の `auto()` は `_generate_next_value_()` をオーバーライドして
|
|
74
|
+
メンバー名を小文字にする。つまり `TaskStatus.PENDING == "pending"` が成立し、
|
|
75
|
+
JSON シリアライズ時に `.value` を呼ぶ必要がない。
|
|
76
|
+
Pydantic v2 + FastAPI ではフィールドに `TaskStatus` を指定するだけで HTTP Body・レスポンスで使える。
|
|
77
|
+
|
|
78
|
+
### 観察2: `Flag` でビット権限管理 — `in` 演算子でチェック
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
class Permission(enum.Flag):
|
|
82
|
+
READ = enum.auto() # 1
|
|
83
|
+
WRITE = enum.auto() # 2
|
|
84
|
+
DELETE = enum.auto() # 4
|
|
85
|
+
ADMIN = READ | WRITE | DELETE # 7
|
|
86
|
+
|
|
87
|
+
rw = Permission.READ | Permission.WRITE
|
|
88
|
+
assert Permission.READ in rw # True
|
|
89
|
+
assert Permission.DELETE not in rw # True
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`Flag` のビット演算(`|`, `&`, `~`)は直感的で、RBAC の権限チェックに最適。
|
|
93
|
+
`Permission.NONE = 0` を定義することで「権限なし」を明示的に表現できる。
|
|
94
|
+
|
|
95
|
+
### 観察3: `IntEnum` は `int` として比較・ソートできる
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
assert Priority.LOW < Priority.HIGH # True(数値比較)
|
|
99
|
+
assert Priority.LOW + Priority.MEDIUM == 6 # 算術演算も可
|
|
100
|
+
sorted_priorities = sorted([Priority.HIGH, Priority.LOW]) # [LOW, HIGH]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
DB のカラム値が整数の場合、`IntEnum` を使うと ORM との相互変換がシームレス。
|
|
104
|
+
ただし算術演算の結果が `int`(Enum でない)になることに注意。
|
|
105
|
+
|
|
106
|
+
### 観察4: Enum に `@property` を追加してドメインロジックをカプセル化
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
class HttpMethod(enum.StrEnum):
|
|
110
|
+
@property
|
|
111
|
+
def is_safe(self) -> bool:
|
|
112
|
+
return self in (HttpMethod.GET,)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def has_body(self) -> bool:
|
|
116
|
+
return self in (HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Enum にメソッドを追加することで、`if method == "GET":` の散在を防ぎ
|
|
120
|
+
ドメインロジックを 1 箇所に集約できる。`@property` との相性も良い。
|
|
121
|
+
|
|
122
|
+
### 観察5: Pydantic v2 は `StrEnum` を文字列バリデーションとして扱う
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
class TransitionBody(BaseModel):
|
|
126
|
+
current_status: TaskStatus # "pending" / "running" / "done" / "failed" のみ許可
|
|
127
|
+
new_status: TaskStatus
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Pydantic v2 は `StrEnum` フィールドに対して、有効な enum 値のみを受け付け、
|
|
131
|
+
無効な値には自動的に 422 を返す。`Literal["pending", "running", ...]` より型安全。
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## nene2-python フレームワークとの統合
|
|
136
|
+
|
|
137
|
+
- `TaskStatus(StrEnum)` は nene2 の Note ドメインの `status` フィールドに直接適用できる
|
|
138
|
+
- `Permission(Flag)` は `ApiKeyAuthMiddleware` の権限スコープ実装に応用可能
|
|
139
|
+
- `Priority(IntEnum)` はタスクスケジューリング・DB ソートに使いやすい
|
|
140
|
+
- Enum をレスポンスモデルに含める場合、FastAPI は自動的に `.value` でシリアライズする
|
|
141
|
+
- `transition_task()` のようなドメイン状態機械は UseCase 内に実装するパターンが nene2 アーキテクチャと整合する
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Developer Experience (DX) Review
|
|
146
|
+
|
|
147
|
+
### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
|
|
148
|
+
|
|
149
|
+
`Enum` の基本(`class Color(Enum): RED = "red"`)は直感的に理解できる。
|
|
150
|
+
`StrEnum`・`IntEnum`・`Flag` の使い分けは最初は混乱する。
|
|
151
|
+
|
|
152
|
+
**ドキュメント理解**: 「どれを使えばいいか」の判断基準がなければ基本 `Enum` だけを使い続ける。
|
|
153
|
+
nene2 の how-to に「HTTP API の状態フィールドは `StrEnum` を使う」という指針があれば一発で決まる。
|
|
154
|
+
|
|
155
|
+
**事故リスク**: 低。Enum の誤用は大きな事故にはつながりにくい。
|
|
156
|
+
ただし `IntEnum` の算術演算結果が `int` になることを知らず、Enum メンバーを期待してバグになる可能性。
|
|
157
|
+
|
|
158
|
+
**規約の使いやすさ**: `StrEnum` + `auto()` のパターンはコピペで使える。
|
|
159
|
+
|
|
160
|
+
### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
|
|
161
|
+
|
|
162
|
+
文字列定数の代わりに `"pending"` / `"running"` をそのまま使う習慣がある。
|
|
163
|
+
`StrEnum` を知ると「これだけで typo を防げるんだ」と即採用する傾向。
|
|
164
|
+
|
|
165
|
+
**コピペ可能性**: `StrEnum` + `auto()` のパターンは一度見れば再現できる。
|
|
166
|
+
|
|
167
|
+
**拡張時の罠**: `Flag` の複合演算(`rw = READ | WRITE`)を見て、
|
|
168
|
+
「`|` は OR だから `rw == READ or rw == WRITE` と同じ」と誤解し、
|
|
169
|
+
`if rw == READ:` と書いてしまう(`in` を使わなければならない)。
|
|
170
|
+
|
|
171
|
+
**セキュリティ的な事故リスク**: 中。`Permission.ADMIN = READ | WRITE | DELETE` を定義したつもりが、
|
|
172
|
+
ビット演算の順序ミスで意図しない権限の組み合わせになるリスク。テストで確認が必要。
|
|
173
|
+
|
|
174
|
+
### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
|
|
175
|
+
|
|
176
|
+
TypeScript の `enum` / `const enum` / `union type` との比較で混乱する可能性。
|
|
177
|
+
Python の `Enum` はクラスであり、TS の `enum` より強力(メソッド追加可能)。
|
|
178
|
+
|
|
179
|
+
**エラーレスポンスの質**: Pydantic が `StrEnum` を自動バリデーションして 422 を返すため、
|
|
180
|
+
クライアントには明確なエラーが返る。
|
|
181
|
+
|
|
182
|
+
**事故リスク**: 低。TS の概念と対応関係が作れる。
|
|
183
|
+
|
|
184
|
+
### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
|
|
185
|
+
|
|
186
|
+
Django の `CharField(choices=...)` + `TextChoices` との比較で評価する。
|
|
187
|
+
`StrEnum` は `TextChoices` より汎用的で型安全性が高い。
|
|
188
|
+
|
|
189
|
+
**他フレームワークとの差異**:
|
|
190
|
+
- Django: `models.TextChoices` → DB のチョイスとモデルを一元管理
|
|
191
|
+
- nene2-python: `StrEnum` → HTTP 境界と UseCase で型安全に使い、Repository が DB マッピングを担当
|
|
192
|
+
- 関心の分離がより明確
|
|
193
|
+
|
|
194
|
+
**本番投入可能性**: 問題なし。`StrEnum` は SQLAlchemy の `Enum` 型と組み合わせると完璧。
|
|
195
|
+
|
|
196
|
+
### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
|
|
197
|
+
|
|
198
|
+
**コードレビューチェックポイント**:
|
|
199
|
+
- [ ] 状態遷移が `transition_task()` のように集約されているか(散在した `if status == "done":` を禁止)
|
|
200
|
+
- [ ] `Flag` の権限チェックで `perm == Permission.READ`(完全一致)でなく `Permission.READ in perm`(部分一致)を使っているか
|
|
201
|
+
- [ ] `IntEnum` の算術演算結果が `int` になることをコードが前提にしていないか
|
|
202
|
+
- [ ] Enum メンバーを文字列比較(`status == "pending"`)でなく Enum 比較(`status == TaskStatus.PENDING`)しているか
|
|
203
|
+
- [ ] DB に保存する Enum 値は `.value` を使って保存し、読み出し時に Enum に変換しているか
|
|
204
|
+
|
|
205
|
+
**チームでの安全なパターン**: 状態遷移グラフは `dict[Status, set[Status]]` で明示的に宣言し、
|
|
206
|
+
UseCase の外部に公開する(テストしやすくなる)。
|
|
207
|
+
|
|
208
|
+
### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
|
|
209
|
+
|
|
210
|
+
**ポリシー達成度**: 高
|
|
211
|
+
|
|
212
|
+
**「初心者でも安全な API」達成度**: 高
|
|
213
|
+
- Pydantic + `StrEnum` の組み合わせにより、HTTP 境界での自動バリデーションが確立される
|
|
214
|
+
- 無効な状態値は 422 で自動的に拒否される
|
|
215
|
+
|
|
216
|
+
**設計上の負債・ドキュメント不足**:
|
|
217
|
+
- nene2 の example ドメイン(Note / Tag / Comment)に `StrEnum` を使っているフィールドがない
|
|
218
|
+
- Note の `status` フィールドに `StrEnum` を導入するサンプルがあると良い
|
|
219
|
+
|
|
220
|
+
**Follow-up Issue 候補**: `docs: StrEnum を使ったドメイン状態管理の how-to を追加`
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Follow-up Issues
|
|
225
|
+
|
|
226
|
+
| 優先度 | タイトル | 種別 |
|
|
227
|
+
|---|---|---|
|
|
228
|
+
| 中 | `docs: StrEnum + Pydantic + 状態遷移パターンの how-to を追加` | docs |
|
|
229
|
+
| 低 | `feat: example/note に status フィールド(StrEnum)を追加してパターンを実演` | feat |
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## まとめ
|
|
234
|
+
|
|
235
|
+
`enum` モジュールは nene2-python のドメインモデル設計に直接適用できる重要な機能。
|
|
236
|
+
`StrEnum` + Pydantic v2 の組み合わせで HTTP 境界の自動バリデーションが完成し、
|
|
237
|
+
`Flag` による権限管理は RBAC の実装基盤になる。
|
|
238
|
+
32 テスト全通過、摩擦ゼロ。
|
|
239
|
+
状態遷移を `dict[Status, set[Status]]` で宣言するパターンは UseCase 内で採用が推奨される。
|