idfkit 1.2.0__tar.gz → 1.3.0rc1__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 (379) hide show
  1. idfkit-1.3.0rc1/.github/workflows/check-identity.yml +12 -0
  2. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/conformance.yml +1 -1
  3. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/main.yml +49 -1
  4. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/rehearse-all.yml +4 -1
  5. {idfkit-1.2.0 → idfkit-1.3.0rc1}/CHANGELOG.md +238 -1
  6. {idfkit-1.2.0 → idfkit-1.3.0rc1}/CLAUDE.md +6 -4
  7. {idfkit-1.2.0 → idfkit-1.3.0rc1}/Makefile +2 -2
  8. {idfkit-1.2.0 → idfkit-1.3.0rc1}/PKG-INFO +39 -21
  9. {idfkit-1.2.0 → idfkit-1.3.0rc1}/README.md +34 -18
  10. idfkit-1.3.0rc1/agent_references/snippets/document-and-objects.py +332 -0
  11. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/hvac-loops.py +30 -11
  12. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/parsing-idf-epjson.py +1 -2
  13. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/result-parsing.py +1 -1
  14. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/schema-and-validation.py +3 -2
  15. idfkit-1.3.0rc1/agent_references/snippets/threading.py +66 -0
  16. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/writing-output.py +6 -0
  17. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/SKILL.md +5 -1
  18. idfkit-1.3.0rc1/agent_references/templates/document-and-objects.md +220 -0
  19. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/hvac-loops.md +9 -9
  20. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/parsing-idf-epjson.md +5 -1
  21. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/reference-tracking.md +1 -1
  22. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/result-parsing.md +1 -1
  23. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/schema-and-validation.md +2 -2
  24. idfkit-1.3.0rc1/agent_references/templates/threading.md +71 -0
  25. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/version-migration.md +2 -0
  26. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/weather-data.md +1 -1
  27. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/writing-output.md +15 -0
  28. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/bench.py +56 -32
  29. idfkit-1.3.0rc1/benchmarks/bench_free_threading.py +177 -0
  30. {idfkit-1.2.0 → idfkit-1.3.0rc1}/pyproject.toml +14 -15
  31. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/check_naming_register.py +20 -4
  32. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/SKILL.md +5 -1
  33. idfkit-1.3.0rc1/src/idfkit/.agents/skills/developing-with-idfkit/references/document-and-objects.md +457 -0
  34. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/hvac-loops.md +39 -20
  35. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/parsing-idf-epjson.md +6 -3
  36. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/reference-tracking.md +1 -1
  37. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/result-parsing.md +2 -2
  38. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/schema-and-validation.md +5 -4
  39. idfkit-1.3.0rc1/src/idfkit/.agents/skills/developing-with-idfkit/references/threading.md +100 -0
  40. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/version-migration.md +2 -0
  41. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/weather-data.md +1 -1
  42. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/writing-output.md +16 -0
  43. idfkit-1.3.0rc1/src/idfkit/_atomic.py +109 -0
  44. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/_compat.py +7 -1
  45. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/_compat_object.py +18 -16
  46. idfkit-1.3.0rc1/src/idfkit/_document_types.py +130 -0
  47. idfkit-1.3.0rc1/src/idfkit/_forksafe.py +56 -0
  48. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/_generated_types.pyi +20564 -7753
  49. idfkit-1.3.0rc1/src/idfkit/codegen/generate_stubs.py +1984 -0
  50. idfkit-1.3.0rc1/src/idfkit/codegen/verify_stubs.py +544 -0
  51. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/compat/_cli.py +3 -3
  52. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/document.py +88 -17
  53. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/document.pyi +2119 -70
  54. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/download.py +4 -19
  55. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/epjson_parser.py +6 -4
  56. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/exceptions.py +17 -9
  57. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/geometry.py +11 -11
  58. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/geometry_builders.py +11 -11
  59. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/idf_parser.py +25 -19
  60. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/introspection.py +6 -2
  61. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/async_runner.py +3 -3
  62. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/diff.py +6 -6
  63. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/runner.py +3 -3
  64. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/objects.py +208 -47
  65. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/scene.py +8 -8
  66. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/builder.py +4 -4
  67. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/evaluate.py +7 -7
  68. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/file.py +57 -9
  69. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/holidays.py +4 -4
  70. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/series.py +5 -5
  71. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/week.py +4 -4
  72. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/year.py +4 -4
  73. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schema.py +119 -29
  74. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/_common.py +14 -15
  75. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/_external_files.py +5 -5
  76. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/async_runner.py +6 -5
  77. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/cache.py +4 -4
  78. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/expand.py +32 -26
  79. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/outputs.py +14 -26
  80. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/runner.py +9 -8
  81. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/surface_matching.py +9 -9
  82. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/validation.py +18 -15
  83. idfkit-1.3.0rc1/src/idfkit/versioned.py +55 -0
  84. idfkit-1.3.0rc1/src/idfkit/versioned.pyi +25311 -0
  85. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/visualization/model.py +6 -6
  86. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/designday.py +51 -14
  87. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/download.py +144 -46
  88. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/geocode.py +10 -5
  89. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/index.py +11 -4
  90. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/writers.py +46 -25
  91. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/zoning.py +12 -10
  92. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/conftest.py +14 -0
  93. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_agent_references.py +2 -2
  94. idfkit-1.3.0rc1/tests/test_atomic.py +173 -0
  95. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_codegen.py +25 -17
  96. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_document.py +214 -30
  97. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_download.py +1 -1
  98. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_eppy_compat.py +5 -19
  99. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_expand.py +14 -4
  100. idfkit-1.3.0rc1/tests/test_fixed_fields_not_flat_extensible.py +222 -0
  101. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_geometry.py +7 -36
  102. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_introspection.py +28 -0
  103. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_parsers.py +3 -2
  104. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_schema.py +0 -8
  105. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_outputs.py +29 -1
  106. idfkit-1.3.0rc1/tests/test_simulation_prep_outputs.py +60 -0
  107. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_runner.py +1 -1
  108. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_strict_typing.py +6 -4
  109. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_surface_matching.py +2 -3
  110. idfkit-1.3.0rc1/tests/test_thread_safety.py +666 -0
  111. idfkit-1.3.0rc1/tests/test_trailing_underscore_fields.py +315 -0
  112. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_validation.py +80 -8
  113. idfkit-1.3.0rc1/tests/test_versioned.py +346 -0
  114. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_weather_designday.py +43 -0
  115. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_weather_download.py +79 -0
  116. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_weather_index.py +20 -0
  117. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_writers.py +130 -1
  118. idfkit-1.3.0rc1/tests/type_checks/typed_fields_fixture.py +65 -0
  119. idfkit-1.3.0rc1/tests/type_checks/versioned_fixture.py +180 -0
  120. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tox.ini +2 -1
  121. {idfkit-1.2.0 → idfkit-1.3.0rc1}/uv.lock +1 -1
  122. idfkit-1.2.0/agent_references/snippets/document-and-objects.py +0 -131
  123. idfkit-1.2.0/agent_references/templates/document-and-objects.md +0 -132
  124. idfkit-1.2.0/src/idfkit/.agents/skills/developing-with-idfkit/references/document-and-objects.md +0 -203
  125. idfkit-1.2.0/src/idfkit/codegen/generate_stubs.py +0 -852
  126. idfkit-1.2.0/tests/test_simulation_prep_outputs.py +0 -37
  127. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.devcontainer/devcontainer.json +0 -0
  128. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.devcontainer/postCreateCommand.sh +0 -0
  129. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/CODEOWNERS +0 -0
  130. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/actions/setup-python-env/action.yml +0 -0
  131. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/ep-testfiles.sparse +0 -0
  132. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/adopt-energyplus.yml +0 -0
  133. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/archive-retired-site.yml +0 -0
  134. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/benchmark.yml +0 -0
  135. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/deploy-py-redirects.yml +0 -0
  136. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/notify-downstream.yml +0 -0
  137. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/on-release-main.yml +0 -0
  138. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.github/workflows/validate-codecov-config.yml +0 -0
  139. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.gitignore +0 -0
  140. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.pre-commit-config.yaml +0 -0
  141. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.pre-commit-hooks.yaml +0 -0
  142. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.python-version +0 -0
  143. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.vscode/extensions.json +0 -0
  144. {idfkit-1.2.0 → idfkit-1.3.0rc1}/.vscode/settings.json +0 -0
  145. {idfkit-1.2.0 → idfkit-1.3.0rc1}/AGENTS.md +0 -0
  146. {idfkit-1.2.0 → idfkit-1.3.0rc1}/CONTRIBUTING.md +0 -0
  147. {idfkit-1.2.0 → idfkit-1.3.0rc1}/LICENSE +0 -0
  148. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/geometry-and-surfaces.py +0 -0
  149. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/geometry-builders-and-zoning.py +0 -0
  150. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/hvac-templates.py +0 -0
  151. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/reference-tracking.py +0 -0
  152. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/schedule-evaluation.py +0 -0
  153. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/simulation-execution.py +0 -0
  154. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/thermal-properties.py +0 -0
  155. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/version-migration.py +0 -0
  156. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/visualization.py +0 -0
  157. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/snippets/weather-data.py +0 -0
  158. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/geometry-and-surfaces.md +0 -0
  159. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/geometry-builders-and-zoning.md +0 -0
  160. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/hvac-templates.md +0 -0
  161. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/schedule-evaluation.md +0 -0
  162. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/simulation-execution.md +0 -0
  163. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/thermal-properties.md +0 -0
  164. {idfkit-1.2.0 → idfkit-1.3.0rc1}/agent_references/templates/visualization.md +0 -0
  165. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/aggregate.py +0 -0
  166. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/bench_eso.py +0 -0
  167. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/bench_progress.py +0 -0
  168. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/bench_typecheckers.py +0 -0
  169. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/eso_results.json +0 -0
  170. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/profile_field_access.py +0 -0
  171. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/profile_lookup.py +0 -0
  172. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/profile_overloads.py +0 -0
  173. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/profile_stubs.py +0 -0
  174. {idfkit-1.2.0 → idfkit-1.3.0rc1}/benchmarks/progress_benchmark_results.json +0 -0
  175. {idfkit-1.2.0 → idfkit-1.3.0rc1}/codecov.yaml +0 -0
  176. {idfkit-1.2.0 → idfkit-1.3.0rc1}/old-sitemaps/README.md +0 -0
  177. {idfkit-1.2.0 → idfkit-1.3.0rc1}/old-sitemaps/developers.idfkit.com.retired.txt +0 -0
  178. {idfkit-1.2.0 → idfkit-1.3.0rc1}/old-sitemaps/developers.idfkit.com.txt +0 -0
  179. {idfkit-1.2.0 → idfkit-1.3.0rc1}/old-sitemaps/js.idfkit.com.sitemap.xml +0 -0
  180. {idfkit-1.2.0 → idfkit-1.3.0rc1}/old-sitemaps/py.idfkit.com.sitemap.xml +0 -0
  181. {idfkit-1.2.0 → idfkit-1.3.0rc1}/redirects/README.md +0 -0
  182. {idfkit-1.2.0 → idfkit-1.3.0rc1}/redirects/path-map.json +0 -0
  183. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/_governance_source.py +0 -0
  184. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/adopt_energyplus.py +0 -0
  185. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/build_redirect_site.py +0 -0
  186. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/build_weather_index.py +0 -0
  187. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/check_parity_ledger.py +0 -0
  188. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/check_redirects.py +0 -0
  189. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/check_release_conformance.py +0 -0
  190. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/check_release_version.py +0 -0
  191. {idfkit-1.2.0 → idfkit-1.3.0rc1}/scripts/sweep_example_files.py +0 -0
  192. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/geometry-and-surfaces.md +0 -0
  193. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/geometry-builders-and-zoning.md +0 -0
  194. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/hvac-templates.md +0 -0
  195. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/schedule-evaluation.md +0 -0
  196. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/simulation-execution.md +0 -0
  197. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/thermal-properties.md +0 -0
  198. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/.agents/skills/developing-with-idfkit/references/visualization.md +0 -0
  199. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/__init__.py +0 -0
  200. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/_accessors.py +0 -0
  201. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/_conformance.py +0 -0
  202. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/codegen/__init__.py +0 -0
  203. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/codegen/bake_references.py +0 -0
  204. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/codegen/generate_conformance.py +0 -0
  205. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/codegen/generate_doc_locations.py +0 -0
  206. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/codegen/generate_epw_sentinels.py +0 -0
  207. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/compat/__init__.py +0 -0
  208. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/compat/_checker.py +0 -0
  209. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/compat/_diff.py +0 -0
  210. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/compat/_extract.py +0 -0
  211. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/compat/_models.py +0 -0
  212. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/compat/_sarif.py +0 -0
  213. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/cst.py +0 -0
  214. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/docs.py +0 -0
  215. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/__init__.py +0 -0
  216. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/async_subprocess_backend.py +0 -0
  217. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/chain.py +0 -0
  218. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/progress.py +0 -0
  219. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/protocol.py +0 -0
  220. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/report.py +0 -0
  221. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/migration/subprocess_backend.py +0 -0
  222. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/py.typed +0 -0
  223. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/references.py +0 -0
  224. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/__init__.py +0 -0
  225. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/compact.py +0 -0
  226. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/day.py +0 -0
  227. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/day_types.py +0 -0
  228. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/time_utils.py +0 -0
  229. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schedules/types.py +0 -0
  230. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V22-1-0/Energy+.schema.epJSON.gz +0 -0
  231. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V22-2-0/Energy+.schema.epJSON.gz +0 -0
  232. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V23-1-0/Energy+.schema.epJSON.gz +0 -0
  233. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V23-2-0/Energy+.schema.epJSON.gz +0 -0
  234. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V24-1-0/Energy+.schema.epJSON.gz +0 -0
  235. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V24-2-0/Energy+.schema.epJSON.gz +0 -0
  236. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V25-1-0/Energy+.schema.epJSON.gz +0 -0
  237. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V25-2-0/Energy+.schema.epJSON.gz +0 -0
  238. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V26-1-0/Energy+.schema.epJSON.gz +0 -0
  239. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V26-2-0/Energy+.schema.epJSON.gz +0 -0
  240. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V8-9-0/Energy+.schema.epJSON.gz +0 -0
  241. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V9-0-1/Energy+.schema.epJSON.gz +0 -0
  242. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V9-1-0/Energy+.schema.epJSON.gz +0 -0
  243. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V9-2-0/Energy+.schema.epJSON.gz +0 -0
  244. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V9-3-0/Energy+.schema.epJSON.gz +0 -0
  245. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V9-4-0/Energy+.schema.epJSON.gz +0 -0
  246. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V9-5-0/Energy+.schema.epJSON.gz +0 -0
  247. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/V9-6-0/Energy+.schema.epJSON.gz +0 -0
  248. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/schemas/doc_locations.json +0 -0
  249. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/__init__.py +0 -0
  250. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/async_batch.py +0 -0
  251. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/batch.py +0 -0
  252. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/config.py +0 -0
  253. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/fs.py +0 -0
  254. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/parsers/__init__.py +0 -0
  255. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/parsers/_timeutil.py +0 -0
  256. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/parsers/csv.py +0 -0
  257. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/parsers/err.py +0 -0
  258. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/parsers/eso.py +0 -0
  259. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/parsers/html.py +0 -0
  260. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/parsers/rdd.py +0 -0
  261. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/parsers/sql.py +0 -0
  262. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/plotting/__init__.py +0 -0
  263. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/plotting/matplotlib.py +0 -0
  264. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/plotting/plotly.py +0 -0
  265. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/plotting/visualizations.py +0 -0
  266. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/progress.py +0 -0
  267. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/progress_bars.py +0 -0
  268. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/simulation/result.py +0 -0
  269. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/thermal/__init__.py +0 -0
  270. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/thermal/gas.py +0 -0
  271. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/thermal/properties.py +0 -0
  272. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/versions.py +0 -0
  273. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/visualization/__init__.py +0 -0
  274. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/visualization/svg.py +0 -0
  275. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/__init__.py +0 -0
  276. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/_browser/__init__.py +0 -0
  277. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/_browser/assets/app.js +0 -0
  278. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/_browser/assets/index.html +0 -0
  279. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/_browser/assets/style.css +0 -0
  280. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/_browser/server.py +0 -0
  281. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/_cli.py +0 -0
  282. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/_epw_sentinels.py +0 -0
  283. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/data/stations.json.gz +0 -0
  284. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/epw.py +0 -0
  285. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/spatial.py +0 -0
  286. {idfkit-1.2.0 → idfkit-1.3.0rc1}/src/idfkit/weather/station.py +0 -0
  287. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/generate_eso_fixtures.py +0 -0
  288. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/sample.csv +0 -0
  289. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/sample.err +0 -0
  290. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/sample.eso +0 -0
  291. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/sample.mdd +0 -0
  292. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/sample.mtr +0 -0
  293. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/sample.rdd +0 -0
  294. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/sample_fatal.err +0 -0
  295. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/simulation/sample_truncated.eso +0 -0
  296. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/weather/empty.ddy +0 -0
  297. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/fixtures/weather/sample.ddy +0 -0
  298. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/__init__.py +0 -0
  299. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_compact.py +0 -0
  300. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_day.py +0 -0
  301. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_day_types.py +0 -0
  302. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_evaluate.py +0 -0
  303. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_file.py +0 -0
  304. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_holidays.py +0 -0
  305. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_series.py +0 -0
  306. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_time_utils.py +0 -0
  307. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_types.py +0 -0
  308. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_week.py +0 -0
  309. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/schedules/test_year.py +0 -0
  310. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_accessors.py +0 -0
  311. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_adopt_energyplus.py +0 -0
  312. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_basic.py +0 -0
  313. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_check_redirects.py +0 -0
  314. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_check_release_version.py +0 -0
  315. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_cli_migrate.py +0 -0
  316. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_compat_cli.py +0 -0
  317. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_compat_diff.py +0 -0
  318. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_compat_extract.py +0 -0
  319. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_docs.py +0 -0
  320. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_epjson_roundtrip.py +0 -0
  321. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_exceptions.py +0 -0
  322. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_extensible_canonical_api.py +0 -0
  323. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_extensible_eppy_compat.py +0 -0
  324. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_extensible_normalization.py +0 -0
  325. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_geometry_builders.py +0 -0
  326. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_governance_source_matches.py +0 -0
  327. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_idf_roundtrip.py +0 -0
  328. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_logging.py +0 -0
  329. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_async_runner.py +0 -0
  330. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_chain.py +0 -0
  331. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_config.py +0 -0
  332. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_diff.py +0 -0
  333. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_e2e.py +0 -0
  334. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_exceptions.py +0 -0
  335. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_report.py +0 -0
  336. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_runner.py +0 -0
  337. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_migration_subprocess_backend.py +0 -0
  338. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_object_update.py +0 -0
  339. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_objects.py +0 -0
  340. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_references.py +0 -0
  341. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_roundtrip.py +0 -0
  342. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_scene.py +0 -0
  343. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_schedule_builder.py +0 -0
  344. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_async.py +0 -0
  345. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_batch.py +0 -0
  346. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_cache.py +0 -0
  347. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_config.py +0 -0
  348. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_csv_parser.py +0 -0
  349. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_e2e.py +0 -0
  350. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_err_parser.py +0 -0
  351. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_eso_parser.py +0 -0
  352. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_external_files.py +0 -0
  353. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_fs.py +0 -0
  354. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_html_parser.py +0 -0
  355. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_plotting.py +0 -0
  356. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_preprocessor_timeout.py +0 -0
  357. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_progress.py +0 -0
  358. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_progress_bars.py +0 -0
  359. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_rdd_parser.py +0 -0
  360. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_result.py +0 -0
  361. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_sql_parser.py +0 -0
  362. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_simulation_version_mismatch.py +0 -0
  363. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_thermal.py +0 -0
  364. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_type_name_lookup.py +0 -0
  365. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_versions.py +0 -0
  366. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_visualization.py +0 -0
  367. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_visualization_model.py +0 -0
  368. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_weather_cli.py +0 -0
  369. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_weather_geocode.py +0 -0
  370. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_weather_offline.py +0 -0
  371. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_weather_spatial.py +0 -0
  372. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_weather_station.py +0 -0
  373. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/test_zoning.py +0 -0
  374. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/type_checks/update_fixture.py +0 -0
  375. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/weather/__init__.py +0 -0
  376. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/weather/epw_fixtures.py +0 -0
  377. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/weather/test_epw.py +0 -0
  378. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/weather/test_epw_sentinels.py +0 -0
  379. {idfkit-1.2.0 → idfkit-1.3.0rc1}/tests/weather/test_index_climate_zone.py +0 -0
