cognite-neat 0.107.0__py3-none-any.whl → 0.108.0__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.

Potentially problematic release.


This version of cognite-neat might be problematic. Click here for more details.

Files changed (52) hide show
  1. cognite/neat/_constants.py +35 -1
  2. cognite/neat/_graph/_shared.py +4 -0
  3. cognite/neat/_graph/extractors/_classic_cdf/_base.py +115 -14
  4. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +83 -6
  5. cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +48 -12
  6. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +19 -1
  7. cognite/neat/_graph/extractors/_dms.py +162 -47
  8. cognite/neat/_graph/extractors/_dms_graph.py +54 -4
  9. cognite/neat/_graph/extractors/_mock_graph_generator.py +1 -1
  10. cognite/neat/_graph/extractors/_rdf_file.py +3 -2
  11. cognite/neat/_graph/loaders/__init__.py +1 -3
  12. cognite/neat/_graph/loaders/_rdf2dms.py +20 -10
  13. cognite/neat/_graph/queries/_base.py +140 -84
  14. cognite/neat/_graph/queries/_construct.py +1 -1
  15. cognite/neat/_graph/transformers/__init__.py +3 -1
  16. cognite/neat/_graph/transformers/_value_type.py +54 -3
  17. cognite/neat/_issues/errors/_resources.py +1 -1
  18. cognite/neat/_issues/warnings/__init__.py +0 -2
  19. cognite/neat/_issues/warnings/_models.py +1 -1
  20. cognite/neat/_issues/warnings/_properties.py +0 -8
  21. cognite/neat/_rules/catalog/classic_model.xlsx +0 -0
  22. cognite/neat/_rules/exporters/_rules2instance_template.py +3 -3
  23. cognite/neat/_rules/importers/__init__.py +3 -1
  24. cognite/neat/_rules/importers/_dtdl2rules/spec.py +1 -2
  25. cognite/neat/_rules/importers/_rdf/__init__.py +2 -2
  26. cognite/neat/_rules/importers/_rdf/_base.py +2 -2
  27. cognite/neat/_rules/importers/_rdf/_inference2rules.py +241 -18
  28. cognite/neat/_rules/models/_base_rules.py +13 -3
  29. cognite/neat/_rules/models/dms/_rules.py +1 -8
  30. cognite/neat/_rules/models/dms/_rules_input.py +4 -0
  31. cognite/neat/_rules/models/information/_rules_input.py +5 -0
  32. cognite/neat/_rules/transformers/__init__.py +6 -0
  33. cognite/neat/_rules/transformers/_converters.py +98 -7
  34. cognite/neat/_session/_base.py +55 -4
  35. cognite/neat/_session/_drop.py +5 -1
  36. cognite/neat/_session/_inspect.py +3 -2
  37. cognite/neat/_session/_read.py +61 -14
  38. cognite/neat/_session/_set.py +27 -0
  39. cognite/neat/_session/_show.py +4 -4
  40. cognite/neat/_session/_state.py +8 -4
  41. cognite/neat/_session/_to.py +4 -1
  42. cognite/neat/_session/_wizard.py +1 -1
  43. cognite/neat/_session/exceptions.py +2 -1
  44. cognite/neat/_store/_graph_store.py +287 -133
  45. cognite/neat/_store/_rules_store.py +108 -1
  46. cognite/neat/_utils/auth.py +1 -1
  47. cognite/neat/_version.py +1 -1
  48. {cognite_neat-0.107.0.dist-info → cognite_neat-0.108.0.dist-info}/METADATA +1 -1
  49. {cognite_neat-0.107.0.dist-info → cognite_neat-0.108.0.dist-info}/RECORD +52 -52
  50. {cognite_neat-0.107.0.dist-info → cognite_neat-0.108.0.dist-info}/LICENSE +0 -0
  51. {cognite_neat-0.107.0.dist-info → cognite_neat-0.108.0.dist-info}/WHEEL +0 -0
  52. {cognite_neat-0.107.0.dist-info → cognite_neat-0.108.0.dist-info}/entry_points.txt +0 -0
@@ -7,11 +7,21 @@ from cognite.neat import _version
7
7
  from cognite.neat._client import NeatClient
8
8
  from cognite.neat._issues import IssueList
9
9
  from cognite.neat._issues.errors import RegexViolationError
10
+ from cognite.neat._issues.errors._general import NeatImportError
10
11
  from cognite.neat._rules import importers
