provide-foundation 0.0.0.dev2__py3-none-any.whl → 0.0.0.dev3__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 (155) hide show
  1. provide/foundation/__init__.py +20 -20
  2. provide/foundation/archive/__init__.py +1 -1
  3. provide/foundation/archive/base.py +15 -14
  4. provide/foundation/archive/bzip2.py +40 -40
  5. provide/foundation/archive/gzip.py +42 -42
  6. provide/foundation/archive/operations.py +90 -91
  7. provide/foundation/archive/tar.py +33 -31
  8. provide/foundation/archive/zip.py +52 -50
  9. provide/foundation/asynctools/__init__.py +20 -0
  10. provide/foundation/asynctools/core.py +126 -0
  11. provide/foundation/cli/__init__.py +2 -2
  12. provide/foundation/cli/commands/deps.py +4 -4
  13. provide/foundation/cli/commands/logs/__init__.py +2 -2
  14. provide/foundation/cli/commands/logs/generate.py +2 -2
  15. provide/foundation/cli/commands/logs/query.py +3 -3
  16. provide/foundation/cli/commands/logs/send.py +2 -2
  17. provide/foundation/cli/commands/logs/tail.py +2 -2
  18. provide/foundation/cli/decorators.py +0 -1
  19. provide/foundation/cli/testing.py +0 -5
  20. provide/foundation/cli/utils.py +1 -2
  21. provide/foundation/config/__init__.py +19 -19
  22. provide/foundation/config/base.py +2 -2
  23. provide/foundation/config/converters.py +81 -83
  24. provide/foundation/config/defaults.py +1 -1
  25. provide/foundation/config/env.py +2 -1
  26. provide/foundation/config/loader.py +1 -1
  27. provide/foundation/config/sync.py +8 -6
  28. provide/foundation/config/types.py +5 -5
  29. provide/foundation/config/validators.py +4 -4
  30. provide/foundation/console/output.py +7 -7
  31. provide/foundation/context/core.py +19 -17
  32. provide/foundation/crypto/certificates/__init__.py +9 -5
  33. provide/foundation/crypto/certificates/base.py +2 -2
  34. provide/foundation/crypto/certificates/certificate.py +48 -19
  35. provide/foundation/crypto/certificates/factory.py +26 -18
  36. provide/foundation/crypto/certificates/generator.py +24 -23
  37. provide/foundation/crypto/certificates/loader.py +24 -16
  38. provide/foundation/crypto/certificates/operations.py +17 -10
  39. provide/foundation/crypto/certificates/trust.py +21 -21
  40. provide/foundation/env/__init__.py +28 -0
  41. provide/foundation/env/core.py +218 -0
  42. provide/foundation/errors/__init__.py +3 -2
  43. provide/foundation/errors/decorators.py +0 -3
  44. provide/foundation/errors/types.py +0 -1
  45. provide/foundation/eventsets/display.py +13 -14
  46. provide/foundation/eventsets/registry.py +61 -31
  47. provide/foundation/eventsets/resolver.py +50 -46
  48. provide/foundation/eventsets/sets/das.py +8 -8
  49. provide/foundation/eventsets/sets/database.py +14 -14
  50. provide/foundation/eventsets/sets/http.py +21 -21
  51. provide/foundation/eventsets/sets/llm.py +16 -16
  52. provide/foundation/eventsets/sets/task_queue.py +13 -13
  53. provide/foundation/eventsets/types.py +7 -7
  54. provide/foundation/file/directory.py +1 -1
  55. provide/foundation/file/lock.py +2 -3
  56. provide/foundation/hub/components.py +19 -21
  57. provide/foundation/hub/config.py +25 -19
  58. provide/foundation/hub/discovery.py +5 -4
  59. provide/foundation/hub/handlers.py +13 -5
  60. provide/foundation/hub/lifecycle.py +10 -9
  61. provide/foundation/hub/manager.py +3 -0
  62. provide/foundation/hub/processors.py +8 -3
  63. provide/foundation/integrations/__init__.py +1 -1
  64. provide/foundation/integrations/openobserve/client.py +2 -2
  65. provide/foundation/integrations/openobserve/commands.py +9 -9
  66. provide/foundation/integrations/openobserve/config.py +2 -2
  67. provide/foundation/integrations/openobserve/otlp.py +2 -2
  68. provide/foundation/integrations/openobserve/search.py +1 -2
  69. provide/foundation/integrations/openobserve/streaming.py +1 -1
  70. provide/foundation/logger/__init__.py +0 -1
  71. provide/foundation/logger/config/base.py +1 -1
  72. provide/foundation/logger/config/logging.py +19 -19
  73. provide/foundation/logger/config/telemetry.py +11 -13
  74. provide/foundation/logger/factories.py +2 -2
  75. provide/foundation/logger/processors/main.py +12 -10
  76. provide/foundation/logger/ratelimit/limiters.py +4 -4
  77. provide/foundation/logger/ratelimit/processor.py +1 -1
  78. provide/foundation/logger/setup/coordinator.py +38 -24
  79. provide/foundation/logger/setup/processors.py +3 -3
  80. provide/foundation/logger/setup/testing.py +14 -0
  81. provide/foundation/logger/trace.py +5 -5
  82. provide/foundation/metrics/__init__.py +1 -1
  83. provide/foundation/metrics/otel.py +3 -1
  84. provide/foundation/observability/__init__.py +1 -1
  85. provide/foundation/process/__init__.py +1 -1
  86. provide/foundation/process/exit.py +6 -5
  87. provide/foundation/process/lifecycle.py +41 -18
  88. provide/foundation/resilience/__init__.py +6 -5
  89. provide/foundation/resilience/circuit.py +32 -30
  90. provide/foundation/resilience/decorators.py +58 -42
  91. provide/foundation/resilience/fallback.py +55 -40
  92. provide/foundation/resilience/retry.py +67 -65
  93. provide/foundation/serialization/__init__.py +16 -0
  94. provide/foundation/serialization/core.py +70 -0
  95. provide/foundation/streams/config.py +8 -9
  96. provide/foundation/streams/console.py +3 -3
  97. provide/foundation/streams/core.py +2 -2
  98. provide/foundation/streams/file.py +1 -1
  99. provide/foundation/testing/__init__.py +22 -7
  100. provide/foundation/testing/archive/__init__.py +7 -7
  101. provide/foundation/testing/archive/fixtures.py +58 -54
  102. provide/foundation/testing/cli.py +3 -6
  103. provide/foundation/testing/common/__init__.py +13 -13
  104. provide/foundation/testing/common/fixtures.py +27 -30
  105. provide/foundation/testing/file/__init__.py +15 -15
  106. provide/foundation/testing/file/content_fixtures.py +65 -92
  107. provide/foundation/testing/file/directory_fixtures.py +19 -19
  108. provide/foundation/testing/file/fixtures.py +14 -17
  109. provide/foundation/testing/file/special_fixtures.py +34 -42
  110. provide/foundation/testing/logger.py +28 -23
  111. provide/foundation/testing/mocking/__init__.py +21 -21
  112. provide/foundation/testing/mocking/fixtures.py +80 -67
  113. provide/foundation/testing/process/__init__.py +23 -23
  114. provide/foundation/testing/process/async_fixtures.py +89 -80
  115. provide/foundation/testing/process/fixtures.py +11 -13
  116. provide/foundation/testing/process/subprocess_fixtures.py +41 -40
  117. provide/foundation/testing/threading/__init__.py +17 -17
  118. provide/foundation/testing/threading/basic_fixtures.py +21 -17
  119. provide/foundation/testing/threading/data_fixtures.py +18 -16
  120. provide/foundation/testing/threading/execution_fixtures.py +67 -52
  121. provide/foundation/testing/threading/fixtures.py +10 -14
  122. provide/foundation/testing/threading/sync_fixtures.py +21 -18
  123. provide/foundation/testing/time/__init__.py +11 -11
  124. provide/foundation/testing/time/fixtures.py +91 -79
  125. provide/foundation/testing/transport/__init__.py +9 -9
  126. provide/foundation/testing/transport/fixtures.py +54 -54
  127. provide/foundation/time/__init__.py +18 -0
  128. provide/foundation/time/core.py +63 -0
  129. provide/foundation/tools/__init__.py +2 -2
  130. provide/foundation/tools/base.py +68 -67
  131. provide/foundation/tools/cache.py +62 -69
  132. provide/foundation/tools/downloader.py +51 -56
  133. provide/foundation/tools/installer.py +51 -57
  134. provide/foundation/tools/registry.py +38 -45
  135. provide/foundation/tools/resolver.py +70 -68
  136. provide/foundation/tools/verifier.py +39 -50
  137. provide/foundation/tracer/spans.py +1 -13
  138. provide/foundation/transport/__init__.py +26 -33
  139. provide/foundation/transport/base.py +32 -30
  140. provide/foundation/transport/client.py +44 -49
  141. provide/foundation/transport/config.py +11 -13
  142. provide/foundation/transport/errors.py +13 -27
  143. provide/foundation/transport/http.py +69 -55
  144. provide/foundation/transport/middleware.py +86 -81
  145. provide/foundation/transport/registry.py +29 -27
  146. provide/foundation/transport/types.py +6 -6
  147. provide/foundation/utils/deps.py +3 -2
  148. provide/foundation/utils/parsing.py +7 -7
  149. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
  150. provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
  151. provide_foundation-0.0.0.dev2.dist-info/RECORD +0 -225
  152. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
  153. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
  154. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
  155. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -2,17 +2,15 @@
