continuo-python-runtime 0.9.0__tar.gz → 0.10.0__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.
Files changed (206) hide show
  1. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/CHANGELOG.md +22 -0
  2. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/PKG-INFO +14 -5
  3. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/README.md +12 -3
  4. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/pyproject.toml +2 -2
  5. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/continuo_postgres_adapter/adapter.py +1 -1
  6. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/pyproject.toml +2 -2
  7. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/pyproject.toml +2 -2
  8. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/contract/loader.py +17 -1
  9. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/contract/merge.py +4 -0
  10. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/contract/model.py +1 -0
  11. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/hashing.py +8 -3
  12. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/keys.py +3 -3
  13. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/pyproject.toml +1 -1
  14. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/docs/boundary-contract.md +12 -1
  15. continuo_python_runtime-0.10.0/image-requirements-duckdb.txt +3 -0
  16. continuo_python_runtime-0.10.0/image-requirements-postgres.txt +3 -0
  17. continuo_python_runtime-0.10.0/image-requirements-trino.txt +3 -0
  18. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/pyproject.toml +2 -2
  19. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/template/Dockerfile +1 -1
  20. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/contract/test_loader.py +20 -1
  21. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/contract/test_merge.py +22 -0
  22. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_hashing.py +7 -0
  23. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/uv.lock +5 -5
  24. continuo_python_runtime-0.9.0/image-requirements-duckdb.txt +0 -3
  25. continuo_python_runtime-0.9.0/image-requirements-postgres.txt +0 -3
  26. continuo_python_runtime-0.9.0/image-requirements-trino.txt +0 -3
  27. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.claude/agents/ddd-architecture-auditor.md +0 -0
  28. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.dockerignore +0 -0
  29. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/CODEOWNERS +0 -0
  30. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  31. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  32. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  33. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  34. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/dependabot.yml +0 -0
  35. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/ci.yml +0 -0
  36. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/dco.yml +0 -0
  37. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/image-build.yml +0 -0
  38. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/image-smoke.yml +0 -0
  39. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/images.yml +0 -0
  40. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/publish-pypi.yml +0 -0
  41. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/release.yml +0 -0
  42. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/security.yml +0 -0
  43. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.github/workflows/verify-packages.yml +0 -0
  44. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.gitignore +0 -0
  45. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.gitleaks.toml +0 -0
  46. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/.python-version +0 -0
  47. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/CLAUDE.md +0 -0
  48. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/CODE_OF_CONDUCT.md +0 -0
  49. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/CONTRIBUTING.md +0 -0
  50. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/DCO +0 -0
  51. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/Dockerfile.duckdb +0 -0
  52. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/Dockerfile.postgres +0 -0
  53. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/Dockerfile.trino +0 -0
  54. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/LICENSE +0 -0
  55. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/NOTICE +0 -0
  56. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/SECURITY.md +0 -0
  57. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/README.md +0 -0
  58. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/__init__.py +0 -0
  59. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/adapter.py +0 -0
  60. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/application/__init__.py +0 -0
  61. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/application/ports.py +0 -0
  62. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/application/warehouse.py +0 -0
  63. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/domain/__init__.py +0 -0
  64. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/domain/columns.py +0 -0
  65. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/domain/identifiers.py +0 -0
  66. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/domain/layout.py +0 -0
  67. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/__init__.py +0 -0
  68. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/ddl.py +0 -0
  69. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/extensions.py +0 -0
  70. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/passfile.py +0 -0
  71. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/session.py +0 -0
  72. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/continuo_duckdb_adapter/infrastructure/settings.py +0 -0
  73. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/__init__.py +0 -0
  74. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/conftest.py +0 -0
  75. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_adapter_duckdb_proxy.py +0 -0
  76. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_adapter_duckdb_runtime.py +0 -0
  77. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_adapter_duckdb_validation.py +0 -0
  78. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_adapter_duckdb_wiring.py +0 -0
  79. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_domain_duckdb_columns.py +0 -0
  80. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_domain_duckdb_identifiers.py +0 -0
  81. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_domain_duckdb_layout.py +0 -0
  82. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_infra_duckdb_session.py +0 -0
  83. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_infra_duckdb_settings_ddl.py +0 -0
  84. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_integration_duckdb_conformance.py +0 -0
  85. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_integration_duckdb_layout.py +0 -0
  86. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_integration_duckdb_nullability_probe.py +0 -0
  87. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_integration_duckdb_runtime.py +0 -0
  88. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_integration_duckdb_stack.py +0 -0
  89. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/duckdb/tests/test_integration_duckdb_validation.py +0 -0
  90. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/README.md +0 -0
  91. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/continuo_postgres_adapter/__init__.py +0 -0
  92. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/tests/__init__.py +0 -0
  93. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/tests/conftest.py +0 -0
  94. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/tests/test_adapter_postgres_validation.py +0 -0
  95. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/tests/test_adapter_runtime_postgres.py +0 -0
  96. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/tests/test_integration_postgres_conformance.py +0 -0
  97. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/tests/test_integration_postgres_validation.py +0 -0
  98. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/postgres/tests/test_integration_runtime_postgres.py +0 -0
  99. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/README.md +0 -0
  100. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/continuo_trino_adapter/__init__.py +0 -0
  101. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/continuo_trino_adapter/adapter.py +0 -0
  102. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/tests/__init__.py +0 -0
  103. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/tests/test_adapter_runtime_trino.py +0 -0
  104. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/tests/test_adapter_trino_validation.py +0 -0
  105. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/tests/test_adapter_trino_writes.py +0 -0
  106. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/tests/test_integration_runtime_trino.py +0 -0
  107. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/tests/test_integration_trino_conformance.py +0 -0
  108. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/tests/test_integration_trino_merge_probe.py +0 -0
  109. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/adapters/trino/tests/test_integration_trino_validation.py +0 -0
  110. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/__init__.py +0 -0
  111. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/cli.py +0 -0
  112. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/closure.py +0 -0
  113. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/conform.py +0 -0
  114. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/context.py +0 -0
  115. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/contract/__init__.py +0 -0
  116. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/contract/kinds.py +0 -0
  117. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/contract/paths.py +0 -0
  118. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/contract/secret_ref.py +0 -0
  119. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/csv_loader.py +0 -0
  120. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/csv_readers/__init__.py +0 -0
  121. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/csv_readers/https.py +0 -0
  122. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/csv_readers/s3.py +0 -0
  123. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/csv_source.py +0 -0
  124. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/errors.py +0 -0
  125. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/harness.py +0 -0
  126. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/lint.py +0 -0
  127. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/shape.py +0 -0
  128. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/types.py +0 -0
  129. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/validation/__init__.py +0 -0
  130. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/validation/runner.py +0 -0
  131. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/continuo_python_runtime/validation/s3.py +0 -0
  132. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/LICENSE +0 -0
  133. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/NOTICE +0 -0
  134. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/README.md +0 -0
  135. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/__init__.py +0 -0
  136. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/config.py +0 -0
  137. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/port.py +0 -0
  138. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/result.py +0 -0
  139. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/sql.py +0 -0
  140. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/testing/__init__.py +0 -0
  141. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/testing/conformance.py +0 -0
  142. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/continuo_engine_contract/types.py +0 -0
  143. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/__init__.py +0 -0
  144. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/test_config.py +0 -0
  145. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/test_conformance_suite.py +0 -0
  146. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/test_contract_keys.py +0 -0
  147. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/test_contract_packaging.py +0 -0
  148. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/test_contract_types.py +0 -0
  149. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/test_port.py +0 -0
  150. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/test_result.py +0 -0
  151. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/contract/tests/test_sql.py +0 -0
  152. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/scripts/apply_rehearsal_version.py +0 -0
  153. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/scripts/bake_duckdb_extensions.py +0 -0
  154. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/scripts/check_version_bumps.py +0 -0
  155. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/scripts/security-scan.sh +0 -0
  156. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/scripts/verify_installed_packages.py +0 -0
  157. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/template/.github/workflows/release.yml +0 -0
  158. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/template/Dockerfile.pip +0 -0
  159. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/template/README.md +0 -0
  160. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/template/contracts/example.yml +0 -0
  161. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/template/contracts/example_csv.yml +0 -0
  162. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/template/requirements.lock +0 -0
  163. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/template/scripts/example.py +0 -0
  164. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/__init__.py +0 -0
  165. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/conftest.py +0 -0
  166. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/contract/__init__.py +0 -0
  167. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/contract/test_kinds.py +0 -0
  168. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/contract/test_model.py +0 -0
  169. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/contract/test_wire_contract_write_modes.py +0 -0
  170. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/fixtures/wire_contract_write_modes.yml +0 -0
  171. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/smoke/duckdb-stack/docker-compose.yml +0 -0
  172. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/smoke/node_smoke/contracts/smoke.yml +0 -0
  173. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/smoke/node_smoke/scripts/smoke.py +0 -0
  174. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/smoke/postgres-stack/docker-compose.yml +0 -0
  175. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/smoke/trino-stack/catalog/iceberg.properties +0 -0
  176. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/smoke/trino-stack/docker-compose.yml +0 -0
  177. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_adapter_naming.py +0 -0
  178. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_check_version_bumps_tags.py +0 -0
  179. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_ci_test_coverage.py +0 -0
  180. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_cli.py +0 -0
  181. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_closure.py +0 -0
  182. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_conform.py +0 -0
  183. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_context.py +0 -0
  184. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_contract_loader.py +0 -0
  185. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_contract_pin_consistency.py +0 -0
  186. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_csv_loader.py +0 -0
  187. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_csv_readers_https.py +0 -0
  188. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_csv_readers_integration.py +0 -0
  189. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_csv_source.py +0 -0
  190. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_errors.py +0 -0
  191. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_harness.py +0 -0
  192. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_harness_write_modes.py +0 -0
  193. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_image_requirements_sync.py +0 -0
  194. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_image_smoke_validation.py +0 -0
  195. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_kind_registries.py +0 -0
  196. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_lint.py +0 -0
  197. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_no_legacy_names.py +0 -0
  198. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_package.py +0 -0
  199. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_python_version_consistency.py +0 -0
  200. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_rehearsal_version.py +0 -0
  201. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_release_pipeline_structure.py +0 -0
  202. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_shape.py +0 -0
  203. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_template.py +0 -0
  204. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_types.py +0 -0
  205. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_validation_runner.py +0 -0
  206. {continuo_python_runtime-0.9.0 → continuo_python_runtime-0.10.0}/tests/test_validation_s3.py +0 -0