11
12
  from cognite.neat._rules.models._base_input import InputRules
12
13
  from cognite.neat._rules.models.information._rules import InformationRules
13
- from cognite.neat._rules.transformers import ConvertToRules, InformationToDMS, VerifyAnyRules
14
- from cognite.neat._rules.transformers._converters import ConversionTransformer
14
+ from cognite.neat._rules.transformers import (
15
+ ConversionTransformer,
16
+ ConvertToRules,
17
+ InformationToDMS,
18
+ MergeDMSRules,
19
+ MergeInformationRules,
20
+ VerifyAnyRules,
21
+ VerifyInformationRules,
22
+ )
23
+ from cognite.neat._store._rules_store import ModelEntity
24
+ from cognite.neat._utils.auxiliary import local_import
15
25
 
16
26
  from ._collector import _COLLECTOR, Collector
17
27
  from ._drop import DropAPI
@@ -70,12 +80,15 @@ class NeatSession:
70
80
  def __init__(
71
81
  self,
72
82
  client: CogniteClient | None = None,
73
- storage: Literal["memory", "oxigraph"] = "memory",
83
+ storage: Literal["memory", "oxigraph"] | None = None,
74
84
  verbose: bool = True,
75
85
  load_engine: Literal["newest", "cache", "skip"] = "cache",
76
86
  ) -> None:
77
87
  self._verbose = verbose
78
- self._state = SessionState(store_type=storage, client=NeatClient(client) if client else None)
88
+ self._state = SessionState(
89
+ store_type=storage or self._select_most_performant_store(),
90
+ client=NeatClient(client) if client else None,
91
+ )
79
92
  self.read = ReadAPI(self._state, verbose)
80
93
  self.to = ToAPI(self._state, verbose)
81
94
  self.prepare = PrepareAPI(self._state, verbose)
@@ -89,6 +102,16 @@ class NeatSession:
89
102
  if load_engine != "skip" and (engine_version := load_neat_engine(client, load_engine)):
90
103
  print(f"Neat Engine {engine_version} loaded.")
91
104
 
105
+ def _select_most_performant_store(self) -> Literal["memory", "oxigraph"]:
106
+ """Select the most performant store based on the current environment."""
107
+
108
+ try:
109
+ local_import("pyoxigraph", "oxi")
110
+ local_import("oxrdflib", "oxi")
111
+ return "oxigraph"
112
+ except NeatImportError:
113
+ return "memory"
114
+
92
115
  @property
93
116
  def version(self) -> str:
94
117
  """Get the current version of neat.
@@ -199,6 +222,34 @@ class NeatSession:
199
222
  )
200
223
  return self._state.rule_import(importer)
201
224
 
225
+ def _infer_subclasses(self) -> IssueList:
226
+ """Infer the subclass of instances."""
227
+ last_information = self._state.rule_store.last_verified_information_rules
228
+ issue_list = IssueList()
229
+ importer = importers.SubclassInferenceImporter(
230
+ issue_list=issue_list,
231
+ graph=self._state.instances.store.graph(),
232
+ rules=last_information,
233
+ max_number_of_instance=-1,
234
+ )
235
+
236
+ unverified_information = importer.to_rules()
237
+ verified_information = VerifyInformationRules().transform(unverified_information)
238
+
239
+ # Hack into the last information rules to merge the rules with the last verified information rules.
240
+ # This is to be able to populate the instances store with the inferred subclasses.
241
+ provenance = self._state.rule_store.provenance
242
+ for change in reversed(provenance):
243
+ target_entity = change.target_entity
244
+ if isinstance(target_entity, ModelEntity) and isinstance(target_entity.result, InformationRules):
245
+ last_information_rules = change.target_entity.result
246
+ new_information_rules = MergeInformationRules(verified_information).transform(last_information_rules)
247
+ object.__setattr__(change.target_entity, "result", new_information_rules)
248
+ break
249
+
250
+ dms_rules = InformationToDMS(reserved_properties="skip").transform(verified_information)
251
+ return self._state.rule_transform(MergeDMSRules(dms_rules))
252
+
202
253
  def _repr_html_(self) -> str:
203
254
  state = self._state
204
255
  if (
@@ -31,7 +31,11 @@ class DropAPI:
31
31
  ```
32
32
  """
33
33
  type_list = type if isinstance(type, list) else [type]