2
2
 
3
3
  import copy
4
4
  import json
5
- import os
6
5
  from pathlib import Path
7
6
  from typing import Any
8
7
 
9
8
  from attrs import define, field, fields, validators
10
9
 
11
- from provide.foundation.config.env import RuntimeConfig
12
- from provide.foundation.config.base import field as config_field, ConfigSource
10
+ from provide.foundation.config.base import ConfigSource, field as config_field
13
11
  from provide.foundation.config.converters import parse_bool_strict
12
+ from provide.foundation.config.env import RuntimeConfig
14
13
  from provide.foundation.logger import get_logger
15
- from provide.foundation.logger.config import TelemetryConfig
16
14
 
17
15
  try:
18
16
  import tomli as tomllib
@@ -46,53 +44,53 @@ class CLIContext(RuntimeConfig):
46
44
  env_var="PROVIDE_LOG_LEVEL",
47
45
  converter=str.upper,
48
46
  validator=validators.in_(VALID_LOG_LEVELS),
49
- description="Logging level for CLI output"
47
+ description="Logging level for CLI output",
50
48
  )
51
49
  profile: str = config_field(
52
50
  default="default",
53
51
  env_var="PROVIDE_PROFILE",
54
- description="Configuration profile to use"
52
+ description="Configuration profile to use",
55
53
  )
