cognite-extractor-utils 7.5.14__py3-none-any.whl → 7.7.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-extractor-utils might be problematic. Click here for more details.

Files changed (47) hide show
  1. cognite/extractorutils/__init__.py +1 -1
  2. cognite/extractorutils/_inner_util.py +1 -1
  3. cognite/extractorutils/base.py +120 -40
  4. cognite/extractorutils/configtools/__init__.py +4 -5
  5. cognite/extractorutils/configtools/_util.py +3 -2
  6. cognite/extractorutils/configtools/elements.py +206 -33
  7. cognite/extractorutils/configtools/loaders.py +68 -16
  8. cognite/extractorutils/configtools/validators.py +5 -1
  9. cognite/extractorutils/exceptions.py +11 -2
  10. cognite/extractorutils/metrics.py +17 -12
  11. cognite/extractorutils/statestore/__init__.py +77 -3
  12. cognite/extractorutils/statestore/_base.py +7 -3
  13. cognite/extractorutils/statestore/hashing.py +129 -15
  14. cognite/extractorutils/statestore/watermark.py +77 -87
  15. cognite/extractorutils/threading.py +30 -4
  16. cognite/extractorutils/unstable/__init__.py +5 -5
  17. cognite/extractorutils/unstable/configuration/__init__.py +3 -0
  18. cognite/extractorutils/unstable/configuration/exceptions.py +13 -2
  19. cognite/extractorutils/unstable/configuration/loaders.py +78 -13
  20. cognite/extractorutils/unstable/configuration/models.py +121 -7
  21. cognite/extractorutils/unstable/core/__init__.py +5 -0
  22. cognite/extractorutils/unstable/core/_dto.py +5 -3
  23. cognite/extractorutils/unstable/core/base.py +113 -4
  24. cognite/extractorutils/unstable/core/errors.py +41 -0
  25. cognite/extractorutils/unstable/core/logger.py +149 -0
  26. cognite/extractorutils/unstable/core/restart_policy.py +16 -2
  27. cognite/extractorutils/unstable/core/runtime.py +44 -6
  28. cognite/extractorutils/unstable/core/tasks.py +53 -1
  29. cognite/extractorutils/unstable/scheduling/__init__.py +13 -0
  30. cognite/extractorutils/unstable/scheduling/_scheduler.py +1 -1
  31. cognite/extractorutils/uploader/__init__.py +9 -5
  32. cognite/extractorutils/uploader/_base.py +4 -5
  33. cognite/extractorutils/uploader/assets.py +13 -8
  34. cognite/extractorutils/uploader/data_modeling.py +37 -2
  35. cognite/extractorutils/uploader/events.py +14 -9
  36. cognite/extractorutils/uploader/files.py +80 -21
  37. cognite/extractorutils/uploader/raw.py +12 -7
  38. cognite/extractorutils/uploader/time_series.py +370 -94
  39. cognite/extractorutils/uploader/upload_failure_handler.py +35 -2
  40. cognite/extractorutils/uploader_extractor.py +47 -9
  41. cognite/extractorutils/uploader_types.py +26 -1
  42. cognite/extractorutils/util.py +76 -23
  43. {cognite_extractor_utils-7.5.14.dist-info → cognite_extractor_utils-7.7.0.dist-info}/METADATA +1 -1
  44. cognite_extractor_utils-7.7.0.dist-info/RECORD +50 -0
  45. cognite_extractor_utils-7.5.14.dist-info/RECORD +0 -50
  46. {cognite_extractor_utils-7.5.14.dist-info → cognite_extractor_utils-7.7.0.dist-info}/WHEEL +0 -0
  47. {cognite_extractor_utils-7.5.14.dist-info → cognite_extractor_utils-7.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,7 @@
1
+ """
2
+ Module containing pre-built elements for common extractor configuration.
3
+ """
4
+
1
5
  # Copyright 2023 Cognite AS
2
6
  #
3
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -56,7 +60,7 @@ _logger = logging.getLogger(__name__)
56
60
  @dataclass
57
61
  class CertificateConfig:
58
62
  """
59
- Configuration parameters for certificates
63
+ Configuration parameters for certificates.
60
64
  """
61
65
 
62
66
  path: str
@@ -67,7 +71,7 @@ class CertificateConfig:
67
71
  @dataclass
68
72
  class AuthenticatorConfig:
69
73
  """
70
- Configuration parameters for an OIDC flow
74
+ Configuration parameters for an OIDC flow.
71
75
  """
72
76
 
73
77
  client_id: str
@@ -85,7 +89,7 @@ class AuthenticatorConfig:
85
89
  @dataclass
86
90
  class ConnectionConfig:
87
91
  """
88
- Configuration parameters for the global_config python SDK settings
92
+ Configuration parameters for the global_config python SDK settings.
89
93
  """
90
94
 
91
95
  disable_gzip: bool = False
@@ -102,6 +106,7 @@ class ConnectionConfig:
102
106
  class EitherIdConfig:
103
107
  """
104
108
  Configuration parameter representing an ID in CDF, which can either be an external or internal ID.
109
+
105
110
  An EitherId can only hold one ID type, not both.
106
111
  """
107
112
 
@@ -110,23 +115,35 @@ class EitherIdConfig:
110
115
 
111
116
  @property
112
117
  def either_id(self) -> EitherId:
118
+ """
119
+ Returns an EitherId object based on the current configuration.
120
+
121
+ Raises:
122
+ TypeError: If both id and external_id are None, or if both are set.
123
+ """
113
124
  return EitherId(id=self.id, external_id=self.external_id)
114
125
 
115
126
 
116
127
  class TimeIntervalConfig(yaml.YAMLObject):
117
128
  """
118
- Configuration parameter for setting a time interval
129
+ Configuration parameter for setting a time interval.
119
130
  """
120
131
 
121
132
  def __init__(self, expression: str) -> None:
122
133
  self._interval, self._expression = TimeIntervalConfig._parse_expression(expression)
123
134
 
124
135
  def __eq__(self, other: object) -> bool:
136
+ """
137
+ Two TimeIntervalConfig objects are equal if they have the same number of seconds in their interval.
138
+ """
125
139
  if not isinstance(other, TimeIntervalConfig):
126
140
  return NotImplemented
127
141
  return self._interval == other._interval
128
142
 
129
143
  def __hash__(self) -> int:
144
+ """
145
+ Hash function for TimeIntervalConfig based on the number of seconds in the interval.
146
+ """
130
147
  return hash(self._interval)
131
148
 
132
149
  @classmethod
@@ -148,42 +165,75 @@ class TimeIntervalConfig(yaml.YAMLObject):
148
165
 
149
166
  @property
150
167
  def seconds(self) -> int:
168
+ """
169
+ Time interval as number of seconds.
170
+ """
151
171
  return self._interval
152
172
 
153
173
  @property
154
174
  def minutes(self) -> float:
175
+ """
176
+ Time interval as number of minutes.
177
+
178
+ This is a float since the underlying interval is in seconds.
179
+ """
155
180
  return self._interval / 60
156
181
 
157
182
  @property
158
183
  def hours(self) -> float:
184
+ """
185
+ Time interval as number of hours.
186
+
187
+ This is a float since the underlying interval is in seconds.
188
+ """
159
189
  return self._interval / (60 * 60)
160
190
 
161
191
  @property
162
192
  def days(self) -> float:
193
+ """
194
+ Time interval as number of days.
195
+
196
+ This is a float since the underlying interval is in seconds.
197
+ """
163
198
  return self._interval / (60 * 60 * 24)
164
199
 
165
200
  @property
166
201
  def timedelta(self) -> timedelta:
202
+ """
203
+ Time interval as a timedelta object.
204
+ """
167
205
  days = self._interval // (60 * 60 * 24)
168
206
  seconds = self._interval % (60 * 60 * 24)
169
207
  return timedelta(days=days, seconds=seconds)
170
208
 
171
209
  def __int__(self) -> int:
210
+ """
211
+ Returns the time interval as a number of seconds.
212
+ """
172
213
  return int(self._interval)
173
214
 
174
215
  def __float__(self) -> float:
216
+ """
217
+ Returns the time interval as a number of seconds.
218
+ """
175
219
  return float(self._interval)
176
220
 
177
221
  def __str__(self) -> str:
222
+ """
223
+ Returns the time interval as a human readable string.
224
+ """
178
225
  return self._expression
179
226
 
180
227
  def __repr__(self) -> str:
228
+ """
229
+ Returns the time interval as a human readable string.
230
+ """
181
231
  return self._expression
182
232
 
183
233
 
184
234
  class FileSizeConfig(yaml.YAMLObject):
185
235
  """
186
- Configuration parameter for setting a file size
236
+ Configuration parameter for setting a file size.
187
237
  """
188
238
 
189
239
  def __init__(self, expression: str) -> None:
@@ -219,50 +269,89 @@ class FileSizeConfig(yaml.YAMLObject):
219
269
 
220
270
  @property
221
271
  def bytes(self) -> int:
272
+ """
273
+ File size in bytes.
274
+ """
222
275
  return self._bytes
223
276
 
224
277
  @property
225
278
  def kilobytes(self) -> float:
279
+ """
280
+ File size in kilobytes.
281
+ """
226
282
  return self._bytes / 1000
227
283
 
228
284
  @property
229
285
  def megabytes(self) -> float:
286
+ """
287
+ File size in megabytes.
288
+ """
230
289
  return self._bytes / 1_000_000
231
290
 
232
291
  @property
233
292
  def gigabytes(self) -> float:
293
+ """
294
+ File size in gigabytes.
295
+ """
234
296
  return self._bytes / 1_000_000_000
235
297
 
236
298
  @property
237
299
  def terabytes(self) -> float:
300
+ """
301
+ File size in terabytes.
302
+ """
238
303
  return self._bytes / 1_000_000_000_000
239
304
 
240
305
  @property
241
306
  def kibibytes(self) -> float:
307
+ """
308
+ File size in kibibytes (1024 bytes).
309
+ """
242
310
  return self._bytes / 1024
243
311
 
244
312
  @property
245
313
  def mebibytes(self) -> float:
314
+ """
315
+ File size in mebibytes (1024 kibibytes).
316
+ """
246
317
  return self._bytes / 1_048_576
247
318
 
248
319
  @property
249
320
  def gibibytes(self) -> float:
321
+ """
322
+ File size in gibibytes (1024 mebibytes).
323
+ """
250
324
  return self._bytes / 1_073_741_824
251
325
 
252
326
  @property
253
327
  def tebibytes(self) -> float:
328
+ """
329
+ File size in tebibytes (1024 gibibytes).
330
+ """
254
331
  return self._bytes / 1_099_511_627_776
255
332
 
256
333
  def __int__(self) -> int:
334
+ """
335
+ Returns the file size as bytes.
336
+ """
257
337
  return int(self._bytes)
258
338
 
259
339
  def __float__(self) -> float:
340
+ """
341
+ Returns the file size as bytes.
342
+ """
260
343
  return float(self._bytes)
261
344
 
262
345
  def __str__(self) -> str:
346
+ """
347
+ Returns the file size as a human readable string.
348
+ """
263
349
  return self._expression
264
350
 
265
351
  def __repr__(self) -> str:
352
+ """
353
+ Returns the file size as a human readable string.
354
+ """
266
355
  return self._expression
267
356
 
268
357
 
@@ -281,7 +370,7 @@ def _validate_https_url(value: str, name: str) -> None:
281
370
  @dataclass
282
371
  class CogniteConfig:
283
372
  """
284
- Configuration parameters for CDF connection, such as project name, host address and authentication
373
+ Configuration parameters for CDF connection, such as project name, host address and authentication.
285
374
  """
286
375
 
287
376
  project: str
@@ -290,7 +379,7 @@ class CogniteConfig:
290
379
  data_set_id: int | None = None
291
380
  data_set_external_id: str | None = None
292
381
  extraction_pipeline: EitherIdConfig | None = None
293
- timeout: TimeIntervalConfig = TimeIntervalConfig("30s")
382
+ timeout: TimeIntervalConfig = field(default_factory=lambda: TimeIntervalConfig("30s"))
294
383
  connection: ConnectionConfig = field(default_factory=ConnectionConfig)
295
384
  security_categories: list[int] | None = None
296
385
  external_id_prefix: str = ""
@@ -302,6 +391,20 @@ class CogniteConfig:
302
391
  token_custom_args: dict[str, str] | None = None,
303
392
  use_experimental_sdk: bool = False,
304
393
  ) -> CogniteClient:
394
+ """
395
+ Creates a CogniteClient based on the configuration.
396
+
397
+ Args:
398
+ client_name: Name of the client, set as the x-cdp-app header in the requests.
399
+ token_custom_args: Additional arguments to pass to the token request, such as resource or audience.
400
+ use_experimental_sdk: If True, use the experimental SDK instead of the stable one.
401
+
402
+ Returns:
403
+ A CogniteClient instance configured with the provided parameters.
404
+
405
+ Raises:
406
+ InvalidConfigError: If the configuration is invalid, such as missing project name or invalid authority URL.
407
+ """
305
408
  from cognite.client.config import global_config
306
409
 
307
410
  global_config.disable_pypi_version_check = True
@@ -390,6 +493,15 @@ class CogniteConfig:
390
493
  return CogniteClient(client_config)
391
494
 
392
495
  def get_data_set(self, cdf_client: CogniteClient) -> DataSet | None:
496
+ """
497
+ Retrieves the DataSet object based on the configuration.
498
+
499
+ Args:
500
+ cdf_client: An instance of CogniteClient to use for retrieving the DataSet.
501
+
502
+ Returns:
503
+ DataSet object if data_set, data_set_id, or data_set_external_id is provided; otherwise None.
504
+ """
393
505
  if self.data_set_external_id:
394
506
  logging.getLogger(__name__).warning(
395
507
  "Using data-set-external-id is deprecated, please use data-set/external-id instead"
@@ -409,6 +521,18 @@ class CogniteConfig:
409
521
  )
410
522
 
411
523
  def get_extraction_pipeline(self, cdf_client: CogniteClient) -> ExtractionPipeline | None:
524
+ """
525
+ Retrieves the ExtractionPipeline object based on the configuration.
526
+
527
+ Args:
528
+ cdf_client: An instance of CogniteClient to use for retrieving the ExtractionPipeline.
529
+
530
+ Returns:
531
+ ExtractionPipeline object if extraction_pipeline is provided, otherwise None.
532
+
533
+ Raises:
534
+ ValueError: If the extraction pipeline with the specified ID is not found.
535
+ """
412
536
  if not self.extraction_pipeline:
413
537
  return None
414
538
 
@@ -437,7 +561,7 @@ class _FileLoggingConfig:
437
561
  @dataclass
438
562
  class LoggingConfig:
439
563
  """
440
- Logging settings, such as log levels and path to log file
564
+ Logging settings, such as log levels and path to log file.
441
565
  """
442
566
 
443
567
  console: _ConsoleLoggingConfig | None
@@ -449,7 +573,7 @@ class LoggingConfig:
449
573
 