34
- uri_type_type = dict((v, k) for k, v in self._state.instances.store.queries.types.items())
34
+
35
+ # Temporary solution until we agree on the form of specifying named graphs
36
+ # it will default to the default named graph
37
+ named_graph = self._state.instances.store.default_named_graph
38
+ uri_type_type = dict((v, k) for k, v in self._state.instances.store.queries.types(named_graph).items())
35
39
  selected_uri_by_type: dict[URIRef, str] = {}
36
40
  for type_item in type_list:
37
41
  if type_item not in uri_type_type:
@@ -94,7 +94,8 @@ class InspectIssues:
94
94
  if self._state.rule_store.provenance:
95
95
  issues = self._state.rule_store.last_issues
96
96
  elif self._state.instances.store.provenance:
97
- issues = self._state.instances.store.provenance[-1].target_entity.issues
97
+ last_change = self._state.instances.store.provenance[-1]
98
+ issues = last_change.target_entity.issues
98
99
  else:
99
100
  self._print("No issues found.")
100
101
  return pd.DataFrame() if return_dataframe else None
@@ -231,7 +232,7 @@ class InspectUploadOutcome:
231
232
  if i < 50:
232
233
  lines.append(f" * {v}")
233
234
  elif i == 50 and total > 50:
234
- lines.append(f" * ... {total-50} more")
235
+ lines.append(f" * ... {total - 50} more")
235
236
  elif i == 50 and total == 50:
236
237
  lines.append(f" * {v}")
237
238
  else:
@@ -7,7 +7,7 @@ from cognite.neat._client import NeatClient
7
7
  from cognite.neat._constants import CLASSIC_CDF_NAMESPACE
8
8
  from cognite.neat._graph import examples as instances_examples
9
9
  from cognite.neat._graph import extractors
10
- from cognite.neat._graph.transformers import ConvertLiteral, LiteralToEntity, LookupRelationshipSourceTarget
10
+ from cognite.neat._graph.transformers import ConvertLiteral, LiteralToEntity
11
11
  from cognite.neat._issues import IssueList
12
12
  from cognite.neat._issues.errors import NeatValueError
13
13
  from cognite.neat._issues.warnings import MissingCogniteClientWarning
@@ -99,20 +99,41 @@ class CDFReadAPI(BaseReadAPI):
99
99
  return self._state.rule_import(importer)
100
100
 
101
101
  def graph(
102
- self, data_model_id: DataModelIdentifier, instance_space: str | SequenceNotStr[str] | None = None
102
+ self,
103
+ data_model_id: DataModelIdentifier,
104
+ instance_space: str | SequenceNotStr[str] | None = None,
105
+ skip_cognite_views: bool = True,
103
106
  ) -> IssueList:
104
107
  """Reads a knowledge graph from Cognite Data Fusion (CDF).
105
108
 
106
109
  Args:
107
110
  data_model_id: Tuple of strings with the id of a CDF Data Model.
108
111
  instance_space: The instance spaces to extract. If None, all instance spaces are extracted.
112
+ skip_cognite_views: If True, all Cognite Views are skipped. For example, if you have the CogniteAsset
113
+ view in you data model, it will ont be used to extract instances.
109
114
 
110
115
  Returns:
111
116
  IssueList: A list of issues that occurred during the extraction.
112
117
 
113
118
  """
119
+ return self._graph(data_model_id, instance_space, skip_cognite_views, unpack_json=False)
120
+
121
+ def _graph(
122
+ self,
123
+ data_model_id: DataModelIdentifier,
124
+ instance_space: str | SequenceNotStr[str] | None = None,
125
+ skip_cognite_views: bool = True,
126
+ unpack_json: bool = False,
127
+ str_to_ideal_type: bool = False,
128
+ ) -> IssueList:
114
129
  extractor = extractors.DMSGraphExtractor.from_data_model_id(
115
- data_model_id, self._get_client, instance_space=instance_space
130
+ # We are skipping the Cognite Views
131
+ data_model_id,
132
+ self._get_client,
133
+ instance_space=instance_space,
134
+ skip_cognite_views=skip_cognite_views,
135
+ unpack_json=unpack_json,
136
+ str_to_ideal_type=str_to_ideal_type,
116
137
  )
117
138
  return self._state.write_graph(extractor)
118
139
 