@@ -5,6 +5,28 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.10.0] - 2026-10-22
9
+
10
+ ### Added
11
+
12
+ - Contracts may set `timeout_minutes` on any node: an integer from 1 to 10,080
13
+ that is the node's task timeout. Other values are refused as a contract
14
+ error naming the node. The merged wire entry carries the key only when set,
15
+ and it is the one wire key outside `config_hash`: changing it alone leaves
16
+ `config_hash` and `content_hash` unchanged. A node that does not set it has
17
+ the wire entry it always had.
18
+
19
+ ### Changed
20
+
21
+ - The "mode_type not allowed for kind" contract error now quotes the offending
22
+ `mode_type` and `kind` (`repr`), matching the sibling "must be one of" error,
23
+ so an empty or whitespace value is legible. Docstring fixes only alongside:
24
+ the postgres `ensure_table` port-signature reference (0.6.0 → 0.8.0) and the
25
+ `ensure_unique_key` message-order claim (pyarrow grouping order is not
26
+ contractual).
27
+ - The trino adapter (0.3.1) and duckdb adapter (0.2.1) release with their
28
+ `continuo-engine-contract` pin moved to 0.8.1; no behavior change.
29
+
8
30
  ## [0.9.0] - 2026-10-11
9
31
 
10
32
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: continuo-python-runtime
3
- Version: 0.9.0
3
+ Version: 0.10.0
4
4
  Summary: Runtime harness, contract tooling, and CI lint for Continuo python nodes.