56
54
  debug: bool = config_field(
57
55
  default=False,
58
56
  env_var="PROVIDE_DEBUG",
59
57
  converter=parse_bool_strict,
60
- description="Enable debug mode"
58
+ description="Enable debug mode",
61
59
  )
62
60
  json_output: bool = config_field(
63
61
  default=False,
64
62
  env_var="PROVIDE_JSON_OUTPUT",
65
63
  converter=parse_bool_strict,
66
- description="Output in JSON format"
64
+ description="Output in JSON format",
67
65
  )
68
66
  config_file: Path | None = config_field(
69
67
  default=None,
70
68
  env_var="PROVIDE_CONFIG_FILE",
71
69
  converter=lambda x: Path(x) if x else None,
72
- description="Path to configuration file"
70
+ description="Path to configuration file",
73
71
  )
74
72
  log_file: Path | None = config_field(
75
73
  default=None,
76
74
  env_var="PROVIDE_LOG_FILE",
77
75
  converter=lambda x: Path(x) if x else None,
78
- description="Path to log file"
76
+ description="Path to log file",
79
77
  )
80
78
  log_format: str = config_field(
81
79
  default="key_value",
82
80
  env_var="PROVIDE_LOG_FORMAT",
83
- description="Log output format (key_value or json)"
81
+ description="Log output format (key_value or json)",
84
82
  )
85
83
  no_color: bool = config_field(
86
84
  default=False,
87
85
  env_var="NO_COLOR",
88
86
  converter=parse_bool_strict,
89
- description="Disable colored output"
87
+ description="Disable colored output",
90
88
  )
91
89
  no_emoji: bool = config_field(
92
90
  default=False,
93
91
  env_var="PROVIDE_NO_EMOJI",
94
92
  converter=parse_bool_strict,
95
- description="Disable emoji in output"
93
+ description="Disable emoji in output",
96
94
  )
97
95
 
98
96
  # Private fields - using Factory for mutable defaults
@@ -116,13 +114,13 @@ class CLIContext(RuntimeConfig):
116
114
  # Create default instance and environment instance
117
115
  default_ctx = self.__class__() # All defaults
118
116
  env_ctx = self.from_env(prefix=prefix) # Environment + defaults
119
-
117
+
120
118
  # Only update fields where environment differs from default
121
119
  for attr in fields(self.__class__):
122
120
  if not attr.name.startswith("_"): # Skip private fields
123
121
  default_value = getattr(default_ctx, attr.name)
124
122
  env_value = getattr(env_ctx, attr.name)
125
-
123
+
126
124
  # If environment value differs from default, it came from env
127
125
  if env_value != default_value:
128
126
  setattr(self, attr.name, env_value)
@@ -142,7 +140,9 @@ class CLIContext(RuntimeConfig):
142
140
  }
143
141
 
144
142
  @classmethod