@@ -0,0 +1,12 @@
1
+ name: Identity
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ identity:
10
+ uses: idfkit/idfkit-brand/.github/workflows/check-touchpoint.yml@identity-2026.5
11
+ with:
12
+ identity_level: identity-2026.5
@@ -68,7 +68,7 @@ jobs:
68
68
 
69
69
  - name: Set up the environment
70
70
  # 3.11 is the floor the corpus runner needs: it reads case files with tomllib, which is
71
- # standard library only from 3.11. The library itself is tested across 3.10 to 3.13 in
71
+ # standard library only from 3.11. The library itself is tested across 3.10 to 3.14 in
72
72
  # main.yml; this job is about the corpus, not about the supported interpreter range.
73
73
  uses: ./.github/actions/setup-python-env
74
74
  with:
@@ -35,7 +35,7 @@ jobs:
35
35
  runs-on: ubuntu-latest
36
36
  strategy:
37
37
  matrix:
38
- python-version: ["3.10", "3.11", "3.12", "3.13"]
38
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
39
39
  fail-fast: false
40
40
  defaults:
41
41
  run:
@@ -62,6 +62,54 @@ jobs:
62
62
  env:
63
63
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
64
64
 
65
+ tests-free-threaded:
66
+ # The suite on free-threaded CPython with the GIL off. The core is pure Python and declares
67
+ # the Free Threading classifier on the strength of this job, and tests/test_thread_safety.py
68
+ # reproduces races only here: on a GIL build most of them cannot happen.
69
+ name: "tests (3.14t, GIL off)"
70
+ runs-on: ubuntu-latest
71
+ # A lock regression shows up as a deadlock. The race tests fail on their own deadline, but this
72
+ # caps whatever they miss well short of GitHub's six-hour default.
73
+ timeout-minutes: 30
74
+ defaults:
75
+ run:
76
+ shell: bash
77
+ steps:
78
+ - name: Check out
79
+ uses: actions/checkout@v4
80
+
81
+ - name: Set up the environment
82
+ uses: ./.github/actions/setup-python-env
83
+ with:
84
+ python-version: "3.14t"
85
+
86
+ # A `3.14t` request that resolved to a GIL build would pass every test below while proving
87
+ # nothing, so fail loudly first. The import check runs WITHOUT PYTHON_GIL=0: an interpreter
88
+ # re-enables the GIL when it imports an extension module that does not declare support, and
89
+ # that override would hide such a module. idfkit and all of its sub-packages must keep it
90
+ # off on their own.
91
+ - name: Check the interpreter is free-threaded and idfkit keeps the GIL off
92
+ run: |
93
+ uv run python - <<'EOF'
94
+ import importlib, pkgutil, sys, sysconfig
95
+ assert sysconfig.get_config_var("Py_GIL_DISABLED"), f"not a free-threaded build: {sys.version}"
96
+ import idfkit
97
+ # walk_packages yields each package before importing it, so the import below is the first and
98
+ # raises on any failure: no `onerror` is needed for a broken package to fail this step.
99
+ for mod in pkgutil.walk_packages(idfkit.__path__, "idfkit."):
100
+ if ".codegen" not in mod.name:
101
+ importlib.import_module(mod.name)
102
+ assert not sys._is_gil_enabled(), "importing idfkit re-enabled the GIL"
103
+ print(sys.version, "- GIL off after importing every idfkit module")
104
+ EOF
105
+
106
+ # PYTHON_GIL=0 because the locked pandas (2.3) re-enables the GIL at import; pandas 3 does
107
+ # not. The override keeps the whole run, the pandas tests included, genuinely GIL-free.
108
+ - name: Run tests
109
+ env:
110
+ PYTHON_GIL: "0"
111
+ run: uv run python -m pytest -W error::DeprecationWarning
112
+
65
113
  example-sweep:
66
114
  # Read every EnergyPlus example file, for every version idfkit bundles a schema for.
67
115
  #
@@ -14,6 +14,9 @@ name: Rehearse a candidate against every consumer
14
14
  # consumer's pull requests, it cannot block any consumer's unrelated work (FR-013).
15
15
  #
16
16
  # A dispatch that does not land fails this run. A rehearsal that reached nobody is not a rehearsal.
17
+ # This run then waits for every rehearsal to conclude and fails if any one fails or times out, so a
18
+ # candidate that breaks a consumer is red here as well. The job timeout covers the consumers' own
19
+ # 45-minute rehearsal jobs.
17
20
  on:
18
21
  workflow_dispatch:
19
22
  inputs:
@@ -28,7 +31,7 @@ permissions:
28
31
  jobs:
29
32
  rehearse:
30
33
  runs-on: ubuntu-latest
31
- timeout-minutes: 10
34
+ timeout-minutes: 60
32
35
  steps:
33
36
  - name: Check out this repository for its governance pin
34
37
  uses: actions/checkout@v4
@@ -7,6 +7,242 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.0-rc.1] - 2026-11-14
11
+
12
+ ### Added
13
+
14
+ - The README opens with the idfkit identity header at `identity-2026.5`: the mark, the
15
+ repository's one sentence, the family links and the badge row. The `Identity` workflow
16
+ (`.github/workflows/check-identity.yml`) holds the header to that tag on every pull request.
17
+ - A field whose epJSON key ends in `_` could be given without the underscore, in `add()`,
18
+ attribute access, `update()` and the eppy helpers: `cond_flag_is_the_basement_conditioned="FALSE"`
19
+ set `GroundHeatTransfer:Basement:Interior.cond_flag_is_the_basement_conditioned_`, and the value
20
+ was stored and written under the schema's key. The underscore comes from IDD names that end in
21
+ punctuation or a space, and EnergyPlus has started dropping it: `WindowShadingControl`'s
22
+ `construction_with_shading_name_` in 9.0.1 and 9.1.0 is `construction_with_shading_name` from
23
+ 9.2.0, and the one spelling worked on both. `name` was never such a spelling. `add()` refused a
24
+ call that gave both spellings of one field, even with one of them blank. The typed `update()`
25
+ stubs name only the schema's key. ([60c47cc](https://github.com/idfkit/idfkit/commit/60c47cc),
26
+ [9b208de](https://github.com/idfkit/idfkit/commit/9b208de))
27
+ - Python 3.14 became a supported version: CI runs the test suite and type check on it, and the package declares the 3.14 classifier ([#253](https://github.com/idfkit/idfkit/pull/253)).
28
+ - Free-threaded CPython 3.14 (`python3.14t`, GIL disabled) became a tested build: a CI job runs the test suite on it with the GIL off and fails if importing any idfkit module re-enables the GIL, and the package declares the `Free Threading :: 2 - Beta` classifier. The threading contract is now stated in the bundled agent references (`threading.md`): the schema manager and module-level caches are safe from any number of threads, separate documents are independent, and one document may be read from many threads but must not be mutated by several at once. Of the optional extras, only `pandas` before 3.0 re-enables the GIL on import. `benchmarks/bench_free_threading.py` measures thread scaling: on 4 CPUs, four threads parse IDF files 2.6 times faster than one thread on the regular build, and other workloads gain 1.0 to 1.6 times ([#254](https://github.com/idfkit/idfkit/pull/254)).
29
+ - `idfkit.weather.designday.sanitize_ddy_bytes()`, the in-memory form of `sanitize_ddy_file()`, which blanks placeholder values in DDY content before it is written anywhere ([#254](https://github.com/idfkit/idfkit/pull/254)).
30
+ - **A document became typeable for the EnergyPlus release it was written for, not only the
31
+ latest one.** `idfkit.versioned` gained one type per supported release,
32
+ `IDFDocumentV<major>_<minor>` from `IDFDocumentV8_9` to `IDFDocumentV26_2` (which is
33
+ `IDFDocument`), and `as_version(doc, IDFDocumentV9_4)`, which returns the document under that
34
+ type after checking its release at run time. Given a tuple of those types, as `isinstance`
35
+ takes one, `as_version` accepts a document of any of them and types it as their union (mypy reduces
36
+ the union to what those releases share), which
37
+ narrows a model of unknown release into a range such as the releases that have `Space`. Such a document is indexed, completed and checked
38
+ with that release's object types, field value types and choices, accessors and `update()`
39
+ keywords: on a 9.4 model `doc["Space"]` is a type error and `People` has
40
+ `zone_or_zonelist_name`. Documents stay typed for the latest release unless their version is
41
+ stated this way. The stubs encode each object type's declaration once per contiguous range of
42
+ releases that share it, and `python -m idfkit.codegen.generate_stubs` now checks what it wrote
43
+ against every bundled schema before finishing, so adopting a release needs no hand edit of
44
+ the stubs, and a regeneration that would misdescribe a release fails ([#194](https://github.com/idfkit/idfkit/issues/194), [#251](https://github.com/idfkit/idfkit/pull/251)).
45
+ - `AnyIDFDocument` (in `idfkit.versioned` and `idfkit.document`) was added. It accepts a
46
+ document of any supported release and, through it, offers what every release has: the object
47
+ types present in all of them. A field whose value type changed between releases reads as any
48
+ release's value and, through assignment and `update()`, accepts only a value every release
49
+ accepts: `heater_fuel_type` can be set to `"NaturalGas"` but not to `"DistrictHeatingWater"`,
50
+ which 9.3 to 23.1 reject. idfkit's own
51
+ functions were retyped to take it, so `save_idf`, `simulate`, the geometry and zoning helpers
52
+ and the rest accept a document typed for any release; `expand_objects` and the ground
53
+ heat-transfer preprocessors return the type they were given. An object type or accessor that
54
+ some release lacks is reported against the type that lacks it, for example
55
+ `"Space" is not a defined key in "IDFDocumentV9_4[bool]"`, and through `AnyIDFDocument` as
56
+ `"Space" is not a defined key in "IDFDocumentAllVersions[bool]"`. Naming one release's type costs
57
+ pyright about what naming `IDFDocument` does, 2 to 3 seconds for a file; a file naming every
58
+ release's type takes about 7 seconds
59
+ ([#194](https://github.com/idfkit/idfkit/issues/194), [#251](https://github.com/idfkit/idfkit/pull/251)).
60
+ - Every object type of the latest release gained an `Any<Class>` type, importable from `idfkit.document` inside
61
+ `if TYPE_CHECKING:`, for annotating code that takes an object of any supported release:
62
+ `def floor_area(zone: AnyZone)` accepts a latest-release zone, a 9.4 zone and a zone reached
63
+ through `AnyIDFDocument`, where `Zone` accepts only the first. Through it, fields follow the
64
+ `AnyIDFDocument` rules, extensible groups included, so `AnyBuildingSurfaceDetailed.vertices` is
65
+ typed. For a type no release changed, `Any<Class>` is the latest class itself (`AnyZoneList` is
66
+ `ZoneList`). For a type that arrived after 8.9, it covers the releases that have it:
67
+ `AnySpace` accepts a `Space` of any release from 9.6, and its docstring says so. A type that
68
+ a release before the latest removed, such as `DistrictHeating`, has none: its classes are
69
+ named for the releases that have it (`DistrictHeating_v8_9_to_v23_1`). `Any<Class>` covers the
70
+ releases idfkit supports, so adopting a release that drops or renames a field removes that
71
+ field from it
72
+ ([#194](https://github.com/idfkit/idfkit/issues/194), [#251](https://github.com/idfkit/idfkit/pull/251)).
73
+
74
+ ### Changed
75
+
76
+ - **Breaking:** for type checking only, an undeclared field name on a typed object is now a type
77
+ error. `material.thermal_resistance = 1.0` on a `Material` taken from `doc["Material"]`, or
78
+ `print(material.bogus)`, used to type-check clean because the catch-all `__getattr__` and
79
+ `__setattr__` of `IDFObject` accepted any name. pyright reports the name whether it is read or
80
+ assigned; mypy reports an assignment, and types a read as `object`, so it reports any use that
81
+ needs a more specific type.
82
+ The same holds for the groups of an extensible type, such as `surface.vertices[0]`. Typed objects
83
+ declare snake_case names only, so an eppy-style capitalised spelling such as
84
+ `material.Thickness = 0.2` is now reported too, although it still works at run time: use
85
+ `material.thickness`. The typed classes describe EnergyPlus 26.2, so a field name that only exists
86
+ in an earlier version, such as `Lights.watts_per_zone_floor_area` up to 23.2, is reported as well;
87
+ on such a model, reach it with item access (`lights["watts_per_zone_floor_area"]`) or `getattr`.
88
+ Objects from the eppy API (`doc.idfobjects[...]`, `newidfobject()`, `getobject()`) and other plain
89
+ `IDFObject`s, such as those from `doc.add()` or `doc.get_collection()`, stay permissive. Declared
90
+ fields, `name`, the spellings of trailing-underscore fields with and without the underscore, and
91
+ wrong-type errors behave as before. Run-time behaviour is unchanged. ([#272](https://github.com/idfkit/idfkit/pull/272))
92
+
93
+ - **Breaking:** `IDFDocument.add()` raised `InvalidFieldError` when given a non-empty name for a
94
+ type without a Name field, such as `Output:Meter`, `Output:Variable`, `Output:SQLite`,
95
+ `Timestep` or `SimulationControl` (165 of the 880 types in the 26.2 schema), whether or not
96
+ `validate` is set. The name used to be stored and then dropped by both writers, so
97
+ `add("Output:SQLite", "SimpleAndTabular")` wrote `Output:SQLite,` and EnergyPlus produced no
98
+ `.sql` file. The message names the field to pass instead (`key_name="Electricity:Facility"`
99
+ for `Output:Meter`, `option_type=...` for `Output:SQLite`); the value is never moved there for
100
+ you. The same rule applies to `obj.name = ...` and to `newidfobject`, `addidfobject` and
101
+ `copyidfobject(..., new_name=...)`. Loading existing IDF and epJSON files is unaffected.
102
+ ([11ac720](https://github.com/idfkit/idfkit/commit/11ac720))
103
+ - **Breaking:** `IDFDocument.add()` refused a key that is not a field of the object type even
104
+ with `validate=False`, raising `InvalidFieldError` that names every such key, whatever its
105
+ value. `validate=False` used to skip schema validation entirely, so the key was stored, then
106
+ silently dropped by `write_idf()` and written as an unknown property by `write_epjson()`. It
107
+ still skips value, required-field and range checks. The object's name belongs in the `name`
108
+ argument, not in `fields`; display spellings such as `"X Origin"` are refused with the field
109
+ to use instead; and `FenestrationSurface:Detailed` takes its fixed `vertex_N_x_coordinate`
110
+ fields, not a `vertices` list. ([1fe5171](https://github.com/idfkit/idfkit/commit/1fe5171),
111
+ [b415635](https://github.com/idfkit/idfkit/commit/b415635))
112
+ - **Breaking:** `IDFDocument.add()` checked the extensible groups it was given in either
113
+ validate mode, as `IDFObject.update()` already did, and raised `TypeError` for a group key the
114
+ group does not name (`reports=[{"report_nme": ...}]`) or a wrapper value that is not a list of
115
+ groups (a string, a list of strings or tuples, or another object's live `vertices` view).
116
+ Validation never looked inside the groups, so these were stored, and `write_idf()` dropped the
117
+ values or crashed. ([b415635](https://github.com/idfkit/idfkit/commit/b415635))
118
+ - `IDFCollection` became covariant in its object type, so `IDFCollection[Zone]` is accepted
119
+ where `IDFCollection[IDFObject]` is expected. `IDFCollection.remove` now takes any `IDFObject`,
120
+ and `get(name, default)` returns the object type or the default's type. The usual cost of
121
+ covariance came with it: a function that takes `IDFCollection[IDFObject]` can `add()` an object of
122
+ another type to the collection it is given, such as a `Material` to `doc["Zone"]`, without a type
123
+ error, and `add()` does not check it at run time
124
+ ([#194](https://github.com/idfkit/idfkit/issues/194), [#251](https://github.com/idfkit/idfkit/pull/251)).
125
+
126
+ ### Removed
127
+
128
+ - **Breaking:** `SchemaManager.get_schema` is no longer wrapped in `functools.lru_cache`, so `get_schema.cache_clear()`, `get_schema.cache_info()` and `get_schema.cache_parameters()` are gone. The manager's own cache already held every loaded version; call `SchemaManager.clear_cache()` to drop it ([#254](https://github.com/idfkit/idfkit/pull/254)).
129
+
130
+ ### Fixed
131
+
132
+ - The bundled agent references described the changes above wrongly or not at all. The HVAC loops
133
+ reference still called `validate_document` and `get_referencing` the safety net for node wiring,
134
+ though neither sees node names, and offered renaming a coil as a fix for a mismatched node name.
135
+ The references now say that typed objects accept only 26.2 snake_case field names, that an empty
136
+ group-only object such as `Output:Table:SummaryReports` is written to IDF as `Type,;`, and that
137
+ `prep_outputs` also requests the `AllSummary` tabular report.
138
+ ([2399407](https://github.com/idfkit/idfkit/commit/2399407))
139
+ - `EpJSONSchema.get_field_names()` dropped the first field of every type, including types without
140
+ a Name field, whose first field is an ordinary one: `get_field_names("Output:Meter")` was
141
+ `['reporting_frequency']`. It now drops the first field only for a named type. As a result
142
+ `describe_object_type()` (and `IDFDocument.describe()`) lists `key_name` for `Output:Meter`,
143
+ and the generated `OutputMeter` class and its `update()` keywords include it; the same holds
144
+ for every other nameless type. Validation no longer counts a name stored on a nameless object
145
+ as a declared name. ([11ac720](https://github.com/idfkit/idfkit/commit/11ac720))
146
+ - Fixed fields whose names look like a flat spelling of the type's own extensible group were
147
+ treated as that group's fields. This affected `MaterialProperty:GlazingSpectralData`
148
+ (`wavelength_1` … `back_reflectance_4`), `Site:SpectrumData` (`wavelength`, `spectrum`,
149
+ `wavelength_1`, `spectrum_2`), `ZoneHVAC:HybridUnitaryHVAC` (`mode_0_*`) and, in EnergyPlus 8.9
150
+ to 9.1, `Table:MultiVariableLookup` (`field_1_…` and `field_2_…`). `add()` moved these values
151
+ into the extensible groups with a deprecation warning, or raised `ValueError` ("cannot mix") when
152
+ the groups were passed too. `load_epjson()` moved them silently, after any groups already in the
153
+ file. Either way `write_idf` wrote the fixed fields blank and the values out of place. On
154
+ `Site:SpectrumData`, `wavelength_1` overwrote `wavelength`. Attribute assignment stored the
155
+ value in a group (for `mode_0_*`, the last mode), attribute reads of an unset field returned a
156
+ group's value, and `update()` refused the field as a legacy flat name. These fields are now
157
+ kept as fixed fields.
158
+ ([cee2d7c](https://github.com/idfkit/idfkit/commit/cee2d7c))
159
+ - A field whose epJSON name ends in `_` was out of reach by attribute: assigning it or passing it
160
+ to `update()` raised `InvalidFieldError`, and so did reading it unset in a strict document,
161
+ because the trailing underscore was stripped into a name the type does not have. In a document
162
+ opened with `strict=False`, the assignment was stored under that name instead, and `write_idf()`
163
+ dropped it. 24 such fields exist across the bundled schemas, among them
164
+ `GroundHeatTransfer:Basement:Interior.cond_flag_is_the_basement_conditioned_`,
165
+ `GroundHeatTransfer:Basement:ManualGrid.nx_number_of_cells_in_the_x_direction_20_` and
166
+ `SizingPeriod:DesignDay.ashrae_clear_sky_optical_depth_for_beam_irradiance_taub_`; `add()`
167
+ already accepted them. `update()` also refused `Output:Table:ReportPeriod.name_` as the
168
+ object's name. The eppy-style dotted `IDFDocument.update()`, `get_referenced_object()`,
169
+ `getrange()`, `checkrange()` and `getreferingobjs(fields=...)` missed them the same way.
170
+ ([a520960](https://github.com/idfkit/idfkit/commit/a520960),
171
+ [9b208de](https://github.com/idfkit/idfkit/commit/9b208de))
172
+ - A flat extensible name with group number 0, such as `vertex_0_x_coordinate` or
173
+ `mode_0_minimum_outdoor_air_temperature`, was read as the last group: assignment overwrote the
174
+ last group's value, or raised `IndexError` when there were no groups, and `add()` inserted it
175
+ as a new first group. Group numbers now start at 1, so such a name is an unknown field.
176
+ ([da8a613](https://github.com/idfkit/idfkit/commit/da8a613))
177
+ - `write_idf` and `save_idf` wrote an object with nothing in it without its terminating `;`, so it
178
+ swallowed the object after it, and EnergyPlus read that object's type name and values as fields
179
+ of the empty one. This happened to a nameless type with no fixed fields
180
+ (`Output:Table:SummaryReports`, `Output:Diagnostics`, `OutdoorAir:NodeList`,
181
+ `EnergyManagementSystem:GlobalVariable` and the six `GroundHeatTransfer` face types) when it held
182
+ no groups. It affected the standard and `nocomment` output types, `doc.copy()`, and objects
183
+ added to a document loaded with `preserve_formatting=True`. Such an object is now written with
184
+ one blank field, `Output:Diagnostics,;`, which keeps the next object intact. IDF has no spelling
185
+ that EnergyPlus reads as one of these objects with nothing in it: `Output:Diagnostics;` swallows
186
+ the next object too, and EnergyPlus reads `Output:Diagnostics,;` as one blank group. That gives a
187
+ warning for `Output:Diagnostics` and a Severe error for `Output:Table:SummaryReports`, and
188
+ EnergyPlus refuses the input for `OutdoorAir:NodeList` and
189
+ `EnergyManagementSystem:GlobalVariable`. Remove such an object, or write the model as epJSON, to
190
+ keep it empty. ([3e3e772](https://github.com/idfkit/idfkit/commit/3e3e772),
191
+ [0dcfdd3](https://github.com/idfkit/idfkit/commit/0dcfdd3))
192
+ - Strict IDF parsing no longer rejects blank fields past the last field of a type without
193
+ repeated groups, such as `Timestep,4,,;` or `GroundHeatTransfer:Slab:XFACE,;`; EnergyPlus accepts
194
+ them too. A non-blank extra value is still an error, and the error counts only the values up to
195
+ the last non-blank one. ([3e3e772](https://github.com/idfkit/idfkit/commit/3e3e772),
196
+ [0dcfdd3](https://github.com/idfkit/idfkit/commit/0dcfdd3))
197
+ - `write_idf` dropped the name and every field of an object whose type was spelled in a different
198
+ case from the schema, such as one added with `addidfobject(IDFObject(obj_type="TIMESTEP", ...))`,
199
+ and of an object of a type the schema does not know. Both are now written with their values.
200
+ ([0dcfdd3](https://github.com/idfkit/idfkit/commit/0dcfdd3))
201
+ - **Breaking:** Validation reported unknown fields (`W003`) on extensible object types too, and
202
+ on unknown fields with a blank value. Extensible types used to be exempt, so
203
+ `doc.add("Output:Table:SummaryReports", "", report_1="AllSummary")` passed the default
204
+ `validate=True` and stored a key that `write_idf()` silently dropped and `write_epjson()`
205
+ emitted as an unknown property. Such a call now raises `ValidationFailedError`, as an unknown
206
+ field on a non-extensible type already did; the wrapper list (`reports=[...]`) and the
207
+ deprecated flat spellings (`report_name`, `report_name_2`) are still accepted.
208
+ `validate_document()` reports the same keys on objects loaded from epJSON.
209
+ ([459258f](https://github.com/idfkit/idfkit/commit/459258f), [b415635](https://github.com/idfkit/idfkit/commit/b415635))
210
+ - `prep_outputs()` requested the `AllSummary` tabular report, which it never had. It passed
211
+ `report_1`, a spelling `Output:Table:SummaryReports` does not have, so `write_idf()` wrote the object with
212
+ no fields and no terminator, and EnergyPlus read the next object into it and stopped with a
213
+ fatal error. ([381c12f](https://github.com/idfkit/idfkit/commit/381c12f),
214
+ [1fe5171](https://github.com/idfkit/idfkit/commit/1fe5171))
215
+ - `OutputVariableIndex.add_all_to_model` stored each meter name in the `key_name` field of the
216
+ `Output:Meter` objects it adds. It used to pass the name as the object name, which
217
+ `Output:Meter` does not have, so both writers dropped it: `write_idf` emitted
218
+ `Output:Meter,,Timestep;` and EnergyPlus stopped with `Missing required property 'key_name'`.
219
+ The added meters now have an empty name, like meters loaded from a file, so
220
+ `doc["Output:Meter"]["<meter>"]` no longer finds them, and calling `add_all_to_model` twice
221
+ adds each meter twice instead of raising `DuplicateObjectError`
222
+ ([d214b51](https://github.com/idfkit/idfkit/commit/d214b51)).
223
+ - `IDFCollection.remove` (and so `IDFDocument.removeidfobject`) removed the first object equal
224
+ to the one passed instead of that object. Two objects of a type with no Name field are equal
225
+ when their fields match, so removing one `Output:Meter` could remove another and leave the
226
+ one passed in the document ([c6514d0](https://github.com/idfkit/idfkit/commit/c6514d0)).
227
+ - The `hvac-loops` and `reference-tracking` agent references no longer state that
228
+ `validate_document` reports node-name typos. It reports a dangling object-name reference as
229
+ `E009`, not `E004`, and does not check node names at all. They also no longer list setpoint
230
+ managers among the objects `get_referencing` returns for a coil, and the node walk under
231
+ "Common mistakes" now finds a node in any field, including branch components and outdoor-air
232
+ mixer streams, so it no longer fails on correct models such as `5ZoneAirCooled`.
233
+ - Threads asking for the same schema version at the same moment each loaded and built their own copy, so a cold start with 16 threads read the schema file 16 times and handed out 16 different `EpJSONSchema` instances. All but one were then pickled by content instead of by version. The manager now loads each version once and gives every thread the same instance, and on a free-threaded build the first calls to `get_schema_manager()` no longer create several managers ([#254](https://github.com/idfkit/idfkit/pull/254)).
234
+ - `ScheduleFileCache.get_values` could raise `KeyError` when another thread cleared or invalidated the cache between its store and its read, and `ScheduleFileCache.invalidate` could raise `RuntimeError: dictionary changed size during iteration` when another thread added an entry while it ran. A read still in flight when another thread called `invalidate` or `clear` also cached what it had read before the call, which silently undid the invalidation; such a read is now returned but not cached ([#254](https://github.com/idfkit/idfkit/pull/254)).
235
+ - `SchemaManager.clear_cache` did not stop a load already in flight from caching its schema afterwards, so the clear was undone and a request made after it could be handed the pre-clear instance. Such a load now returns its schema to its caller without caching it. A logging handler or other code that asks for the version being loaded no longer deadlocks on the new load lock ([#254](https://github.com/idfkit/idfkit/pull/254)).
236
+ - `copy.deepcopy` of a `ScheduleFileCache` or `SchemaManager` that other threads were using could raise `RuntimeError: dictionary changed size during iteration`, and `copy.copy` of a cache returned an alias that shared its entries but not its invalidations. Both now copy a consistent snapshot into an independent object, and instances pickled by idfkit 1.2.0 still load and work ([#254](https://github.com/idfkit/idfkit/pull/254)).
237
+ - `ScheduleFileCache.invalidate` discarded every read in flight, of any file, so a steady stream of invalidations of one file stopped the cache caching the others. It now discards only reads of the file it invalidates. A child forked while another thread was inside `invalidate` or `clear` no longer inherits a half-invalidated cache ([#254](https://github.com/idfkit/idfkit/pull/254)).
238
+ - The weather cache is now safe to share between threads and processes. `WeatherDownloader.download` wrote the archive, the extracted files and the cleaned DDY in place, so callers downloading the same station at once could read a truncated file or raise "Downloaded file is not a valid ZIP archive", and a truncated file could stay in the cache for good; `StationIndex.refresh()` likewise rewrote `stations.json.gz` and its KML sources in place, so a concurrent `StationIndex.load()` raised `EOFError` or moved the half-written index aside. Every weather cache file, the IP-location cache and downloaded schemas are now written under a temporary name and renamed into place, with the permissions an in-place write would have given them. Extracted files carry their archive's modification time and size, so a file left truncated by an older version, or extracted from an archive that has since been replaced, is extracted again, and a corrupt archive is downloaded again on the next call. Threads of one process download a station once. Files inside folders of an archive, such as macOS `__MACOSX` resource forks, are no longer extracted beside the weather files ([#254](https://github.com/idfkit/idfkit/pull/254)).
239
+ - A child process forked while another thread was waiting out the geocoding rate limit hung on its first `geocode()` or `detect_location()` call ([#254](https://github.com/idfkit/idfkit/pull/254)).
240
+ - `simulate` computed the cache key from the caller's model before copying it, so an edit made on another thread in between could store one model's results under another model's key. `async_simulate` read the caller's model on a worker thread after copying it, which raised on a free-threaded build when another coroutine edited the model meanwhile. Both now work only from their copy ([#254](https://github.com/idfkit/idfkit/pull/254)).
241
+ - `expand_objects`, `IDFDocument.expand` and the ground heat-transfer preprocessors returned a
242
+ strict document for a model opened with `strict=False` whenever a preprocessor ran; only a model
243
+ with nothing to expand came back as it was. The result now keeps the model's strictness, as
244
+ `IDFDocument.copy` does ([#194](https://github.com/idfkit/idfkit/issues/194), [#251](https://github.com/idfkit/idfkit/pull/251)).
245
+
10
246
  ## [1.2.0] - 2026-10-28
11
247
 
12
248
  This release moves to `conformance-2026.21`. Through `conformance-2026.19`, the level the TypeScript library also declares, the corpus adds five cases for the multi-field `update` shared with the TypeScript library, an `update` operation and a `post-operation-epjson` assertion (`conformance-2026.17`), and removes the divergence entries for those cases in both languages: the ten Python entries that `IDFObject.update` closes (`conformance-2026.18`) and the six TypeScript ones (`conformance-2026.19`). `conformance-2026.20` then removes the two Python round-trip entries that #183 closes. An idfkit release without `IDFObject.update` fails the five update cases at this level, and one that still stores a blank fixed field as `""` fails those two. `conformance-2026.21` removes the three Python entries that #187 closes, so a release that still drops an unterminated final object fails `malformed-truncated-object` and `preserve-unterminated-final`.
@@ -977,7 +1213,8 @@ Initial public release.
977
1213
  - Performance benchmarks comparing idfkit against eppy and opyplus. ([#5](https://github.com/idfkit/idfkit/pull/5))
978
1214
  - MkDocs Material documentation site with a full API reference, an eppy migration guide, and a getting-started Jupyter notebook. ([#2](https://github.com/idfkit/idfkit/pull/2))
979
1215
 
980
- [Unreleased]: https://github.com/idfkit/idfkit/compare/v1.2.0...HEAD
1216
+ [Unreleased]: https://github.com/idfkit/idfkit/compare/v1.3.0-rc.1...HEAD
1217
+ [1.3.0-rc.1]: https://github.com/idfkit/idfkit/compare/v1.2.0...v1.3.0-rc.1
981
1218
  [1.2.0]: https://github.com/idfkit/idfkit/compare/v1.1.1...v1.2.0
982
1219
  [1.1.1]: https://github.com/idfkit/idfkit/compare/v1.1.0...v1.1.1
983
1220
  [1.1.0]: https://github.com/idfkit/idfkit/compare/v1.0.0...v1.1.0
@@ -14,12 +14,14 @@ Key capabilities: IDF/epJSON parsing and writing, schema-driven validation, 3D g
14
14
 
15
15
  The core package — including simulation, schedules, thermal, and geometry — has **zero third-party dependencies** (stdlib only). Optional extras (defined in `pyproject.toml`):
16
16
 
17
- - `weather` — openpyxl (for station index refresh; bundled index works without it)
18
- - `pandas` / `dataframes` — pandas (for DataFrame result conversion)
17
+ - `sim` — empty; simulation needs no extras
18
+ - `pandas` — pandas (for DataFrame result conversion); `dataframes` is an alias
19
19
  - `plot` — matplotlib | `plotly` — plotly (plotting backends)
20
20
  - `progress` — tqdm (simulation progress bars)
21
- - `s3` / `async-s3` / `cloud` — boto3 / aiobotocore (cloud storage)
22
- - `all` — everything above
21
+ - `s3` — boto3 (`cloud` is an alias) | `async-s3` — aiobotocore (cloud storage)
22
+ - `all` — pandas, matplotlib, plotly, boto3, aiobotocore and tqdm
23
+
24
+ There is no `weather` extra: weather search, download and station-index refresh are stdlib only.
23
25
 
24
26
  ## Common Commands
25
27
 
@@ -12,7 +12,7 @@ install: ## Install the virtual environment and install the pre-commit hooks
12
12
  check-stubs: ## Verify generated type stubs are up-to-date
13
13
  @echo "🚀 Checking type stub consistency"
14
14
  @uv run python -m idfkit.codegen.generate_stubs
15
- @git diff --exit-code src/idfkit/_generated_types.pyi src/idfkit/document.pyi || \
15
+ @git diff --exit-code src/idfkit/_generated_types.pyi src/idfkit/document.pyi src/idfkit/versioned.pyi src/idfkit/versioned.py || \
16
16
  (echo "❌ Generated stubs are out of date. Run: uv run python -m idfkit.codegen.generate_stubs" && exit 1)
17
17
 
18
18
  .PHONY: check-conformance-level
@@ -99,7 +99,7 @@ test: ## Test the code with pytest
99
99
  @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml
100
100
 
101
101
  .PHONY: weather-index
102
- weather-index: ## Rebuild the bundled weather station index (requires network + openpyxl)
102
+ weather-index: ## Rebuild the bundled weather station index (requires network; standard library only)
103
103
  @echo "🚀 Building weather station index"
104
104
  @uv run python scripts/build_weather_index.py
105
105
 
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: idfkit
3
- Version: 1.2.0
3
+ Version: 1.3.0rc1
4
4
  Summary: A fast, modern EnergyPlus IDF/epJSON parser with O(1) lookups and reference tracking
5
- Project-URL: Homepage, https://py.idfkit.com/
5
+ Project-URL: Homepage, https://developers.idfkit.com/
6
6
  Project-URL: Repository, https://github.com/idfkit/idfkit
7
- Project-URL: Documentation, https://py.idfkit.com/
7
+ Project-URL: Documentation, https://developers.idfkit.com/
8
8
  Author-email: Samuel Letellier-Duchesne <developers@idfkit.com>
9
9
  License-File: LICENSE
10
10
  Keywords: building simulation,energy modeling,energyplus,epjson,idf,parser
@@ -18,6 +18,8 @@ Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
21
23
  Classifier: Topic :: Scientific/Engineering
22
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
25
  Classifier: Typing :: Typed
@@ -48,12 +50,21 @@ Requires-Dist: boto3>=1.26; extra == 's3'
48
50
  Provides-Extra: sim
49
51
  Description-Content-Type: text/markdown
50
52
 
53
+ <p align="center">
54
+ <picture>
55
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/idfkit/idfkit-brand/identity-2026.5/marks/mark-on-dark.svg">
56
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/idfkit/idfkit-brand/identity-2026.5/marks/mark-on-light.svg">
57
+ <img alt="idfkit" width="72" src="https://raw.githubusercontent.com/idfkit/idfkit-brand/identity-2026.5/marks/tile.png">
58
+ </picture>
59
+ </p>
60
+
51
61
  # idfkit
52
62
 
53
- [![Release](https://img.shields.io/github/v/release/idfkit/idfkit)](https://github.com/idfkit/idfkit/releases)
54
- [![Build status](https://img.shields.io/github/actions/workflow/status/idfkit/idfkit/main.yml?branch=main)](https://github.com/idfkit/idfkit/actions/workflows/main.yml?query=branch%3Amain)
55
- [![codecov](https://codecov.io/gh/idfkit/idfkit/branch/main/graph/badge.svg)](https://codecov.io/gh/idfkit/idfkit)
56
- [![License](https://img.shields.io/github/license/idfkit/idfkit)](https://github.com/idfkit/idfkit/blob/main/LICENSE)
63
+ An EnergyPlus IDF and epJSON toolkit for Python, with no third-party dependencies.
64
+
65
+ [idfkit](https://idfkit.com) · [Developers](https://developers.idfkit.com) · [EnergyPlus docs](https://docs.idfkit.com) · [Envelop](https://app.idfkit.com) · [MCP](https://mcp.idfkit.com) · [Shoebox](https://shoebox.idfkit.com) · [GitHub](https://github.com/idfkit)
66
+
67
+ [![Release](https://img.shields.io/github/v/release/idfkit/idfkit)](https://github.com/idfkit/idfkit/releases) [![Build status](https://img.shields.io/github/actions/workflow/status/idfkit/idfkit/main.yml?branch=main)](https://github.com/idfkit/idfkit/actions/workflows/main.yml?query=branch%3Amain) [![codecov](https://codecov.io/gh/idfkit/idfkit/branch/main/graph/badge.svg)](https://codecov.io/gh/idfkit/idfkit) [![License](https://img.shields.io/github/license/idfkit/idfkit)](https://github.com/idfkit/idfkit/blob/main/LICENSE) [![Capability parity](https://img.shields.io/badge/capability-parity-informational)](https://developers.idfkit.com/explanation/parity/)
57
68
 
58
69
  **A fast, modern EnergyPlus IDF/epJSON toolkit for Python.**
59
70
 
@@ -64,9 +75,9 @@ Description-Content-Type: text/markdown
64
75
  >
65
76
  > Code that still calls `write_*` with a path fails without naming the cause:
66
77
  > `write_epjson(doc, path)` raises `TypeError: can't multiply sequence by
67
- > non-int of type 'PosixPath'`, and `write_idf(doc, path)` writes nothing,
68
- > leaving an empty file that surfaces later as
69
- > `Could not detect EnergyPlus version in file`.
78
+ > non-int of type 'PosixPath'`, and `write_idf(doc, path)` takes the path as
79
+ > its `output_type` argument, returns the IDF text, and writes no file, so the
80
+ > failure surfaces only when the file is next read.
70
81
  >
71
82
  > Search for `write_idf(` and `write_epjson(` called with two positional
72
83
  > arguments, or run a type checker, which rejects both. Full notes are in the
@@ -115,8 +126,8 @@ formats.
115
126
 
116
127
  ## Performance
117
128
 
118
- idfkit is designed from the ground up for speed. On a **1,700-object IDF**,
119
- looking up a single object by name is **over 3000x faster** than eppy and opyplus
129
+ idfkit is designed from the ground up for speed. On a **1,700-object IDF**
130
+ (Darwin arm64, median of 5 runs), looking up a single object by name is **over 2,000x** faster than eppy, **over 3,500x** faster than opyplus, and **over 130x** faster than energyplus-idd-idf-utilities
120
131
  thanks to O(1) dict-based indexing:
121
132
 
122
133
  <picture>
@@ -127,6 +138,7 @@ thanks to O(1) dict-based indexing:
127
138
 
128
139
  See [full benchmark results](https://developers.idfkit.com/benchmarks/)
129
140
  for all six operations (load, get by type, get by name, add, modify, write) across four tools.
141
+ energyplus-idd-idf-utilities is measured on four of them (load, get by type, get by name, write).
130
142
 
131
143
  ## Installation
132
144
 
@@ -146,13 +158,16 @@ uv add idfkit
146
158
 
147
159
  | Extra | Install command | What it adds |
148
160
  |-------|----------------|--------------|
149
- | `weather` | `pip install idfkit[weather]` | Refresh weather station indexes from source (openpyxl) |
150
- | `dataframes` | `pip install idfkit[dataframes]` | DataFrame result conversion (pandas) |
161
+ | `sim` | `pip install idfkit[sim]` | Nothing; simulation requires no extra packages |
162
+ | `pandas` | `pip install idfkit[pandas]` | DataFrame result conversion (pandas) |
163
+ | `dataframes` | `pip install idfkit[dataframes]` | Alias of `pandas` |
151
164
  | `s3` | `pip install idfkit[s3]` | S3 cloud storage backend (boto3) |
165
+ | `async-s3` | `pip install idfkit[async-s3]` | Asynchronous S3 storage backend (aiobotocore) |
166
+ | `cloud` | `pip install idfkit[cloud]` | Alias of `s3` |
152
167
  | `plot` | `pip install idfkit[plot]` | Matplotlib plotting |
153
168
  | `plotly` | `pip install idfkit[plotly]` | Plotly interactive charts |
154
169
  | `progress` | `pip install idfkit[progress]` | tqdm progress bars for simulations |
155
- | `all` | `pip install idfkit[all]` | Everything above |
170
+ | `all` | `pip install idfkit[all]` | pandas, matplotlib, plotly, boto3, aiobotocore, and tqdm |
156
171
 
157
172
  ## Quick Example
158
173
 
@@ -177,9 +192,11 @@ for obj in doc.get_referencing("Office"):
177
192
  save_idf(doc, "out.idf")
178
193
  ```
179
194
 
180
- > **Note:** `load_idf()` defaults to strict parsing (`strict=True`) and raises
181
- > `IDFParseError` on malformed objects. Use `strict=False` only as a tolerant
182
- > migration/compatibility fallback for legacy or noisy files.
195
+ > **Note:** `load_idf()` defaults to strict parsing (`strict_parsing=True`) and
196
+ > raises `IDFParseError` on malformed objects. Use `strict_parsing=False` only as a
197
+ > tolerant migration/compatibility fallback for legacy or noisy files. The separate
198
+ > `strict` argument (default `True`) makes reading or setting an unknown field name
199
+ > on an object raise `InvalidFieldError` instead of returning `None`.
183
200
 
184
201
  ### Creating a model from scratch
185
202
 
@@ -239,10 +256,12 @@ The two share a vocabulary and are held to a
239
256
  that proves they read and write the same files the same way.
240
257
 
241
258
  They are not equivalent, and this page will not imply that they are. All
242
- thirteen first-tier capabilities exist in both: parsing, the object model,
259
+ fourteen first-tier capabilities exist in both: parsing, the object model,
243
260
  references, writers, schema access, validation, introspection, documentation
244
261
  addresses, generated object types, parse diagnostics, the weather station index,
245
- weather file retrieval, and geocoding. Almost everything else on this page is
262
+ weather file retrieval, geocoding, and the conformance declaration. Three of them
263
+ are recorded as partial on one side: generated object types in Python, and the
264
+ weather station index and weather file retrieval in JavaScript. Almost everything else on this page is
246
265
  Python-only today, including running EnergyPlus locally, reading simulation
247
266
  results, geometry authoring, zoning, schedules, thermal properties, and
248
267
  migration. Some of that is a port not yet done; some of it, such as driving a
@@ -296,7 +315,6 @@ baked into that exact version.
296
315
  make install # Install dependencies and pre-commit hooks
297
316
  make check # Run linting, formatting, and type checks
298
317
  make test # Run tests with coverage
299
- make docs # Serve documentation locally
300
318
  ```
301
319
 
302
320
  ## Contributing