5
5
  Author: Simone Carolini
6
6
  Maintainer: Simone Carolini
@@ -12,7 +12,7 @@ Classifier: Intended Audience :: Developers
12
12
  Classifier: Programming Language :: Python :: 3.14
13
13
  Requires-Python: >=3.14
14
14
  Requires-Dist: boto3==1.43.85
15
- Requires-Dist: continuo-engine-contract==0.8.0
15
+ Requires-Dist: continuo-engine-contract==0.8.1
16
16
  Requires-Dist: pyarrow==25.0.1
17
17
  Requires-Dist: pyyaml==6.0.3
18
18
  Requires-Dist: sqlglot==30.17.0
@@ -196,6 +196,15 @@ produced, never how it is checked or written.
196
196
  script reads with `os.environ[...]`. The contract never holds a secret value
197
197
  or env var name; the Secret itself is created in the cluster by the operator.
198
198
 
199
+ ### Task timeout
200
+
201
+ Any node may set `timeout_minutes:` — an integer from 1 to 10,080 (one week) —
202
+ to declare how long its task may run before continuo gives up on it. A value
203
+ outside that range, or one that is not an integer, fails
204
+ `continuo-runtime validate`/`merge`/`lint`. It is the one key that is not part
205
+ of `config_hash`, so changing only the timeout never changes the node's
206
+ `content_hash`.
207
+
199
208
  ## Write modes