450
574
  def setup_logging(self, suppress_console: bool = False) -> None:
451
575
  """
452
- Sets up the default logger in the logging package to be configured as defined in this config object
576
+ Sets up the default logger in the logging package to be configured as defined in this config object.
453
577
 
454
578
  Args:
455
579
  suppress_console: Don't log to console regardless of config. Useful when running an extractor as a Windows
@@ -510,7 +634,7 @@ class _PushGatewayConfig:
510
634
  password: str | None
511
635
 
512
636
  clear_after: TimeIntervalConfig | None
513
- push_interval: TimeIntervalConfig = TimeIntervalConfig("30s")
637
+ push_interval: TimeIntervalConfig = field(default_factory=lambda: TimeIntervalConfig("30s"))
514
638
 
515
639
 
516
640
  @dataclass
@@ -526,14 +650,15 @@ class _CogniteMetricsConfig:
526
650
  asset_external_id: str | None
527
651
  data_set: EitherIdConfig | None = None
528
652
 
529
- push_interval: TimeIntervalConfig = TimeIntervalConfig("30s")
653
+ push_interval: TimeIntervalConfig = field(default_factory=lambda: TimeIntervalConfig("30s"))
530
654
 
531
655
 
532
656
  @dataclass
533
657
  class MetricsConfig:
534
658
  """
