cognite-neat 0.123.2__py3-none-any.whl → 0.127.30__py3-none-any.whl

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 (333) hide show
  1. cognite/neat/__init__.py +2 -2
  2. cognite/neat/_client/__init__.py +4 -0
  3. cognite/neat/_client/api.py +8 -0
  4. cognite/neat/_client/client.py +21 -0
  5. cognite/neat/_client/config.py +40 -0
  6. cognite/neat/_client/containers_api.py +125 -0
  7. cognite/neat/_client/data_classes.py +44 -0
  8. cognite/neat/_client/data_model_api.py +115 -0
  9. cognite/neat/_client/spaces_api.py +115 -0
  10. cognite/neat/_client/statistics_api.py +24 -0
  11. cognite/neat/_client/views_api.py +129 -0
  12. cognite/neat/_config.py +185 -0
  13. cognite/neat/_data_model/_analysis.py +196 -0
  14. cognite/neat/_data_model/_constants.py +67 -0
  15. cognite/neat/_data_model/_identifiers.py +61 -0
  16. cognite/neat/_data_model/_shared.py +41 -0
  17. cognite/neat/_data_model/deployer/_differ.py +140 -0
  18. cognite/neat/_data_model/deployer/_differ_container.py +360 -0
  19. cognite/neat/_data_model/deployer/_differ_data_model.py +54 -0
  20. cognite/neat/_data_model/deployer/_differ_space.py +9 -0
  21. cognite/neat/_data_model/deployer/_differ_view.py +299 -0
  22. cognite/neat/_data_model/deployer/data_classes.py +529 -0
  23. cognite/neat/_data_model/deployer/deployer.py +401 -0
  24. cognite/neat/_data_model/exporters/__init__.py +15 -0
  25. cognite/neat/_data_model/exporters/_api_exporter.py +37 -0
  26. cognite/neat/_data_model/exporters/_base.py +24 -0
  27. cognite/neat/_data_model/exporters/_table_exporter/exporter.py +128 -0
  28. cognite/neat/_data_model/exporters/_table_exporter/workbook.py +409 -0
  29. cognite/neat/_data_model/exporters/_table_exporter/writer.py +421 -0
  30. cognite/neat/_data_model/importers/__init__.py +5 -0
  31. cognite/neat/_data_model/importers/_api_importer.py +166 -0
  32. cognite/neat/_data_model/importers/_base.py +16 -0
  33. cognite/neat/_data_model/importers/_table_importer/data_classes.py +295 -0
  34. cognite/neat/_data_model/importers/_table_importer/importer.py +192 -0
  35. cognite/neat/_data_model/importers/_table_importer/reader.py +1063 -0
  36. cognite/neat/_data_model/importers/_table_importer/source.py +94 -0
  37. cognite/neat/_data_model/models/conceptual/_base.py +18 -0
  38. cognite/neat/_data_model/models/conceptual/_concept.py +67 -0
  39. cognite/neat/_data_model/models/conceptual/_data_model.py +51 -0
  40. cognite/neat/_data_model/models/conceptual/_properties.py +104 -0
  41. cognite/neat/_data_model/models/conceptual/_property.py +105 -0
  42. cognite/neat/_data_model/models/dms/__init__.py +206 -0
  43. cognite/neat/_data_model/models/dms/_base.py +31 -0
  44. cognite/neat/_data_model/models/dms/_constants.py +48 -0
  45. cognite/neat/_data_model/models/dms/_constraints.py +42 -0
  46. cognite/neat/_data_model/models/dms/_container.py +159 -0
  47. cognite/neat/_data_model/models/dms/_data_model.py +95 -0
  48. cognite/neat/_data_model/models/dms/_data_types.py +195 -0
  49. cognite/neat/_data_model/models/dms/_http.py +28 -0
  50. cognite/neat/_data_model/models/dms/_indexes.py +30 -0
  51. cognite/neat/_data_model/models/dms/_limits.py +96 -0
  52. cognite/neat/_data_model/models/dms/_references.py +135 -0
  53. cognite/neat/_data_model/models/dms/_schema.py +18 -0
  54. cognite/neat/_data_model/models/dms/_space.py +48 -0
  55. cognite/neat/_data_model/models/dms/_types.py +17 -0
  56. cognite/neat/_data_model/models/dms/_view_filter.py +282 -0
  57. cognite/neat/_data_model/models/dms/_view_property.py +235 -0
  58. cognite/neat/_data_model/models/dms/_views.py +210 -0
  59. cognite/neat/_data_model/models/entities/__init__.py +50 -0
  60. cognite/neat/_data_model/models/entities/_base.py +101 -0
  61. cognite/neat/_data_model/models/entities/_constants.py +22 -0
  62. cognite/neat/_data_model/models/entities/_data_types.py +144 -0
  63. cognite/neat/_data_model/models/entities/_identifiers.py +61 -0
  64. cognite/neat/_data_model/models/entities/_parser.py +226 -0
  65. cognite/neat/_data_model/validation/dms/__init__.py +75 -0
  66. cognite/neat/_data_model/validation/dms/_ai_readiness.py +364 -0
  67. cognite/neat/_data_model/validation/dms/_base.py +307 -0
  68. cognite/neat/_data_model/validation/dms/_connections.py +638 -0
  69. cognite/neat/_data_model/validation/dms/_consistency.py +57 -0
  70. cognite/neat/_data_model/validation/dms/_containers.py +174 -0
  71. cognite/neat/_data_model/validation/dms/_limits.py +420 -0
  72. cognite/neat/_data_model/validation/dms/_orchestrator.py +222 -0
  73. cognite/neat/_data_model/validation/dms/_views.py +103 -0
  74. cognite/neat/_exceptions.py +56 -0
  75. cognite/neat/_issues.py +68 -0
  76. cognite/neat/_session/__init__.py +3 -0
  77. cognite/neat/_session/_html/_render.py +30 -0
  78. cognite/neat/_session/_html/static/__init__.py +8 -0
  79. cognite/neat/_session/_html/static/deployment.css +303 -0
  80. cognite/neat/_session/_html/static/deployment.js +150 -0
  81. cognite/neat/_session/_html/static/issues.css +211 -0
  82. cognite/neat/_session/_html/static/issues.js +168 -0
  83. cognite/neat/_session/_html/static/shared.css +186 -0
  84. cognite/neat/_session/_html/templates/__init__.py +4 -0
  85. cognite/neat/_session/_html/templates/deployment.html +75 -0
  86. cognite/neat/_session/_html/templates/issues.html +45 -0
  87. cognite/neat/_session/_issues.py +81 -0
  88. cognite/neat/_session/_opt.py +35 -0
  89. cognite/neat/_session/_physical.py +261 -0
  90. cognite/neat/_session/_result.py +236 -0
  91. cognite/neat/_session/_session.py +88 -0
  92. cognite/neat/_session/_usage_analytics/__init__.py +0 -0
  93. cognite/neat/_session/_usage_analytics/_collector.py +131 -0
  94. cognite/neat/_session/_usage_analytics/_constants.py +23 -0
  95. cognite/neat/_session/_usage_analytics/_storage.py +240 -0
  96. cognite/neat/_session/_wrappers.py +82 -0
  97. cognite/neat/_state_machine/__init__.py +10 -0
  98. cognite/neat/_state_machine/_base.py +37 -0
  99. cognite/neat/_state_machine/_states.py +52 -0
  100. cognite/neat/_store/__init__.py +3 -0
  101. cognite/neat/_store/_provenance.py +81 -0
  102. cognite/neat/_store/_store.py +156 -0
  103. cognite/neat/_utils/__init__.py +0 -0
  104. cognite/neat/_utils/_reader.py +194 -0
  105. cognite/neat/_utils/auxiliary.py +39 -0
  106. cognite/neat/_utils/collection.py +11 -0
  107. cognite/neat/_utils/http_client/__init__.py +39 -0
  108. cognite/neat/_utils/http_client/_client.py +245 -0
  109. cognite/neat/_utils/http_client/_config.py +19 -0
  110. cognite/neat/_utils/http_client/_data_classes.py +294 -0
  111. cognite/neat/_utils/http_client/_tracker.py +31 -0
  112. cognite/neat/_utils/text.py +71 -0
  113. cognite/neat/_utils/useful_types.py +37 -0
  114. cognite/neat/_utils/validation.py +154 -0
  115. cognite/neat/_version.py +1 -1
  116. cognite/neat/v0/__init__.py +0 -0
  117. cognite/neat/v0/core/__init__.py +0 -0
  118. cognite/neat/v0/core/_client/_api/__init__.py +0 -0
  119. cognite/neat/{core → v0/core}/_client/_api/data_modeling_loaders.py +86 -7
  120. cognite/neat/{core → v0/core}/_client/_api/neat_instances.py +5 -5
  121. cognite/neat/{core → v0/core}/_client/_api/schema.py +5 -5
  122. cognite/neat/{core → v0/core}/_client/_api/statistics.py +3 -3
  123. cognite/neat/{core → v0/core}/_client/_api_client.py +1 -1
  124. cognite/neat/v0/core/_client/data_classes/__init__.py +0 -0
  125. cognite/neat/{core → v0/core}/_client/data_classes/schema.py +4 -4
  126. cognite/neat/{core → v0/core}/_client/testing.py +1 -1
  127. cognite/neat/{core → v0/core}/_constants.py +10 -3
  128. cognite/neat/v0/core/_data_model/__init__.py +0 -0
  129. cognite/neat/{core → v0/core}/_data_model/_constants.py +9 -6
  130. cognite/neat/{core → v0/core}/_data_model/_shared.py +5 -5
  131. cognite/neat/{core → v0/core}/_data_model/analysis/_base.py +12 -8
  132. cognite/neat/{core → v0/core}/_data_model/exporters/__init__.py +1 -2
  133. cognite/neat/{core → v0/core}/_data_model/exporters/_base.py +7 -7
  134. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2dms.py +9 -9
  135. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2excel.py +13 -13
  136. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2instance_template.py +4 -4
  137. cognite/neat/{core/_data_model/exporters/_data_model2ontology.py → v0/core/_data_model/exporters/_data_model2semantic_model.py} +126 -133
  138. cognite/neat/{core → v0/core}/_data_model/exporters/_data_model2yaml.py +1 -1
  139. cognite/neat/{core → v0/core}/_data_model/importers/__init__.py +4 -6
  140. cognite/neat/{core → v0/core}/_data_model/importers/_base.py +5 -5
  141. cognite/neat/{core → v0/core}/_data_model/importers/_base_file_reader.py +2 -2
  142. cognite/neat/{core → v0/core}/_data_model/importers/_dict2data_model.py +6 -6
  143. cognite/neat/{core → v0/core}/_data_model/importers/_dms2data_model.py +19 -16
  144. cognite/neat/v0/core/_data_model/importers/_graph2data_model.py +299 -0
  145. cognite/neat/v0/core/_data_model/importers/_rdf/__init__.py +4 -0
  146. cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_base.py +13 -13
  147. cognite/neat/{core → v0/core}/_data_model/importers/_rdf/_inference2rdata_model.py +14 -14
  148. cognite/neat/v0/core/_data_model/importers/_rdf/_owl2data_model.py +144 -0
  149. cognite/neat/v0/core/_data_model/importers/_rdf/_shared.py +255 -0
  150. cognite/neat/{core → v0/core}/_data_model/importers/_spreadsheet2data_model.py +94 -13
  151. cognite/neat/{core → v0/core}/_data_model/models/__init__.py +3 -3
  152. cognite/neat/{core → v0/core}/_data_model/models/_base_verified.py +5 -5
  153. cognite/neat/v0/core/_data_model/models/_import_contexts.py +82 -0
  154. cognite/neat/{core → v0/core}/_data_model/models/_types.py +5 -5
  155. cognite/neat/{core → v0/core}/_data_model/models/conceptual/_unverified.py +18 -12
  156. cognite/neat/v0/core/_data_model/models/conceptual/_validation.py +308 -0
  157. cognite/neat/{core → v0/core}/_data_model/models/conceptual/_verified.py +13 -11
  158. cognite/neat/{core → v0/core}/_data_model/models/data_types.py +14 -4
  159. cognite/neat/{core → v0/core}/_data_model/models/entities/__init__.py +6 -0
  160. cognite/neat/v0/core/_data_model/models/entities/_loaders.py +155 -0
  161. cognite/neat/{core → v0/core}/_data_model/models/entities/_multi_value.py +2 -2
  162. cognite/neat/v0/core/_data_model/models/entities/_restrictions.py +230 -0
  163. cognite/neat/{core → v0/core}/_data_model/models/entities/_single_value.py +121 -16
  164. cognite/neat/{core → v0/core}/_data_model/models/entities/_types.py +10 -0
  165. cognite/neat/{core → v0/core}/_data_model/models/mapping/_classic2core.py +5 -5
  166. cognite/neat/{core → v0/core}/_data_model/models/physical/__init__.py +1 -1
  167. cognite/neat/{core → v0/core}/_data_model/models/physical/_exporter.py +28 -21
  168. cognite/neat/{core → v0/core}/_data_model/models/physical/_unverified.py +141 -38
  169. cognite/neat/{core → v0/core}/_data_model/models/physical/_validation.py +190 -24
  170. cognite/neat/{core → v0/core}/_data_model/models/physical/_verified.py +135 -15
  171. cognite/neat/{core → v0/core}/_data_model/transformers/__init__.py +2 -0
  172. cognite/neat/{core → v0/core}/_data_model/transformers/_base.py +4 -4
  173. cognite/neat/{core → v0/core}/_data_model/transformers/_converters.py +39 -32
  174. cognite/neat/{core → v0/core}/_data_model/transformers/_mapping.py +7 -7
  175. cognite/neat/v0/core/_data_model/transformers/_union_conceptual.py +208 -0
  176. cognite/neat/{core → v0/core}/_data_model/transformers/_verification.py +7 -7
  177. cognite/neat/v0/core/_instances/__init__.py +0 -0
  178. cognite/neat/{core → v0/core}/_instances/_tracking/base.py +1 -1
  179. cognite/neat/{core → v0/core}/_instances/_tracking/log.py +1 -1
  180. cognite/neat/{core → v0/core}/_instances/extractors/__init__.py +1 -1
  181. cognite/neat/{core → v0/core}/_instances/extractors/_base.py +6 -6
  182. cognite/neat/v0/core/_instances/extractors/_classic_cdf/__init__.py +0 -0
  183. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_base.py +7 -7
  184. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_classic.py +12 -12
  185. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_relationships.py +3 -3
  186. cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_sequences.py +2 -2
  187. cognite/neat/{core → v0/core}/_instances/extractors/_dict.py +6 -3
  188. cognite/neat/{core → v0/core}/_instances/extractors/_dms.py +6 -6
  189. cognite/neat/{core → v0/core}/_instances/extractors/_dms_graph.py +11 -11
  190. cognite/neat/{core → v0/core}/_instances/extractors/_mock_graph_generator.py +10 -10
  191. cognite/neat/{core → v0/core}/_instances/extractors/_raw.py +3 -3
  192. cognite/neat/{core → v0/core}/_instances/extractors/_rdf_file.py +7 -7
  193. cognite/neat/{core → v0/core}/_instances/loaders/_base.py +5 -5
  194. cognite/neat/{core → v0/core}/_instances/loaders/_rdf2dms.py +17 -17
  195. cognite/neat/{core → v0/core}/_instances/loaders/_rdf_to_instance_space.py +11 -11
  196. cognite/neat/{core → v0/core}/_instances/queries/_select.py +29 -3
  197. cognite/neat/{core → v0/core}/_instances/queries/_update.py +1 -1
  198. cognite/neat/{core → v0/core}/_instances/transformers/_base.py +4 -4
  199. cognite/neat/{core → v0/core}/_instances/transformers/_classic_cdf.py +6 -6
  200. cognite/neat/{core → v0/core}/_instances/transformers/_prune_graph.py +4 -4
  201. cognite/neat/{core → v0/core}/_instances/transformers/_rdfpath.py +1 -1
  202. cognite/neat/{core → v0/core}/_instances/transformers/_value_type.py +4 -4
  203. cognite/neat/{core → v0/core}/_issues/_base.py +11 -6
  204. cognite/neat/{core → v0/core}/_issues/_contextmanagers.py +8 -6
  205. cognite/neat/{core → v0/core}/_issues/_factory.py +11 -8
  206. cognite/neat/{core → v0/core}/_issues/errors/__init__.py +3 -1
  207. cognite/neat/{core → v0/core}/_issues/errors/_external.py +1 -1
  208. cognite/neat/{core → v0/core}/_issues/errors/_general.py +1 -1
  209. cognite/neat/{core → v0/core}/_issues/errors/_properties.py +12 -1
  210. cognite/neat/{core → v0/core}/_issues/errors/_resources.py +2 -2
  211. cognite/neat/{core → v0/core}/_issues/errors/_wrapper.py +7 -3
  212. cognite/neat/{core → v0/core}/_issues/warnings/__init__.py +5 -1
  213. cognite/neat/{core → v0/core}/_issues/warnings/_external.py +1 -1
  214. cognite/neat/{core → v0/core}/_issues/warnings/_general.py +1 -1
  215. cognite/neat/{core → v0/core}/_issues/warnings/_models.py +39 -4
  216. cognite/neat/{core → v0/core}/_issues/warnings/_properties.py +13 -2
  217. cognite/neat/{core → v0/core}/_issues/warnings/_resources.py +1 -1
  218. cognite/neat/{core → v0/core}/_issues/warnings/user_modeling.py +1 -1
  219. cognite/neat/{core → v0/core}/_store/_data_model.py +13 -12
  220. cognite/neat/{core → v0/core}/_store/_instance.py +45 -12
  221. cognite/neat/{core → v0/core}/_store/_provenance.py +3 -3
  222. cognite/neat/{core → v0/core}/_store/exceptions.py +4 -4
  223. cognite/neat/v0/core/_utils/__init__.py +0 -0
  224. cognite/neat/{core → v0/core}/_utils/auth.py +1 -1
  225. cognite/neat/{core → v0/core}/_utils/auxiliary.py +7 -1
  226. cognite/neat/{core → v0/core}/_utils/collection_.py +2 -2
  227. cognite/neat/{core → v0/core}/_utils/graph_transformations_report.py +1 -1
  228. cognite/neat/{core → v0/core}/_utils/rdf_.py +38 -14
  229. cognite/neat/{core → v0/core}/_utils/reader/_base.py +1 -1
  230. cognite/neat/{core → v0/core}/_utils/spreadsheet.py +22 -4
  231. cognite/neat/v0/core/_utils/tarjan.py +44 -0
  232. cognite/neat/{core → v0/core}/_utils/text.py +1 -1
  233. cognite/neat/{core → v0/core}/_utils/upload.py +3 -3
  234. cognite/neat/v0/plugins/__init__.py +4 -0
  235. cognite/neat/v0/plugins/_base.py +9 -0
  236. cognite/neat/v0/plugins/_data_model.py +48 -0
  237. cognite/neat/{plugins → v0/plugins}/_issues.py +1 -1
  238. cognite/neat/{plugins → v0/plugins}/_manager.py +7 -16
  239. cognite/neat/{session → v0/session}/_base.py +13 -10
  240. cognite/neat/{session → v0/session}/_collector.py +1 -1
  241. cognite/neat/v0/session/_diff.py +51 -0
  242. cognite/neat/{session → v0/session}/_drop.py +3 -3
  243. cognite/neat/{session → v0/session}/_explore.py +2 -2
  244. cognite/neat/{session → v0/session}/_fix.py +2 -2
  245. cognite/neat/{session → v0/session}/_inspect.py +3 -3
  246. cognite/neat/{session → v0/session}/_mapping.py +3 -3
  247. cognite/neat/{session → v0/session}/_plugin.py +4 -5
  248. cognite/neat/{session → v0/session}/_prepare.py +8 -8
  249. cognite/neat/{session → v0/session}/_read.py +33 -43
  250. cognite/neat/{session → v0/session}/_set.py +8 -8
  251. cognite/neat/{session → v0/session}/_show.py +5 -5
  252. cognite/neat/{session → v0/session}/_state.py +22 -8
  253. cognite/neat/{session → v0/session}/_subset.py +4 -4
  254. cognite/neat/{session → v0/session}/_template.py +11 -11
  255. cognite/neat/{session → v0/session}/_to.py +12 -12
  256. cognite/neat/{session → v0/session}/_wizard.py +1 -1
  257. cognite/neat/{session → v0/session}/engine/_load.py +1 -1
  258. cognite/neat/{session → v0/session}/exceptions.py +5 -5
  259. cognite/neat/v1.py +3 -0
  260. {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/METADATA +9 -8
  261. cognite_neat-0.127.30.dist-info/RECORD +319 -0
  262. {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/WHEEL +1 -1
  263. cognite/neat/core/_data_model/importers/_dtdl2data_model/__init__.py +0 -3
  264. cognite/neat/core/_data_model/importers/_dtdl2data_model/_unit_lookup.py +0 -224
  265. cognite/neat/core/_data_model/importers/_dtdl2data_model/dtdl_converter.py +0 -320
  266. cognite/neat/core/_data_model/importers/_dtdl2data_model/dtdl_importer.py +0 -155
  267. cognite/neat/core/_data_model/importers/_dtdl2data_model/spec.py +0 -363
  268. cognite/neat/core/_data_model/importers/_rdf/__init__.py +0 -5
  269. cognite/neat/core/_data_model/importers/_rdf/_imf2data_model.py +0 -98
  270. cognite/neat/core/_data_model/importers/_rdf/_owl2data_model.py +0 -87
  271. cognite/neat/core/_data_model/importers/_rdf/_shared.py +0 -168
  272. cognite/neat/core/_data_model/models/conceptual/_validation.py +0 -294
  273. cognite/neat/core/_data_model/models/entities/_loaders.py +0 -75
  274. cognite/neat/plugins/__init__.py +0 -3
  275. cognite/neat/plugins/data_model/importers/__init__.py +0 -5
  276. cognite/neat/plugins/data_model/importers/_base.py +0 -28
  277. cognite_neat-0.123.2.dist-info/RECORD +0 -197
  278. /cognite/neat/{core → _data_model}/__init__.py +0 -0
  279. /cognite/neat/{core/_client/_api → _data_model/deployer}/__init__.py +0 -0
  280. /cognite/neat/{core/_client/data_classes → _data_model/exporters/_table_exporter}/__init__.py +0 -0
  281. /cognite/neat/{core/_data_model → _data_model/importers/_table_importer}/__init__.py +0 -0
  282. /cognite/neat/{core/_instances → _data_model/models}/__init__.py +0 -0
  283. /cognite/neat/{core/_instances/extractors/_classic_cdf → _data_model/models/conceptual}/__init__.py +0 -0
  284. /cognite/neat/{core/_utils → _data_model/validation}/__init__.py +0 -0
  285. /cognite/neat/{plugins/data_model → _session/_html}/__init__.py +0 -0
  286. /cognite/neat/{core → v0/core}/_client/__init__.py +0 -0
  287. /cognite/neat/{core → v0/core}/_client/data_classes/data_modeling.py +0 -0
  288. /cognite/neat/{core → v0/core}/_client/data_classes/neat_sequence.py +0 -0
  289. /cognite/neat/{core → v0/core}/_client/data_classes/statistics.py +0 -0
  290. /cognite/neat/{core → v0/core}/_config.py +0 -0
  291. /cognite/neat/{core → v0/core}/_data_model/analysis/__init__.py +0 -0
  292. /cognite/neat/{core → v0/core}/_data_model/catalog/__init__.py +0 -0
  293. /cognite/neat/{core → v0/core}/_data_model/catalog/classic_model.xlsx +0 -0
  294. /cognite/neat/{core → v0/core}/_data_model/catalog/conceptual-imf-data-model.xlsx +0 -0
  295. /cognite/neat/{core → v0/core}/_data_model/catalog/hello_world_pump.xlsx +0 -0
  296. /cognite/neat/{core → v0/core}/_data_model/models/_base_unverified.py +0 -0
  297. /cognite/neat/{core → v0/core}/_data_model/models/conceptual/__init__.py +0 -0
  298. /cognite/neat/{core → v0/core}/_data_model/models/entities/_constants.py +0 -0
  299. /cognite/neat/{core → v0/core}/_data_model/models/entities/_wrapped.py +0 -0
  300. /cognite/neat/{core → v0/core}/_data_model/models/mapping/__init__.py +0 -0
  301. /cognite/neat/{core → v0/core}/_data_model/models/mapping/_classic2core.yaml +0 -0
  302. /cognite/neat/{core → v0/core}/_instances/_shared.py +0 -0
  303. /cognite/neat/{core → v0/core}/_instances/_tracking/__init__.py +0 -0
  304. /cognite/neat/{core → v0/core}/_instances/examples/Knowledge-Graph-Nordic44-dirty.xml +0 -0
  305. /cognite/neat/{core → v0/core}/_instances/examples/Knowledge-Graph-Nordic44.xml +0 -0
  306. /cognite/neat/{core → v0/core}/_instances/examples/__init__.py +0 -0
  307. /cognite/neat/{core → v0/core}/_instances/examples/skos-capturing-sheet-wind-topics.xlsx +0 -0
  308. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_assets.py +0 -0
  309. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_data_sets.py +0 -0
  310. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_events.py +0 -0
  311. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_files.py +0 -0
  312. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_labels.py +0 -0
  313. /cognite/neat/{core → v0/core}/_instances/extractors/_classic_cdf/_timeseries.py +0 -0
  314. /cognite/neat/{core → v0/core}/_instances/loaders/__init__.py +0 -0
  315. /cognite/neat/{core → v0/core}/_instances/queries/__init__.py +0 -0
  316. /cognite/neat/{core → v0/core}/_instances/queries/_base.py +0 -0
  317. /cognite/neat/{core → v0/core}/_instances/queries/_queries.py +0 -0
  318. /cognite/neat/{core → v0/core}/_instances/transformers/__init__.py +0 -0
  319. /cognite/neat/{core → v0/core}/_issues/__init__.py +0 -0
  320. /cognite/neat/{core → v0/core}/_issues/formatters.py +0 -0
  321. /cognite/neat/{core → v0/core}/_shared.py +0 -0
  322. /cognite/neat/{core → v0/core}/_store/__init__.py +0 -0
  323. /cognite/neat/{core → v0/core}/_utils/io_.py +0 -0
  324. /cognite/neat/{core → v0/core}/_utils/reader/__init__.py +0 -0
  325. /cognite/neat/{core → v0/core}/_utils/time_.py +0 -0
  326. /cognite/neat/{core → v0/core}/_utils/xml_.py +0 -0
  327. /cognite/neat/{session → v0/session}/__init__.py +0 -0
  328. /cognite/neat/{session → v0/session}/_experimental.py +0 -0
  329. /cognite/neat/{session → v0/session}/_state/README.md +0 -0
  330. /cognite/neat/{session → v0/session}/engine/__init__.py +0 -0
  331. /cognite/neat/{session → v0/session}/engine/_import.py +0 -0
  332. /cognite/neat/{session → v0/session}/engine/_interface.py +0 -0
  333. {cognite_neat-0.123.2.dist-info → cognite_neat-0.127.30.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,156 @@
1
+ from collections import UserList
2
+ from collections.abc import Callable
3
+ from datetime import datetime, timezone
4
+ from typing import Any, cast
5
+
6
+ from cognite.neat._data_model._shared import OnSuccess, OnSuccessIssuesChecker, OnSuccessResultProducer
7
+ from cognite.neat._data_model.deployer.data_classes import DeploymentResult
8
+ from cognite.neat._data_model.exporters import DMSExporter, DMSFileExporter
9
+ from cognite.neat._data_model.importers import DMSImporter, DMSTableImporter
10
+ from cognite.neat._data_model.models.dms import RequestSchema as PhysicalDataModel
11
+ from cognite.neat._exceptions import DataModelImportException
12
+ from cognite.neat._issues import IssueList
13
+ from cognite.neat._state_machine._states import EmptyState, PhysicalState, State
14
+ from cognite.neat._utils.text import NEWLINE
15
+
16
+ from ._provenance import Change, Provenance
17
+
18
+ Agents = DMSExporter | DMSTableImporter | DMSImporter
19
+
20
+
21
+ class NeatStore:
22
+ def __init__(self) -> None:
23
+ self.physical_data_model = DataModelList()
24
+ self.provenance = Provenance()
25
+ self.state: State = EmptyState()
26
+
27
+ def read_physical(self, reader: DMSImporter, on_success: OnSuccess | None = None) -> None:
28
+ """Read object from the store"""
29
+ self._can_agent_do_activity(reader)
30
+
31
+ change, data_model = self._do_activity(reader.to_data_model, on_success)
32
+
33
+ if data_model:
34
+ change.target_entity = self.physical_data_model.generate_reference(cast(PhysicalDataModel, data_model))
35
+ self.physical_data_model.append(data_model)
36
+ self.state = self.state.transition(reader)
37
+ change.target_state = self.state
38
+
39
+ self.provenance.append(change)
40
+
41
+ def write_physical(self, writer: DMSExporter, on_success: OnSuccess | None = None, **kwargs: Any) -> None:
42
+ """Write object into the store"""
43
+ self._can_agent_do_activity(writer)
44
+
45
+ activity: Callable
46
+ if isinstance(writer, DMSFileExporter):
47
+ activity = writer.export_to_file
48
+ if not kwargs.get("file_path"):
49
+ raise RuntimeError("file_path must be provided when using a DMSFileExporter")
50
+ else:
51
+ activity = writer.export
52
+
53
+ change, _ = self._do_activity(activity, on_success, data_model=self.physical_data_model[-1], **kwargs)
54
+
55
+ if not change.issues:
56
+ change.target_entity = "ExternalEntity"
57
+ self.state = self.state.transition(writer)
58
+ change.target_state = self.state
59
+
60
+ self.provenance.append(change)
61
+
62
+ def _can_agent_do_activity(self, agent: Agents) -> None:
63
+ """Validate if activity can be performed in the current state and considering provenance"""
64
+ if not self.state.can_transition(agent):
65
+ # specific error messages for common mistakes
66
+ if isinstance(agent, DMSImporter) and isinstance(self.state, PhysicalState):
67
+ raise RuntimeError(
68
+ "⚠️ Cannot read data model, there is already a data model in the session!"
69
+ f"{NEWLINE}Start a new session to read a new data model."
70
+ )
71
+
72
+ if isinstance(agent, DMSExporter) and isinstance(self.state, EmptyState):
73
+ raise RuntimeError(
74
+ "⚠️ Cannot write data model, there is no data model in the session!"
75
+ f"{NEWLINE}Read a data model first!"
76
+ )
77
+ raise RuntimeError(f"Cannot run {type(agent).__name__} in state {self.state}")
78
+
79
+ if (
80
+ isinstance(agent, DMSExporter)
81
+ and self.provenance.last_change
82
+ and (error_count := self.provenance.last_change.error_count) > 0
83
+ ):
84
+ raise RuntimeError(
85
+ f"⚠️ Cannot write data model, the model has {error_count} errors!"
86
+ f"{NEWLINE}Resolve issues before exporting the data model."
87
+ f"{NEWLINE}You can inspect issues using neat.issues"
88
+ )
89
+
90
+ # need implementation of checking if required predecessor activities have been done
91
+ # this will be done by running self.provenance.can_agent_do_activity(agent)
92
+
93
+ def _do_activity(
94
+ self, activity: Callable, on_success: OnSuccess | None = None, **kwargs: Any
95
+ ) -> tuple[Change, PhysicalDataModel | None]:
96
+ """Execute activity and capture timing, results, and issues"""
97
+ start = datetime.now(timezone.utc)
98
+ created_data_model: PhysicalDataModel | None = None
99
+ issues = IssueList()
100
+ errors = IssueList()
101
+ deployment_result: DeploymentResult | None = None
102
+
103
+ try:
104
+ created_data_model = activity(**kwargs)
105
+ if created_data_model and on_success:
106
+ on_success.run(created_data_model)
107
+ if isinstance(on_success, OnSuccessIssuesChecker):
108
+ issues.extend(on_success.issues)
109
+ elif isinstance(on_success, OnSuccessResultProducer):
110
+ deployment_result = on_success.result
111
+ else:
112
+ raise RuntimeError(f"Unknown OnSuccess type {type(on_success).__name__}")
113
+
114
+ # we catch import exceptions to capture issues and errors in provenance
115
+ except DataModelImportException as e:
116
+ errors.extend(e.errors)
117
+
118
+ # these are all other errors, such as missing file, wrong format, etc.
119
+ except Exception as e:
120
+ raise e
121
+
122
+ end = datetime.now(timezone.utc)
123
+
124
+ return Change(
125
+ start=start,
126
+ end=end,
127
+ source_state=self.state,
128
+ agent=type(activity.__self__).__name__ if hasattr(activity, "__self__") else "UnknownAgent",
129
+ issues=issues,
130
+ errors=errors,
131
+ result=deployment_result,
132
+ activity=Change.standardize_activity_name(activity.__name__, start, end),
133
+ ), created_data_model
134
+
135
+
136
+ class DataModelList(UserList[PhysicalDataModel]):
137
+ def iteration(self, data_model: PhysicalDataModel) -> int:
138
+ """Get iteration number for data model"""
139
+ for i, existing in enumerate(self):
140
+ if existing.data_model == data_model.data_model:
141
+ return i + 2
142
+ return 1
143
+
144
+ def generate_reference(self, data_model: PhysicalDataModel) -> str:
145
+ """Generate reference string for data model based on iteration"""
146
+ space = data_model.data_model.space
147
+ external_id = data_model.data_model.external_id
148
+ version = data_model.data_model.version
149
+ iteration = self.iteration(data_model)
150
+
151
+ return f"physical/{space}/{external_id}/{version}/{iteration}"
152
+
153
+ def get_by_reference(self, reference: str) -> PhysicalDataModel | None:
154
+ """Get data model by reference string"""
155
+
156
+ raise NotImplementedError("Not implemented yet")
File without changes
@@ -0,0 +1,194 @@
1
+ import tempfile
2
+ from abc import ABC, abstractmethod
3
+ from collections.abc import Iterable
4
+ from io import StringIO
5
+ from pathlib import Path
6
+ from typing import IO, Any, TextIO
7
+ from urllib.parse import urlparse
8
+
9
+ import requests
10
+
11
+
12
+ class NeatReader(ABC):
13
+ @classmethod
14
+ def create(cls, io: Any) -> "NeatReader":
15
+ if isinstance(io, str):
16
+ url = urlparse(io)
17
+ if url.scheme == "https" and url.netloc.endswith("github.com"):
18
+ return GitHubReader(io)
19
+ elif url.scheme == "https":
20
+ return HttpFileReader(io, url.path)
21
+
22
+ if isinstance(io, str):
23
+ return PathReader(Path(io))
24
+ if isinstance(io, Path):
25
+ return PathReader(io)
26
+ raise ValueError(f"Unsupported type: {type(io)}")
27
+
28
+ @property
29
+ def name(self) -> str:
30
+ return str(self)
31
+
32
+ @abstractmethod
33
+ def read_text(self) -> str:
34
+ """Read the buffer as a string"""
35
+ raise NotImplementedError()
36
+
37
+ @abstractmethod
38
+ def read_bytes(self) -> bytes:
39
+ """Read the buffer as bytes"""
40
+ raise NotImplementedError()
41
+
42
+ @abstractmethod
43
+ def size(self) -> int:
44
+ """Size of the buffer in bytes"""
45
+ raise NotImplementedError()
46
+
47
+ @abstractmethod
48
+ def iterate(self, chunk_size: int) -> Iterable[str]:
49
+ """Iterate over the buffer in chunks
50
+
51
+ Args:
52
+ chunk_size: Size of each chunk in bytes
53
+ """
54
+ raise NotImplementedError()
55
+
56
+ @abstractmethod
57
+ def __enter__(self) -> IO:
58
+ raise NotImplementedError()
59
+
60
+ @abstractmethod
61
+ def __str__(self) -> str:
62
+ raise NotImplementedError()
63
+
64
+ @abstractmethod
65
+ def exists(self) -> bool:
66
+ raise NotImplementedError
67
+
68
+ @abstractmethod
69
+ def materialize_path(self) -> Path:
70
+ raise NotImplementedError
71
+
72
+
73
+ class PathReader(NeatReader):
74
+ def __init__(self, path: Path):
75
+ self.path = path
76
+ self._io: TextIO | None = None
77
+
78
+ @property
79
+ def name(self) -> str:
80
+ return self.path.name
81
+
82
+ def read_text(self) -> str:
83
+ return self.path.read_text()
84
+
85
+ def read_bytes(self) -> bytes:
86
+ return self.path.read_bytes()
87
+
88
+ def size(self) -> int:
89
+ return self.path.stat().st_size
90
+
91
+ def iterate(self, chunk_size: int) -> Iterable[str]:
92
+ with self.path.open(mode="r") as f:
93
+ while chunk := f.read(chunk_size):
94
+ yield chunk
95
+
96
+ def __enter__(self) -> TextIO:
97
+ file = self.path.open(mode="r")
98
+ self._io = file
99
+ return file
100
+
101
+ def __exit__(self) -> None:
102
+ if self._io:
103
+ self._io.close()
104
+
105
+ def __str__(self) -> str:
106
+ return self.path.as_posix()
107
+
108
+ def exists(self) -> bool:
109
+ return self.path.exists()
110
+
111
+ def materialize_path(self) -> Path:
112
+ return self.path
113
+
114
+
115
+ class HttpFileReader(NeatReader):
116
+ def __init__(self, url: str, path: str):
117
+ self._url = url
118
+ self.path = path
119
+
120
+ @property
121
+ def name(self) -> str:
122
+ if "/" in self.path:
123
+ return self.path.rsplit("/", maxsplit=1)[-1]
124
+ return self.path
125
+
126
+ def read_text(self) -> str:
127
+ response = requests.get(self._url)
128
+ response.raise_for_status()
129
+ return response.text
130
+
131
+ def read_bytes(self) -> bytes:
132
+ response = requests.get(self._url)
133
+ response.raise_for_status()
134
+ return response.content
135
+
136
+ def size(self) -> int:
137
+ response = requests.head(self._url)
138
+ response.raise_for_status()
139
+ return int(response.headers["Content-Length"])
140
+
141
+ def iterate(self, chunk_size: int) -> Iterable[str]:
142
+ with requests.get(self._url, stream=True) as response:
143
+ response.raise_for_status()
144
+ for chunk in response.iter_content(chunk_size):
145
+ yield chunk.decode("utf-8")
146
+
147
+ def __enter__(self) -> IO:
148
+ return StringIO(self.read_text())
149
+
150
+ def __str__(self) -> str:
151
+ return self._url
152
+
153
+ def exists(self) -> bool:
154
+ response = requests.head(self._url)
155
+ return 200 <= response.status_code < 400
156
+
157
+ def materialize_path(self) -> Path:
158
+ path = Path(tempfile.gettempdir()).resolve() / "neat" / self.name
159
+ path.parent.mkdir(parents=True, exist_ok=True)
160
+ path.write_bytes(self.read_bytes())
161
+ return path
162
+
163
+
164
+ class GitHubReader(HttpFileReader):
165
+ raw_url = "https://raw.githubusercontent.com/"
166
+
167
+ def __init__(self, raw: str):
168
+ self.raw = raw
169
+ self.repo, path = self._parse_url(raw)
170
+ super().__init__(f"{self.raw_url}{self.repo}/main/{path}", path)
171
+
172
+ @staticmethod
173
+ def _parse_url(url: str) -> tuple[str, str]:
174
+ parsed = urlparse(url)
175
+ if parsed.scheme != "https":
176
+ raise ValueError(f"Unsupported scheme: {parsed.scheme}")
177
+
178
+ path = parsed.path.lstrip("/")
179
+ if parsed.netloc == "github.com":
180
+ repo, path = path.split("/blob/main/", maxsplit=1)
181
+ return repo, path
182
+
183
+ elif parsed.netloc == "api.github.com":
184
+ repo, path = path.removeprefix("repos/").split("/contents/", maxsplit=1)
185
+ return repo, path
186
+
187
+ elif parsed.netloc == "raw.githubusercontent.com":
188
+ repo, path = path.split("/main/", maxsplit=1)
189
+ return repo, path
190
+
191
+ raise ValueError(f"Unsupported netloc: {parsed.netloc}")
192
+
193
+ def __str__(self) -> str:
194
+ return self.raw
@@ -0,0 +1,39 @@
1
+ import inspect
2
+ from abc import ABC
3
+ from typing import TypeVar
4
+
5
+ from cognite.neat import _version
6
+
7
+
8
+ def get_current_neat_version() -> str:
9
+ return _version.__version__
10
+
11
+
12
+ T_Cls = TypeVar("T_Cls")
13
+
14
+
15
+ def get_concrete_subclasses(base_cls: type[T_Cls], exclude_direct_abc_inheritance: bool = True) -> list[type[T_Cls]]:
16
+ """
17
+ Returns a list of all concrete subclasses of the given base class.
18
+ Args:
19
+ base_cls (type[T_Cls]): The base class to find subclasses for.
20
+ exclude_direct_abc_inheritance (bool): If True, excludes classes that directly inherit from `abc.ABC`.
21
+ This is used as a marker to filter out intermediate base classes. Defaults to True.
22
+ Returns:
23
+ list[type[T_Cls]]: A list of concrete subclasses of the base class.
24
+ """
25
+ to_check = [base_cls]
26
+ subclasses: list[type[T_Cls]] = []
27
+ seen: set[type[T_Cls]] = {base_cls}
28
+ while to_check:
29
+ current_cls = to_check.pop()
30
+ for subclass in current_cls.__subclasses__():
31
+ if subclass in seen:
32
+ continue
33
+ if (not inspect.isabstract(subclass)) and (
34
+ not exclude_direct_abc_inheritance or ABC not in subclass.__bases__
35
+ ):
36
+ subclasses.append(subclass)
37
+ seen.add(subclass)
38
+ to_check.append(subclass)
39
+ return subclasses
@@ -0,0 +1,11 @@
1
+ from collections.abc import Iterator, Sequence
2
+ from typing import TypeVar
3
+
4
+ T_Sequence = TypeVar("T_Sequence", bound=Sequence)
5
+
6
+
7
+ def chunker_sequence(sequence: T_Sequence, size: int) -> Iterator[T_Sequence]:
8
+ """Yield successive n-sized chunks from sequence."""
9
+ for i in range(0, len(sequence), size):
10
+ # MyPy does not expect sequence[i : i + size] to be of type T_Sequence
11
+ yield sequence[i : i + size] # type: ignore[misc]
@@ -0,0 +1,39 @@
1
+ from ._client import HTTPClient
2
+ from ._data_classes import (
3
+ ErrorDetails,
4
+ FailedRequestItems,
5
+ FailedRequestMessage,
6
+ FailedResponse,
7
+ FailedResponseItems,
8
+ HTTPMessage,
9
+ ItemBody,
10
+ ItemIDBody,
11
+ ItemMessage,
12
+ ItemsRequest,
13
+ ParametersRequest,
14
+ RequestMessage,
15
+ ResponseMessage,
16
+ SimpleBodyRequest,
17
+ SuccessResponse,
18
+ SuccessResponseItems,
19
+ )
20
+
21
+ __all__ = [
22
+ "ErrorDetails",
23
+ "FailedRequestItems",
24
+ "FailedRequestMessage",
25
+ "FailedResponse",
26
+ "FailedResponseItems",
27
+ "HTTPClient",
28
+ "HTTPMessage",
29
+ "ItemBody",
30
+ "ItemIDBody",
31
+ "ItemMessage",
32
+ "ItemsRequest",
33
+ "ParametersRequest",
34
+ "RequestMessage",
35
+ "ResponseMessage",
36
+ "SimpleBodyRequest",
37
+ "SuccessResponse",
38
+ "SuccessResponseItems",
39
+ ]
@@ -0,0 +1,245 @@
1
+ import gzip
2
+ import random
3
+ import sys
4
+ import time
5
+ from collections import deque
6
+ from collections.abc import MutableMapping, Sequence, Set
7
+ from typing import Literal
8
+
9
+ import httpx
10
+ from cognite.client import ClientConfig, global_config
11
+
12
+ from cognite.neat._utils.auxiliary import get_current_neat_version
13
+ from cognite.neat._utils.http_client._config import get_user_agent
14
+ from cognite.neat._utils.http_client._data_classes import (
15
+ APIResponse,
16
+ BodyRequest,
17
+ ErrorDetails,
18
+ FailedRequestMessage,
19
+ HTTPMessage,
20
+ ItemsRequest,
21
+ ParametersRequest,
22
+ RequestMessage,
23
+ ResponseMessage,
24
+ )
25
+ from cognite.neat._utils.useful_types import PrimaryTypes
26
+
27
+ if sys.version_info >= (3, 11):
28
+ from typing import Self
29
+ else:
30
+ from typing_extensions import Self
31
+
32
+
33
+ class HTTPClient:
34
+ """An HTTP client.
35
+
36
+ This class handles rate limiting, retries, and error handling for HTTP requests.
37
+
38
+ Args:
39
+ config (ClientConfig): Configuration for the client.
40
+ pool_connections (int): The number of connection pools to cache. Default is 10.
41
+ pool_maxsize (int): The maximum number of connections to save in the pool. Default
42
+ is 20.
43
+ max_retries (int): The maximum number of retries for a request. Default is 10.
44
+ retry_status_codes (frozenset[int]): HTTP status codes that should trigger a retry.
45
+ Default is {408, 429, 502, 503, 504}.
46
+ split_items_status_codes (frozenset[int]): In the case of ItemRequest with multiple
47
+ items, these status codes will trigger splitting the request into smaller batches.
48
+
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ config: ClientConfig,
54
+ max_retries: int = 10,
55
+ pool_connections: int = 10,
56
+ pool_maxsize: int = 20,
57
+ retry_status_codes: Set[int] = frozenset({429, 502, 503, 504}),
58
+ split_items_status_codes: Set[int] = frozenset({400, 408, 409, 422, 502, 503, 504}),
59
+ ):
60
+ self.config = config
61
+ self._max_retries = max_retries
62
+ self._pool_connections = pool_connections
63
+ self._pool_maxsize = pool_maxsize
64
+ self._retry_status_codes = retry_status_codes
65
+ self._split_items_status_codes = split_items_status_codes
66
+
67
+ # Thread-safe session for connection pooling
68
+ self.session = self._create_thread_safe_session()
69
+
70
+ def __enter__(self) -> Self:
71
+ return self
72
+
73
+ def __exit__(
74
+ self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: object | None
75
+ ) -> Literal[False]:
76
+ """Close the session when exiting the context."""
77
+ self.session.close()
78
+ return False # Do not suppress exceptions
79
+
80
+ def request(self, message: RequestMessage) -> Sequence[HTTPMessage]:
81
+ """Send an HTTP request and return the response.
82
+
83
+ Args:
84
+ message (RequestMessage): The request message to send.
85
+
86
+ Returns:
87
+ Sequence[HTTPMessage]: The response message(s). This can also
88
+ include RequestMessage(s) to be retried.
89
+ """
90
+ if isinstance(message, ItemsRequest) and message.tracker and message.tracker.limit_reached():
91
+ error_msg = (
92
+ f"Aborting further splitting of requests after {message.tracker.failed_split_count} failed attempts."
93
+ )
94
+ return message.create_failed_request(error_msg)
95
+ try:
96
+ response = self._make_request(message)
97
+ results = self._handle_response(response, message)
98
+ except Exception as e:
99
+ results = self._handle_error(e, message)
100
+ return results
101
+
102
+ def request_with_retries(self, message: RequestMessage) -> APIResponse:
103
+ """Send an HTTP request and handle retries.
104
+
105
+ This method will keep retrying the request until it either succeeds or
106
+ exhausts the maximum number of retries.
107
+
108
+ Note this method will use the current thread to process all request, thus
109
+ it is blocking.
110
+
111
+ Args:
112
+ message (RequestMessage): The request message to send.
113
+
114
+ Returns:
115
+ Sequence[ResponseMessage | FailedRequestMessage]: The final response
116
+ messages, which can be either successful responses or failed requests.
117
+ """
118
+ if message.total_attempts > 0:
119
+ raise RuntimeError(f"RequestMessage has already been attempted {message.total_attempts} times.")
120
+ pending_requests: deque[RequestMessage] = deque()
121
+ pending_requests.append(message)
122
+ final_responses = APIResponse()
123
+
124
+ while pending_requests:
125
+ current_request = pending_requests.popleft()
126
+ results = self.request(current_request)
127
+
128
+ for result in results:
129
+ if isinstance(result, RequestMessage):
130
+ pending_requests.append(result)
131
+ elif isinstance(result, ResponseMessage | FailedRequestMessage):
132
+ final_responses.append(result)
133
+ else:
134
+ raise TypeError(f"Unexpected result type: {type(result)}")
135
+
136
+ return final_responses
137
+
138
+ def _create_thread_safe_session(self) -> httpx.Client:
139
+ return httpx.Client(
140
+ limits=httpx.Limits(
141
+ max_connections=self._pool_maxsize,
142
+ max_keepalive_connections=self._pool_connections,
143
+ ),
144
+ timeout=self.config.timeout,
145
+ )
146
+
147
+ def _create_headers(self, api_version: str | None = None) -> MutableMapping[str, str]:
148
+ headers: MutableMapping[str, str] = {}
149
+ headers["User-Agent"] = f"httpx/{httpx.__version__} {get_user_agent()}"
150
+ auth_name, auth_value = self.config.credentials.authorization_header()
151
+ headers[auth_name] = auth_value
152
+ headers["content-type"] = "application/json"
153
+ headers["accept"] = "application/json"
154
+ headers["x-cdp-sdk"] = f"CogniteNeat:{get_current_neat_version()}"
155
+ headers["x-cdp-app"] = self.config.client_name
156
+ headers["cdf-version"] = api_version or self.config.api_subversion
157
+ if not global_config.disable_gzip:
158
+ headers["Content-Encoding"] = "gzip"
159
+ return headers
160
+
161
+ def _make_request(self, item: RequestMessage) -> httpx.Response:
162
+ headers = self._create_headers(item.api_version)
163
+ params: dict[str, PrimaryTypes] | None = None
164
+ if isinstance(item, ParametersRequest):
165
+ params = item.parameters
166
+ data: str | bytes | None = None
167
+ if isinstance(item, BodyRequest):
168
+ data = item.data()
169
+ if not global_config.disable_gzip:
170
+ data = gzip.compress(data.encode("utf-8"))
171
+ return self.session.request(
172
+ method=item.method,
173
+ url=item.endpoint_url,
174
+ content=data,
175
+ headers=headers,
176
+ params=params,
177
+ timeout=self.config.timeout,
178
+ follow_redirects=False,
179
+ )
180
+
181
+ def _handle_response(
182
+ self,
183
+ response: httpx.Response,
184
+ request: RequestMessage,
185
+ ) -> Sequence[HTTPMessage]:
186
+ if 200 <= response.status_code < 300:
187
+ return request.create_success_response(response)
188
+
189
+ if (
190
+ isinstance(request, ItemsRequest)
191
+ and len(request.body.items) > 1
192
+ and response.status_code in self._split_items_status_codes
193
+ ):
194
+ # 4XX: Status there is at least one item that is invalid, split the batch to get all valid items processed
195
+ # 5xx: Server error, split to reduce the number of items in each request, and count as a status attempt
196
+ status_attempts = request.status_attempt
197
+ if 500 <= response.status_code < 600:
198
+ status_attempts += 1
199
+ splits = request.split(status_attempts=status_attempts)
200
+ if splits[0].tracker and splits[0].tracker.limit_reached():
201
+ return request.create_failure_response(response)
202
+ return splits
203
+
204
+ error = ErrorDetails.from_response(response)
205
+
206
+ if request.status_attempt < self._max_retries and (
207
+ response.status_code in self._retry_status_codes or error.is_auto_retryable
208
+ ):
209
+ request.status_attempt += 1
210
+ time.sleep(self._backoff_time(request.total_attempts))
211
+ return [request]
212
+ else:
213
+ # Permanent failure
214
+ return request.create_failure_response(response)
215
+
216
+ @staticmethod
217
+ def _backoff_time(attempts: int) -> float:
218
+ backoff_time = 0.5 * (2**attempts)
219
+ return min(backoff_time, global_config.max_retry_backoff) * random.uniform(0, 1.0)
220
+
221
+ def _handle_error(
222
+ self,
223
+ e: Exception,
224
+ request: RequestMessage,
225
+ ) -> Sequence[HTTPMessage]:
226
+ if isinstance(e, httpx.ReadTimeout | httpx.TimeoutException):
227
+ error_type = "read"
228
+ request.read_attempt += 1
229
+ attempts = request.read_attempt
230
+ elif isinstance(e, ConnectionError | httpx.ConnectError | httpx.ConnectTimeout):
231
+ error_type = "connect"
232
+ request.connect_attempt += 1
233
+ attempts = request.connect_attempt
234
+ else:
235
+ error_msg = f"Unexpected exception: {e!s}"
236
+ return request.create_failed_request(error_msg)
237
+
238
+ if attempts <= self._max_retries:
239
+ time.sleep(self._backoff_time(request.total_attempts))
240
+ return [request]
241
+ else:
242
+ # We have already incremented the attempt count, so we subtract 1 here
243
+ error_msg = f"RequestException after {request.total_attempts - 1} attempts ({error_type} error): {e!s}"
244
+
245
+ return request.create_failed_request(error_msg)