@@ -130,7 +151,12 @@ class CDFClassicAPI(BaseReadAPI):
130
151
  raise ValueError("No client provided. Please provide a client to read a data model.")
131
152
  return self._state.client
132
153
 
133
- def graph(self, root_asset_external_id: str, limit_per_type: int | None = None) -> IssueList:
154
+ def graph(
155
+ self,
156
+ root_asset_external_id: str,
157
+ limit_per_type: int | None = None,
158
+ identifier: Literal["id", "externalId"] = "id",
159
+ ) -> IssueList:
134
160
  """Reads the classic knowledge graph from CDF.
135
161
 
136
162
  The Classic Graph consists of the following core resource type.
@@ -165,6 +191,8 @@ class CDFClassicAPI(BaseReadAPI):
165
191
  Args:
166
192
  root_asset_external_id: The external id of the root asset
167
193
  limit_per_type: The maximum number of nodes to extract per core node type. If None, all nodes are extracted.
194
+ identifier: The identifier to use for the core nodes. Note selecting "id" can cause issues if the external
195
+ ID of the core nodes is missing. Default is "id".
168
196
 
169
197
  Returns:
170
198
  IssueList: A list of issues that occurred during the extraction.
@@ -174,6 +202,18 @@ class CDFClassicAPI(BaseReadAPI):
174
202
  neat.read.cdf.graph("root_asset_external_id")
175
203
  ```
176
204
  """
205
+ return self._graph(
206
+ root_asset_external_id, limit_per_type, identifier, reference_timeseries=False, reference_files=False
207
+ )
208
+
209
+ def _graph(
210
+ self,
211
+ root_asset_external_id: str,
212
+ limit_per_type: int | None = None,
213
+ identifier: Literal["id", "externalId"] = "id",
214
+ reference_timeseries: bool = False,
215
+ reference_files: bool = False,
216
+ ) -> IssueList:
177
217
  namespace = CLASSIC_CDF_NAMESPACE
178
218
  extractor = extractors.ClassicGraphExtractor(
179
219
  self._get_client,
@@ -181,14 +221,12 @@ class CDFClassicAPI(BaseReadAPI):
181
221
  limit_per_type=limit_per_type,
182
222
  namespace=namespace,
183
223
  prefix="Classic",
224
+ identifier=identifier,
184
225
  )
185
- issues = self._state.write_graph(extractor)
186
- issues.action = "Read Classic Graph"
187
- if issues:
188
- print("Use the .inspect.issues() for more details")
226
+ extract_issues = self._state.write_graph(extractor)
227
+ if identifier == "externalId":
228
+ self._state.quoted_source_identifiers = True
189
229
 