535
- Destination(s) for metrics, including options for one or several Prometheus push gateways, and pushing as CDF Time
536
- Series.
659
+ Destination(s) for metrics.
660
+
661
+ Including options for one or several Prometheus push gateways, and pushing as CDF Time Series.
537
662
  """
538
663
 
539
664
  push_gateways: list[_PushGatewayConfig] | None
@@ -541,6 +666,13 @@ class MetricsConfig:
541
666
  server: _PromServerConfig | None
542
667
 
543
668
  def start_pushers(self, cdf_client: CogniteClient, cancellation_token: CancellationToken | None = None) -> None:
669
+ """
670
+ Starts the configured metrics pushers.
671
+
672
+ Args:
673
+ cdf_client: An instance of CogniteClient to use for pushing metrics to CDF.
674
+ cancellation_token: Optional cancellation token to stop the pushers gracefully.
675
+ """
544
676
  self._pushers: list[AbstractMetricsPusher] = []
545
677
  self._clear_on_stop: dict[PrometheusPusher, int] = {}
546
678
 
@@ -589,6 +721,11 @@ class MetricsConfig:
589
721
  start_http_server(self.server.port, self.server.host, registry=REGISTRY)
590
722
 
591
723
  def stop_pushers(self) -> None:
724
+ """
725
+ DEPRECATED. Use cancellation_token to stop pushers instead.
726
+
727
+ Manually stop pushers and clear gateways if configured.
728
+ """
592
729
  pushers = self.__dict__.get("_pushers") or []
593
730
 
594
731
  for pusher in pushers:
@@ -599,11 +736,15 @@ class MetricsConfig:
599
736
  _logger.debug("Waiting %d seconds before clearing gateways", wait_time)
600
737
 
601
738
  sleep(wait_time)
602
- for pusher in self._clear_on_stop.keys():
739
+ for pusher in self._clear_on_stop:
603
740
  pusher.clear_gateway()
604
741
 
605
742
 
606
743
  class ConfigType(Enum):
744
+ """
745
+ Type of configuration, either local or remote.
746
+ """
747
+
607
748
  LOCAL = "local"
608
749
  REMOTE = "remote"
609
750
 
@@ -619,7 +760,7 @@ class _BaseConfig:
619
760
  @dataclass
620
761
  class BaseConfig(_BaseConfig):
621
762
  """
622
- Basis for an extractor config, containing config version, ``CogniteConfig`` and ``LoggingConfig``
763
+ Basis for an extractor config, containing config version, ``CogniteConfig`` and ``LoggingConfig``.
623
764
  """
624
765
 
625
766
  version: str | int | None
@@ -629,7 +770,7 @@ class BaseConfig(_BaseConfig):
629
770
  @dataclass
630
771
  class RawDestinationConfig:
631
772
  """
632
- Configuration parameters for using Raw
773
+ Configuration parameters for using Raw.
633
774
  """
634
775
 
635
776
  database: str
@@ -639,26 +780,26 @@ class RawDestinationConfig:
639
780
  @dataclass
640
781
  class RawStateStoreConfig(RawDestinationConfig):
641
782
  """
642
- Configuration of a state store based on CDF RAW
783
+ Configuration of a state store based on CDF RAW.
643
784
  """
644
785
 
645
- upload_interval: TimeIntervalConfig = TimeIntervalConfig("30s")
786
+ upload_interval: TimeIntervalConfig = field(default_factory=lambda: TimeIntervalConfig("30s"))
646
787
 
647
788
 
648
789
  @dataclass
649
790
  class LocalStateStoreConfig:
650
791
  """
651
- Configuration of a state store using a local JSON file
792
+ Configuration of a state store using a local JSON file.
652
793
  """
653
794
 
654
795
  path: Path
655
- save_interval: TimeIntervalConfig = TimeIntervalConfig("30s")
796
+ save_interval: TimeIntervalConfig = field(default_factory=lambda: TimeIntervalConfig("30s"))
656
797
 
657
798
 
658
799
  @dataclass
659
800
  class StateStoreConfig:
660
801
  """
