nene2-python 1.8.62__tar.gz → 1.8.64__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.62 → nene2_python-1.8.64}/PKG-INFO +1 -1
- nene2_python-1.8.64/docs/field-trials/2026-05-field-trial-191.md +214 -0
- nene2_python-1.8.64/docs/field-trials/2026-05-field-trial-192.md +485 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/INDEX.md +4 -2
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/todo/current.md +7 -5
- {nene2_python-1.8.62 → nene2_python-1.8.64}/pyproject.toml +1 -1
- {nene2_python-1.8.62 → nene2_python-1.8.64}/.env.example +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/.gitignore +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/AGENTS.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/CHANGELOG.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/CLAUDE.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/Dockerfile +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/LICENSE +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/README.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic/README +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic/env.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic.ini +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/compose.yaml +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/de/index.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-100.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-101.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-102.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-103.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-104.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-105.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-106.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-107.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-108.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-109.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-110.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-111.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-112.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-113.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-114.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-115.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-116.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-117.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-118.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-119.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-120.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-121.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-122.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-123.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-124.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-125.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-126.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-127.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-128.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-129.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-130.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-131.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-132.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-133.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-134.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-135.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-136.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-137.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-138.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-139.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-140.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-141.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-142.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-143.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-144.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-145.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-146.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-147.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-148.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-149.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-150.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-151.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-152.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-153.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-154.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-155.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-156.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-157.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-158.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-159.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-160.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-161.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-162.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-163.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-164.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-165.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-166.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-167.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-168.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-169.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-170.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-171.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-172.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-173.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-174.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-175.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-176.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-177.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-178.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-179.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-180.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-181.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-182.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-183.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-184.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-185.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-186.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-187.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-188.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-189.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-190.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-68.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-69.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-70.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-71.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-72.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-73.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-74.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-75.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-76.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-77.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-78.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-79.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-80.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-81.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-82.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-83.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-84.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-85.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-86.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-87.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-88.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-89.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-90.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-91.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-92.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-93.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-94.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-95.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-96.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-97.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-98.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-99.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/fr/index.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/api-versioning.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/background-tasks.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/cors.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/custom-auth-middleware.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/decimal-unicode-input.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/dependency-injection.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/domain-events.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/email-address-parsing.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/file-upload.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/lifespan-and-app-state.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/middleware-stack.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/response-patterns.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/soft-delete.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/streaming.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/structured-logging.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/webhook.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/index.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/index.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/reference/api.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/roadmap.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/templates/field-trial-report.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/zh/index.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/package-lock.json +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/package.json +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/__main__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/app.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/mcp.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/schema.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/deps.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/cache/ttl.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/etag.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/setup.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/security/webhook.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/conftest.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/test_make_require_auth.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/cache/test_ttl.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/test_etag.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/mcp/test_server.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/security/test_webhook.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/scripts/test_export_openapi.py +0 -0
- {nene2_python-1.8.62 → nene2_python-1.8.64}/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.64
|
|
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,214 @@
|
|
|
1
|
+
# FT191: concurrent.futures モジュール
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-21
|
|
4
|
+
**テーマ**: ThreadPoolExecutor / ProcessPoolExecutor / Future — 高レベル並行処理 API
|
|
5
|
+
**セキュリティ診断**: なし(191 % 3 = 2)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 概要
|
|
10
|
+
|
|
11
|
+
`concurrent.futures` は threading と multiprocessing の上に薄い高レベル API を提供する stdlib モジュール。`ThreadPoolExecutor` / `ProcessPoolExecutor` を同一インターフェースで操作でき、`Future` オブジェクトで非同期タスクを管理できる。
|
|
12
|
+
|
|
13
|
+
本 FT では `.map()` / `.submit()` / `as_completed()` / `wait()` / タイムアウト / キャンセル / エラーハンドリングを FastAPI エンドポイントから検証する。FT188(threading)・FT189(subprocess)・FT190(multiprocessing)の高レベル抽象として位置付ける。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装したサンプルアプリ
|
|
18
|
+
|
|
19
|
+
**場所**: `/home/xi/docker/nene2-python-FT/ft191-concurrent-futures/`
|
|
20
|
+
|
|
21
|
+
### 主要機能
|
|
22
|
+
|
|
23
|
+
| 関数/クラス | 概要 |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `thread_pool_map(values, workers)` | ThreadPoolExecutor.map で並列二乗 |
|
|
26
|
+
| `thread_pool_submit(values, workers)` | ThreadPoolExecutor.submit で Future 取得 |
|
|
27
|
+
| `thread_as_completed(values, workers)` | as_completed で完了順に収集 |
|
|
28
|
+
| `thread_wait_all_completed(values, workers)` | wait(ALL_COMPLETED) |
|
|
29
|
+
| `thread_wait_first_completed(values, workers)` | wait(FIRST_COMPLETED) |
|
|
30
|
+
| `thread_wait_first_exception(values, workers)` | wait(FIRST_EXCEPTION) |
|
|
31
|
+
| `batch_with_error_handling(values, workers)` | 例外を握りつぶさず成功/失敗分類 |
|
|
32
|
+
| `process_pool_map(values, workers)` | ProcessPoolExecutor.map で CPU バウンド |
|
|
33
|
+
| `process_pool_submit(values, workers)` | ProcessPoolExecutor.submit |
|
|
34
|
+
| `process_as_completed(values, workers)` | ProcessPoolExecutor + as_completed |
|
|
35
|
+
| `submit_with_timeout(seconds, timeout)` | タイムアウト付き Future |
|
|
36
|
+
| `submit_and_cancel(values)` | キャンセル試行 |
|
|
37
|
+
| `thread_map_with_chunksize(values, chunksize, workers)` | チャンクサイズ指定 map |
|
|
38
|
+
|
|
39
|
+
### HTTP エンドポイント
|
|
40
|
+
|
|
41
|
+
| メソッド | パス | 概要 |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| POST | `/futures/thread-map` | ThreadPoolExecutor.map |
|
|
44
|
+
| POST | `/futures/thread-submit` | submit で Future 取得 |
|
|
45
|
+
| POST | `/futures/thread-as-completed` | as_completed |
|
|
46
|
+
| POST | `/futures/thread-wait-all` | wait(ALL_COMPLETED) |
|
|
47
|
+
| POST | `/futures/thread-wait-first` | wait(FIRST_COMPLETED) |
|
|
48
|
+
| POST | `/futures/thread-wait-exception` | wait(FIRST_EXCEPTION) |
|
|
49
|
+
| POST | `/futures/batch-errors` | エラーハンドリング付きバッチ |
|
|
50
|
+
| POST | `/futures/process-map` | ProcessPoolExecutor.map |
|
|
51
|
+
| POST | `/futures/process-submit` | ProcessPoolExecutor.submit |
|
|
52
|
+
| POST | `/futures/process-as-completed` | ProcessPoolExecutor + as_completed |
|
|
53
|
+
| POST | `/futures/timeout` | タイムアウト付き Future |
|
|
54
|
+
| POST | `/futures/chunksize` | チャンクサイズ指定 |
|
|
55
|
+
| POST | `/futures/cancel` | キャンセル試行 |
|
|
56
|
+
| GET | `/futures/info` | 実行環境情報 |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## テスト結果
|
|
61
|
+
|
|
62
|
+
**51 passed**
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
51 passed in 10.86s
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 摩擦ポイント
|
|
71
|
+
|
|
72
|
+
### F-1: `submit_and_cancel` の戻り値型 `dict[str, int]` が `dict[str, object]` に非互換(深刻度: 低)
|
|
73
|
+
|
|
74
|
+
**事象**: `submit_and_cancel()` が `dict[str, int]` を返し、エンドポイントの戻り値型 `dict[str, object]` に対して mypy --strict が `Incompatible return value type` エラーを出した。
|
|
75
|
+
|
|
76
|
+
**原因**: mypy では `dict[str, int]` は `dict[str, object]` の部分型でない(`dict` は invariant)。TS の `Record<string, number>` を `Record<string, unknown>` に代入できないのと同じ理屈。
|
|
77
|
+
|
|
78
|
+
**対応**: `submit_and_cancel` の戻り値型を `dict[str, object]` に変更。戻り値の幅を広げても型安全性は失われない(返す値はすべて `int`)。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 観察点
|
|
83
|
+
|
|
84
|
+
### 観察1: ThreadPoolExecutor vs ProcessPoolExecutor の使い分け
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# I/O バウンド → ThreadPoolExecutor(GIL 解放待ちの間に他スレッドが走る)
|
|
88
|
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
89
|
+
results = list(executor.map(fetch_url, urls))
|
|
90
|
+
|
|
91
|
+
# CPU バウンド → ProcessPoolExecutor(GIL を完全に回避)
|
|
92
|
+
with ProcessPoolExecutor(max_workers=4) as executor:
|
|
93
|
+
results = list(executor.map(heavy_compute, values))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
threading と multiprocessing の低レベル API と同一の選択基準だが、インターフェースが統一されているため交換が容易。
|
|
97
|
+
|
|
98
|
+
### 観察2: as_completed vs map の選択
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
# map: 送信順で結果が返る(遅いタスクがブロック)
|
|
102
|
+
results = list(executor.map(func, values))
|
|
103
|
+
|
|
104
|
+
# as_completed: 完了順で返る(高速タスクの結果を先に処理可能)
|
|
105
|
+
for future in as_completed(futures):
|
|
106
|
+
result = future.result()
|
|
107
|
+
process_early(result)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
HTTP API でストリームレスポンスを返す場合や、部分結果を早期返却する設計では `as_completed` が有利。
|
|
111
|
+
|
|
112
|
+
### 観察3: wait() の return_when フラグ
|
|
113
|
+
|
|
114
|
+
| フラグ | 用途 |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `ALL_COMPLETED` | 全タスク完了を待つ(デフォルト) |
|
|
117
|
+
| `FIRST_COMPLETED` | 最初のタスクが終わったら戻る |
|
|
118
|
+
| `FIRST_EXCEPTION` | 最初の例外発生で戻る(残タスクはキャンセルしない) |
|
|
119
|
+
|
|
120
|
+
`FIRST_EXCEPTION` は例外をすぐ検知したいが残タスクは並行継続したい場合に使う。
|
|
121
|
+
|
|
122
|
+
### 観察4: Future.cancel() の制約
|
|
123
|
+
|
|
124
|
+
`cancel()` はタスクが**まだ実行開始されていない**場合のみ成功する。既に実行中のタスクはキャンセルできない(Python の Future はキャンセル可能 Flag のみで、OS レベルのプロセス終了は行わない)。max_workers=1 でタスクを大量投入した場合のみキャンセルが効果的。
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## nene2-python フレームワークとの統合
|
|
129
|
+
|
|
130
|
+
- `ThreadPoolExecutor` は I/O バウンド UseCase(外部 API 並列呼び出し等)に適用可能。ただし FastAPI はデフォルト非同期(asyncio)であり、重い I/O は `httpx.AsyncClient` での `asyncio.gather` が自然な選択
|
|
131
|
+
- `ProcessPoolExecutor` は CPU バウンド変換処理(画像変換・暗号化・データ集計)を同期 UseCase として切り出す際に使う
|
|
132
|
+
- `max_workers` の上限制限は DoS 防止のために必須。`min(workers, MAX_WORKERS)` パターンを全関数で適用
|
|
133
|
+
- ProcessPoolExecutor のワーカー関数も multiprocessing と同様 pickle 可能なモジュールレベル関数に限定される(FT190 F-1 と同じ制約)
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Developer Experience (DX) Review
|
|
138
|
+
|
|
139
|
+
### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
|
|
140
|
+
|
|
141
|
+
threading/multiprocessing を学んだ後、高レベル API として concurrent.futures を使おうとしている段階。
|
|
142
|
+
|
|
143
|
+
**ドキュメント理解**: `with ThreadPoolExecutor() as executor: executor.map(func, data)` のパターンは直感的で理解しやすい。`as_completed`・`wait` は公式ドキュメントの例が豊富で困らない。`FIRST_EXCEPTION` フラグの意味は名前から推測できる。
|
|
144
|
+
**事故リスク**: 低。エラーハンドリングを省略すると `future.result()` で例外が再 raise されるため、未処理の例外は実行時に気づける。
|
|
145
|
+
**規約の使いやすさ**: `with executor:` の `with` ブロックは必須習慣で、抜け漏れ時は executor が自動終了するため安全。
|
|
146
|
+
|
|
147
|
+
### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
|
|
148
|
+
|
|
149
|
+
既存スクリプトの `for` ループを並列化したくてコピーして使うスタイル。
|
|
150
|
+
|
|
151
|
+
**コピペ可能性**: `executor.map(func, data)` のサンプルはそのままコピーして動く。`as_completed` のパターンも明確。
|
|
152
|
+
**拡張時の罠**: ProcessPoolExecutor でラムダを渡すと PicklingError(FT190 F-1 の再現)。threading でも同じコードで動くため気づきにくい。
|
|
153
|
+
**セキュリティ的な事故リスク**: 中。`max_workers` に上限がないと DoS につながる。本実装では `MAX_WORKERS = 8` で制限。
|
|
154
|
+
|
|
155
|
+
### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
|
|
156
|
+
|
|
157
|
+
JavaScript の `Promise.all` / `Promise.race` との比較で理解しようとしている段階。
|
|
158
|
+
|
|
159
|
+
**エラーレスポンスの質**: `batch_with_error_handling` パターンで成功/失敗を分けて返すと、クライアントが部分成功を処理しやすい。422 バリデーションエラーは自動返却される。
|
|
160
|
+
**Python 固有概念の学習コスト**: `Future` は JS の `Promise` に近い。`as_completed` は `Promise.race` の複数解決版として理解できる。`wait(FIRST_COMPLETED)` が `Promise.race`、`wait(ALL_COMPLETED)` が `Promise.all` に相当する。
|
|
161
|
+
**事故リスク**: 低。HTTP 入力のバリデーションが Pydantic で保護されている。
|
|
162
|
+
|
|
163
|
+
### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
|
|
164
|
+
|
|
165
|
+
asyncio vs concurrent.futures の使い分けを判断する立場。
|
|
166
|
+
|
|
167
|
+
**他フレームワークとの差異**: FastAPI は async/await が基本なので、I/O 並列は `asyncio.gather` が自然な選択。`concurrent.futures` は CPU バウンドと、非同期対応していないレガシーライブラリの同期 I/O をスレッドプールで包む用途に限定される。`loop.run_in_executor()` で asyncio と統合できる。
|
|
168
|
+
**nene2-python の薄さへの評価**: UseCase 層が HTTP 非依存なので、`ThreadPoolExecutor` を UseCase 内で直接使う設計も許容される。ただし `asyncio` 移行を前提とする場合は技術的負債になりやすい。
|
|
169
|
+
**本番投入可能性**: チームが asyncio に慣れているなら concurrent.futures は補助的な役割に留めるべき。混在するとコードの可読性が下がる。
|
|
170
|
+
|
|
171
|
+
### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
|
|
172
|
+
|
|
173
|
+
コードレビューで concurrent.futures の誤用を検出する立場。
|
|
174
|
+
|
|
175
|
+
**コードレビューチェックポイント**:
|
|
176
|
+
- [ ] `max_workers` に上限制限があるか(無制限はリソース枯渇)
|
|
177
|
+
- [ ] `with executor:` のコンテキストマネージャーを使っているか(`executor.shutdown()` の漏れ防止)
|
|
178
|
+
- [ ] ProcessPoolExecutor のワーカー関数がモジュールレベルか(PicklingError 防止)
|
|
179
|
+
- [ ] `future.result()` の例外ハンドリングが書かれているか(未処理は実行時エラーが伝播する)
|
|
180
|
+
- [ ] タイムアウトが指定されているか(`future.result(timeout=N)` や `wait(timeout=N)`)
|
|
181
|
+
|
|
182
|
+
**チームでの安全な共有パターン**: ワーカー関数を `_workers.py` に分離する規則を設けると、pickle 可能性と単体テスト可能性が高まる。
|
|
183
|
+
**ツール追加の必要性**: なし(ruff には concurrent.futures 固有の追加ルールはない)。
|
|
184
|
+
|
|
185
|
+
### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
|
|
186
|
+
|
|
187
|
+
CLAUDE.md ポリシーとの整合性を確認する。
|
|
188
|
+
|
|
189
|
+
**ポリシー達成度**: 高
|
|
190
|
+
**「初心者でも安全な API」達成度**: 高(`with executor:` がリソースリーク防止を保証、HTTP 境界の Pydantic バリデーションで DoS 制限)
|
|
191
|
+
**設計上の負債・ドキュメント不足**: `asyncio` と concurrent.futures の使い分けガイドが CLAUDE.md に不足。FastAPI アプリでは asyncio が優先される旨を追記する価値がある。
|
|
192
|
+
**Follow-up Issues**: なし(即時対応済み)
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Follow-up Issues
|
|
197
|
+
|
|
198
|
+
### 即時対応済み
|
|
199
|
+
|
|
200
|
+
| 対応内容 | 対応方法 |
|
|
201
|
+
|---|---|
|
|
202
|
+
| `dict[str, int]` を `dict[str, object]` に変更(F-1) | `submit_and_cancel` の戻り値型を修正 |
|
|
203
|
+
|
|
204
|
+
### 新規 Issue
|
|
205
|
+
|
|
206
|
+
なし(セキュリティ診断なし、全問題は即時解決済み)
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## まとめ
|
|
211
|
+
|
|
212
|
+
concurrent.futures の主要パターン(ThreadPoolExecutor / ProcessPoolExecutor・submit / map / as_completed / wait・タイムアウト・キャンセル・エラーハンドリング)を 14 エンドポイント・51 テストで検証した。FT191 固有の発見は 1 点: `dict[str, int]` → `dict[str, object]` の invariant 問題(mypy --strict 即時検出)。
|
|
213
|
+
|
|
214
|
+
threading(FT188)・multiprocessing(FT190)の高レベル API として concurrent.futures は使いやすく、処理系(スレッド/プロセス)の交換コストが低い。FastAPI + asyncio 環境では補助的な位置付けになるが、CPU バウンド処理のオフロードには有効。
|