190
- # Converting the instances from classic to core
191
- self._state.instances.store.transform(LookupRelationshipSourceTarget(namespace, "Classic"))
192
230
  self._state.instances.store.transform(
193
231
  ConvertLiteral(
194
232
  namespace["ClassicTimeSeries"],
@@ -200,11 +238,21 @@ class CDFClassicAPI(BaseReadAPI):
200
238
  LiteralToEntity(None, namespace["source"], "ClassicSourceSystem", "name"),
201
239
  )
202
240
  # Updating the information model.
203
- self._state.rule_store.transform(ClassicPrepareCore(namespace))
241
+ prepare_issues = self._state.rule_store.transform(
242
+ ClassicPrepareCore(namespace, reference_timeseries, reference_files)
243
+ )
204
244
  # Update the instance store with the latest rules
205
245
  information_rules = self._state.rule_store.last_verified_information_rules
206
- self._state.instances.store.rules = information_rules
207
- return issues
246
+ self._state.instances.store.rules[self._state.instances.store.default_named_graph] = information_rules
247
+
248
+ all_issues = IssueList(extract_issues + prepare_issues)
249
+ # Update the provenance with all issue.
250
+ object.__setattr__(self._state.instances.store.provenance[-1].target_entity, "issues", all_issues)
251
+ all_issues.action = "Read Classic Graph"
252
+ if all_issues:
253
+ print("Use the .inspect.issues() for more details")
254
+
255
+ return all_issues
208
256
 
209
257
 
210
258
  @session_class_wrapper
@@ -241,7 +289,6 @@ class ExcelReadAPI(BaseReadAPI):
241
289
  class ExcelExampleAPI(BaseReadAPI):
242
290
  """Used as example for reading some data model into the NeatSession."""
243
291
 
244
- @property
245
292
  def pump_example(self) -> IssueList:
246
293
  """Reads the Hello World pump example into the NeatSession."""
247
294
  importer: importers.ExcelImporter = importers.ExcelImporter(catalog.hello_world_pump)
@@ -3,9 +3,12 @@ from cognite.client import data_modeling as dm
3
3
 
4
4
  from cognite.neat._client import NeatClient
5
5
  from cognite.neat._constants import COGNITE_MODELS
6
+ from cognite.neat._graph.transformers import SetNeatType
6
7
  from cognite.neat._issues import IssueList
8
+ from cognite.neat._issues.errors import NeatValueError
7
9
  from cognite.neat._rules.models import DMSRules
8
10
  from cognite.neat._rules.transformers import SetIDDMSModel
11
+ from cognite.neat._utils.text import humanize_collection
9
12
 
10
13
  from ._state import SessionState
11
14
  from .exceptions import NeatSessionError, session_class_wrapper
@@ -44,3 +47,27 @@ class SetAPI:
44
47
  if self._verbose:
45
48
  print(f"Client set to {self._state.client.config.project} CDF project.")
46
49
  return None
50
+
51
+ def _instance_sub_type(self, type: str, property: str, drop_property: bool = False) -> None:
52
+ """Sets the sub type of an instance based on the property."""
53
+ type_uri = self._state.instances.store.queries.type_uri(type)
54
+ property_uri = self._state.instances.store.queries.property_uri(property)
55
+
56
+ if not type_uri:
57
+ raise NeatValueError(f"Type {type} does not exist in the graph.")
58
+ elif len(type_uri) > 1:
59
+ raise NeatValueError(f"{type} has multiple ids found in the graph: {humanize_collection(type_uri)}.")
60
+
61
+ if not property_uri:
62
+ raise NeatValueError(f"Property {property} does not exist in the graph.")
63
+ elif len(type_uri) > 1:
64
+ raise NeatValueError(
65
+ f"{property} has multiple ids found in the graph: {humanize_collection(property_uri)}."
66
+ )
67
+
68
+ if not self._state.instances.store.queries.type_with_property(type_uri[0], property_uri[0]):
69
+ raise NeatValueError(f"Property {property} is not defined for type {type}.")
70
+
71
+ self._state.instances.store.transform(SetNeatType(type_uri[0], property_uri[0], drop_property))
72
+
73
+ return None
@@ -112,7 +112,7 @@ class ShowDataModelAPI(ShowBaseAPI):
112
112
  else:
113
113
  # This should never happen, but we need to handle it to satisfy mypy
114
114
  raise NeatSessionError(
115
- f"Unsupported type {type(rules) }. Make sure you have either information or DMS rules."
115
+ f"Unsupported type {type(rules)}. Make sure you have either information or DMS rules."
116
116
  )
117
117
  identifier = to_directory_compatible(str(rules.metadata.identifier))
118
118
  name = f"{identifier}.html"
@@ -201,7 +201,7 @@ class ShowDataModelImplementsAPI(ShowBaseAPI):
201
201
  else:
202
202
  # This should never happen, but we need to handle it to satisfy mypy
203
203
  raise NeatSessionError(
204
- f"Unsupported type {type(rules) }. Make sure you have either information or DMS rules."
204
+ f"Unsupported type {type(rules)}. Make sure you have either information or DMS rules."
205
205
  )
206
206
  identifier = to_directory_compatible(str(rules.metadata.identifier))
207
207
  name = f"{identifier}_implements.html"
@@ -364,7 +364,7 @@ class ShowInstanceAPI(ShowBaseAPI):
364
364
  'Try setting [bold]NeatSession(storage="oxigraph")[/bold] enable Oxigraph store.'
365
365
  )
366
366
 
367
- if not self._state.instances.store.graph:
367
+ if not self._state.instances.store.dataset:
368
368
  raise NeatSessionError("No instances available. Try using [bold].read[/bold] to load instances.")
369
369
 
370
370
  di_graph = self._generate_instance_di_graph_and_types()
@@ -395,7 +395,7 @@ class ShowInstanceAPI(ShowBaseAPI):
395
395
  object,
396
396
  subject_type,
397
397
  object_type,
398
- ) in self._state.instances.store.graph.query(query):
398
+ ) in self._state.instances.store.dataset.query(query):
399
399
  subject = remove_namespace_from_uri(subject)
