nene2-python 1.8.57__tar.gz → 1.8.59__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.57 → nene2_python-1.8.59}/PKG-INFO +1 -1
- nene2_python-1.8.59/docs/field-trials/2026-05-field-trial-187.md +261 -0
- nene2_python-1.8.59/docs/field-trials/2026-05-field-trial-188.md +434 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/INDEX.md +5 -3
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/todo/current.md +7 -5
- {nene2_python-1.8.57 → nene2_python-1.8.59}/pyproject.toml +1 -1
- {nene2_python-1.8.57 → nene2_python-1.8.59}/uv.lock +44 -1
- {nene2_python-1.8.57 → nene2_python-1.8.59}/.env.example +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/.gitignore +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/AGENTS.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/CHANGELOG.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/CLAUDE.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/Dockerfile +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/LICENSE +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/README.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/alembic/README +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/alembic/env.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/alembic.ini +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/compose.yaml +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/de/index.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-100.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-101.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-102.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-103.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-104.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-105.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-106.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-107.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-108.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-109.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-110.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-111.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-112.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-113.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-114.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-115.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-116.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-117.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-118.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-119.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-120.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-121.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-122.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-123.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-124.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-125.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-126.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-127.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-128.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-129.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-130.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-131.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-132.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-133.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-134.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-135.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-136.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-137.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-138.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-139.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-140.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-141.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-142.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-143.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-144.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-145.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-146.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-147.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-148.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-149.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-150.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-151.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-152.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-153.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-154.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-155.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-156.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-157.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-158.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-159.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-160.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-161.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-162.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-163.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-164.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-165.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-166.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-167.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-168.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-169.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-170.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-171.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-172.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-173.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-174.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-175.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-176.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-177.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-178.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-179.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-180.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-181.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-182.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-183.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-184.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-185.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-186.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-68.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-69.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-70.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-71.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-72.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-73.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-74.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-75.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-76.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-77.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-78.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-79.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-80.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-81.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-82.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-83.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-84.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-85.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-86.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-87.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-88.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-89.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-90.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-91.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-92.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-93.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-94.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-95.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-96.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-97.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-98.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/field-trials/2026-05-field-trial-99.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/fr/index.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/api-versioning.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/background-tasks.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/cors.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/custom-auth-middleware.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/dependency-injection.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/domain-events.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/file-upload.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/lifespan-and-app-state.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/middleware-stack.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/response-patterns.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/soft-delete.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/streaming.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/structured-logging.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/how-to/webhook.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/index.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/index.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/reference/api.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/roadmap.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/templates/field-trial-report.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/zh/index.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/package-lock.json +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/package.json +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/__main__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/app.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/mcp.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/schema.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/auth/deps.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/cache/ttl.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/http/etag.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/setup.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/security/webhook.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/conftest.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/auth/test_make_require_auth.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/cache/test_ttl.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/http/test_etag.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/mcp/test_server.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/security/test_webhook.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.57 → nene2_python-1.8.59}/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.59
|
|
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,261 @@
|
|
|
1
|
+
# FT187: collections
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-21
|
|
4
|
+
**テーマ**: collections モジュール — Counter・defaultdict・deque・ChainMap・NamedTuple・OrderedDict
|
|
5
|
+
**セキュリティ診断**: なし(187 % 3 = 1)
|
|
6
|
+
**クラッカーペンテスト**: なし(187 % 4 = 3)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 概要
|
|
11
|
+
|
|
12
|
+
Python 標準ライブラリ `collections` は汎用コンテナ型の拡張集である。
|
|
13
|
+
`Counter`(頻度カウント)、`defaultdict`(デフォルト値付き辞書)、`deque`(両端キュー)、`ChainMap`(複数辞書のビュー)、`NamedTuple`(型付き名前付きタプル)、`OrderedDict`(挿入順序保持辞書)の主要 6 型を検証した。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装したサンプルアプリ
|
|
18
|
+
|
|
19
|
+
**場所**: `/home/xi/docker/nene2-python-FT/ft187-collections/`
|
|
20
|
+
|
|
21
|
+
### 主要機能
|
|
22
|
+
|
|
23
|
+
| 関数/クラス | 概要 |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `word_frequency(text)` | Counter で単語頻度を集計 |
|
|
26
|
+
| `top_n_words(text, n)` | `most_common(n)` で上位 N 単語を取得 |
|
|
27
|
+
| `character_frequency(text)` | 文字頻度を集計(空白除外)|
|
|
28
|
+
| `merge_counters(a, b)` | Counter の `+` 演算子でマージ |
|
|
29
|
+
| `subtract_counters(a, b)` | Counter の `-` 演算子で差分(正の値のみ)|
|
|
30
|
+
| `group_by_length(words)` | defaultdict でワード長別グループ化 |
|
|
31
|
+
| `build_inverted_index(docs)` | defaultdict で転置インデックス構築 |
|
|
32
|
+
| `count_nested(items, sep)` | ネスト defaultdict でカテゴリ別集計 |
|
|
33
|
+
| `BoundedHistory` | `deque(maxlen=N)` でサイズ制限付き履歴 |
|
|
34
|
+
| `sliding_window_average(values, window)` | deque + maxlen でスライディング平均 |
|
|
35
|
+
| `rotate_list(items, steps)` | `deque.rotate()` でリストをローテーション |
|
|
36
|
+
| `resolve_config(*layers)` | ChainMap で設定レイヤーを優先順解決 |
|
|
37
|
+
| `get_with_override(base, override, key)` | ChainMap で override 優先の値取得 |
|
|
38
|
+
| `Coordinate` | `typing.NamedTuple` で型付き座標 |
|
|
39
|
+
| `deduplicate_preserving_order(items)` | OrderedDict で順序保持重複除去 |
|
|
40
|
+
| `LruDict` | OrderedDict を使った LRU キャッシュ |
|
|
41
|
+
|
|
42
|
+
### HTTP エンドポイント
|
|
43
|
+
|
|
44
|
+
| メソッド | パス | 概要 |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| POST | `/counter/words` | 単語頻度集計 |
|
|
47
|
+
| POST | `/counter/top` | 上位 N 単語 |
|
|
48
|
+
| POST | `/counter/chars` | 文字頻度集計 |
|
|
49
|
+
| POST | `/counter/merge` | カウンターマージ |
|
|
50
|
+
| POST | `/counter/subtract` | カウンター差分 |
|
|
51
|
+
| POST | `/defaultdict/group` | 長さ別グループ化 |
|
|
52
|
+
| POST | `/defaultdict/index` | 転置インデックス |
|
|
53
|
+
| POST | `/defaultdict/nested` | ネストカウント |
|
|
54
|
+
| POST | `/deque/sliding-window` | スライディング平均 |
|
|
55
|
+
| POST | `/deque/rotate` | リストローテーション |
|
|
56
|
+
| POST | `/chainmap/resolve` | 設定レイヤー解決 |
|
|
57
|
+
| POST | `/ordereddict/deduplicate` | 順序保持重複除去 |
|
|
58
|
+
| POST | `/namedtuple/coordinates` | 座標パース |
|
|
59
|
+
| POST | `/ordereddict/lru` | LRU キャッシュ操作 |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## テスト結果
|
|
64
|
+
|
|
65
|
+
**56 passed**
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
56 passed in 0.35s
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
mypy --strict: Success
|
|
72
|
+
ruff check: All checks passed
|
|
73
|
+
pip-audit: PYSEC-2025-183 (PyJWT via mcp transitive dep — 許容済み)
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 摩擦ポイント
|
|
78
|
+
|
|
79
|
+
### F-1: `parse_coordinates` の引数型に `dict[str, float | str]` を使うと mypy エラー(深刻度: 低)
|
|
80
|
+
|
|
81
|
+
**事象**: `parse_coordinates(raw: list[dict[str, float | str]])` を定義し、呼び出し側で `[{"latitude": c.latitude, ...}]` を渡したところ、mypy が `Argument 1 has incompatible type "list[dict[str, object]]"` エラーを出した。辞書リテラルの型が `dict[str, object]` として推論されるため。
|
|
82
|
+
|
|
83
|
+
**原因**: Python の辞書リテラル `{"latitude": 35.0, "label": "A"}` は `dict[str, float | str]` ではなく `dict[str, object]` として推論される(値の型が異なる場合)。
|
|
84
|
+
|
|
85
|
+
**対応**: 入力専用のデータクラス `RawCoordinate(dataclass(frozen=True, slots=True))` を定義して型安全な引数にした。`dict` を渡す代わりに `RawCoordinate` オブジェクトを渡す。
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
@dataclass(frozen=True, slots=True)
|
|
89
|
+
class RawCoordinate:
|
|
90
|
+
latitude: float
|
|
91
|
+
longitude: float
|
|
92
|
+
label: str = ""
|
|
93
|
+
|
|
94
|
+
def parse_coordinates(raw: list[RawCoordinate]) -> list[Coordinate]:
|
|
95
|
+
return [Coordinate(latitude=r.latitude, ...) for r in raw]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 観察点
|
|
101
|
+
|
|
102
|
+
### 観察1: Counter の算術演算子
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
a = Counter({"x": 5, "y": 2})
|
|
106
|
+
b = Counter({"x": 3, "y": 4})
|
|
107
|
+
|
|
108
|
+
a + b # {"x": 8, "y": 6} — 合計
|
|
109
|
+
a - b # {"x": 2} — 差分(正の値のみ残る)
|
|
110
|
+
a & b # {"x": 3, "y": 2} — 最小値(intersection)
|
|
111
|
+
a | b # {"x": 5, "y": 4} — 最大値(union)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`Counter` は `dict` のサブクラスで算術演算子が使えるため、集合演算的な使い方ができる。`-` の結果は正の値のみ(負になったキーは除外)になる点が直感と異なる場合がある。
|
|
115
|
+
|
|
116
|
+
### 観察2: defaultdict のネスト — `lambda` を使ったデフォルト値
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
# ネストした defaultdict
|
|
120
|
+
result: defaultdict[str, defaultdict[str, int]] = \
|
|
121
|
+
defaultdict(lambda: defaultdict(int))
|
|
122
|
+
|
|
123
|
+
# 使用例
|
|
124
|
+
result["fruit"]["apple"] += 1
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`lambda: defaultdict(int)` でネストした自動生成が可能。ただし `lambda` の型は mypy で推論が難しいため、明示的な型注釈が必要になる場合がある。
|
|
128
|
+
|
|
129
|
+
### 観察3: deque の `maxlen` による自動エビクション
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
dq = deque(maxlen=3)
|
|
133
|
+
dq.append(1) # [1]
|
|
134
|
+
dq.append(2) # [1, 2]
|
|
135
|
+
dq.append(3) # [1, 2, 3]
|
|
136
|
+
dq.append(4) # [2, 3, 4] — 左端の 1 が自動除去
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`deque(maxlen=N)` は満杯時に反対側の要素を自動除去する。スライディングウィンドウ・LRU 的な「最近 N 件だけ保持」のユースケースに適している。`appendleft` を使うと最新が先頭になる(`BoundedHistory` パターン)。
|
|
140
|
+
|
|
141
|
+
### 観察4: ChainMap の「先頭優先」セマンティクス
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
chain = ChainMap(override, base)
|
|
145
|
+
chain["key"] # override の値を優先(なければ base を参照)
|
|
146
|
+
chain["new"] = "value" # 先頭の override に追加(base は変更されない)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`ChainMap` は辞書のコピーを作らずビューを提供する。環境変数 → 設定ファイル → デフォルト値という優先順位を持つ設定解決に適している。`new_child()` で新しいスコープを作成することも可能。
|
|
150
|
+
|
|
151
|
+
### 観察5: `typing.NamedTuple` vs `collections.namedtuple`
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
# collections.namedtuple — フィールド名のみ、型なし
|
|
155
|
+
Point = namedtuple("Point", ["x", "y"])
|
|
156
|
+
|
|
157
|
+
# typing.NamedTuple — 型付きフィールド、デフォルト値、メソッド定義可
|
|
158
|
+
class Coordinate(NamedTuple):
|
|
159
|
+
latitude: float
|
|
160
|
+
longitude: float
|
|
161
|
+
label: str = ""
|
|
162
|
+
def distance_to(self, other: "Coordinate") -> float: ...
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`typing.NamedTuple` は型安全でデフォルト値・メソッドを持てるため、`dataclass` の代替として不変の小さな値オブジェクトに使える。ただし `frozen=True` の `dataclass` と異なり、継承で問題が生じやすいため、単純な値型に限定するのが無難。
|
|
166
|
+
|
|
167
|
+
### 観察6: OrderedDict を使った LRU キャッシュ
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
cache = OrderedDict()
|
|
171
|
+
|
|
172
|
+
def get(key):
|
|
173
|
+
cache.move_to_end(key) # アクセスで MRU 端へ移動
|
|
174
|
+
return cache[key]
|
|
175
|
+
|
|
176
|
+
def put(key, value):
|
|
177
|
+
if len(cache) >= capacity:
|
|
178
|
+
cache.popitem(last=False) # LRU 端(先頭)から除去
|
|
179
|
+
cache[key] = value
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Python 3.2+ の `dict` は挿入順序保持が保証されているが、`OrderedDict` は `move_to_end()` と `popitem(last=False/True)` を持ち、LRU 実装が簡潔に書ける。
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Follow-up Issues
|
|
187
|
+
|
|
188
|
+
今回の FT では実装上の重大な摩擦はなかった。F-1 は mypy --strict での型推論の限界によるものであり、`dataclass` を入力型として導入することで解決した。
|
|
189
|
+
|
|
190
|
+
GitHub Issues: なし
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## DX Review — 6ペルソナ
|
|
195
|
+
|
|
196
|
+
### 1. 初心者(Python 歴1年・独学中・女性・バックエンド志望)
|
|
197
|
+
|
|
198
|
+
`Counter` は「リストの頻度を数える」という非常に頻繁に必要とされる操作をワンライナーで実現できる。`most_common(n)` の使いやすさは特に印象的で、手書きのループより明快。
|
|
199
|
+
|
|
200
|
+
**ドキュメント理解**: `defaultdict` の「キーが存在しなければデフォルト値を自動生成する」という動作は、通常の辞書で `KeyError` に何度もぶつかった後に習得するパターン。デモのネスト例は実用的で理解しやすい。
|
|
201
|
+
|
|
202
|
+
**事故リスク**: 低 — ただし `Counter` の `-` が「負値を除外する」挙動を知らないと驚く。テストで明示したことで習得しやすい。
|
|
203
|
+
|
|
204
|
+
**規約の使いやすさ**: `LruDict` のパターン(`move_to_end` + `popitem`)は初心者には高度だが、「なぜ OrderedDict を使うのか」が実例で見えると理解が進む。
|
|
205
|
+
|
|
206
|
+
### 2. ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
|
|
207
|
+
|
|
208
|
+
`Counter.most_common()` はログ解析・アクセス集計でそのまま使えるため実務価値が高い。`defaultdict(list)` による転置インデックスも業務で頻繁に必要になる。
|
|
209
|
+
|
|
210
|
+
**コピペ可能性**: `BoundedHistory`(deque + maxlen)・`LruDict`・`resolve_config`(ChainMap)はそのまま流用できるユーティリティ。
|
|
211
|
+
|
|
212
|
+
**拡張時の罠**: `ChainMap` の `chain["key"] = value` は先頭辞書のみ変更する。「両方の辞書を更新したい」場合は `ChainMap` は不適切。
|
|
213
|
+
|
|
214
|
+
**事故リスク**: 低
|
|
215
|
+
|
|
216
|
+
### 3. フロントエンド寄り(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
|
|
217
|
+
|
|
218
|
+
TypeScript では `Map<string, number>` や `Record<string, number>` で表現するものが Python では `Counter` / `defaultdict` に対応する。`ChainMap` は TS にない概念だが、React の Context や CSS カスケードと近いセマンティクスで理解できる。
|
|
219
|
+
|
|
220
|
+
**エラーレスポンスの質**: `/deque/sliding-window` の `window=0` に対する 422 バリデーションエラーは適切。FastAPI の `Field(ge=1)` が自動で機能している。
|
|
221
|
+
|
|
222
|
+
**Python 固有概念の学習コスト**: `NamedTuple` は TypeScript のインターフェースに近い。デフォルト値・メソッド定義できる点は TS の interface と同じ感覚で理解できる。
|
|
223
|
+
|
|
224
|
+
**事故リスク**: 低
|
|
225
|
+
|
|
226
|
+
### 4. バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
|
|
227
|
+
|
|
228
|
+
`Counter` の算術演算子や `defaultdict` のネストは Django の `annotate()` / `aggregate()` では難しいデータ変換を純粋 Python で行う際に重宝する。`ChainMap` は Django の設定(`DJANGO_SETTINGS_MODULE` → デフォルト設定)と類似のパターン。
|
|
229
|
+
|
|
230
|
+
**他フレームワークとの差異**: `LruDict` は `functools.lru_cache` と比べて「任意の引数に対応する柔軟性」と「キャッシュの明示的管理」がメリット。
|
|
231
|
+
|
|
232
|
+
**nene2 の薄さへの評価**: `collections` は nene2 フレームワークと独立しているため、ドメインロジック層の実装にそのまま使えるコレクション群として評価が高い。
|
|
233
|
+
|
|
234
|
+
**事故リスク**: 低
|
|
235
|
+
|
|
236
|
+
### 5. シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
|
|
237
|
+
|
|
238
|
+
**コードレビューチェックポイント**:
|
|
239
|
+
- `defaultdict` が「サービス境界を超える」ケースに注意(外部に返す前に `dict(dd)` で変換しているか)
|
|
240
|
+
- `Counter` の `subtract()` メソッドと `-` 演算子の違い(`subtract` は負値を保持、`-` は正値のみ)
|
|
241
|
+
- `deque` に `maxlen` が設定されているか(無制限 deque は意図しないメモリ増大の原因)
|
|
242
|
+
- `ChainMap` の変更は先頭辞書のみに反映されることを把握しているか
|
|
243
|
+
|
|
244
|
+
**チームでの安全なパターン**: `BoundedHistory`(maxlen 付き deque)は監査ログ・エラー履歴の実装に、`resolve_config`(ChainMap)は設定オーバーライドに使えるチーム共有ユーティリティ。
|
|
245
|
+
|
|
246
|
+
**事故リスク**: 低
|
|
247
|
+
|
|
248
|
+
### 6. 設計者(nene2-python 設計ポリシー目線)
|
|
249
|
+
|
|
250
|
+
**CLAUDE.md ポリシー整合性**:
|
|
251
|
+
- `dataclass(frozen=True, slots=True)`: `DequeSnapshot`・`RawCoordinate` で適用済み ✅
|
|
252
|
+
- Pydantic は HTTP 境界のみ: `app.py` の Request/Response モデルのみ ✅
|
|
253
|
+
- `create_app()` はファイル末尾: 適用済み ✅(FT182 の教訓)
|
|
254
|
+
- `max_length` 指定: 全文字列・リストフィールドに設定済み ✅
|
|
255
|
+
- 型安全: `dict[str, float | str]` の問題を `RawCoordinate` dataclass で解決 ✅
|
|
256
|
+
|
|
257
|
+
**初心者でも安全な API 達成度**: `BoundedHistory` が「maxlen を必ず指定して使う」パターンを示し、`LruDict` が「容量が決まっている場合のみ OrderedDict LRU を使う」ことを Pydantic の `le=100` で強制している設計は初心者でも誤用しにくい。
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
*バージョン: v1.8.58*
|