cognite-toolkit 0.7.49__py3-none-any.whl → 0.7.50__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 (87) hide show
  1. cognite_toolkit/_cdf_tk/builders/_function.py +81 -9
  2. cognite_toolkit/_cdf_tk/client/{resource_classes/base.py → _resource_base.py} +24 -9
  3. cognite_toolkit/_cdf_tk/client/cdf_client/api.py +23 -24
  4. cognite_toolkit/_cdf_tk/client/http_client/__init__.py +5 -3
  5. cognite_toolkit/_cdf_tk/client/http_client/_client.py +4 -2
  6. cognite_toolkit/_cdf_tk/client/http_client/_data_classes2.py +1 -106
  7. cognite_toolkit/_cdf_tk/client/http_client/_item_classes.py +118 -0
  8. cognite_toolkit/_cdf_tk/client/resource_classes/agent.py +1 -1
  9. cognite_toolkit/_cdf_tk/client/resource_classes/annotation.py +2 -2
  10. cognite_toolkit/_cdf_tk/client/resource_classes/apm_config.py +1 -1
  11. cognite_toolkit/_cdf_tk/client/resource_classes/asset.py +2 -2
  12. cognite_toolkit/_cdf_tk/client/resource_classes/charts_data.py +1 -1
  13. cognite_toolkit/_cdf_tk/client/resource_classes/cognite_file.py +1 -1
  14. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_constraints.py +1 -1
  15. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_container.py +1 -1
  16. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_data_model.py +1 -1
  17. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_data_types.py +1 -1
  18. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_indexes.py +1 -1
  19. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_instance.py +1 -1
  20. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_space.py +1 -1
  21. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_view.py +1 -1
  22. cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_view_property.py +1 -1
  23. cognite_toolkit/_cdf_tk/client/resource_classes/dataset.py +3 -3
  24. cognite_toolkit/_cdf_tk/client/resource_classes/event.py +3 -3
  25. cognite_toolkit/_cdf_tk/client/resource_classes/extraction_pipeline.py +3 -3
  26. cognite_toolkit/_cdf_tk/client/resource_classes/filemetadata.py +2 -2
  27. cognite_toolkit/_cdf_tk/client/resource_classes/function.py +1 -1
  28. cognite_toolkit/_cdf_tk/client/resource_classes/function_schedule.py +1 -1
  29. cognite_toolkit/_cdf_tk/client/resource_classes/graphql_data_model.py +1 -1
  30. cognite_toolkit/_cdf_tk/client/resource_classes/group/acls.py +1 -1
  31. cognite_toolkit/_cdf_tk/client/resource_classes/group/capability.py +1 -1
  32. cognite_toolkit/_cdf_tk/client/resource_classes/group/group.py +1 -1
  33. cognite_toolkit/_cdf_tk/client/resource_classes/group/scopes.py +1 -1
  34. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_destination.py +3 -3
  35. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_job.py +3 -3
  36. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_mapping.py +3 -3
  37. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_auth.py +1 -1
  38. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_base.py +3 -3
  39. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_certificate.py +1 -1
  40. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_eventhub.py +1 -1
  41. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_kafka.py +1 -1
  42. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_mqtt.py +1 -1
  43. cognite_toolkit/_cdf_tk/client/resource_classes/hosted_extractor_source/_rest.py +1 -1
  44. cognite_toolkit/_cdf_tk/client/resource_classes/identifiers.py +1 -1
  45. cognite_toolkit/_cdf_tk/client/resource_classes/infield.py +1 -1
  46. cognite_toolkit/_cdf_tk/client/resource_classes/instance_api.py +1 -1
  47. cognite_toolkit/_cdf_tk/client/resource_classes/label.py +1 -1
  48. cognite_toolkit/_cdf_tk/client/resource_classes/location_filter.py +1 -1
  49. cognite_toolkit/_cdf_tk/client/resource_classes/raw.py +1 -1
  50. cognite_toolkit/_cdf_tk/client/resource_classes/relationship.py +3 -3
  51. cognite_toolkit/_cdf_tk/client/resource_classes/resource_view_mapping.py +1 -1
  52. cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_capability.py +3 -3
  53. cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_common.py +1 -1
  54. cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_data_post_processing.py +3 -3
  55. cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_frame.py +3 -3
  56. cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_location.py +2 -2
  57. cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_map.py +3 -3
  58. cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_robot.py +3 -3
  59. cognite_toolkit/_cdf_tk/client/resource_classes/search_config.py +1 -1
  60. cognite_toolkit/_cdf_tk/client/resource_classes/securitycategory.py +1 -1
  61. cognite_toolkit/_cdf_tk/client/resource_classes/sequence.py +3 -3
  62. cognite_toolkit/_cdf_tk/client/resource_classes/sequence_rows.py +1 -1
  63. cognite_toolkit/_cdf_tk/client/resource_classes/simulator_model.py +2 -2
  64. cognite_toolkit/_cdf_tk/client/resource_classes/streamlit_.py +3 -3
  65. cognite_toolkit/_cdf_tk/client/resource_classes/streams.py +1 -1
  66. cognite_toolkit/_cdf_tk/client/resource_classes/three_d.py +9 -2
  67. cognite_toolkit/_cdf_tk/client/resource_classes/timeseries.py +3 -3
  68. cognite_toolkit/_cdf_tk/client/resource_classes/transformation.py +3 -3
  69. cognite_toolkit/_cdf_tk/client/resource_classes/workflow.py +1 -1
  70. cognite_toolkit/_cdf_tk/client/resource_classes/workflow_trigger.py +1 -1
  71. cognite_toolkit/_cdf_tk/client/resource_classes/workflow_version.py +1 -1
  72. cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +1 -1
  73. cognite_toolkit/_cdf_tk/commands/build_cmd.py +11 -1
  74. cognite_toolkit/_cdf_tk/data_classes/_tracking_info.py +4 -0
  75. cognite_toolkit/_cdf_tk/feature_flags.py +4 -0
  76. cognite_toolkit/_cdf_tk/tk_warnings/__init__.py +2 -0
  77. cognite_toolkit/_cdf_tk/tk_warnings/fileread.py +20 -0
  78. cognite_toolkit/_cdf_tk/utils/__init__.py +3 -0
  79. cognite_toolkit/_cdf_tk/utils/pip_validator.py +96 -0
  80. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  81. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  82. cognite_toolkit/_resources/cdf.toml +1 -1
  83. cognite_toolkit/_version.py +1 -1
  84. {cognite_toolkit-0.7.49.dist-info → cognite_toolkit-0.7.50.dist-info}/METADATA +1 -1
  85. {cognite_toolkit-0.7.49.dist-info → cognite_toolkit-0.7.50.dist-info}/RECORD +87 -85
  86. {cognite_toolkit-0.7.49.dist-info → cognite_toolkit-0.7.50.dist-info}/WHEEL +0 -0
  87. {cognite_toolkit-0.7.49.dist-info → cognite_toolkit-0.7.50.dist-info}/entry_points.txt +0 -0
