nene2-python 1.8.37__tar.gz → 1.8.38__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.38}/PKG-INFO +1 -1
- nene2_python-1.8.38/docs/field-trials/2026-05-field-trial-167.md +239 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/pyproject.toml +1 -1
- {nene2_python-1.8.37 → nene2_python-1.8.38}/.env.example +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/.gitignore +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/AGENTS.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/CHANGELOG.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/CLAUDE.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/Dockerfile +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/LICENSE +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/README.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/alembic/README +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/alembic/env.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/alembic.ini +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/compose.yaml +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/de/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-100.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-101.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-102.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-103.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-104.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-105.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-106.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-107.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-108.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-109.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-110.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-111.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-112.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-113.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-114.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-115.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-116.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-117.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-118.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-119.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-120.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-121.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-122.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-123.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-124.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-125.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-126.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-127.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-128.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-129.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-130.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-131.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-132.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-133.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-134.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-135.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-136.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-137.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-138.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-139.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-140.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-141.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-142.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-143.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-144.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-145.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-146.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-147.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-148.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-149.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-150.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-151.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-152.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-153.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-154.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-155.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-156.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-157.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-158.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-159.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-160.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-161.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-162.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-163.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-164.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-165.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-166.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-68.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-69.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-70.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-71.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-72.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-73.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-74.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-75.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-76.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-77.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-78.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-79.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-80.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-81.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-82.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-83.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-84.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-85.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-86.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-87.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-88.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-89.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-90.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-91.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-92.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-93.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-94.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-95.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-96.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-97.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-98.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-99.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/fr/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/api-versioning.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/background-tasks.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/cors.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/custom-auth-middleware.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/dependency-injection.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/domain-events.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/file-upload.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/lifespan-and-app-state.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/middleware-stack.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/response-patterns.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/soft-delete.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/streaming.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/structured-logging.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/how-to/webhook.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/reference/api.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/roadmap.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/templates/field-trial-report.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/todo/current.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/zh/index.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/package-lock.json +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/package.json +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/__main__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/app.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/mcp.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/schema.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/auth/deps.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/cache/ttl.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/http/etag.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/setup.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/security/webhook.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/conftest.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/auth/test_make_require_auth.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/cache/test_ttl.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/http/test_etag.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/mcp/test_server.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/security/test_webhook.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/tests/scripts/test_export_openapi.py +0 -0
- {nene2_python-1.8.37 → nene2_python-1.8.38}/uv.lock +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.38
|
|
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 内で採用が推奨される。
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nene2_python-1.8.37 → nene2_python-1.8.38}/alembic/versions/001_create_notes_and_tags_tables.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|