400
400
  property_ = remove_namespace_from_uri(property_)
401
401
  object = remove_namespace_from_uri(object)
@@ -22,11 +22,14 @@ class SessionState:
22
22
  self.rule_store = NeatRulesStore()
23
23
  self.last_reference: DMSRules | InformationRules | None = None
24
24
  self.client = client
25
+ self.quoted_source_identifiers = False
25
26
 
26
27
  def rule_transform(self, *transformer: RulesTransformer) -> IssueList:
27
28
  if not transformer:
28
29
  raise NeatSessionError("No transformers provided.")
29
30
  first_transformer = transformer[0]
31
+
32
+ # This should not be allowed to be done automatically
30
33
  pruned = self.rule_store.prune_until_compatible(first_transformer)
31
34
  if pruned:
32
35
  type_hint = first_transformer.transform_type_hint()
@@ -64,10 +67,11 @@ class SessionState:
64
67
  return issues
65
68
 
66
69
  def write_graph(self, extractor: KnowledgeGraphExtractor) -> IssueList:
67
- self.instances.store.write(extractor)
68
- issue_list = self.rule_store.import_graph(extractor)
70
+ extract_issues = self.instances.store.write(extractor)
71
+ issues = self.rule_store.import_graph(extractor)
69
72
  self.instances.store.add_rules(self.rule_store.last_verified_information_rules)
70
- return issue_list
73
+ issues.extend(extract_issues)
74
+ return issues
71
75
 
72
76
 
73
77
  @dataclass
@@ -81,7 +85,7 @@ class InstancesState:
81
85
  def store(self) -> NeatGraphStore:
82
86
  if not self.has_store:
83
87
  if self.store_type == "oxigraph":
84
- self._store = NeatGraphStore.from_oxi_store()
88
+ self._store = NeatGraphStore.from_oxi_local_store()
85
89
  else:
86
90
  self._store = NeatGraphStore.from_memory_store()
87
91
  return cast(NeatGraphStore, self._store)
@@ -104,7 +104,7 @@ class ToAPI:
104
104
 
105
105
  with zipfile.ZipFile(filepath, "w") as zip_ref:
106
106
  zip_ref.writestr(
107
- "neat-session/instances/instances.ttl",
107
+ "neat-session/instances/instances.trig",
108
108
  self._state.instances.store.serialize(),
109
109
  )
110
110
 
@@ -220,6 +220,9 @@ class CDFToAPI:
220
220
  self._state.instances.store,
221
221
  instance_space=space,
222
222
  client=client,
223
+ # In case urllib.parse.quote() was run on the extraction, we need to run
224
+ # urllib.parse.unquote() on the load.
225
+ unquote_external_ids=self._state.quoted_source_identifiers,
223
226
  )
224
227
  result = loader.load_into_cdf(client)
225
228
  self._state.instances.outcome.append(result)
@@ -26,7 +26,7 @@ _T_Option = TypeVar("_T_Option")
26
26
 
27
27
 
28
28
  def _selection(message: str, options: Sequence[_T_Option]) -> _T_Option:
29
- option_text = "\n ".join([f"{i+1}) {option}" for i, option in enumerate(options)])
29
+ option_text = "\n ".join([f"{i + 1}) {option}" for i, option in enumerate(options)])
30
30
  selected_index = (
31
31
  IntPrompt().ask(f"{message}\n {option_text}\n", choices=list(map(str, range(1, len(options) + 1)))) - 1
32
32
  )
@@ -3,6 +3,7 @@ from collections.abc import Callable
3
3
  from typing import Any
4
4
 
5
5
  from cognite.neat._issues.errors import CDFMissingClientError, NeatImportError
6
+ from cognite.neat._issues.errors._general import NeatValueError
6
7
 
7
8
  from ._collector import _COLLECTOR
8
9
 
@@ -33,7 +34,7 @@ def _session_method_wrapper(func: Callable, cls_name: str):
33
34
  except NeatSessionError as e:
34
35
  action = _get_action()
35
36
  print(f"{_PREFIX} Cannot {action}: {e}")
36
- except (CDFMissingClientError, NeatImportError) as e:
37
+ except (CDFMissingClientError, NeatImportError, NeatValueError) as e:
37
38
  print(f"{_PREFIX} {escape(e.as_message())}")
38
39
  except ModuleNotFoundError as e:
39
40
  if e.name == "neatengine":