145
- def from_dict(cls, data: dict[str, Any], source: ConfigSource = ConfigSource.RUNTIME) -> "CLIContext":
143
+ def from_dict(
144
+ cls, data: dict[str, Any], source: ConfigSource = ConfigSource.RUNTIME
145
+ ) -> "CLIContext":
146
146
  """
147
147
  Create context from dictionary.
148
148
 
@@ -265,7 +265,9 @@ class CLIContext(RuntimeConfig):
265
265
 
266
266
  path.write_text(content)
267
267
 
268
- def merge(self, other: "CLIContext", override_defaults: bool = False) -> "CLIContext":
268
+ def merge(
269
+ self, other: "CLIContext", override_defaults: bool = False
270
+ ) -> "CLIContext":
269
271
  """
270
272
  Merge with another context, with other taking precedence.
271
273
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  # Import from submodules using absolute imports
4
4
  from provide.foundation.crypto.certificates.base import (
5
+ _HAS_CRYPTO,
5
6
  CertificateBase,
6
7
  CertificateConfig,
7
8
  CertificateError,
@@ -9,7 +10,6 @@ from provide.foundation.crypto.certificates.base import (
9
10
  KeyPair,
10
11
  KeyType,
11
12
  PublicKey,
12
- _HAS_CRYPTO,
13
13
  _require_crypto,
14
14
  )
15
15
  from provide.foundation.crypto.certificates.certificate import Certificate
@@ -21,14 +21,18 @@ from provide.foundation.crypto.certificates.operations import (
21
21
 
22
22
  # Re-export public types - maintaining exact same API
23
23
  __all__ = [
24
+ "_HAS_CRYPTO", # For testing
24
25
  "Certificate",
25
26
  "CertificateBase",
26
27
  "CertificateConfig",
27
28
  "CertificateError",
28
29
  "CurveType",
30
+ "KeyPair",
29
31
  "KeyType",
30
- "create_self_signed",
31
- "create_ca",
32
- "_HAS_CRYPTO", # For testing
32
+ "PublicKey",
33
33
  "_require_crypto", # For testing
34
- ]
34
+ "create_ca",
35
+ "create_self_signed",
36
+ "create_x509_certificate",
37
+ "validate_signature",
38
+ ]
@@ -5,7 +5,7 @@ from enum import StrEnum, auto
5
5
  import traceback
6
6
  from typing import NotRequired, Self, TypeAlias, TypedDict
7
7
 
8
- from attrs import define, field
8
+ from attrs import define
9
9
 
10
10
  try:
11
11
  from cryptography import x509
@@ -170,4 +170,4 @@ class CertificateBase:
170
170
  x509.NameAttribute(NameOID.COMMON_NAME, common_name),
171
171
  x509.NameAttribute(NameOID.ORGANIZATION_NAME, org),
172
172
  ]
173
- )
173
+ )
@@ -11,7 +11,7 @@ try:
11
11
  from cryptography.hazmat.primitives import serialization
12
12
  from cryptography.hazmat.primitives.asymmetric import ec, rsa
13
13
  from cryptography.x509 import Certificate as X509Certificate
14
-
14
+
15
15
  _HAS_CRYPTO = True
16
16
  except ImportError:
17
17
  x509 = None
@@ -22,11 +22,17 @@ except ImportError:
22
22
  _HAS_CRYPTO = False
23
23
 
24
24
  from provide.foundation import logger
25
- from provide.foundation.crypto.certificates.base import CertificateBase, CertificateError, PublicKey
25
+ from provide.foundation.crypto.certificates.base import (
26
+ CertificateBase,
27
+ CertificateError,
28
+ PublicKey,
29
+ )
26
30
  from provide.foundation.crypto.certificates.generator import generate_certificate
27
31
  from provide.foundation.crypto.certificates.loader import load_certificate_from_pem
28
32
  from provide.foundation.crypto.certificates.operations import create_x509_certificate
29
- from provide.foundation.crypto.certificates.trust import verify_trust as verify_trust_impl
33
+ from provide.foundation.crypto.certificates.trust import (
34
+ verify_trust as verify_trust_impl,
35
+ )
30
36
  from provide.foundation.crypto.constants import (
31
37
  DEFAULT_CERTIFICATE_CURVE,
32
38
  DEFAULT_CERTIFICATE_KEY_TYPE,
@@ -83,13 +89,10 @@ class Certificate:
83
89
  else:
84
90
  # Load existing certificate
85
91
  if not self.cert_pem_or_uri:
86
- raise CertificateError(
87
- "cert_pem_or_uri required when not generating"
88
- )
89
-
92
+ raise CertificateError("cert_pem_or_uri required when not generating")
93
+
90
94
  base, x509_cert, private_key, cert_pem, key_pem = load_certificate_from_pem(
91
- self.cert_pem_or_uri,
92
- self.key_pem_or_uri
95
+ self.cert_pem_or_uri, self.key_pem_or_uri
93
96
  )
94
97
  self._base = base
95
98
  self._cert = x509_cert
@@ -174,9 +177,14 @@ class Certificate:
174
177
  ) -> "Certificate":