@@ -1,9 +1,9 @@
1
1
  from typing import Any, ClassVar, Literal
2
2
 
3
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
3
+ from cognite_toolkit._cdf_tk.client._resource_base import (
4
4
  BaseModelObject,
5
- RequestUpdateable,
6
5
  ResponseResource,
6
+ UpdatableRequestResource,
7
7
  )
8
8
  from cognite_toolkit._cdf_tk.client.resource_classes.identifiers import DataSetId
9
9
 
@@ -36,7 +36,7 @@ class Robot(BaseModelObject):
36
36
  return DataSetId(data_set_id=self.data_set_id)
37
37
 
38
38
 
39
- class RobotRequest(Robot, RequestUpdateable):
39
+ class RobotRequest(Robot, UpdatableRequestResource):
40
40
  """Request resource for creating or updating a Robot."""
41
41
 
42
42
  container_fields: ClassVar[frozenset[str]] = frozenset({"metadata"})
@@ -1,4 +1,4 @@
1
- from cognite_toolkit._cdf_tk.client.resource_classes.base import BaseModelObject, RequestResource, ResponseResource
1
+ from cognite_toolkit._cdf_tk.client._resource_base import BaseModelObject, RequestResource, ResponseResource
2
2
 
3
3
  from .identifiers import Identifier
