httk-core 2.1.1__tar.gz → 2.1.2__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 (258) hide show
  1. {httk_core-2.1.1 → httk_core-2.1.2}/PKG-INFO +1 -1
  2. {httk_core-2.1.1 → httk_core-2.1.2}/pyproject.toml +1 -1
  3. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/__init__.py +5 -0
  4. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/data_records.py +24 -2
  5. httk_core-2.1.2/src/httk/core/entry_records.py +238 -0
  6. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/exactmath.py +198 -44
  7. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/fracvector.py +6 -1
  8. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/surdvector.py +38 -0
  9. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk_core.egg-info/PKG-INFO +1 -1
  10. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk_core.egg-info/SOURCES.txt +2 -0
  11. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_data_records.py +23 -2
  12. httk_core-2.1.2/tests/test_entry_records.py +212 -0
  13. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_exactmath.py +5 -5
  14. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_exactmath_api.py +55 -4
  15. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_fracvector.py +11 -0
  16. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_surdvector.py +11 -0
  17. {httk_core-2.1.1 → httk_core-2.1.2}/LICENSE +0 -0
  18. {httk_core-2.1.1 → httk_core-2.1.2}/README.md +0 -0
  19. {httk_core-2.1.1 → httk_core-2.1.2}/setup.cfg +0 -0
  20. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/_discover.py +0 -0
  21. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/_iris.py +0 -0
  22. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/_json.py +0 -0
  23. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/_manifest.py +0 -0
  24. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/_plugins.py +0 -0
  25. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/_registry_tool.py +0 -0
  26. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/_sentinel.py +0 -0
  27. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/atomic_write.py +0 -0
  28. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/building.py +0 -0
  29. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/citations.py +0 -0
  30. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/cli.py +0 -0
  31. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/converting.py +0 -0
  32. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/crypto.py +0 -0
  33. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/dataset_loader.py +0 -0
  34. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datasets.py +0 -0
  35. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/__init__.py +0 -0
  36. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_api.py +0 -0
  37. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_backend.py +0 -0
  38. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_bytes.py +0 -0
  39. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_bytes_view.py +0 -0
  40. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_common.py +0 -0
  41. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_file.py +0 -0
  42. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_file_view.py +0 -0
  43. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_filename.py +0 -0
  44. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_filename_view.py +0 -0
  45. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_like.py +0 -0
  46. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_request.py +0 -0
  47. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_request_view.py +0 -0
  48. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_url.py +0 -0
  49. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_url_view.py +0 -0
  50. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/bytestream_view.py +0 -0
  51. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/compression.py +0 -0
  52. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/datastream_url.py +0 -0
  53. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/network_policy.py +0 -0
  54. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_api.py +0 -0
  55. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_backend.py +0 -0
  56. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_common.py +0 -0
  57. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_file.py +0 -0
  58. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_file_view.py +0 -0
  59. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_filename.py +0 -0
  60. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_filename_view.py +0 -0
  61. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_like.py +0 -0
  62. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_request.py +0 -0
  63. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_request_view.py +0 -0
  64. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_string.py +0 -0
  65. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_string_view.py +0 -0
  66. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_url.py +0 -0
  67. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_url_view.py +0 -0
  68. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/datastream/textstream_view.py +0 -0
  69. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/digests.py +0 -0
  70. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/__init__.py +0 -0
  71. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/__main__.py +0 -0
  72. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/assets/selector.css +0 -0
  73. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/assets/selector.js +0 -0
  74. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/cli.py +0 -0
  75. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/config.py +0 -0
  76. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/ecosystem.py +0 -0
  77. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/gitsite.py +0 -0
  78. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/inventories.py +0 -0
  79. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/lockfile.py +0 -0
  80. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/manifests.py +0 -0
  81. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/redirect.py +0 -0
  82. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/release.py +0 -0
  83. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/semver.py +0 -0
  84. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/sitetree.py +0 -0
  85. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/docs/sphinx_ext.py +0 -0
  86. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/entry_ids.py +0 -0
  87. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/entry_provider.py +0 -0
  88. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/entry_types.py +0 -0
  89. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/fetching.py +0 -0
  90. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/files.py +0 -0
  91. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/identity.py +0 -0
  92. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/identity_cli.py +0 -0
  93. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/loading.py +0 -0
  94. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/memguard.py +0 -0
  95. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/__init__.py +0 -0
  96. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/entries.py +0 -0
  97. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/filter/README.md +0 -0
  98. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/filter/__init__.py +0 -0
  99. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/filter/_miniparser.py +0 -0
  100. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/filter/optimade_filter_grammar.ebnf +0 -0
  101. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/filter/parser.py +0 -0
  102. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/resources.py +0 -0
  103. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/optimade/standard_names.py +0 -0
  104. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/plugins/__init__.py +0 -0
  105. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/plugins/cli.py +0 -0
  106. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/plugins/install.py +0 -0
  107. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/plugins/installed.py +0 -0
  108. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/plugins/manifest.py +0 -0
  109. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/precision.py +0 -0
  110. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/project/__init__.py +0 -0
  111. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/project/anchor.py +0 -0
  112. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/project/cli.py +0 -0
  113. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/project/export.py +0 -0
  114. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/project/manifests.py +0 -0
  115. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/project/members.py +0 -0
  116. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/project/sealing.py +0 -0
  117. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/project/templates.py +0 -0
  118. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/property_definitions.py +0 -0
  119. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/provenance.py +0 -0
  120. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/py.typed +0 -0
  121. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/records.py +0 -0
  122. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/register/__init__.py +0 -0
  123. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/register/_base.py +0 -0
  124. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/register/cli.py +0 -0
  125. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/register/entries.py +0 -0
  126. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/register/io.py +0 -0
  127. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/register/members.py +0 -0
  128. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/register/schemas.py +0 -0
  129. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/report.py +0 -0
  130. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/saving.py +0 -0
  131. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/schema_check.py +0 -0
  132. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/services.py +0 -0
  133. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/storage/__init__.py +0 -0
  134. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/storage/identity.py +0 -0
  135. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/storage/markers.py +0 -0
  136. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/storage/rational_text.py +0 -0
  137. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/storage/stored_properties.py +0 -0
  138. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/system.py +0 -0
  139. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/userdirs.py +0 -0
  140. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/validation/__init__.py +0 -0
  141. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/validation/iris.py +0 -0
  142. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/__init__.py +0 -0
  143. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/_nested.py +0 -0
  144. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/_squarefree.py +0 -0
  145. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/leaf_codecs.py +0 -0
  146. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/mutablefracvector.py +0 -0
  147. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/numeric.py +0 -0
  148. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/scalar_like.py +0 -0
  149. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_api.py +0 -0
  150. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_backend.py +0 -0
  151. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_frac_view.py +0 -0
  152. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_like.py +0 -0
  153. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_native_backend.py +0 -0
  154. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_native_view.py +0 -0
  155. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_numpy_backend.py +0 -0
  156. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_numpy_view.py +0 -0
  157. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_surd_view.py +0 -0
  158. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vector_view.py +0 -0
  159. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/vectors/vectormath.py +0 -0
  160. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/views/__init__.py +0 -0
  161. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/views/backend.py +0 -0
  162. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/views/coercion.py +0 -0
  163. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/views/unviewing.py +0 -0
  164. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/views/unwrapping.py +0 -0
  165. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/core/views/view.py +0 -0
  166. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/cli/core/__init__.py +0 -0
  167. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/entries/core/__init__.py +0 -0
  168. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/LICENSE +0 -0
  169. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/LICENSE.httk +0 -0
  170. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/README.md +0 -0
  171. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/__init__.py +0 -0
  172. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/calculations.json +0 -0
  173. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/files.json +0 -0
  174. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/records.json +0 -0
  175. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/references.json +0 -0
  176. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk/registry/schemas/core/runs.json +0 -0
  177. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk_core.egg-info/dependency_links.txt +0 -0
  178. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk_core.egg-info/entry_points.txt +0 -0
  179. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk_core.egg-info/requires.txt +0 -0
  180. {httk_core-2.1.1 → httk_core-2.1.2}/src/httk_core.egg-info/top_level.txt +0 -0
  181. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_building.py +0 -0
  182. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_building_engine.py +0 -0
  183. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_citations.py +0 -0
  184. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_cli.py +0 -0
  185. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_coercion.py +0 -0
  186. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_compression.py +0 -0
  187. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_converting.py +0 -0
  188. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_crypto.py +0 -0
  189. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_dataset_loader.py +0 -0
  190. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_datasets.py +0 -0
  191. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_datastream_url.py +0 -0
  192. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_digests.py +0 -0
  193. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_cli.py +0 -0
  194. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_config.py +0 -0
  195. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_ecosystem.py +0 -0
  196. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_gitsite.py +0 -0
  197. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_integration.py +0 -0
  198. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_inventories_release.py +0 -0
  199. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_lockfile.py +0 -0
  200. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_manifests_redirect.py +0 -0
  201. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_repair.py +0 -0
  202. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_semver.py +0 -0
  203. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_sitetree.py +0 -0
  204. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_docs_sphinx_ext.py +0 -0
  205. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_entry_ids.py +0 -0
  206. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_entry_provider.py +0 -0
  207. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_entry_types.py +0 -0
  208. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_examples.py +0 -0
  209. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_fetching.py +0 -0
  210. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_files.py +0 -0
  211. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_identity.py +0 -0
  212. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_identity_cli.py +0 -0
  213. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_leaf_codecs.py +0 -0
  214. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_load_many.py +0 -0
  215. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_loading.py +0 -0
  216. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_memguard.py +0 -0
  217. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_mutablefracvector.py +0 -0
  218. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_network_policy.py +0 -0
  219. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_numeric.py +0 -0
  220. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_operator_identity.py +0 -0
  221. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_optimade_filter.py +0 -0
  222. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_optimade_resources.py +0 -0
  223. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_optimade_standard_names.py +0 -0
  224. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_optimade_timestamps.py +0 -0
  225. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_plugins_cli.py +0 -0
  226. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_plugins_install.py +0 -0
  227. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_plugins_manifest.py +0 -0
  228. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_precision.py +0 -0
  229. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_project.py +0 -0
  230. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_project_cli_seal.py +0 -0
  231. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_project_export.py +0 -0
  232. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_project_init_template.py +0 -0
  233. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_project_manifests.py +0 -0
  234. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_project_members.py +0 -0
  235. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_project_sealing.py +0 -0
  236. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_project_templates.py +0 -0
  237. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_property_definitions.py +0 -0
  238. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_provenance.py +0 -0
  239. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_records.py +0 -0
  240. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_registry_discovery.py +0 -0
  241. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_registry_tool.py +0 -0
  242. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_release_checker.py +0 -0
  243. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_release_prepare.py +0 -0
  244. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_report.py +0 -0
  245. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_saving.py +0 -0
  246. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_schema_check.py +0 -0
  247. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_services.py +0 -0
  248. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_storage_markers.py +0 -0
  249. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_stored_properties.py +0 -0
  250. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_system_cli.py +0 -0
  251. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_textstream.py +0 -0
  252. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_unview.py +0 -0
  253. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_userdirs.py +0 -0
  254. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_validation_iris.py +0 -0
  255. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_vector_family.py +0 -0
  256. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_vector_hash_equality.py +0 -0
  257. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_vectormath.py +0 -0
  258. {httk_core-2.1.1 → httk_core-2.1.2}/tests/test_view_laziness.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: httk-core
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: httk₂ core functionality
5
5
  Author-email: Rickard Armiento <rickard-gpg@armiento.net>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "httk-core"