661
- Configuration of the State Store, containing ``LocalStateStoreConfig`` or ``RawStateStoreConfig``
802
+ Configuration of the State Store, containing ``LocalStateStoreConfig`` or ``RawStateStoreConfig``.
662
803
  """
663
804
 
664
805
  raw: RawStateStoreConfig | None = None
@@ -677,6 +818,7 @@ class StateStoreConfig:
677
818
  cdf_client: CogniteClient object to use in case of a RAW state store (ignored otherwise)
678
819
  default_to_local: If true, return a LocalStateStore if no state store is configured. Otherwise return a
679
820
  NoStateStore
821
+ cancellation_token: Cancellation token to pass to created state stores
680
822
 
681
823
  Returns:
682
824
  An (uninitialized) state store
@@ -713,12 +855,22 @@ class StateStoreConfig:
713
855
 
714
856
 
715
857
  class RegExpFlag(Enum):
858
+ """
859
+ Flags for regular expressions.
860
+ """
861
+
716
862
  IGNORECASE = "ignore-case"
717
863
  IC = "i"
718
864
  ASCII = "ascii-only"
719
865
  A = "a"
720
866
 
721
867
  def get_regex_flag(self) -> int:
868
+ """
869
+ Returns the corresponding regex flag for the enum value.
870
+
871
+ Returns:
872
+ The regex flag corresponding to the enum value.
873
+ """
722
874
  if self in (RegExpFlag.IGNORECASE, RegExpFlag.IC):
723
875
  return re.IGNORECASE
724
876
  elif self.value in (RegExpFlag.ASCII, RegExpFlag.A):
@@ -729,7 +881,7 @@ class RegExpFlag(Enum):
729
881
  @dataclass
730
882
  class IgnorePattern:
731
883
  """
732
- Configuration for regexp for ignore pattern
884
+ Configuration for regexp for ignore pattern.
733
885
  """
734
886
 
735
887
  pattern: str
@@ -749,6 +901,12 @@ class IgnorePattern:
749
901
  return re.compile(self.pattern, flag)
750
902
 
751
903
  def __post_init__(self) -> None:
904
+ """
905
+ Validate the configuration after initialization.
906
+
907
+ Raises:
908
+ ValueError: If both 'options' and 'flags' are specified, or if neither is specified.
909
+ """
752
910
  if self.options is not None and self.flags is not None:
753
911
  raise ValueError("Only one of either 'options' or 'flags' can be specified.")
754
912
  if self.options is None and self.flags is None:
@@ -762,19 +920,27 @@ class IgnorePattern:
762
920
 
763
921
  class CastableInt(int):
764
922
  """
765
- Represents an integer in a config schema. Difference from regular int is that the
766
- value if this type can be either a string or an integer in the yaml file.
923
+ Represents an integer in a config schema.
924
+
925
+ The difference from regular int is that the value if this type can be either a string or an integer in the yaml
926
+ file.
767
927
  """
768
928
 
769
929
  def __new__(cls, value: Any) -> "CastableInt":
770
930
  """
771
- Returns value as is if it's int. If it's str or bytes try to convert to int.
772
- Raises ValueError if conversion is unsuccessful or value is of not supported type.
931
+ Returns value as is if it's int.
773
932
 
774
- Type check is required to avoid unexpected behaviour, such as implictly casting booleans,
775
- floats and other types supported by standard int.
776
- """
933
+ If it's str or bytes try to convert to int.
934
+
935
+ Type check is required to avoid unexpected behavior, such as implicitly casting booleans, floats and other types
936
+ supported by standard int.
777
937
 
938
+ Args:
939
+ value: The value to be casted to int.
940
+
941
+ Raises:
942
+ ValueError: If the value can not be converted to an int.
943
+ """
778
944
  if not isinstance(value, int | str | bytes):
779
945
  raise ValueError(f"CastableInt cannot be created form value {value!r} of type {type(value)!r}.")
780
946
 
@@ -783,14 +949,21 @@ class CastableInt(int):
783
949
 
784
950
  class PortNumber(CastableInt):
785
951
  """
786
- A subclass of int to be used in config schemas. It represents a valid port number (0 to 65535) and allows the value
787
- to be of either str or int type. If the value is not a valid port number raises a ValueError at instantiation.
952
+ Represents a port number in a config schema.
953
+
954
+ It represents a valid port number (0 to 65535) and allows the value to be of either str or int type. If the value is
955
+ not a valid port number raises a ValueError at instantiation.
788
956
  """
789
957
 
790
958
  def __new__(cls, value: Any) -> "PortNumber":
791
959
  """
792
- Try to convert the `value` to int. If successful, check if it's within a valid range for a port number.
793
- Raises ValueError if conversion to int or validation is unsuccessful.
960
+ Try to cast the value to an integer and validate it as a port number.
961
+
962
+ Args:
963
+ value: The value to be casted to a port number.
964
+
965
+ Raises:
966
+ ValueError: If the value is not a valid port number (not an int or out of range).
794
967
  """
795
968
  value = super().__new__(cls, value)
796
969