175
178
  """Creates a new self-signed CA certificate."""
176
179
  from provide.foundation.crypto.certificates.factory import create_ca_certificate
180
+
177
181
  return create_ca_certificate(
178
- common_name, organization_name, validity_days,
179
- key_type, key_size, ecdsa_curve
182
+ common_name,
183
+ organization_name,
184
+ validity_days,
185
+ key_type,
186
+ key_size,
187
+ ecdsa_curve,
180
188
  )
181
189
 
182
190
  @classmethod
@@ -193,10 +201,20 @@ class Certificate:
193
201
  is_client_cert: bool = False,
194
202
  ) -> "Certificate":
195
203
  """Creates a new certificate signed by the provided CA certificate."""
196
- from provide.foundation.crypto.certificates.factory import create_signed_certificate
204
+ from provide.foundation.crypto.certificates.factory import (
205
+ create_signed_certificate,
206
+ )
207
+
197
208
  return create_signed_certificate(
198
- ca_certificate, common_name, organization_name, validity_days,
199
- alt_names, key_type, key_size, ecdsa_curve, is_client_cert
209
+ ca_certificate,
210
+ common_name,
211
+ organization_name,
212
+ validity_days,
213
+ alt_names,
214
+ key_type,
215
+ key_size,
216
+ ecdsa_curve,
217
+ is_client_cert,
200
218
  )
201
219
 
202
220
  @classmethod
@@ -211,10 +229,18 @@ class Certificate:
211
229
  ecdsa_curve: str = DEFAULT_CERTIFICATE_CURVE,
212
230
  ) -> "Certificate":
213
231
  """Creates a new self-signed end-entity certificate suitable for a server."""
214
- from provide.foundation.crypto.certificates.factory import create_self_signed_server_cert
232
+ from provide.foundation.crypto.certificates.factory import (
233
+ create_self_signed_server_cert,
234
+ )
235
+
215
236
  return create_self_signed_server_cert(
216
- common_name, organization_name, validity_days,
217
- alt_names, key_type, key_size, ecdsa_curve
237
+ common_name,
238
+ organization_name,
239
+ validity_days,
240
+ alt_names,
241
+ key_type,
242
+ key_size,
243
+ ecdsa_curve,
218
244
  )
219
245
 
220
246
  def verify_trust(self, other_cert: Self) -> bool:
@@ -243,7 +269,10 @@ class Certificate:
243
269
  self, signed_cert: "Certificate", signing_cert: "Certificate"
244
270
  ) -> bool:
245
271
  """Internal helper: Validates signature and issuer/subject match."""
246
- from provide.foundation.crypto.certificates.trust import validate_signature_wrapper
272
+ from provide.foundation.crypto.certificates.trust import (
273
+ validate_signature_wrapper,
274
+ )
275
+
247
276
  return validate_signature_wrapper(signed_cert, signing_cert)
248
277
 
249
278
  def __eq__(self, other: object) -> bool:
@@ -287,4 +316,4 @@ class Certificate:
287
316
 
288
317
 
289
318
  # Type alias for backwards compatibility
290
- KeyPair = rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey | None
319
+ KeyPair = rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey | None
@@ -1,9 +1,14 @@
1
1
  """Certificate factory methods."""
2
2
 
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from provide.foundation.crypto.certificates.certificate import Certificate
7
+
3
8
  try:
4
9
  from cryptography import x509
5
10
  from cryptography.hazmat.primitives import serialization
6
-
11
+
7
12
  _HAS_CRYPTO = True
8
13
  except ImportError:
9
14
  x509 = None
@@ -11,7 +16,10 @@ except ImportError:
11
16
  _HAS_CRYPTO = False
12
17
 
13
18
  from provide.foundation import logger
14
- from provide.foundation.crypto.certificates.base import CertificateError, _require_crypto
19
+ from provide.foundation.crypto.certificates.base import (
20
+ CertificateError,
21
+ _require_crypto,
22
+ )
15
23
  from provide.foundation.crypto.certificates.operations import create_x509_certificate