4
4
 
@@ -1,4 +1,4 @@
1
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
1
+ from cognite_toolkit._cdf_tk.client._resource_base import (
2
2
  BaseModelObject,
3
3
  RequestResource,
4
4
  ResponseResource,
@@ -1,9 +1,9 @@
1
1
  from typing import ClassVar, Literal
2
2
 
3
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
3
+ from cognite_toolkit._cdf_tk.client._resource_base import (
4
4
  BaseModelObject,
5
- RequestUpdateable,
6
5
  ResponseResource,
6
+ UpdatableRequestResource,
7
7
  )
8
8
 
9
9
  from .identifiers import ExternalId
@@ -32,7 +32,7 @@ class Sequence(BaseModelObject):
32
32
  return ExternalId(external_id=self.external_id)
33
33
 
34
34
 
35
- class SequenceRequest(Sequence, RequestUpdateable):
35
+ class SequenceRequest(Sequence, UpdatableRequestResource):
36
36
  container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "columns"})
37
37
 
38
38
 
@@ -1,6 +1,6 @@
1
1
  from typing import Literal
2
2
 
3
- from cognite_toolkit._cdf_tk.client.resource_classes.base import BaseModelObject, RequestResource, ResponseResource
3
+ from cognite_toolkit._cdf_tk.client._resource_base import BaseModelObject, RequestResource, ResponseResource
4
4
 
5
5
  from .identifiers import ExternalId
6
6
 
@@ -2,12 +2,12 @@ from typing import Any, Literal
2
2
 
3
3
  from pydantic import Field
4
4
 
5
- from cognite_toolkit._cdf_tk.client.resource_classes.base import RequestUpdateable, ResponseResource
5
+ from cognite_toolkit._cdf_tk.client._resource_base import ResponseResource, UpdatableRequestResource
6
6
 
7
7
  from .identifiers import ExternalId
8
8
 
9
9
 
10
- class SimulatorModelRequest(RequestUpdateable):
10
+ class SimulatorModelRequest(UpdatableRequestResource):
11
11
  # The 'id' field is not part of the request when creating a new resource,
12
12
  # but is needed when updating an existing resource.
13
13
  id: int | None = Field(default=None, exclude=True)
@@ -1,9 +1,9 @@
1
1
  from typing import Any, Literal
2
2
 
3
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
3
+ from cognite_toolkit._cdf_tk.client._resource_base import (
4
4
  BaseModelObject,
5
- RequestUpdateable,
6
5
  ResponseResource,
6
+ UpdatableRequestResource,
7
7
  )
8
8
 
9
9
  from .filemetadata import FileMetadataRequest
@@ -25,7 +25,7 @@ class StreamlitFile(BaseModelObject):
25
25
  cognite_toolkit_app_hash: str | None = None
26
26
 
27
27
 
28
- class StreamlitRequest(StreamlitFile, RequestUpdateable):
28
+ class StreamlitRequest(StreamlitFile, UpdatableRequestResource):
29
29
  """Request resource for creating/updating Streamlit apps."""
30
30
 
31
31
  def as_id(self) -> ExternalId:
@@ -1,8 +1,8 @@
1
1
  from typing import Literal
2
2
 
3
+ from cognite_toolkit._cdf_tk.client._resource_base import BaseModelObject, RequestResource, ResponseResource
3
4
  from cognite_toolkit._cdf_tk.constants import StreamTemplateName
4
5
 
5
- from .base import BaseModelObject, RequestResource, ResponseResource
6
6
  from .identifiers import ExternalId
7
7
 
8
8
 
@@ -3,7 +3,14 @@ from typing import ClassVar, Literal
3
3
 