200
209
 
201
210
  `mode_type:` chooses how the harness writes a node's table on each run:
@@ -347,11 +356,11 @@ A domain repo picks its warehouse engine by which base image it builds
347
356
  `FROM`:
348
357
 
349
358
  ```dockerfile
350
- FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.9.0
359
+ FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.10.0
351
360
  # or
352
- FROM ghcr.io/carolsimone/continuo-python-runtime-trino:v0.9.0
361
+ FROM ghcr.io/carolsimone/continuo-python-runtime-trino:v0.10.0
353
362
  # or
354
- FROM ghcr.io/carolsimone/continuo-python-runtime-duckdb:v0.9.0
363
+ FROM ghcr.io/carolsimone/continuo-python-runtime-duckdb:v0.10.0
355
364
  ```
356
365
 
357
366
  The engine is part of the image **name**; the tag is the bare version, so
@@ -176,6 +176,15 @@ produced, never how it is checked or written.
176
176
  script reads with `os.environ[...]`. The contract never holds a secret value
177
177
  or env var name; the Secret itself is created in the cluster by the operator.
178
178
 
179
+ ### Task timeout
180
+
181
+ Any node may set `timeout_minutes:` — an integer from 1 to 10,080 (one week) —
182
+ to declare how long its task may run before continuo gives up on it. A value
183
+ outside that range, or one that is not an integer, fails
184
+ `continuo-runtime validate`/`merge`/`lint`. It is the one key that is not part
185
+ of `config_hash`, so changing only the timeout never changes the node's
186
+ `content_hash`.
187
+
179
188
  ## Write modes
180
189
 
181
190
  `mode_type:` chooses how the harness writes a node's table on each run:
@@ -327,11 +336,11 @@ A domain repo picks its warehouse engine by which base image it builds
327
336
  `FROM`:
328
337
 
329
338
  ```dockerfile
330
- FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.9.0
339
+ FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.10.0
331
340
  # or
332
- FROM ghcr.io/carolsimone/continuo-python-runtime-trino:v0.9.0
341
+ FROM ghcr.io/carolsimone/continuo-python-runtime-trino:v0.10.0
333
342
  # or
334
- FROM ghcr.io/carolsimone/continuo-python-runtime-duckdb:v0.9.0
343
+ FROM ghcr.io/carolsimone/continuo-python-runtime-duckdb:v0.10.0
335
344
  ```
336
345
 
337
346
  The engine is part of the image **name**; the tag is the bare version, so
@@ -1,5 +1,5 @@
1
1
  [project]
2
- version = "0.2.0"
2
+ version = "0.2.1"
3
3
  name = "continuo-duckdb-adapter"
4
4
  description = "DuckDB (DuckLake) warehouse adapter for Continuo: validation and python-node runtime."
5
5
  authors = [{ name = "Simone Carolini" }]
@@ -10,7 +10,7 @@ dependencies = [
10
10
  # Pinned exactly for the same reason as the postgres adapter: the
11
11
  # [tool.uv.sources] redirect below is uv-only and never reaches built
12
12
  # metadata, and this adapter subclasses the contract's port directly.
13
- "continuo-engine-contract==0.8.0",
13
+ "continuo-engine-contract==0.8.1",
14
14
  "duckdb==1.5.6",
15
15
  "pyarrow==25.0.1",
16
16
  ]
@@ -706,7 +706,7 @@ class PostgresAdapter(WarehouseAdapter, AppendCapable, UpsertCapable):
706
706
  this engine's physical-layout vocabulary — ``indexes`` — validated by
707
707
  :func:`_validated_indexes` before any DDL runs, so a malformed config
708
708
  never leaves a half-built table behind (fail closed). Required and
709
- keyword-only, matching the contract 0.6.0 port signature. The create
709
+ keyword-only, matching the contract 0.8.0 port signature. The create
710
710
  and every index either all land or none do, in the same explicit
711
711
  BEGIN/COMMIT/ROLLBACK shape build_empty_from_columns uses.
712
712
 
@@ -1,5 +1,5 @@
1
1
  [project]
2
- version = "0.3.0"
2
+ version = "0.3.1"
3
3
  name = "continuo-postgres-adapter"
4
4
  description = "Postgres warehouse adapter for Continuo: validation and python-node runtime."
5
5
  authors = [{ name = "Simone Carolini" }]
