nene2-python 1.8.39__tar.gz → 1.8.40__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.39 → nene2_python-1.8.40}/PKG-INFO +1 -1
- nene2_python-1.8.40/docs/field-trials/2026-05-field-trial-169.md +272 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/pyproject.toml +1 -1
- {nene2_python-1.8.39 → nene2_python-1.8.40}/uv.lock +1 -1
- {nene2_python-1.8.39 → nene2_python-1.8.40}/.env.example +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/.gitignore +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/AGENTS.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/CHANGELOG.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/CLAUDE.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/Dockerfile +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/LICENSE +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/README.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/alembic/README +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/alembic/env.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/alembic.ini +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/compose.yaml +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/de/index.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-100.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-101.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-102.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-103.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-104.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-105.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-106.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-107.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-108.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-109.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-110.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-111.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-112.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-113.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-114.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-115.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-116.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-117.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-118.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-119.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-120.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-121.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-122.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-123.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-124.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-125.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-126.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-127.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-128.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-129.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-130.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-131.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-132.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-133.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-134.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-135.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-136.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-137.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-138.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-139.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-140.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-141.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-142.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-143.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-144.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-145.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-146.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-147.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-148.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-149.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-150.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-151.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-152.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-153.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-154.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-155.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-156.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-157.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-158.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-159.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-160.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-161.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-162.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-163.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-164.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-165.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-166.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-167.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-168.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-68.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-69.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-70.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-71.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-72.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-73.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-74.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-75.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-76.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-77.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-78.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-79.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-80.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-81.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-82.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-83.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-84.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-85.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-86.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-87.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-88.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-89.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-90.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-91.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-92.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-93.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-94.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-95.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-96.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-97.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-98.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/field-trials/2026-05-field-trial-99.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/fr/index.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/api-versioning.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/background-tasks.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/cors.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/custom-auth-middleware.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/dependency-injection.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/domain-events.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/file-upload.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/lifespan-and-app-state.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/middleware-stack.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/response-patterns.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/soft-delete.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/streaming.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/structured-logging.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/how-to/webhook.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/index.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/index.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/reference/api.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/roadmap.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/templates/field-trial-report.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/todo/current.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/zh/index.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/package-lock.json +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/package.json +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/__main__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/app.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/mcp.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/schema.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/auth/deps.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/cache/ttl.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/http/etag.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/setup.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/security/webhook.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/conftest.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/auth/test_make_require_auth.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/cache/test_ttl.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/http/test_etag.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/mcp/test_server.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/security/test_webhook.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.39 → nene2_python-1.8.40}/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.40
|
|
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,272 @@
|
|
|
1
|
+
# FT169: typing モジュール
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-21
|
|
4
|
+
**テーマ**: `typing` モジュール — `TypedDict`・`Protocol`・`overload`・`Literal`・`TypeGuard`・`Required`/`NotRequired`
|
|
5
|
+
**セキュリティ診断**: なし(169 % 3 = 2)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 概要
|
|
10
|
+
|
|
11
|
+
Python 標準ライブラリの `typing` モジュール(Python 3.12+ の先進的な機能を含む)を
|
|
12
|
+
nene2-python フレームワーク上で検証した。
|
|
13
|
+
`typing` は nene2 の「strict typing」設計哲学の根幹であり、
|
|
14
|
+
HTTP 境界の型安全性・ドメインモデルの不変性・プロトコルによる構造的サブタイピングを
|
|
15
|
+
支える重要モジュール。
|
|
16
|
+
CLAUDE.md の型安全ポリシー(`Any` 禁止・`TypedDict`・`Protocol` 活用)に直接対応する。
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 実装したサンプルアプリ
|
|
21
|
+
|
|
22
|
+
**場所**: `/home/xi/docker/nene2-python-FT/ft169-typing/`
|
|
23
|
+
|
|
24
|
+
### 主要機能
|
|
25
|
+
|
|
26
|
+
| 関数/クラス | 概要 |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `NoteDict` (TypedDict) | 構造化辞書の型定義。`id`, `title`, `content` |
|
|
29
|
+
| `NoteCreateDict` (TypedDict) | `NotRequired[str]` でオプショナルフィールドを表現 |
|
|
30
|
+
| `NoteWithMetaDict` (TypedDict 継承) | `total=False` でオプショナル拡張フィールドを追加 |
|
|
31
|
+
| `Serializable` (Protocol) | `@runtime_checkable` で `isinstance()` チェック可能な構造的サブタイプ |
|
|
32
|
+
| `Closeable` (Protocol) | `close()` を持つリソースのプロトコル |
|
|
33
|
+
| `parse_id()` (@overload) | `str` 引数 → `int \| None`、`int` 引数 → `int` の型多態性 |
|
|
34
|
+
| `double()` (@overload) | `int` / `str` / `float` それぞれに異なる戻り値型 |
|
|
35
|
+
| `SortOrder` / `NoteStatus` / `HttpStatusCode` (Literal) | `type` エイリアス + `Literal` で定数列挙 |
|
|
36
|
+
| `is_note_dict()` (TypeGuard) | 実行時の型絞り込み関数 |
|
|
37
|
+
| `build_search_params()` | `Required` / `NotRequired` を持つ `SearchQuery` TypedDict の利用例 |
|
|
38
|
+
| `inspect_hints()` | `get_type_hints()` で実行時にクラスメソッドの型情報を取得 |
|
|
39
|
+
|
|
40
|
+
### HTTP エンドポイント
|
|
41
|
+
|
|
42
|
+
| メソッド | パス | 概要 |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| POST | `/typing/notes` | TypedDict でノートを作成 |
|
|
45
|
+
| GET | `/typing/notes` | Literal `SortOrder` でノートをソート |
|
|
46
|
+
| POST | `/typing/notes/tags` | TypedDict 継承でタグを追加 |
|
|
47
|
+
| GET | `/typing/parse-id` | @overload — 文字列 → int 変換 |
|
|
48
|
+
| GET | `/typing/double` | @overload — 型別の倍返し |
|
|
49
|
+
| POST | `/typing/search` | Required/NotRequired を持つ SearchQuery |
|
|
50
|
+
| GET | `/typing/type-guard` | TypeGuard で型絞り込み |
|
|
51
|
+
| GET | `/typing/protocol` | Protocol + is_serializable() |
|
|
52
|
+
| POST | `/typing/close-resources` | Closeable Protocol でリソース管理 |
|
|
53
|
+
| GET | `/typing/hints` | get_type_hints() でクラスのヒント取得 |
|
|
54
|
+
| GET | `/typing/filter-status` | Literal[NoteStatus] でフィルタリング |
|
|
55
|
+
| GET | `/typing/response` | Literal[HttpStatusCode] でレスポンス生成 |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## テスト結果
|
|
60
|
+
|
|
61
|
+
**45 passed(摩擦ゼロ)**
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
45 passed in 0.86s
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 摩擦ポイント
|
|
70
|
+
|
|
71
|
+
**今回の FT では実装上の摩擦はゼロだった。**
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 観察点
|
|
76
|
+
|
|
77
|
+
### 観察1: `type` エイリアス構文(Python 3.12+)で `Literal` を名前付き型にできる
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
type SortOrder = Literal["asc", "desc"]
|
|
81
|
+
type NoteStatus = Literal["draft", "published", "archived"]
|
|
82
|
+
type HttpStatusCode = Literal[200, 201, 204, 400, 401, 403, 404, 422, 500]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Python 3.12 の `type` ステートメントを使うと、`Literal` に意味のある名前がつく。
|
|
86
|
+
`TypeAlias` アノテーション(旧形式)より明示的で mypy --strict に通る。
|
|
87
|
+
定数の列挙は Enum でなく Literal + type エイリアスが軽量な代替になる。
|
|
88
|
+
|
|
89
|
+
### 観察2: `TypedDict` + `NotRequired` で HTTP BodyModel の代替が作れる
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
class NoteCreateDict(TypedDict):
|
|
93
|
+
title: str
|
|
94
|
+
content: NotRequired[str] # 省略可能
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Pydantic BaseModel が必要な HTTP 境界では引き続き Pydantic を使うが、
|
|
98
|
+
UseCase 内部のデータ構造・関数の引数・レポジトリの返り値には
|
|
99
|
+
`TypedDict` が軽量で mypy に完全対応する。
|
|
100
|
+
`total=False` よりも `NotRequired` を使う方が、どのフィールドが省略可能かが明確。
|
|
101
|
+
|
|
102
|
+
### 観察3: `@runtime_checkable Protocol` で `isinstance()` による構造的型チェックが可能
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
@runtime_checkable
|
|
106
|
+
class Serializable(Protocol):
|
|
107
|
+
def to_dict(self) -> dict[str, object]: ...
|
|
108
|
+
|
|
109
|
+
note = InMemoryNote(1, "Test")
|
|
110
|
+
assert isinstance(note, Serializable) # True — クラス継承不要
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`InMemoryNote` は `Serializable` を継承していないが、`to_dict()` を持つため
|
|
114
|
+
`isinstance()` が `True` を返す。
|
|
115
|
+
nene2 のリポジトリパターンで「`to_dict()` を持つドメインオブジェクトなら何でも受け付ける」
|
|
116
|
+
関数を書くときに有効。ただし `@runtime_checkable` はメソッドの「存在」しか確認せず、
|
|
117
|
+
引数・戻り値型の一致は確認しない。
|
|
118
|
+
|
|
119
|
+
### 観察4: `@overload` で引数の型による戻り値型の分岐を型安全に表現できる
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
@overload
|
|
123
|
+
def parse_id(value: str) -> int | None: ...
|
|
124
|
+
@overload
|
|
125
|
+
def parse_id(value: int) -> int: ...
|
|
126
|
+
|
|
127
|
+
def parse_id(value: str | int) -> int | None:
|
|
128
|
+
...
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`parse_id(42)` のとき mypy は戻り値を `int`(`None` なし)と判断し、
|
|
132
|
+
`parse_id("foo")` のとき `int | None` と判断する。
|
|
133
|
+
`None` チェックを呼び出し側で毎回書く必要がなくなる箇所で効果的。
|
|
134
|
+
|
|
135
|
+
### 観察5: `TypeGuard` で `Any` 型の辞書を型安全に絞り込める
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
def is_note_dict(obj: object) -> TypeGuard[NoteDict]:
|
|
139
|
+
if not isinstance(obj, dict):
|
|
140
|
+
return False
|
|
141
|
+
return (
|
|
142
|
+
isinstance(obj.get("id"), int)
|
|
143
|
+
and isinstance(obj.get("title"), str)
|
|
144
|
+
and isinstance(obj.get("content"), str)
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def process_unknown(data: Any) -> str:
|
|
148
|
+
if is_note_dict(data):
|
|
149
|
+
return note_summary(data) # mypy はここで data を NoteDict として扱う
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
外部 JSON・DB 生クエリ結果など `Any` 型を安全に使う唯一の公式手段。
|
|
153
|
+
`cast()` は「信頼してキャスト」するだけだが、`TypeGuard` は実行時チェックと型絞り込みを両立する。
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## nene2-python フレームワークとの統合
|
|
158
|
+
|
|
159
|
+
- `TypedDict` は nene2 の UseCase Input/Output DTO の軽量代替として使える(Pydantic より軽い)
|
|
160
|
+
- `Protocol` は `RepositoryInterface` の ABC 代替として `NoteRepositoryProtocol` を定義するのに有効
|
|
161
|
+
- `Literal` は nene2 の `SortOrder` / `DB_ADAPTER` 等の設定値型に直接適用できる
|
|
162
|
+
- `TypeGuard` は `/notes` 一覧取得の外部データを型安全に絞り込む Use Case で使える
|
|
163
|
+
- CLAUDE.md の「`Any` 禁止 / `TypedDict` で Dict 構造を型付け」ポリシーと完全に整合
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Developer Experience (DX) Review
|
|
168
|
+
|
|
169
|
+
### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
|
|
170
|
+
|
|
171
|
+
`TypedDict` は `class` で辞書を型定義する概念が新しく、
|
|
172
|
+
「Pydantic の `BaseModel` と何が違うの?」という混乱が起きる。
|
|
173
|
+
|
|
174
|
+
**ドキュメント理解**: nene2 how-to に「HTTP 境界は Pydantic・UseCase 内部は TypedDict」の
|
|
175
|
+
使い分けガイドがあれば即解決する。現時点では CLAUDE.md を読み込まないと判断できない。
|
|
176
|
+
|
|
177
|
+
**事故リスク**: 低。`TypedDict` の誤用は実行時エラーにはならず、mypy が検出してくれる。
|
|
178
|
+
ただし mypy なしで開発すると TypedDict の型安全の恩恵がゼロになる。
|
|
179
|
+
|
|
180
|
+
**規約の使いやすさ**: `NoteDict(TypedDict)` のパターンはコピペで書ける。
|
|
181
|
+
`total=False` と `NotRequired` の使い分けは最初は迷う。
|
|
182
|
+
|
|
183
|
+
### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
|
|
184
|
+
|
|
185
|
+
`Protocol` を「インターフェース」として理解できるが、
|
|
186
|
+
`@runtime_checkable` の「struct subtyping = 継承不要」は最初は驚く。
|
|
187
|
+
「実行してみたら通った」という体験で理解が定着する。
|
|
188
|
+
|
|
189
|
+
**コピペ可能性**: 高。`@runtime_checkable class Xxx(Protocol)` はテンプレートとして使える。
|
|
190
|
+
|
|
191
|
+
**拡張時の罠**: `@runtime_checkable Protocol` はメソッドの引数・戻り値型を確認しない。
|
|
192
|
+
`to_dict() -> dict[str, object]` を `to_dict() -> str` に変えても `isinstance()` は `True` を返す。
|
|
193
|
+
実行時の型安全は Protocol の責務ではなく、mypy の責務であることを理解が必要。
|
|
194
|
+
|
|
195
|
+
**セキュリティ的な事故リスク**: 低。typing の誤用がセキュリティインシデントに直結するケースは稀。
|
|
196
|
+
|
|
197
|
+
### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
|
|
198
|
+
|
|
199
|
+
TypeScript の `interface` / `type` / `as const` との対応関係が作れる。
|
|
200
|
+
- TS の `interface` → Python の `TypedDict`(完全対応)
|
|
201
|
+
- TS の `as const` 配列 → Python の `Literal`(概念的に近い)
|
|
202
|
+
- TS の `function overloads` → Python の `@overload`(ほぼ同じ)
|
|
203
|
+
- TS の `type predicate` (`is`) → Python の `TypeGuard`(同等)
|
|
204
|
+
|
|
205
|
+
**エラーレスポンスの質**: 型ミスマッチは mypy が捕捉し、HTTP 境界では Pydantic が 422 を返す。
|
|
206
|
+
フロントエンド開発者にとって馴染みのある体験。
|
|
207
|
+
|
|
208
|
+
**事故リスク**: 低。TS の型システムの経験が直接活かせる。
|
|
209
|
+
|
|
210
|
+
### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
|
|
211
|
+
|
|
212
|
+
Django の `TypedDict` 活用は限定的(Django は dict よりモデルクラスを使う傾向)。
|
|
213
|
+
FastAPI ユーザーにとっては Pydantic v2 が `TypedDict` をスキーマとして受け付けるため、
|
|
214
|
+
境界が曖昧になりやすい。
|
|
215
|
+
|
|
216
|
+
**他フレームワークとの差異**:
|
|
217
|
+
- Django: ドメインは Model クラスで表現、typing との組み合わせは発展途上
|
|
218
|
+
- nene2: TypedDict(UseCase 内部)+ Pydantic(HTTP 境界)の明確な使い分けが nene2 の差別化
|
|
219
|
+
|
|
220
|
+
**本番投入可能性**: 問題なし。`@overload` / `TypeGuard` は FastAPI ルーターとも問題なく共存する。
|
|
221
|
+
|
|
222
|
+
### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
|
|
223
|
+
|
|
224
|
+
**コードレビューチェックポイント**:
|
|
225
|
+
- [ ] `TypedDict` フィールドに `NotRequired` を使って省略可能性を明示しているか(`total=False` は非推奨)
|
|
226
|
+
- [ ] `@runtime_checkable Protocol` を型安全の唯一の砦にしていないか(mypy でも確認必須)
|
|
227
|
+
- [ ] `@overload` の実装本体に型注釈が適切についているか(オーバーロードシグネチャのみ書いて本体を忘れるミス)
|
|
228
|
+
- [ ] `TypeGuard` 関数が実行時チェック(isinstance 等)を実施しているか(`return True` のみは危険)
|
|
229
|
+
- [ ] `cast()` には `# reason:` コメントがついているか(CLAUDE.md ポリシー)
|
|
230
|
+
|
|
231
|
+
**チームでの安全なパターン**: `TypedDict` は UseCase I/O の DTO として標準化し、
|
|
232
|
+
HTTP 境界は Pydantic、内部は TypedDict という二層構造を CLAUDE.md に明記する。
|
|
233
|
+
|
|
234
|
+
**ツール追加の必要性**: `mypy --strict` で `TypedDict` の型チェックが網羅されるため追加不要。
|
|
235
|
+
|
|
236
|
+
### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
|
|
237
|
+
|
|
238
|
+
**ポリシー達成度**: 高
|
|
239
|
+
|
|
240
|
+
**「初心者でも安全な API」達成度**: 高
|
|
241
|
+
- `TypedDict` + mypy の組み合わせで、実行前に型エラーが検出される
|
|
242
|
+
- `TypeGuard` で `Any` 由来のデータを安全に絞り込める
|
|
243
|
+
|
|
244
|
+
**設計上の負債・ドキュメント不足**:
|
|
245
|
+
- CLAUDE.md の「TypedDict で Dict 構造を型付け」ポリシーは記載あるが、Pydantic との使い分けが未記載
|
|
246
|
+
- nene2 の UseCase Input/Output は `dataclass(frozen=True)` で実装されているが、
|
|
247
|
+
`TypedDict` が適切な場面(JSON 応答の直接マッピング等)も存在する
|
|
248
|
+
|
|
249
|
+
**Follow-up Issue 候補**: `docs: TypedDict vs Pydantic vs dataclass の使い分けガイドを how-to に追加`
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Follow-up Issues
|
|
254
|
+
|
|
255
|
+
| 優先度 | タイトル | 種別 |
|
|
256
|
+
|---|---|---|
|
|
257
|
+
| 中 | `docs: TypedDict vs Pydantic vs dataclass の使い分けガイドを how-to に追加` | docs |
|
|
258
|
+
| 低 | `feat: NoteRepositoryProtocol を TypedDict + Protocol で再実装するサンプルを追加` | feat |
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## まとめ
|
|
263
|
+
|
|
264
|
+
`typing` モジュールは nene2-python の型安全設計の根幹をなす機能群。
|
|
265
|
+
45 テスト全通過、摩擦ゼロ。
|
|
266
|
+
|
|
267
|
+
`TypedDict` + `NotRequired` は UseCase 内部データ構造の軽量 DTO として直接使える。
|
|
268
|
+
`Protocol` + `@runtime_checkable` は nene2 の `RepositoryInterface` の ABC 代替として有効で、
|
|
269
|
+
継承なしの構造的サブタイピングを実現する。
|
|
270
|
+
`TypeGuard` は `Any` 型データを型安全に絞り込む唯一の公式手段で、外部データ処理に必須。
|
|
271
|
+
Python 3.12 の `type X = Literal[...]` 構文で定数列挙が軽量に書ける。
|
|
272
|
+
|
|
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.39 → nene2_python-1.8.40}/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
|