4
4
  from pydantic import Field
5
5
 
6
- from .base import BaseModelObject, Identifier, RequestResource, RequestUpdateable, ResponseResource
6
+ from cognite_toolkit._cdf_tk.client._resource_base import (
7
+ BaseModelObject,
8
+ Identifier,
9
+ RequestResource,
10
+ ResponseResource,
11
+ UpdatableRequestResource,
12
+ )
13
+
7
14
  from .identifiers import InternalId
8
15
  from .instance_api import NodeReference
9
16
 
@@ -33,7 +40,7 @@ class ThreeDModelRequest(RequestResource):
33
40
  return InternalId(id=self.id)
34
41
 
35
42
 
36
- class ThreeDModelClassicRequest(ThreeDModelRequest, RequestUpdateable):
43
+ class ThreeDModelClassicRequest(ThreeDModelRequest, UpdatableRequestResource):
37
44
  container_fields: ClassVar[frozenset[str]] = frozenset({"metadata"})
38
45
  data_set_id: int | None = None
39
46
  metadata: dict[str, str] | None = None
@@ -1,9 +1,9 @@
1
1
  from typing import Any, ClassVar, Literal
2
2
 
3
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
3
+ from cognite_toolkit._cdf_tk.client._resource_base import (
4
4
  BaseModelObject,
5
- RequestUpdateable,
6
5
  ResponseResource,
6
+ UpdatableRequestResource,
7
7
  )
8
8
 
9
9
  from .identifiers import ExternalId, InternalOrExternalId
@@ -29,7 +29,7 @@ class TimeSeries(BaseModelObject):
29
29
  return ExternalId(external_id=self.external_id)
30
30
 
31
31
 
32
- class TimeSeriesRequest(TimeSeries, RequestUpdateable):
32
+ class TimeSeriesRequest(TimeSeries, UpdatableRequestResource):
33
33
  container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "security_categories"})
34
34
  non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"is_step"})
35
35
 
@@ -2,10 +2,10 @@ from typing import Annotated, ClassVar, Literal
2
2
 
3
3
  from pydantic import Field, JsonValue
4
4
 
5
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
5
+ from cognite_toolkit._cdf_tk.client._resource_base import (
6
6
  BaseModelObject,
7
- RequestUpdateable,
8
7
  ResponseResource,
8
+ UpdatableRequestResource,
9
9
  )
10
10
 
11
11
  from .identifiers import ExternalId
@@ -106,7 +106,7 @@ class Transformation(BaseModelObject):
106
106
  return ExternalId(external_id=self.external_id)
107
107
 
108
108
 
109
- class TransformationRequest(Transformation, RequestUpdateable):
109
+ class TransformationRequest(Transformation, UpdatableRequestResource):
110
110
  container_fields: ClassVar[frozenset[str]] = frozenset({"tags"})
111
111
  non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"is_public", "query", "destination"})
112
112
  query: str | None = None