7
- version = "2.1.1"
7
+ version = "2.1.2"
8
8
  description = "httk₂ core functionality"
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-or-later"
@@ -60,6 +60,7 @@ from .entry_ids import (
60
60
  parse_immutable_id,
61
61
  )
62
62
  from .entry_provider import EntryProvider, RelatedEntry
63
+ from .entry_records import DataEntryRecord, EntryRecord, Property, entry_record
63
64
  from .entry_types import Calculation, File, Reference
64
65
  from .fetching import fetch
65
66
  from .files import FileEntry, FileRecord
@@ -144,6 +145,7 @@ __all__ = [
144
145
  "CLIContext",
145
146
  "Calculation",
146
147
  "CompressionCodec",
148
+ "DataEntryRecord",
147
149
  "DataRecord",
148
150
  "DataRecordEntry",
149
151
  "Dataset",
@@ -155,6 +157,7 @@ __all__ = [
155
157
  "DatastreamLike",
156
158
  "DatastreamURL",
157
159
  "EntryProvider",
160
+ "EntryRecord",
158
161
  "EntryTypeDefinition",
159
162
  "File",
160
163
  "FileEntry",
@@ -167,6 +170,7 @@ __all__ = [
167
170
  "MutableFracVector",
168
171
  "NumericVector",
169
172
  "ProductLink",
173
+ "Property",
170
174
  "PropertyDefinition",
171
175
  "Reference",
172
176
  "Related",
@@ -199,6 +203,7 @@ __all__ = [
199
203
  "content_id",
200
204
  "credits",
201
205
  "decimal_precision",
206
+ "entry_record",
202
207
  "fetch",
203
208
  "format_alternative_id",
204
209
  "format_entry_id",
@@ -3,11 +3,12 @@
3
3
  import datetime
4
4
  import json
5
5
  import math
6
- from collections.abc import Mapping
6
+ from collections.abc import Iterable, Mapping
7
7
  from dataclasses import dataclass, field, fields
8
8
  from typing import Annotated, Any, ClassVar, Self
9
9
 
10
- from .storage import IdentitySkip, Indexed, StorageInfo, Unique, stored_property
10
+ from .provenance import RunEdge, _edges
11
+ from .storage import IdentitySkip, Indexed, StorageInfo, StrongLink, Unique, stored_property
11
12
 
12
13
  RECORDS_DEFINITION_ID = "https://schemas.httk.org/defs/v0.1/entrytypes/records"
13
14
  _CANONICAL_JSON_ERROR = "value_json must be canonical JSON — use DataRecord.from_value."
@@ -60,9 +61,19 @@ class DataRecord:
60
61
  The human-readable and immutable identifiers and timestamp metadata are
61
62
  excluded from content identity.
62
63
 
64
+ ``product_of`` names the entries this value describes, as
65
+ :class:`~httk.core.storage.StrongLink` edges with the same
66
+ :class:`~httk.core.provenance.RunEdge` scheme a :class:`~httk.core.Run` uses:
67
+ string ``(label, entry_type, entry_id)`` triples, part of the record's content
68
+ and therefore pinned to the exact entry revision. It is served forward as the
69
+ ``product_of`` relationship and in reverse as ``has_product`` on the target,
70
+ and is searchable through ``record.links.product_of`` and
71
+ ``target.links.has_product``.
72
+
63
73
  :param definition_id: The property definition IRI for the value.
64
74
  :param name: The property name.
65
75
  :param value_json: The canonical JSON representation of the value.
76
+ :param product_of: The entries this value is a product of, as labeled edges.
66
77
  :param id: The human-readable entry id shared by all revisions; minted by the store when None.
67
78
  :param immutable_id: The per-revision immutable id; minted by the store when None.
68
79
  :param last_modified: The optional timezone-aware metadata timestamp.
@@ -77,6 +88,7 @@ class DataRecord:
77
88
  definition_id: str
78
89
  name: str
79
90
  value_json: str
91
+ product_of: Annotated[tuple[RunEdge, ...], StrongLink("product_of", reverse="has_product", role="subject")] = ()
80
92
  id: Annotated[str | None, IdentitySkip(), Indexed()] = field(default=None, compare=False)
81
93
  immutable_id: Annotated[str | None, IdentitySkip(), Unique()] = field(default=None, compare=False)
82
94
  last_modified: Annotated[datetime.datetime | None, IdentitySkip()] = field(default=None, compare=False)
@@ -115,6 +127,13 @@ class DataRecord:
115
127
  raise ValueError(_CANONICAL_JSON_ERROR) from exc
116
128
  if canonical != self.value_json:
117
129
  raise ValueError(_CANONICAL_JSON_ERROR)
130
+ edges = _edges(self.product_of)
131
+ labels: set[str] = set()
132
+ for edge in edges:
133
+ if edge.label in labels:
134
+ raise ValueError(f"Duplicate label {edge.label!r} on DataRecord product_of.")
135
+ labels.add(edge.label)
136
+ object.__setattr__(self, "product_of", edges)
118
137
  _validate_timestamp(self.last_modified, "last_modified")
119
138
 
120
139
  @classmethod
@@ -124,6 +143,7 @@ class DataRecord:
124
143
  name: str,
125
144
  value: Any,
126
145
  *,
146
+ product_of: Iterable[RunEdge | Mapping[str, Any]] = (),
127
147
  id: str | None = None,
128
148
  immutable_id: str | None = None,
129
149
  last_modified: datetime.datetime | None = None,
@@ -133,6 +153,7 @@ class DataRecord:
133
153
  :param definition_id: The property definition IRI for the value.
134
154
  :param name: The property name.
135
155
  :param value: The JSON value to encode.
156
+ :param product_of: The entries this value is a product of, as labeled edges.
136
157
  :param id: The human-readable entry id shared by all revisions; minted by the store when None.
137
158
  :param immutable_id: The per-revision immutable id; minted by the store when None.
138
159
  :param last_modified: The optional timezone-aware metadata timestamp.
@@ -144,6 +165,7 @@ class DataRecord:
144
165
  definition_id,
145
166
  name,
146
167
  json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False),
168
+ product_of=_edges(product_of),
147
169
  id=id,
148
170
  immutable_id=immutable_id,
149
171
  last_modified=last_modified,
@@ -0,0 +1,238 @@
1
+ """Declarative frozen records with explicit OPTIMADE property metadata."""
2
+
3
+ import datetime
4
+ import hashlib
5
+ import types
6
+ from collections.abc import Callable, Mapping
7
+ from dataclasses import dataclass, field, fields, replace
8
+ from inspect import get_annotations
9
+ from typing import Annotated, Any, ClassVar, Union, dataclass_transform, get_args, get_origin, get_type_hints
10
+
11
+ from .data_records import RECORDS_DEFINITION_ID
12
+ from .property_definitions import EntryTypeDefinition, PropertyDefinition, known_definition_prefixes
13
+ from .register.schemas import load_entry_type_definition
14
+ from .storage import IdentitySkip, Indexed, Skip, StorageInfo, StoredPropertyProjection, Unique
15
+ from .storage.stored_properties import QueryContext, QueryExpression
16
+
17
+ __all__ = ["DataEntryRecord", "EntryRecord", "Property", "entry_record"]
18
+
19
+
20
+ @dataclass(frozen=True, kw_only=True)
21
+ class EntryRecord:
22
+ """Common metadata for application-defined served records.
23
+
24
+ Subclasses declare their entry family's ``type`` and ``definition_id`` and
25
+ use :func:`entry_record` to become concrete frozen records. Metadata fields
26
+ are keyword-only and excluded from equality and content identity.
27
+
28
+ :param id: Public lineage identifier, assigned by the store when omitted.
29
+ :param immutable_id: Revision identifier, assigned by the store when omitted.
30
+ :param last_modified: Optional timezone-aware source modification time.
31
+ :raises ValueError: If an identifier is empty or a timestamp lacks a timezone.
32
+ """
33
+
34
+ id: Annotated[str | None, IdentitySkip(), Indexed()] = field(default=None, compare=False)
35
+ immutable_id: Annotated[str | None, IdentitySkip(), Unique()] = field(default=None, compare=False)
36
+ last_modified: Annotated[datetime.datetime | None, IdentitySkip()] = field(default=None, compare=False)
37
+
38
+ type: ClassVar[str]
39
+ definition_id: ClassVar[str]
40
+ __httk_entry_name__: ClassVar[str]
41
+ __httk_property_definitions__: ClassVar[Mapping[str, PropertyDefinition]]
42
+ __httk_stored_properties__: ClassVar[Mapping[str, StoredPropertyProjection]]
43
+ __httk_storage__: ClassVar[StorageInfo]
44
+
45
+ def __post_init__(self) -> None:
46
+ """Validate explicitly supplied entry metadata."""
47
+ for name in ("id", "immutable_id"):
48
+ value = getattr(self, name)
49
+ if value is not None and (not isinstance(value, str) or not value or value != value.strip()):
50
+ raise ValueError(f"{name} must be a nonempty string without surrounding whitespace or None")
51
+ if self.last_modified is not None and (
52
+ not isinstance(self.last_modified, datetime.datetime) or self.last_modified.utcoffset() is None
53
+ ):
54
+ raise ValueError("last_modified must be a timezone-aware datetime or None")
55
+
56
+ @classmethod
57
+ def entry_type_definition(cls) -> EntryTypeDefinition:
58
+ """Return this concrete record's extended entry-family definition.
59
+
60
+ :return: The base family definition extended with declared properties.
61
+ :raises TypeError: If the class was not decorated with :func:`entry_record`.
62
+ :raises ValueError: If a standard property definition is replaced.
63
+ """
64
+ if "__httk_property_definitions__" not in vars(cls):
65
+ raise TypeError(f"{cls.__name__} must be decorated with entry_record")
66
+ base = load_entry_type_definition(cls.definition_id)
67
+ extra = {}
68
+ for name, definition in cls.__httk_property_definitions__.items():
69
+ if name in base.properties:
70
+ if definition.as_optimade() != base.properties[name].as_optimade():
71
+ raise ValueError(f"{name}: a standard property definition cannot be replaced")
72
+ else:
73
+ extra[name] = definition
74
+ return base.extended(extra)
75
+
76
+
77
+ class DataEntryRecord(EntryRecord):
78
+ """Base for typed application records served at ``_httk_records``.
79
+
80
+ No scientific fields are prescribed. This does not replace the existing
81
+ :class:`~httk.core.data_records.DataRecord` single-property value model.
82
+ """
83
+
84
+ type: ClassVar[str] = "records"
85
+ definition_id: ClassVar[str] = RECORDS_DEFINITION_ID
86
+
87
+
88
+ @dataclass(frozen=True, kw_only=True)
89
+ class Property:
90
+ """Declare a served scalar field through ``Annotated`` metadata.
91
+
92
+ The Python annotation supplies the type (``str``, ``int``, ``float``, or
93
+ ``bool``, optionally nullable). Only marked fields become served attributes.
94
+ Use an existing :class:`~httk.core.property_definitions.PropertyDefinition`
95
+ as the annotation metadata instead when a curated definition exists.
96
+
97
+ :param description: Scientific meaning of the field, including normalization.
98
+ :param unit: Property unit; omitted means dimensionless.
99
+ :param name: Served name; omitted uses ``_httk_custom_<field>``.
100
+ """
101
+
102
+ description: str
103
+ unit: str | None = None
104
+ name: str | None = None
105
+
106
+
107
+ def _scalar_type(annotation: Any) -> tuple[str, bool]:
108
+ """Resolve the exact scalar types supported by direct field mappings."""
109
+ nullable = False
110
+ if get_origin(annotation) in (types.UnionType, Union):
111
+ args = get_args(annotation)
112
+ if len(args) == 2 and type(None) in args:
113
+ annotation = next(arg for arg in args if arg is not type(None))
114
+ nullable = True
115
+ names = {str: "string", int: "integer", float: "float", bool: "boolean"}
116
+ if annotation not in names:
117
+ raise TypeError(
118
+ "Property fields require str, int, float, or bool (optionally | None); use explicit projections otherwise"
119
+ )
120
+ return names[annotation], nullable
121
+
122
+
123
+ def _projection(field_name: str) -> StoredPropertyProjection:
124
+ """Build independent direct-field callbacks for one scalar property."""
125
+
126
+ def query(context: QueryContext, operator: str, value: object) -> QueryExpression:
127
+ return context.compare(context.field(field_name), operator, context.constant(value))
128
+
129
+ return StoredPropertyProjection(
130
+ response=lambda record: getattr(record, field_name),
131
+ query=query,
132
+ sort=lambda context: context.field(field_name),
133
+ )
134
+
135
+
136
+ @dataclass_transform(frozen_default=True)
137
+ def entry_record[T: EntryRecord](name: str) -> Callable[[type[T]], type[T]]:
138
+ """Declare a frozen served record with a stable, explicit identity.
139
+
140
+ This decorator does not register global plugins. Pass the resulting class
141
+ explicitly to the store on creation and reopening. Referenced records remain
142
+ ordinary dataclass fields. Property mappings are compiled for each concrete
143
+ class; subclasses must opt in with their own decoration and identity.
144
+
145
+ :param name: Stable logical identity, independent of the Python module/name.
146
+ :return: A decorator that creates a frozen dataclass and its served mappings.
147
+ :raises TypeError: If the class, field type, or declaration is unsupported.
148
+ :raises ValueError: If identities, property names, or definitions conflict.
149
+ """
150
+ if not isinstance(name, str) or not name or name != name.strip():
151
+ raise ValueError("entry_record requires a nonempty stable name without surrounding whitespace")
152
+
153
+ def decorate(cls: type[T]) -> type[T]:
154
+ if not issubclass(cls, EntryRecord):
155
+ raise TypeError("entry_record requires an EntryRecord subclass")
156
+ if "__dataclass_fields__" in vars(cls):
157
+ raise TypeError("entry_record replaces @dataclass; do not apply both decorators")
158
+ # Resolve this class's own annotations, including deferred ones on Python 3.14.
159
+ if any(key in get_annotations(cls) for key in ("id", "immutable_id", "last_modified")):
160
+ raise TypeError("entry_record metadata fields must retain the EntryRecord declarations")
161
+ if "__httk_stored_properties__" in vars(cls) or "__httk_property_definitions__" in vars(cls):
162
+ raise TypeError(
163
+ "entry_record generates property declarations; use an explicit record for custom projections"
164
+ )
165
+ if not isinstance(getattr(cls, "definition_id", None), str) or not isinstance(getattr(cls, "type", None), str):
166
+ raise TypeError("EntryRecord subclasses must declare type and definition_id")
167
+ base = load_entry_type_definition(cls.definition_id)
168
+ if cls.type != base.name:
169
+ raise ValueError("record type must match its entry-family definition")
170
+ storage = vars(cls).get("__httk_storage__", StorageInfo())
171
+ if not isinstance(storage, StorageInfo):
172
+ raise TypeError("__httk_storage__ must be StorageInfo")
173
+ if storage.identity_name not in (None, name):
174
+ raise ValueError("StorageInfo.identity_name conflicts with the entry_record name")
175
+ cls = dataclass(frozen=True)(cls)
176
+ hints = get_type_hints(cls, localns={cls.__name__: cls}, include_extras=True)
177
+ for field_name, hint in hints.items():
178
+ if get_origin(hint) is ClassVar:
179
+ inner = get_args(hint)[0]
180
+ if get_origin(inner) is Annotated and any(
181
+ isinstance(marker, (Property, PropertyDefinition)) for marker in get_args(inner)[1:]
182
+ ):
183
+ raise TypeError(f"{field_name}: a served property must be an instance field, not ClassVar")
184
+ definitions: dict[str, PropertyDefinition] = {}
185
+ projections: dict[str, StoredPropertyProjection] = {}
186
+ for item in fields(cls):
187
+ hint = hints[item.name]
188
+ if get_origin(hint) is not Annotated:
189
+ continue
190
+ annotation, *metadata = get_args(hint)
191
+ markers = [marker for marker in metadata if isinstance(marker, (Property, PropertyDefinition))]
192
+ if not markers:
193
+ continue
194
+ if len(markers) != 1:
195
+ raise ValueError(f"{item.name} must have exactly one property declaration")
196
+ if any(isinstance(marker, IdentitySkip) for marker in metadata):
197
+ raise ValueError(f"{item.name}: identity metadata cannot declare a scientific property")
198
+ if any(isinstance(marker, Skip) for marker in metadata):
199
+ raise ValueError(f"{item.name}: a skipped field cannot declare a served property")
200
+ fulltype, nullable = _scalar_type(annotation)
201
+ marker = markers[0]
202
+ if isinstance(marker, Property):
203
+ if not isinstance(marker.description, str) or not marker.description.strip():
204
+ raise ValueError(f"{item.name}: Property requires a nonempty description")
205
+ if marker.name is not None and (not isinstance(marker.name, str) or not marker.name.isidentifier()):
206
+ raise ValueError(f"{item.name}: Property.name must be a nonempty identifier")
207
+ definition = PropertyDefinition.from_simple(
208
+ marker.name or f"_httk_custom_{item.name}",
209
+ description=marker.description,
210
+ fulltype=fulltype,
211
+ unit=marker.unit,
212
+ )
213
+ else:
214
+ definition = marker
215
+ if definition.optimade_type != fulltype or (nullable and not definition.nullable):
216
+ raise ValueError(f"{item.name}: property definition conflicts with the Python field type")
217
+ served_name = definition.name
218
+ if served_name not in base.properties and not any(
219
+ served_name.startswith(prefix) for prefix in known_definition_prefixes()
220
+ ):
221
+ raise ValueError(f"{served_name}: custom property names require a registered provider prefix")
222
+ if served_name in definitions or served_name in ("id", "type", "immutable_id", "last_modified"):
223
+ raise ValueError(f"duplicate or reserved served property name {served_name!r}")
224
+ definitions[served_name] = definition
225
+ projections[served_name] = _projection(item.name)
226
+ cls.__httk_entry_name__ = name
227
+ cls.__httk_storage__ = replace(
228
+ storage,
229
+ identity_name=name,
230
+ storage_name=storage.storage_name or "entry_" + hashlib.sha256(name.encode()).hexdigest()[:24],
231
+ )
232
+ cls.__httk_property_definitions__ = definitions
233
+ cls.__httk_stored_properties__ = projections
234
+ # Validate extension compatibility at definition time, not the first request.
235
+ cls.entry_type_definition()
236
+ return cls
237
+
238
+ return decorate