@@ -12,7 +12,7 @@ dependencies = [
12
12
  # of this adapter would otherwise accept whatever contract release is
13
13
  # newest. This adapter subclasses the contract's WarehouseAdapter port
14
14
  # directly, so the pair must stay the tested one.
15
- "continuo-engine-contract==0.8.0",
15
+ "continuo-engine-contract==0.8.1",
16
16
  "psycopg2-binary==2.9.12",
17
17
  "pyarrow==25.0.1",
18
18
  ]
@@ -1,5 +1,5 @@
1
1
  [project]
2
- version = "0.3.0"
2
+ version = "0.3.1"
3
3
  name = "continuo-trino-adapter"
4
4
  description = "Trino warehouse adapter for Continuo: validation and python-node runtime."
5
5
  authors = [{ name = "Simone Carolini" }]
@@ -12,7 +12,7 @@ dependencies = [
12
12
  # of this adapter would otherwise accept whatever contract release is
13
13
  # newest. This adapter subclasses the contract's WarehouseAdapter port
14
14
  # directly, so the pair must stay the tested one.
15
- "continuo-engine-contract==0.8.0",
15
+ "continuo-engine-contract==0.8.1",
16
16
  "trino==0.339.0",
17
17
  "pyarrow==25.0.1",
18
18
  ]
@@ -44,8 +44,13 @@ _ALLOWED_KEYS = {
44
44
  "secret_ref",
45
45
  "mode_type",
46
46
  "primary_key",
47
+ "timeout_minutes",
47
48
  }
48
49
 
50
+ # Bounds of a node's declared task timeout, in minutes (one minute to one week).
51
+ MIN_TIMEOUT_MINUTES = 1
52
+ MAX_TIMEOUT_MINUTES = 10_080
53
+
49
54
  _REQUIRED_STRING_FIELDS = ("schema", "table", "owner", "schedule")
50
55
 
51
56
  _ALLOWED_OUTPUT_COLUMN_KEYS = {"name", "type", "nullable"}
