nene2-python 1.8.40__tar.gz → 1.8.41__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.40 → nene2_python-1.8.41}/PKG-INFO +1 -1
- nene2_python-1.8.41/docs/field-trials/2026-05-field-trial-170.md +242 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/pyproject.toml +1 -1
- {nene2_python-1.8.40 → nene2_python-1.8.41}/uv.lock +1 -1
- {nene2_python-1.8.40 → nene2_python-1.8.41}/.env.example +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/.gitignore +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/AGENTS.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/CHANGELOG.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/CLAUDE.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/Dockerfile +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/LICENSE +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/README.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/alembic/README +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/alembic/env.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/alembic.ini +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/compose.yaml +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/de/index.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-100.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-101.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-102.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-103.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-104.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-105.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-106.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-107.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-108.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-109.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-110.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-111.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-112.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-113.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-114.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-115.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-116.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-117.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-118.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-119.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-120.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-121.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-122.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-123.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-124.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-125.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-126.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-127.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-128.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-129.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-130.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-131.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-132.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-133.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-134.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-135.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-136.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-137.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-138.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-139.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-140.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-141.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-142.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-143.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-144.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-145.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-146.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-147.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-148.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-149.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-150.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-151.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-152.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-153.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-154.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-155.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-156.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-157.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-158.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-159.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-160.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-161.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-162.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-163.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-164.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-165.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-166.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-167.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-168.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-169.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-45.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-46.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-47.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-48.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-49.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-50.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-51.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-52.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-53.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-54.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-55.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-56.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-57.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-58.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-59.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-60.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-61.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-62.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-63.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-64.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-65.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-66.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-67.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-68.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-69.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-70.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-71.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-72.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-73.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-74.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-75.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-76.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-77.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-78.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-79.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-80.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-81.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-82.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-83.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-84.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-85.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-86.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-87.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-88.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-89.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-90.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-91.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-92.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-93.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-94.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-95.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-96.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-97.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-98.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/field-trials/2026-05-field-trial-99.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/fr/index.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/api-versioning.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/background-tasks.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/cors.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/custom-auth-middleware.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/dependency-injection.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/domain-events.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/file-upload.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/lifespan-and-app-state.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/middleware-stack.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/response-patterns.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/soft-delete.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/streaming.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/structured-logging.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/how-to/webhook.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/index.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/index.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/reference/api.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/roadmap.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/templates/field-trial-report.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/todo/current.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/zh/index.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/package-lock.json +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/package.json +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/__main__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/app.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/mcp.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/schema.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/auth/deps.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/cache/ttl.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/http/etag.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/security_headers.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/setup.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/security/webhook.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/conftest.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/auth/test_make_require_auth.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/cache/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/cache/test_ttl.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/http/test_etag.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/mcp/test_server.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/test_security_headers.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/security/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/security/test_webhook.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.40 → nene2_python-1.8.41}/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.41
|
|
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,242 @@
|
|
|
1
|
+
# FT170: collections モジュール
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-21
|
|
4
|
+
**テーマ**: `collections` モジュール — `namedtuple`・`defaultdict`・`Counter`・`deque`・`OrderedDict`・`ChainMap`
|
|
5
|
+
**セキュリティ診断**: なし(170 % 3 = 2)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 概要
|
|
10
|
+
|
|
11
|
+
Python 標準ライブラリの `collections` モジュールを nene2-python フレームワーク上で検証した。
|
|
12
|
+
`collections` は Python の基本データ構造を拡張する実用的なモジュールで、
|
|
13
|
+
ドメインデータ集計・キャッシュ・グラフ探索・設定管理に直接使えるパターンを提供する。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装したサンプルアプリ
|
|
18
|
+
|
|
19
|
+
**場所**: `/home/xi/docker/nene2-python-FT/ft170-collections/`
|
|
20
|
+
|
|
21
|
+
### 主要機能
|
|
22
|
+
|
|
23
|
+
| クラス/関数 | 概要 |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `ApiError` (namedtuple) | エラー情報の軽量イミュータブル型。`_asdict()` で辞書変換 |
|
|
26
|
+
| `group_by_first_char()` | `defaultdict(list)` でグループ集計 |
|
|
27
|
+
| `count_tags()` | `defaultdict(int)` で出現頻度集計 |
|
|
28
|
+
| `build_adjacency_list()` | `defaultdict(list)` でグラフ構築 |
|
|
29
|
+
| `word_frequency()` / `top_n_words()` | `Counter` で単語頻度・トップN |
|
|
30
|
+
| `tag_overlap()` | `Counter` の intersection で共通タグを抽出 |
|
|
31
|
+
| `merge_counts()` | 複数 `Counter` を `update()` で合算 |
|
|
32
|
+
| `sliding_window_max()` | `deque` で O(n) スライディングウィンドウ最大値 |
|
|
33
|
+
| `recent_n()` | `deque(maxlen=n)` でリングバッファ |
|
|
34
|
+
| `bfs_path()` | `deque` をキューとして BFS 最短経路探索 |
|
|
35
|
+
| `LruCache` | `OrderedDict` + `move_to_end()` で O(1) LRU キャッシュ |
|
|
36
|
+
| `resolve_config()` / `config_source()` | `ChainMap` で env > file > defaults の優先順位設定 |
|
|
37
|
+
|
|
38
|
+
### HTTP エンドポイント
|
|
39
|
+
|
|
40
|
+
| メソッド | パス | 概要 |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| GET | `/collections/namedtuple` | namedtuple デモ(距離計算・エラー構造体) |
|
|
43
|
+
| POST | `/collections/group-by` | defaultdict でグループ集計 |
|
|
44
|
+
| POST | `/collections/count-tags` | defaultdict でタグ集計 |
|
|
45
|
+
| GET | `/collections/word-freq` | Counter で単語頻度分析 |
|
|
46
|
+
| GET | `/collections/sliding-window` | deque でスライディングウィンドウ |
|
|
47
|
+
| GET | `/collections/recent` | deque(maxlen) リングバッファ |
|
|
48
|
+
| POST | `/collections/bfs` | BFS グラフ探索 |
|
|
49
|
+
| PUT/GET | `/collections/lru/{key}` | OrderedDict LRU キャッシュ |
|
|
50
|
+
| POST | `/collections/config` | ChainMap 設定レイヤー解決 |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## テスト結果
|
|
55
|
+
|
|
56
|
+
**36 passed(摩擦ゼロ)**
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
36 passed in 0.94s
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 摩擦ポイント
|
|
65
|
+
|
|
66
|
+
**今回の FT では実装上の摩擦はゼロだった。**
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 観察点
|
|
71
|
+
|
|
72
|
+
### 観察1: `defaultdict` は「キーがなければ初期値」を明示的に設計できる
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
result: defaultdict[str, list[str]] = defaultdict(list)
|
|
76
|
+
for word in words:
|
|
77
|
+
result[word[0]].append(word) # KeyError なし
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`dict.setdefault()` より意図が明確で、`if key not in d:` 分岐が不要になる。
|
|
81
|
+
グループ集計・グラフ隣接リスト構築・カウンタ実装の3パターンで多用できる。
|
|
82
|
+
|
|
83
|
+
### 観察2: `Counter` は集合演算(`+`, `-`, `&`, `|`)が使える辞書
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
Counter(["python", "typing", "python"]) & Counter(["python", "asyncio"])
|
|
87
|
+
# → Counter({"python": 1}) — min(2,1) = 1
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`Counter` 同士の `+` は合算、`&` は最小値、`|` は最大値。
|
|
91
|
+
複数の集計結果をマージする `merge_counts()` は `Counter.update()` で自然に書ける。
|
|
92
|
+
`most_common(n)` で上位 N 件を O(n log n) で取得できる。
|
|
93
|
+
|
|
94
|
+
### 観察3: `deque(maxlen=n)` はリングバッファとして使える
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
buf: deque[str] = deque(maxlen=5)
|
|
98
|
+
buf.extend(["a", "b", "c", "d", "e", "f"])
|
|
99
|
+
list(buf) # → ["b", "c", "d", "e", "f"] — 最新5件のみ保持
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`maxlen` を指定すると、追加時に先頭から自動削除される。
|
|
103
|
+
ログ末尾 N 行・最近の操作履歴・スライディングウィンドウのバッファとして最適。
|
|
104
|
+
`collections.deque` は `list` と異なり先頭操作が O(1)。
|
|
105
|
+
|
|
106
|
+
### 観察4: `OrderedDict.move_to_end()` で O(1) LRU キャッシュが実装できる
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
class LruCache:
|
|
110
|
+
def get(self, key: str) -> Any:
|
|
111
|
+
if key not in self._cache:
|
|
112
|
+
return None
|
|
113
|
+
self._cache.move_to_end(key) # 最近使用済みとしてマーク
|
|
114
|
+
return self._cache[key]
|
|
115
|
+
|
|
116
|
+
def put(self, key: str, value: Any) -> None:
|
|
117
|
+
...
|
|
118
|
+
if len(self._cache) > self.capacity:
|
|
119
|
+
self._cache.popitem(last=False) # 最も古いものを削除
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`Python 3.7+` の `dict` は挿入順を保証するが、`move_to_end()` がないため
|
|
123
|
+
LRU の「使用順序の更新」には `OrderedDict` が必要。
|
|
124
|
+
nene2 の `TtlCache` と組み合わせた TTL+LRU キャッシュへの発展も可能。
|
|
125
|
+
|
|
126
|
+
### 観察5: `ChainMap` で設定レイヤーのオーバーライドが宣言的に書ける
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
chain = ChainMap(env_vars, file_config, defaults)
|
|
130
|
+
chain["DB_HOST"] # env_vars → file_config → defaults の優先順位で検索
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`os.environ` + ファイル設定 + デフォルト値の優先順位解決は
|
|
134
|
+
従来 `{**defaults, **file_config, **env_vars}` で実装していたが、
|
|
135
|
+
`ChainMap` は元の辞書を変更せず参照のみのため副作用がない。
|
|
136
|
+
`chain.maps[0]` で最優先レイヤー、`chain.new_child()` でスコープを重ねることもできる。
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## nene2-python フレームワークとの統合
|
|
141
|
+
|
|
142
|
+
- `Counter` はタグ・カテゴリの集計 Use Case で `GROUP BY` SQL の代替になる(小規模データ)
|
|
143
|
+
- `LruCache` は nene2 の `TtlCache[V]` と組み合わせて TTL + LRU の複合キャッシュに発展できる
|
|
144
|
+
- `ChainMap` は `AppSettings` の `pydantic-settings` が行っている env > file > default 解決と同じパターン
|
|
145
|
+
- `deque` は WebSocket メッセージキューやストリーミングレスポンスのバッファとして適用できる
|
|
146
|
+
- `namedtuple` は UseCase の軽量 Output DTO として `dataclass(frozen=True)` より軽量な選択肢になる
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Developer Experience (DX) Review
|
|
151
|
+
|
|
152
|
+
### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
|
|
153
|
+
|
|
154
|
+
`defaultdict` は「エラーが出なくなった辞書」として直感的に受け入れられる。
|
|
155
|
+
`Counter` は「辞書の特殊版」として理解でき、`.most_common()` が使いやすい。
|
|
156
|
+
`deque` の「両端キュー」という概念は最初はピンとこないが、`maxlen` のリングバッファ用途はすぐに理解できる。
|
|
157
|
+
|
|
158
|
+
**ドキュメント理解**: `defaultdict(list)` のファクトリ関数の渡し方(`list` を呼び出さない)は最初に混乱する。
|
|
159
|
+
`defaultdict(lambda: [])` との違いを最初に説明すると理解が早い。
|
|
160
|
+
|
|
161
|
+
**事故リスク**: 中。`defaultdict` は存在しないキーにアクセスすると自動で作成するため、
|
|
162
|
+
タイポキーが無音で `{}` や `[]` に変わり、後続処理でのデバッグが難しくなる可能性。
|
|
163
|
+
|
|
164
|
+
**規約の使いやすさ**: コピペで使えるパターンが多い。
|
|
165
|
+
|
|
166
|
+
### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
|
|
167
|
+
|
|
168
|
+
`Counter` を知らずに `dict` + `if key in d: d[key] += 1 else: d[key] = 1` を書いている。
|
|
169
|
+
`Counter` を知ると即採用する。
|
|
170
|
+
|
|
171
|
+
**コピペ可能性**: 高。特に `Counter(list).most_common(n)` のワンライナーは即戦力。
|
|
172
|
+
|
|
173
|
+
**拡張時の罠**: `LruCache` で `OrderedDict` を使っているが、
|
|
174
|
+
Python 3.7+ の `dict` で書き直そうとして `move_to_end()` がないことに気づかず壊す可能性。
|
|
175
|
+
「`OrderedDict` には `move_to_end()` がある」という固有 API を README に明記するべき。
|
|
176
|
+
|
|
177
|
+
**セキュリティ的な事故リスク**: 低。`collections` の誤用は機能バグには繋がるが、セキュリティリスクは低い。
|
|
178
|
+
|
|
179
|
+
### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
|
|
180
|
+
|
|
181
|
+
`Counter` は JavaScript の `reduce` で頻度集計するパターンと概念的に近い。
|
|
182
|
+
`namedtuple` は TypeScript の `readonly struct` 的に理解できる。
|
|
183
|
+
|
|
184
|
+
**エラーレスポンスの質**: `/collections/sliding-window` で `"a,b,c"` を送ると 422 が返る。
|
|
185
|
+
クライアントには `{"error": "values must be comma-separated integers"}` が届き明確。
|
|
186
|
+
|
|
187
|
+
**事故リスク**: 低。
|
|
188
|
+
|
|
189
|
+
### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
|
|
190
|
+
|
|
191
|
+
Django の `QuerySet.values().annotate(count=Count(...))` と `Counter` の使い分けが判断ポイント。
|
|
192
|
+
DB に集計クエリを投げられるなら Django ORM が適切。
|
|
193
|
+
インメモリ集計(小規模・一時的)には `Counter` が軽量。
|
|
194
|
+
|
|
195
|
+
**本番投入可能性**: 問題なし。`LruCache` は nene2 の `TtlCache` と組み合わせて即本番投入できる。
|
|
196
|
+
|
|
197
|
+
### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
|
|
198
|
+
|
|
199
|
+
**コードレビューチェックポイント**:
|
|
200
|
+
- [ ] `defaultdict` のキーが意図せず作成されていないか(`d[key]` アクセスだけでキーが生える)
|
|
201
|
+
- [ ] `Counter` の `most_common()` が `None` を返さないことを前提にしているか(空リストなら空リストを返す)
|
|
202
|
+
- [ ] `LruCache` が複数リクエストからアクセスされるグローバル状態の場合、`asyncio.Lock()` が必要か確認
|
|
203
|
+
- [ ] `ChainMap` の子マップへの書き込みが親マップに伝播しないことを理解しているか
|
|
204
|
+
|
|
205
|
+
**チームでの安全なパターン**: グローバルな `LruCache` インスタンスは `asyncio.Lock()` でガードするか、
|
|
206
|
+
スレッドセーフな実装(`threading.RLock`)に置き換える。
|
|
207
|
+
|
|
208
|
+
### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
|
|
209
|
+
|
|
210
|
+
**ポリシー達成度**: 高
|
|
211
|
+
|
|
212
|
+
**「初心者でも安全な API」達成度**: 中
|
|
213
|
+
- `defaultdict` のキー自動生成は初心者には予期しない動作になりうる
|
|
214
|
+
- `LruCache` のスレッドセーフ性は nene2 の非同期環境では注意が必要
|
|
215
|
+
|
|
216
|
+
**設計上の負債・ドキュメント不足**:
|
|
217
|
+
- nene2 の `TtlCache[V]` と `LruCache` の組み合わせパターンが未文書化
|
|
218
|
+
- グローバルキャッシュインスタンスの非同期安全性に関する how-to がない
|
|
219
|
+
|
|
220
|
+
**Follow-up Issue 候補**: `docs: キャッシュの TTL + LRU 複合パターンと非同期安全性の how-to を追加`
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Follow-up Issues
|
|
225
|
+
|
|
226
|
+
| 優先度 | タイトル | 種別 |
|
|
227
|
+
|---|---|---|
|
|
228
|
+
| 中 | `docs: collections.Counter をタグ集計 Use Case に適用するパターンを how-to に追加` | docs |
|
|
229
|
+
| 低 | `feat: TtlCache に LRU 退去ポリシーを追加するオプションを検討` | feat |
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## まとめ
|
|
234
|
+
|
|
235
|
+
`collections` モジュールは nene2-python の集計・キャッシュ・探索・設定管理に直接使える実用的な機能群。
|
|
236
|
+
36 テスト全通過、摩擦ゼロ。
|
|
237
|
+
|
|
238
|
+
`defaultdict` / `Counter` / `deque` の三点セットは Python バックエンドの必須知識。
|
|
239
|
+
`OrderedDict.move_to_end()` による LRU キャッシュは nene2 の `TtlCache` と組み合わせる価値がある。
|
|
240
|
+
`ChainMap` は `pydantic-settings` が内部でやっている設定レイヤー解決と同じパターンで、
|
|
241
|
+
環境別設定のオーバーライドを副作用なしに実装できる。
|
|
242
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nene2_python-1.8.40 → nene2_python-1.8.41}/alembic/versions/001_create_notes_and_tags_tables.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|