16
24
  from provide.foundation.crypto.constants import (
17
25
  DEFAULT_CERTIFICATE_CURVE,
@@ -32,7 +40,7 @@ def create_ca_certificate(
32
40
  """Creates a new self-signed CA certificate."""
33
41
  # Import here to avoid circular dependency
34
42
  from provide.foundation.crypto.certificates.certificate import Certificate
35
-
43
+
36
44
  logger.info(
37
45
  f"📜🔑🏭 Creating new CA certificate: CN={common_name}, Org={organization_name}"
38
46
  )
@@ -76,7 +84,7 @@ def create_signed_certificate(
76
84
  """Creates a new certificate signed by the provided CA certificate."""
77
85
  # Import here to avoid circular dependency
78
86
  from provide.foundation.crypto.certificates.certificate import Certificate
79
-
87
+
80
88
  logger.info(
81
89
  f"📜🔑🏭 Creating new certificate signed by CA '{ca_certificate.subject}': "
82
90
  f"CN={common_name}, Org={organization_name}, ClientCert={is_client_cert}"
@@ -91,7 +99,7 @@ def create_signed_certificate(
91
99
  f"📜🔑⚠️ Signing certificate (Subject: {ca_certificate.subject}) "
92
100
  "is not marked as a CA. This might lead to validation issues."
93
101
  )
94
-
102
+
95
103
  new_cert_obj = Certificate(
96
104
  generate_keypair=True,
97
105
  common_name=common_name,
@@ -102,7 +110,7 @@ def create_signed_certificate(
102
110
  key_size=key_size,
103
111
  ecdsa_curve=ecdsa_curve,
104
112
  )
105
-
113
+
106
114
  signed_x509_cert = create_x509_certificate(
107
115
  base=new_cert_obj._base,
108
116
  private_key=new_cert_obj._private_key,
@@ -112,12 +120,12 @@ def create_signed_certificate(
112
120
  is_ca=False,
113
121
  is_client_cert=is_client_cert,
114
122
  )
115
-
123
+
116
124
  new_cert_obj._cert = signed_x509_cert
117
125
  new_cert_obj.cert = signed_x509_cert.public_bytes(
118
126
  serialization.Encoding.PEM
119
127
  ).decode("utf-8")
120
-
128
+
121
129
  logger.info(
122
130
  f"📜🔑✅ Successfully created and signed certificate for "
123
131
  f"CN={common_name} by CA='{ca_certificate.subject}'"
@@ -137,12 +145,12 @@ def create_self_signed_server_cert(
137
145
  """Creates a new self-signed end-entity certificate suitable for a server."""
138
146
  # Import here to avoid circular dependency
139
147
  from provide.foundation.crypto.certificates.certificate import Certificate
140
-
148
+
141
149
  logger.info(
142
150
  f"📜🔑🏭 Creating new self-signed SERVER certificate: "
143
151
  f"CN={common_name}, Org={organization_name}"
144
152
  )
145
-
153
+
146
154
  cert_obj = Certificate(
147
155
  generate_keypair=True,
148
156
  common_name=common_name,
@@ -153,12 +161,12 @@ def create_self_signed_server_cert(
153
161
  key_size=key_size,
154
162
  ecdsa_curve=ecdsa_curve,
155
163
  )
156
-
164
+
157
165
  if not cert_obj._private_key:
158
166
  raise CertificateError(
159
167
  "Private key not generated for self-signed server certificate"
160
168
  )
161
-
169
+
162
170
  actual_x509_cert = create_x509_certificate(
163
171
  base=cert_obj._base,
164
172
  private_key=cert_obj._private_key,
@@ -166,12 +174,12 @@ def create_self_signed_server_cert(
166
174
  is_ca=False,
167
175
  is_client_cert=False,
168
176
  )
169
-
177
+
170
178
  cert_obj._cert = actual_x509_cert
171
- cert_obj.cert = actual_x509_cert.public_bytes(
172
- serialization.Encoding.PEM
173
- ).decode("utf-8")
174
-
179
+ cert_obj.cert = actual_x509_cert.public_bytes(serialization.Encoding.PEM).decode(
180
+ "utf-8"
181
+ )
182
+
175
183
  logger.info(
176
184
  f"📜🔑✅ Successfully created self-signed SERVER certificate for CN={common_name}"
177
185
  )
@@ -210,4 +218,4 @@ def create_ca(
210
218
  organization_name=organization,
211
219
  validity_days=validity_days,
212
220
  key_type=key_type,
213
- )
221
+ )
@@ -7,7 +7,7 @@ try:
7
7
  from cryptography import x509
8
8
  from cryptography.hazmat.primitives import serialization
9
9
  from cryptography.hazmat.primitives.asymmetric import ec, rsa
10
-
10
+
11
11
  _HAS_CRYPTO = True
12
12
  except ImportError:
13
13
  x509 = None
@@ -28,7 +28,6 @@ from provide.foundation.crypto.certificates.operations import create_x509_certif
28
28
  from provide.foundation.crypto.constants import (
29
29
  DEFAULT_CERTIFICATE_CURVE,
30
30
  DEFAULT_CERTIFICATE_KEY_TYPE,
31
- DEFAULT_CERTIFICATE_VALIDITY_DAYS,
32
31
  DEFAULT_RSA_KEY_SIZE,
33
32
  )
34
33
 
@@ -43,20 +42,26 @@ def generate_certificate(
43
42
  alt_names: list[str] | None = None,
44
43
  is_ca: bool = False,
45
44
  is_client_cert: bool = False,
46
- ) -> tuple[CertificateBase, "x509.Certificate", "rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey", str, str]:
45
+ ) -> tuple[
46
+ CertificateBase,
47
+ "x509.Certificate",
48
+ "rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey",
49
+ str,
50
+ str,
51
+ ]:
47
52
  """
48
53
  Generate a new certificate with a keypair.
49
-
54
+
50
55
  Returns:
51
56
  Tuple of (CertificateBase, X509Certificate, private_key, cert_pem, key_pem)
52
57
  """
53
58
  try:
54
59
  logger.debug("📜🔑🚀 Generating new keypair")
55
-
60
+
56
61
  now = datetime.now(UTC)
57
62
  not_valid_before = now - timedelta(days=1)
58
63
  not_valid_after = now + timedelta(days=validity_days)
59
-
64
+
60
65
  # Parse key type
61
66
  normalized_key_type_str = key_type.lower()
62
67
  match normalized_key_type_str:
@@ -69,21 +74,19 @@ def generate_certificate(
69
74
  f"Unsupported key_type string: '{key_type}'. "
70
75
  "Must be 'rsa' or 'ecdsa'."
71
76
  )
72
-
77
+
73
78
  # Configure key parameters
74
79
  gen_curve: CurveType | None = None
75
80
  gen_key_size = None
76
-
81
+
77
82
  if gen_key_type == KeyType.ECDSA:
78
83
  try:
79
84
  gen_curve = CurveType[ecdsa_curve.upper()]
80
85
  except KeyError as e_curve:
81
- raise ValueError(
82
- f"Unsupported ECDSA curve: {ecdsa_curve}"
83
- ) from e_curve
86
+ raise ValueError(f"Unsupported ECDSA curve: {ecdsa_curve}") from e_curve
84
87
  else: # RSA
85
88
  gen_key_size = key_size
86
-
89
+
87
90
  # Build configuration
88
91
  conf: CertificateConfig = {
89
92
  "common_name": common_name,
@@ -98,10 +101,10 @@ def generate_certificate(
98
101
  if gen_key_size is not None:
99
102
  conf["key_size"] = gen_key_size
100
103
  logger.debug(f"📜🔑🚀 Generation config: {conf}")
101
-
104
+
102
105
  # Generate base certificate and private key
103
106
  base, private_key = CertificateBase.create(conf)
104
-
107
+
105
108
  # Create X.509 certificate
106
109
  x509_cert = create_x509_certificate(
107
110
  base=base,
@@ -110,12 +113,10 @@ def generate_certificate(
110
113
  is_ca=is_ca,
111
114
  is_client_cert=is_client_cert,
112
115
  )
113
-
116
+
114
117
  if x509_cert is None:
115
- raise CertificateError(
116
- "Certificate object (_cert) is None after creation"
117
- )
118
-
118
+ raise CertificateError("Certificate object (_cert) is None after creation")
119
+
119
120
  # Convert to PEM format
120
121
  cert_pem = x509_cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")
121
122
  key_pem = private_key.private_bytes(
@@ -123,11 +124,11 @@ def generate_certificate(
123
124
  format=serialization.PrivateFormat.PKCS8,
124
125
  encryption_algorithm=serialization.NoEncryption(),
125
126
  ).decode("utf-8")
126
-
127
+
127
128
  logger.debug("📜🔑✅ Generated cert and key")
128
-
129
+
129
130
  return base, x509_cert, private_key, cert_pem, key_pem
130
-
131
+
131
132
  except Exception as e:
132
133
  logger.error(
133
134
  f"📜❌ Failed to generate certificate. Error: {type(e).__name__}: {e}",
@@ -135,4 +136,4 @@ def generate_certificate(
135
136
  )
136
137
  raise CertificateError(
137
138
  f"Failed to initialize certificate. Original error: {type(e).__name__}"
138
- ) from e
139
+ ) from e
@@ -10,7 +10,7 @@ try:
10
10
  from cryptography.hazmat.primitives import serialization
11
11
  from cryptography.hazmat.primitives.asymmetric import ec, rsa
12
12
  from cryptography.hazmat.primitives.serialization import load_pem_private_key
13
-
13
+
14
14
  _HAS_CRYPTO = True
15
15
  except ImportError:
16
16
  x509 = None
@@ -21,7 +21,10 @@ except ImportError:
21
21
  _HAS_CRYPTO = False
22
22
 
23
23
  from provide.foundation import logger
24
- from provide.foundation.crypto.certificates.base import CertificateBase, CertificateError
24
+ from provide.foundation.crypto.certificates.base import (
25
+ CertificateBase,
26
+ CertificateError,
27
+ )
25
28
 
26
29
 
27
30
  def load_from_uri_or_pem(data: str) -> str:
@@ -53,30 +56,35 @@ def load_from_uri_or_pem(data: str) -> str:
53
56
 
54
57
 
55
58
  def load_certificate_from_pem(
56
- cert_pem_or_uri: str,
57
- key_pem_or_uri: str | None = None
58
- ) -> tuple[CertificateBase, "x509.Certificate", "rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey | None", str, str | None]:
59
+ cert_pem_or_uri: str, key_pem_or_uri: str | None = None
60
+ ) -> tuple[
61
+ CertificateBase,
62
+ "x509.Certificate",
63
+ "rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey | None",
64
+ str,
65
+ str | None,
66
+ ]:
59
67
  """
60
68
  Load a certificate and optionally its private key from PEM data or file URIs.
61
-
69
+
62
70
  Returns:
63
71
  Tuple of (CertificateBase, X509Certificate, private_key, cert_pem, key_pem)
64
72
  """
65
73
  try:
66
74
  logger.debug("📜🔑🚀 Loading certificate from provided data")
67
75
  cert_data = load_from_uri_or_pem(cert_pem_or_uri)
68
-
76
+
69
77
  logger.debug("📜🔑🔍 Loading X.509 certificate from PEM data")
70
78
  x509_cert = x509.load_pem_x509_certificate(cert_data.encode("utf-8"))
71
79
  logger.debug("📜🔑✅ X.509 certificate object loaded from PEM")
72
-
80
+
73
81
  private_key = None
74
82
  key_data = None
75
-
83
+
76
84
  if key_pem_or_uri:
77
85
  logger.debug("📜🔑🚀 Loading private key")
78
86
  key_data = load_from_uri_or_pem(key_pem_or_uri)
79
-
87
+
80
88
  loaded_priv_key = load_pem_private_key(
81
89
  key_data.encode("utf-8"), password=None
82
90
  )
@@ -90,7 +98,7 @@ def load_certificate_from_pem(
90
98
  )
91
99
  private_key = loaded_priv_key
92
100
  logger.debug("📜🔑✅ Private key object loaded and type validated")
93
-
101
+
94
102
  # Extract certificate details for CertificateBase
95
103
  loaded_not_valid_before = x509_cert.not_valid_before_utc
96
104
  loaded_not_valid_after = x509_cert.not_valid_after_utc
@@ -98,7 +106,7 @@ def load_certificate_from_pem(
98
106
  loaded_not_valid_before = loaded_not_valid_before.replace(tzinfo=UTC)
99
107
  if loaded_not_valid_after.tzinfo is None:
100
108
  loaded_not_valid_after = loaded_not_valid_after.replace(tzinfo=UTC)
101
-
109
+
102
110
  cert_public_key = x509_cert.public_key()
103
111
  if not isinstance(
104
112
  cert_public_key, rsa.RSAPublicKey | ec.EllipticCurvePublicKey
@@ -107,7 +115,7 @@ def load_certificate_from_pem(
107
115
  f"Certificate's public key is of unsupported type: {type(cert_public_key)}. "
108
116
  "Expected RSA or ECDSA public key."
109
117
  )
110
-
118
+
111
119
  base = CertificateBase(
112
120
  subject=x509_cert.subject,
113
121
  issuer=x509_cert.issuer,
@@ -117,9 +125,9 @@ def load_certificate_from_pem(
117
125
  serial_number=x509_cert.serial_number,
118
126
  )
119
127
  logger.debug("📜🔑✅ Reconstructed CertificateBase from loaded cert")
120
-
128
+
121
129
  return base, x509_cert, private_key, cert_data, key_data
122
-
130
+
123
131
  except Exception as e:
124
132
  logger.error(
125
133
  f"📜❌ Failed to load certificate. Error: {type(e).__name__}: {e}",
@@ -127,4 +135,4 @@ def load_certificate_from_pem(
127
135
  )
128
136
  raise CertificateError(
129
137
  f"Failed to initialize certificate. Original error: {type(e).__name__}"
130
- ) from e
138
+ ) from e