@@ -295,6 +300,16 @@ def parse_node(
295
300
  raise ContractError(f"{label}: 'secret_ref' is not allowed for kind {kind}")
296
301
  secret_ref = validate_secret_ref(raw["secret_ref"], label)
297
302
 
303
+ timeout_minutes = raw.get("timeout_minutes")
304
+ if "timeout_minutes" in raw and (
305
+ type(timeout_minutes) is not int
306
+ or not MIN_TIMEOUT_MINUTES <= timeout_minutes <= MAX_TIMEOUT_MINUTES
307
+ ):
308
+ raise ContractError(
309
+ f"{label}: 'timeout_minutes' must be an integer between "
310
+ f"{MIN_TIMEOUT_MINUTES} and {MAX_TIMEOUT_MINUTES}, got {timeout_minutes!r}"
311
+ )
312
+
298
313
  mode_type = raw.get("mode_type", DEFAULT_MODE_TYPE)
299
314
  if not isinstance(mode_type, str) or mode_type not in MODE_TYPES:
300
315
  raise ContractError(
@@ -302,7 +317,7 @@ def parse_node(
302
317
  )
303
318
  if mode_type not in rules.mode_types:
304
319
  raise ContractError(
305
- f"{label}: 'mode_type' {mode_type} is not allowed for kind {kind} "
320
+ f"{label}: 'mode_type' {mode_type!r} is not allowed for kind {kind!r} "
306
321
  f"(allowed: {sorted(rules.mode_types)})"
307
322
  )
308
323
 
@@ -375,6 +390,7 @@ def parse_node(
375
390
  secret_ref=secret_ref,
376
391
  mode_type=mode_type,
377
392
  primary_key=primary_key,
393
+ timeout_minutes=timeout_minutes,
378
394
  )
379
395
 
380
396
 
@@ -170,6 +170,10 @@ def build_wire_contract(
170
170
 
171
171
  primary_bytes, member_bytes = _HASH_INPUTS[node.kind](node, repo_root)
172
172
  entry.update(hash_parts(entry, primary_bytes, member_bytes))
173
+ # The one wire key outside config_hash: it is written after the hashes
174
+ # are computed, so changing it alone never changes content_hash.
175
+ if node.timeout_minutes is not None:
176
+ entry["timeout_minutes"] = node.timeout_minutes
173
177
 
174
178
  wire_nodes.append(entry)
175
179
 
@@ -45,6 +45,7 @@ class Node:
45
45
  secret_ref: str = ""
46
46
  mode_type: str = DEFAULT_MODE_TYPE
47
47
  primary_key: tuple[str, ...] = ()
48
+ timeout_minutes: int | None = None
48
49
 
49
50
  @property
50
51
  def relation(self) -> str:
@@ -21,6 +21,10 @@ from json import dumps
21
21
 
22
22
  HASH_PART_FIELDS = ("source_hash", "shared_code_hash", "config_hash")
23
23
  CONTENT_HASH_FIELD = "content_hash"
24
+ # A wire key that is not part of the config_hash basis: it tunes how the node
25
+ # runs (the task timeout) and says nothing about what the node computes, so
26
+ # changing it alone is not a code change.
27
+ NON_HASHED_FIELDS = ("timeout_minutes",)
24
28
 
25
29
 
26
30
  def canonical_entry(entry: dict) -> dict:
@@ -28,13 +32,14 @@ def canonical_entry(entry: dict) -> dict:
28
32
 
29
33
  Drops ``content_hash`` and all three of ``HASH_PART_FIELDS`` from the
30
34
  basis, and whitespace-normalizes every ``str`` value in ``reads`` via
31
- ``" ".join(sql.split())``. Everything else is preserved verbatim,
32
- including ``config``.
35
+ ``" ".join(sql.split())``. ``timeout_minutes`` is the one other wire key
36
+ left out of the basis (``NON_HASHED_FIELDS``). Everything else is
37
+ preserved verbatim, including ``config``.
33
38
  """
34
39
  canonical = deepcopy(entry)
35
40
 
36
41
  canonical.pop(CONTENT_HASH_FIELD, None)
37
- for field in HASH_PART_FIELDS:
42
+ for field in (*HASH_PART_FIELDS, *NON_HASHED_FIELDS):
38
43
  canonical.pop(field, None)
39
44
 
40
45
  reads = canonical.get("reads")
@@ -29,9 +29,9 @@ def ensure_unique_key(data: "pyarrow.Table", primary_key: Sequence[str]) -> None
29
29
  Groups the rows by the key columns. NULL counts as a value here; the
30
30
  contract requires every key column to be ``nullable: false`` and the
31
31
  harness's ``conform()`` rejects NULLs in such columns before this runs.
32
- The message names the first repeated keys, in order of first appearance,
33
- with their row counts. Only methods of *data* are called, so this module
34
- needs no pyarrow import.
32
+ The message names up to a fixed number of the repeated keys with their row
33
+ counts; their order follows pyarrow's grouping and is not contractual. Only
34
+ methods of *data* are called, so this module needs no pyarrow import.
35
35
 
36
36
  Raises:
37
37
  ValueError: If *primary_key* is empty, names a column *data* lacks, or
@@ -1,5 +1,5 @@
1
1
  [project]
2
- version = "0.8.0"
2
+ version = "0.8.1"
3
3
  name = "continuo-engine-contract"
4
4
  description = "The contract a Continuo warehouse engine must satisfy: the WarehouseAdapter port and the result-block wire format."
5
5
  authors = [{ name = "Simone Carolini" }]
@@ -61,6 +61,16 @@ s3://<bucket>/<service>/<release_id>/contract.yaml
61
61
  node's wire entry or `config_hash` changes), and it is part of
62
62
  `config_hash`, so `content_hash` changes when the referenced Secret name
63
63
  changes. `source_hash` does not.
64
+ - **`timeout_minutes`** is additive and optional, valid on every `kind`: an
65
+ integer from 1 to 10,080 (one week) that is the node's task timeout. Any
66
+ other value (zero, above the bound, a string, a float, a boolean) fails
67
+ `ContractError` naming the node and the key. On the merged wire entry it
68
+ appears only when the node declares it, and it is the one wire key **outside
69
+ `config_hash`**: it is written after the hash parts are computed and is
70
+ excluded from the `config_hash` basis, so changing it alone changes none of
71
+ the four hashes and is not a code change. A node that does not declare it
72
+ has the wire entry, `config_hash` and `content_hash` it always had, and
73
+ `contract_version` stays 1.
64
74
  - **`mode_type`** and **`primary_key`** are additive and optional. `mode_type`
65
75
  (`replace`, the default; `append`; `upsert`) says how the harness writes
66
76
  the node's table on each run; `python-csv` allows only `replace`.
@@ -222,7 +232,8 @@ content_hash = "sha256:" + sha256(source_hash + "|" + shared_code_hash
222
232
  collapse to a single space, then stripped); `content_hash` **and all
223
233
  three part fields** (`source_hash`, `shared_code_hash`, `config_hash`)
224
234
  are excluded from the basis — `config_hash` cannot include itself, and
225
- none of the four hash fields participates in another's basis. Every
235
+ none of the four hash fields participates in another's basis. So is
236
+ `timeout_minutes`, the one non-hash wire key left out of the basis. Every
226
237
  other field, including `config`, participates verbatim.
227
238
 
228
239
  Invariant: formatting-only edits (spaces in SQL, yaml indentation/key
@@ -0,0 +1,3 @@
1
+ continuo-python-runtime==0.10.0
2
+ continuo-duckdb-adapter==0.2.1
3
+ continuo-engine-contract==0.8.1
@@ -0,0 +1,3 @@
1
+ continuo-python-runtime==0.10.0
2
+ continuo-postgres-adapter==0.3.1
3
+ continuo-engine-contract==0.8.1
@@ -0,0 +1,3 @@
1
+ continuo-python-runtime==0.10.0
2
+ continuo-trino-adapter==0.3.1
3
+ continuo-engine-contract==0.8.1
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "continuo-python-runtime"
3
- version = "0.9.0"
3
+ version = "0.10.0"
4
4
  description = "Runtime harness, contract tooling, and CI lint for Continuo python nodes."
5
5
  authors = [{ name = "Simone Carolini" }]
6
6
  maintainers = [{ name = "Simone Carolini" }]
@@ -19,7 +19,7 @@ dependencies = [
19
19
  # directly. Both packages ship from this repo on one v* tag, so a new
20
20
  # contract version never exists without a new runtime version to match it;
21
21
  # an exact pin therefore costs nothing and keeps the tested pair together.
22
- "continuo-engine-contract==0.8.0",
22
+ "continuo-engine-contract==0.8.1",
23
23
  "boto3==1.43.85",
24
24
  "pyarrow==25.0.1",
25
25
  "PyYAML==6.0.3",
@@ -1,5 +1,5 @@
1
1
  # Tags are v<version>; one image per engine (continuo-python-runtime-postgres / -trino).
2
- FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.9.0
2
+ FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.10.0
3
3
  COPY contracts/ /app/contracts/
4
4
  COPY scripts/ /app/scripts/
5
5
  # COPY every OTHER directory your scripts import from, too. CI folds a script's
@@ -786,7 +786,7 @@ def test_python_csv_only_allows_replace(mode):
786
786
  entry = _csv_entry(mode_type=mode)
787
787
  if mode == "upsert":
788
788
  entry["primary_key"] = ["order_id"]
789
- with pytest.raises(ContractError, match=f"'mode_type' {mode} is not allowed for kind python-csv"):
789
+ with pytest.raises(ContractError, match=f"'mode_type' '{mode}' is not allowed for kind 'python-csv'"):
790
790
  parse_node(entry, "f.yml", check_reads=False)
791
791
 
792
792
 
@@ -878,3 +878,22 @@ def test_primary_key_names_a_column_that_differs_only_in_case_from_another():
878
878
  entry["output_columns"].append({"name": "Base", "type": "VARCHAR(3)", "nullable": False})
879
879
  node = parse_node(entry, "c.yml")
880
880
  assert node.primary_key == ("rate_date", "Base")
881
+
882
+
883
+ # --- timeout_minutes ------------------------------------------------------------
884
+
885
+
886
+ def test_timeout_minutes_is_read():
887
+ assert parse_node({**VALID, "timeout_minutes": 120}, "f.yml").timeout_minutes == 120
888
+ assert parse_node({**VALID, "timeout_minutes": 1}, "f.yml").timeout_minutes == 1
889
+ assert parse_node({**VALID, "timeout_minutes": 10080}, "f.yml").timeout_minutes == 10080
890
+
891
+
892
+ def test_timeout_minutes_defaults_to_none():
893
+ assert parse_node(VALID, "f.yml").timeout_minutes is None
894
+
895
+
896
+ @pytest.mark.parametrize("bad", [0, 10081, "2h", 1.5, True])
897
+ def test_a_bad_timeout_minutes_is_refused(bad):
898
+ with pytest.raises(ContractError, match=r"f\.yml \(analytics\.t\): 'timeout_minutes'"):
899
+ parse_node({**VALID, "timeout_minutes": bad}, "f.yml")
@@ -617,3 +617,25 @@ def test_a_replace_entry_hashes_exactly_as_it_did_before_write_modes(extra):
617
617
  entry = node_entry(parse_node({**_REPLACE_NODE, **extra}, "f.yml"))
618
618
  assert "mode_type" not in entry and "primary_key" not in entry
619
619
  assert config_hash(entry) == _PRE_WRITE_MODES_CONFIG_HASH
620
+
621
+
622
+ # --- timeout_minutes ----------------------------------------------------------
623
+
624
+
625
+ def test_timeout_minutes_is_outside_the_config_hash(tmp_path):
626
+ without = _only_entry(_modes_repo(tmp_path / "a"))
627
+ short = _only_entry(_modes_repo(tmp_path / "b", timeout_minutes=120))
628
+ long = _only_entry(_modes_repo(tmp_path / "c", timeout_minutes=480))
629
+ assert short["config_hash"] == long["config_hash"] == without["config_hash"]
630
+ assert short["content_hash"] == long["content_hash"] == without["content_hash"]
631
+ # The wire entry hashes exactly as the same entry would without the key.
632
+ assert config_hash(short) == short["config_hash"]
633
+
634
+
635
+ def test_the_wire_entry_carries_timeout_minutes_only_when_set(tmp_path):
636
+ without = _only_entry(_modes_repo(tmp_path / "a"))
637
+ declared = _only_entry(_modes_repo(tmp_path / "b", timeout_minutes=120))
638
+ assert "timeout_minutes" not in without
639
+ assert set(without) == WIRE_ENTRY_KEYS
640
+ assert declared["timeout_minutes"] == 120
641
+ assert set(declared) == WIRE_ENTRY_KEYS | {"timeout_minutes"}
@@ -186,3 +186,10 @@ def test_hash_parts_returns_four_keys_and_consistent_fold():
186
186
  assert parts["content_hash"] == content_hash_fold(
187
187
  parts["source_hash"], parts["shared_code_hash"], parts["config_hash"]
188
188
  )
189
+
190
+
191
+ def test_timeout_minutes_is_outside_the_config_hash_basis():
192
+ with_timeout = {**ENTRY, "timeout_minutes": 120}
193
+ assert "timeout_minutes" not in canonical_entry(with_timeout)
194
+ assert config_hash(with_timeout) == config_hash(ENTRY)
195
+ assert config_hash({**ENTRY, "timeout_minutes": 480}) == config_hash(ENTRY)
@@ -162,7 +162,7 @@ wheels = [
162
162
 
163
163
  [[package]]
164
164
  name = "continuo-duckdb-adapter"
165
- version = "0.2.0"
165
+ version = "0.2.1"
166
166
  source = { editable = "adapters/duckdb" }
167
167
  dependencies = [
168
168
  { name = "continuo-engine-contract" },
@@ -203,7 +203,7 @@ test = [
203
203
 
204
204
  [[package]]
205
205
  name = "continuo-engine-contract"
206
- version = "0.8.0"
206
+ version = "0.8.1"
207
207
  source = { editable = "contract" }
208
208
  dependencies = [
209
209
  { name = "sqlglot" },
@@ -225,7 +225,7 @@ provides-extras = ["testing"]
225
225
 
226
226
  [[package]]
227
227
  name = "continuo-postgres-adapter"
228
- version = "0.3.0"
228
+ version = "0.3.1"
229
229
  source = { editable = "adapters/postgres" }
230
230
  dependencies = [
231
231
  { name = "continuo-engine-contract" },
@@ -262,7 +262,7 @@ test = [
262
262
 
263
263
  [[package]]
264
264
  name = "continuo-python-runtime"
265
- version = "0.9.0"
265
+ version = "0.10.0"
266
266
  source = { editable = "." }
267
267
  dependencies = [
268
268
  { name = "boto3" },
@@ -305,7 +305,7 @@ test = [
305
305
 
306
306
  [[package]]
307
307
  name = "continuo-trino-adapter"
308
- version = "0.3.0"
308
+ version = "0.3.1"
309
309
  source = { editable = "adapters/trino" }
310
310
  dependencies = [
311
311
  { name = "continuo-engine-contract" },
@@ -1,3 +0,0 @@
1
- continuo-python-runtime==0.9.0
2
- continuo-duckdb-adapter==0.2.0
3
- continuo-engine-contract==0.8.0
@@ -1,3 +0,0 @@
1
- continuo-python-runtime==0.9.0
2
- continuo-postgres-adapter==0.3.0
3
- continuo-engine-contract==0.8.0
@@ -1,3 +0,0 @@
1
- continuo-python-runtime==0.9.0
2
- continuo-trino-adapter==0.3.0
3
- continuo-engine-contract==0.8.0