@@ -1,4 +1,4 @@
1
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
1
+ from cognite_toolkit._cdf_tk.client._resource_base import (
2
2
  BaseModelObject,
3
3
  RequestResource,
4
4
  ResponseResource,
@@ -2,7 +2,7 @@ from typing import Annotated, Literal
2
2
 
3
3
  from pydantic import Field, JsonValue
4
4
 
5
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
5
+ from cognite_toolkit._cdf_tk.client._resource_base import (
6
6
  BaseModelObject,
7
7
  RequestResource,
8
8
  ResponseResource,
@@ -3,7 +3,7 @@ from typing import Annotated, Any, Literal
3
3
  from pydantic import Field, JsonValue, field_validator
4
4
  from pydantic_core.core_schema import ValidationInfo
5
5
 
6
- from cognite_toolkit._cdf_tk.client.resource_classes.base import (
6
+ from cognite_toolkit._cdf_tk.client._resource_base import (
7
7
  BaseModelObject,
8
8
  RequestResource,
9
9
  ResponseResource,
@@ -11,7 +11,7 @@ from cognite.client.utils._identifier import InstanceId
11
11
  from cognite.client.utils._text import to_camel_case
12
12
  from pydantic import BaseModel, BeforeValidator, Field, field_validator, model_validator
13
13
 
14
- from cognite_toolkit._cdf_tk.client.resource_classes.base import BaseModelObject, RequestResource
14
+ from cognite_toolkit._cdf_tk.client._resource_base import BaseModelObject, RequestResource
15
15
  from cognite_toolkit._cdf_tk.client.resource_classes.identifiers import InternalId
16
16
  from cognite_toolkit._cdf_tk.client.resource_classes.instance_api import InstanceIdentifier
17
17
  from cognite_toolkit._cdf_tk.client.resource_classes.legacy.instances import InstanceApplyList
@@ -11,7 +11,7 @@ from rich import print
11
11
  from rich.panel import Panel
12
12
  from rich.progress import track
13
13
 
14
- from cognite_toolkit._cdf_tk.builders import Builder, create_builder
14
+ from cognite_toolkit._cdf_tk.builders import Builder, FunctionBuilder, create_builder
15
15
  from cognite_toolkit._cdf_tk.cdf_toml import CDFToml
16
16
  from cognite_toolkit._cdf_tk.client import ToolkitClient
17
17
  from cognite_toolkit._cdf_tk.client.resource_classes.legacy.raw import RawDatabase
@@ -33,6 +33,7 @@ from cognite_toolkit._cdf_tk.cruds import (
33
33
  DataSetsCRUD,
34
34
  ExtractionPipelineConfigCRUD,
35
35
  FileCRUD,
36
+ FunctionCRUD,
36
37
  LocationFilterCRUD,
37
38
  NodeCRUD,
38
39
  RawDatabaseCRUD,
@@ -415,6 +416,15 @@ class BuildCommand(ToolkitCommand):
415
416
 
416
417
  build_resources_by_folder[resource_name].extend(built_resources)
417
418
 
419
+ # Collect validation metrics from FunctionBuilder
420
+ if resource_name == FunctionCRUD.folder_name and isinstance(builder, FunctionBuilder):
421
+ self._additional_tracking_info.function_validation_count += builder.validation_count
422
+ self._additional_tracking_info.function_validation_failures += builder.validation_failures
423
+ self._additional_tracking_info.function_validation_credential_errors += (
424
+ builder.validation_credential_errors
425
+ )
426
+ self._additional_tracking_info.function_validation_time_ms += builder.validation_time_ms
427
+
418
428
  return build_resources_by_folder
419
429
 
420
430
  def _get_builder(self, build_dir: Path, resource_name: str) -> Builder:
@@ -32,6 +32,10 @@ class CommandTrackingInfo(BaseModel):
32
32
  downloaded_library_ids: set[str] = Field(default_factory=set, alias="downloadedLibraryIds")
33
33
  downloaded_package_ids: set[str] = Field(default_factory=set, alias="downloadedPackageIds")
34
34
  downloaded_module_ids: set[str] = Field(default_factory=set, alias="downloadedModuleIds")
35
+ function_validation_count: int = Field(default=0, alias="functionValidationCount")
36
+ function_validation_failures: int = Field(default=0, alias="functionValidationFailures")
37
+ function_validation_credential_errors: int = Field(default=0, alias="functionValidationCredentialErrors")
38
+ function_validation_time_ms: int = Field(default=0, alias="functionValidationTimeMs")
35
39
 
36
40
  def to_dict(self) -> dict[str, Any]:
37
41
  """Convert the tracking info to a dictionary for Mixpanel.
@@ -65,6 +65,10 @@ class Flags(Enum):
65
65
  visible=True,
66
66
  description="Enables the support for simulator model resources",
67
67
  )
68
+ FUNCTION_REQUIREMENTS_VALIDATION = FlagMetadata(
69
+ visible=True,
70
+ description="Enables validation of function requirements.txt during build using pip dry-run",
71
+ )
68
72
 
69
73
  def is_enabled(self) -> bool:
70
74
  return FeatureFlag.is_enabled(self)
@@ -12,6 +12,7 @@ from .fileread import (
12
12
  EnvironmentVariableMissingWarning,
13
13
  FileExistsWarning,
14
14
  FileReadWarning,
15
+ FunctionRequirementsValidationWarning,
15
16
  MissingFileWarning,
16
17
  MissingReferencedWarning,
17
18
  MissingRequiredParameterWarning,
@@ -51,6 +52,7 @@ __all__ = [
51
52
  "EnvironmentVariableMissingWarning",
52
53
  "FileExistsWarning",
53
54
  "FileReadWarning",
55
+ "FunctionRequirementsValidationWarning",
54
56
  "GeneralWarning",
55
57
  "HTTPWarning",
56
58
  "HighSeverityWarning",
@@ -270,6 +270,26 @@ class StreamlitRequirementsWarning(FileReadWarning):
270
270
  return f"Missing dependencies in requirements.txt: {', '.join(self.dependencies)}"
271
271
 
272
272
 
273
+ @dataclass(frozen=True)
274
+ class FunctionRequirementsValidationWarning(FileReadWarning):
275
+ severity: ClassVar[SeverityLevel] = SeverityLevel.HIGH
276
+ function_external_id: str
277
+ error_details: str
278
+ is_credential_error: bool
279
+
280
+ def get_message(self) -> str:
281
+ message = (
282
+ f"Function [bold]{self.function_external_id}[/bold] requirements.txt validation failed. "
283
+ f"Packages could not be resolved: {self.error_details}"
284
+ )
285
+ if self.is_credential_error:
286
+ message += (
287
+ f"\n{HINT_LEAD_TEXT}This appears to be a credential/authentication issue. "
288
+ "Check if the Personal Access Token (PAT) or credentials in indexUrl are valid and not expired."
289
+ )
290
+ return message
291
+
292
+
273
293
  @dataclass(frozen=True)
274
294
  class ResourceFormatWarning(FileReadWarning):
275
295
  severity: ClassVar[SeverityLevel] = SeverityLevel.HIGH
@@ -26,10 +26,12 @@ from .modules import (
26
26
  module_path_display_name,
27
27
  resource_folder_from_path,
28
28
  )
29
+ from .pip_validator import PipValidationResult, validate_requirements_with_pip
29
30
  from .sentry_utils import sentry_exception_filter
30
31
 
31
32
  __all__ = [
32
33
  "GraphQLParser",
34
+ "PipValidationResult",
33
35
  "YAMLComment",
34
36
  "YAMLWithComments",
35
37
  "calculate_directory_hash",
@@ -57,4 +59,5 @@ __all__ = [
57
59
  "stringify_value_by_key_in_yaml",
58
60
  "tmp_build_directory",
59
61
  "to_diff",
62
+ "validate_requirements_with_pip",
60
63
  ]
@@ -0,0 +1,96 @@
1
+ """Utilities for validating Python package requirements."""
2
+
3
+ import subprocess
4
+ import sys
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ # Maximum number of error lines to include in warnings
9
+ _MAX_ERROR_LINES = 3
10
+
11
+
12
+ @dataclass
13
+ class PipValidationResult:
14
+ """Result from validating a requirements.txt file."""
15
+
16
+ error_message: str | None = None
17
+
18
+ @property
19
+ def success(self) -> bool:
20
+ """Validation succeeded if there's no error message."""
21
+ return self.error_message is None
22
+
23
+ @property
24
+ def is_credential_error(self) -> bool:
25
+ """Check if the error appears to be related to authentication/credentials.
26
+
27
+ Only checks for explicit HTTP authentication errors to avoid false positives
28
+ from legitimate package not found errors.
29
+ """
30
+ if not self.error_message:
31
+ return False
32
+ credential_indicators = [
33
+ "401",
34
+ "403",
35
+ "Unauthorized",
36
+ "Forbidden",
37
+ "Authentication",
38
+ ]
39
+ return any(indicator in self.error_message for indicator in credential_indicators)
40
+
41
+
42
+ def validate_requirements_with_pip(
43
+ requirements_txt_path: Path,
44
+ index_url: str | None = None,
45
+ extra_index_urls: list[str] | None = None,
46
+ timeout: int = 10,
47
+ ) -> PipValidationResult:
48
+ """Validate that requirements.txt can be resolved using pip install --dry-run.
49
+
50
+ This simulates package installation without actually installing anything.
51
+ It validates that:
52
+ - All packages exist
53
+ - All specified versions are available
54
+ - Credentials for private repositories are valid
55
+ - Index URLs are accessible
56
+
57
+ Args:
58
+ requirements_txt_path: Path to the requirements.txt file
59
+ index_url: Optional custom package index URL (replaces PyPI)
60
+ extra_index_urls: Optional additional package index URLs
61
+ timeout: Timeout in seconds for the pip command
62
+
63
+ Returns:
64
+ PipValidationResult with success status and error details if failed
65
+
66
+ """
67
+ if not requirements_txt_path.exists():
68
+ return PipValidationResult(error_message=f"Requirements file not found: {requirements_txt_path}")
69
+
70
+ # Build pip command with custom index URLs
71
+ args = [
72
+ sys.executable,
73
+ "-m",
74
+ "pip",
75
+ "install",
76
+ "--dry-run",
77
+ "--ignore-installed",
78
+ "--no-deps",
79
+ "--disable-pip-version-check",
80
+ "-r",
81
+ str(requirements_txt_path),
82
+ *(["--index-url", index_url] if index_url else []),
83
+ *([arg for url in (extra_index_urls or []) for arg in ["--extra-index-url", url]]),
84
+ ]
85
+
86
+ try:
87
+ result = subprocess.run(args, capture_output=True, text=True, timeout=timeout, check=False)
88
+ if result.returncode == 0:
89
+ return PipValidationResult()
90
+ return PipValidationResult(
91
+ error_message=f"pip validation failed with exit code {result.returncode}:\n{result.stderr}",
92
+ )
93
+ except subprocess.TimeoutExpired:
94
+ return PipValidationResult(error_message=f"pip validation timed out after {timeout} seconds")
95
+ except (OSError, RuntimeError) as e:
96
+ return PipValidationResult(error_message=f"Error running pip validation: {e!s}")
@@ -12,7 +12,7 @@ jobs:
12
12
  environment: dev
13
13
  name: Deploy
14
14
  container:
15
- image: cognite/toolkit:0.7.49
15
+ image: cognite/toolkit:0.7.50
16
16
  env:
17
17
  CDF_CLUSTER: ${{ vars.CDF_CLUSTER }}
18
18
  CDF_PROJECT: ${{ vars.CDF_PROJECT }}
@@ -10,7 +10,7 @@ jobs:
10
10
  environment: dev
11
11
  name: Deploy Dry Run
12
12
  container:
13
- image: cognite/toolkit:0.7.49
13
+ image: cognite/toolkit:0.7.50
14
14
  env:
15
15
  CDF_CLUSTER: ${{ vars.CDF_CLUSTER }}
16
16
  CDF_PROJECT: ${{ vars.CDF_PROJECT }}
@@ -4,7 +4,7 @@ default_env = "<DEFAULT_ENV_PLACEHOLDER>"
4
4
  [modules]
5
5
  # This is the version of the modules. It should not be changed manually.
6
6
  # It will be updated by the 'cdf modules upgrade' command.
7
- version = "0.7.49"
7
+ version = "0.7.50"
8
8
 
9
9
 
10
10
  [plugins]
@@ -1 +1 @@
1
- __version__ = "0.7.49"
1
+ __version__ = "0.7.50"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite_toolkit
3
- Version: 0.7.49
3
+ Version: 0.7.50
4
4
  Summary: Official Cognite Data Fusion tool for project templates and configuration deployment
5
5
  Author: Cognite AS
6
6
  Author-email: Cognite AS <support@cognite.com>