fraiseql-confiture 0.3.4__cp311-cp311-win_amd64.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 (119) hide show
  1. confiture/__init__.py +48 -0
  2. confiture/_core.cp311-win_amd64.pyd +0 -0
  3. confiture/cli/__init__.py +0 -0
  4. confiture/cli/dry_run.py +116 -0
  5. confiture/cli/lint_formatter.py +193 -0
  6. confiture/cli/main.py +1656 -0
  7. confiture/config/__init__.py +0 -0
  8. confiture/config/environment.py +263 -0
  9. confiture/core/__init__.py +51 -0
  10. confiture/core/anonymization/__init__.py +0 -0
  11. confiture/core/anonymization/audit.py +485 -0
  12. confiture/core/anonymization/benchmarking.py +372 -0
  13. confiture/core/anonymization/breach_notification.py +652 -0
  14. confiture/core/anonymization/compliance.py +617 -0
  15. confiture/core/anonymization/composer.py +298 -0
  16. confiture/core/anonymization/data_subject_rights.py +669 -0
  17. confiture/core/anonymization/factory.py +319 -0
  18. confiture/core/anonymization/governance.py +737 -0
  19. confiture/core/anonymization/performance.py +1092 -0
  20. confiture/core/anonymization/profile.py +284 -0
  21. confiture/core/anonymization/registry.py +195 -0
  22. confiture/core/anonymization/security/kms_manager.py +547 -0
  23. confiture/core/anonymization/security/lineage.py +888 -0
  24. confiture/core/anonymization/security/token_store.py +686 -0
  25. confiture/core/anonymization/strategies/__init__.py +41 -0
  26. confiture/core/anonymization/strategies/address.py +359 -0
  27. confiture/core/anonymization/strategies/credit_card.py +374 -0
  28. confiture/core/anonymization/strategies/custom.py +161 -0
  29. confiture/core/anonymization/strategies/date.py +218 -0
  30. confiture/core/anonymization/strategies/differential_privacy.py +398 -0
  31. confiture/core/anonymization/strategies/email.py +141 -0
  32. confiture/core/anonymization/strategies/format_preserving_encryption.py +310 -0
  33. confiture/core/anonymization/strategies/hash.py +150 -0
  34. confiture/core/anonymization/strategies/ip_address.py +235 -0
  35. confiture/core/anonymization/strategies/masking_retention.py +252 -0
  36. confiture/core/anonymization/strategies/name.py +298 -0
  37. confiture/core/anonymization/strategies/phone.py +119 -0
  38. confiture/core/anonymization/strategies/preserve.py +85 -0
  39. confiture/core/anonymization/strategies/redact.py +101 -0
  40. confiture/core/anonymization/strategies/salted_hashing.py +322 -0
  41. confiture/core/anonymization/strategies/text_redaction.py +183 -0
  42. confiture/core/anonymization/strategies/tokenization.py +334 -0
  43. confiture/core/anonymization/strategy.py +241 -0
  44. confiture/core/anonymization/syncer_audit.py +357 -0
  45. confiture/core/blue_green.py +683 -0
  46. confiture/core/builder.py +500 -0
  47. confiture/core/checksum.py +358 -0
  48. confiture/core/connection.py +132 -0
  49. confiture/core/differ.py +522 -0
  50. confiture/core/drift.py +564 -0
  51. confiture/core/dry_run.py +182 -0
  52. confiture/core/health.py +313 -0
  53. confiture/core/hooks/__init__.py +87 -0
  54. confiture/core/hooks/base.py +232 -0
  55. confiture/core/hooks/context.py +146 -0
  56. confiture/core/hooks/execution_strategies.py +57 -0
  57. confiture/core/hooks/observability.py +220 -0
  58. confiture/core/hooks/phases.py +53 -0
  59. confiture/core/hooks/registry.py +295 -0
  60. confiture/core/large_tables.py +775 -0
  61. confiture/core/linting/__init__.py +70 -0
  62. confiture/core/linting/composer.py +192 -0
  63. confiture/core/linting/libraries/__init__.py +17 -0
  64. confiture/core/linting/libraries/gdpr.py +168 -0
  65. confiture/core/linting/libraries/general.py +184 -0
  66. confiture/core/linting/libraries/hipaa.py +144 -0
  67. confiture/core/linting/libraries/pci_dss.py +104 -0
  68. confiture/core/linting/libraries/sox.py +120 -0
  69. confiture/core/linting/schema_linter.py +491 -0
  70. confiture/core/linting/versioning.py +151 -0
  71. confiture/core/locking.py +389 -0
  72. confiture/core/migration_generator.py +298 -0
  73. confiture/core/migrator.py +793 -0
  74. confiture/core/observability/__init__.py +44 -0
  75. confiture/core/observability/audit.py +323 -0
  76. confiture/core/observability/logging.py +187 -0
  77. confiture/core/observability/metrics.py +174 -0
  78. confiture/core/observability/tracing.py +192 -0
  79. confiture/core/pg_version.py +418 -0
  80. confiture/core/pool.py +406 -0
  81. confiture/core/risk/__init__.py +39 -0
  82. confiture/core/risk/predictor.py +188 -0
  83. confiture/core/risk/scoring.py +248 -0
  84. confiture/core/rollback_generator.py +388 -0
  85. confiture/core/schema_analyzer.py +769 -0
  86. confiture/core/schema_to_schema.py +590 -0
  87. confiture/core/security/__init__.py +32 -0
  88. confiture/core/security/logging.py +201 -0
  89. confiture/core/security/validation.py +416 -0
  90. confiture/core/signals.py +371 -0
  91. confiture/core/syncer.py +540 -0
  92. confiture/exceptions.py +192 -0
  93. confiture/integrations/__init__.py +0 -0
  94. confiture/models/__init__.py +0 -0
  95. confiture/models/lint.py +193 -0
  96. confiture/models/migration.py +180 -0
  97. confiture/models/schema.py +203 -0
  98. confiture/scenarios/__init__.py +36 -0
  99. confiture/scenarios/compliance.py +586 -0
  100. confiture/scenarios/ecommerce.py +199 -0
  101. confiture/scenarios/financial.py +253 -0
  102. confiture/scenarios/healthcare.py +315 -0
  103. confiture/scenarios/multi_tenant.py +340 -0
  104. confiture/scenarios/saas.py +295 -0
  105. confiture/testing/FRAMEWORK_API.md +722 -0
  106. confiture/testing/__init__.py +38 -0
  107. confiture/testing/fixtures/__init__.py +11 -0
  108. confiture/testing/fixtures/data_validator.py +229 -0
  109. confiture/testing/fixtures/migration_runner.py +167 -0
  110. confiture/testing/fixtures/schema_snapshotter.py +352 -0
  111. confiture/testing/frameworks/__init__.py +10 -0
  112. confiture/testing/frameworks/mutation.py +587 -0
  113. confiture/testing/frameworks/performance.py +479 -0
  114. confiture/testing/utils/__init__.py +0 -0
  115. fraiseql_confiture-0.3.4.dist-info/METADATA +438 -0
  116. fraiseql_confiture-0.3.4.dist-info/RECORD +119 -0
  117. fraiseql_confiture-0.3.4.dist-info/WHEEL +4 -0
  118. fraiseql_confiture-0.3.4.dist-info/entry_points.txt +2 -0
  119. fraiseql_confiture-0.3.4.dist-info/licenses/LICENSE +21 -0
File without changes
@@ -0,0 +1,263 @@
1
+ """Environment configuration management
2
+
3
+ Handles loading and validation of environment-specific configuration from YAML files.
4
+ """
5
+
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import yaml
10
+ from pydantic import BaseModel, Field, field_validator
11
+
12
+ from confiture.exceptions import ConfigurationError
13
+
14
+
15
+ class BuildConfig(BaseModel):
16
+ """Build configuration options."""
17
+
18
+ sort_mode: str = "alphabetical" # Options: alphabetical, hex
19
+
20
+
21
+ class LockingConfig(BaseModel):
22
+ """Distributed locking configuration.
23
+
24
+ Controls how Confiture acquires locks to prevent concurrent migrations
25
+ in multi-pod Kubernetes deployments.
26
+
27
+ Attributes:
28
+ enabled: Whether locking is enabled (default: True)
29
+ timeout_ms: Lock acquisition timeout in milliseconds (default: 30000)
30
+ """
31
+
32
+ enabled: bool = True
33
+ timeout_ms: int = 30000 # 30 seconds default
34
+
35
+
36
+ class MigrationConfig(BaseModel):
37
+ """Migration configuration options.
38
+
39
+ Attributes:
40
+ strict_mode: Whether to fail on warnings/notices (default: False)
41
+ locking: Distributed locking configuration
42
+ """
43
+
44
+ strict_mode: bool = False # Whether to fail on warnings/notices
45
+ locking: LockingConfig = Field(default_factory=LockingConfig)
46
+
47
+
48
+ class DirectoryConfig(BaseModel):
49
+ """Directory configuration with pattern matching."""
50
+
51
+ path: str
52
+ recursive: bool = True
53
+ include: list[str] = Field(default_factory=lambda: ["**/*.sql"])
54
+ exclude: list[str] = Field(default_factory=list)
55
+ auto_discover: bool = True
56
+ order: int = 0
57
+
58
+
59
+ class DatabaseConfig(BaseModel):
60
+ """Database connection configuration.
61
+
62
+ Can be initialized from a connection URL or individual parameters.
63
+ """
64
+
65
+ host: str = "localhost"
66
+ port: int = 5432
67
+ database: str = "postgres"
68
+ user: str = "postgres"
69
+ password: str = ""
70
+
71
+ @classmethod
72
+ def from_url(cls, url: str) -> "DatabaseConfig":
73
+ """Parse database configuration from PostgreSQL URL.
74
+
75
+ Args:
76
+ url: PostgreSQL connection URL (postgresql://user:pass@host:port/dbname)
77
+
78
+ Returns:
79
+ DatabaseConfig instance
80
+
81
+ Example:
82
+ >>> config = DatabaseConfig.from_url("postgresql://user:pass@localhost:5432/mydb")
83
+ >>> config.host
84
+ 'localhost'
85
+ """
86
+ import re
87
+
88
+ # Parse URL: postgresql://user:pass@host:port/dbname
89
+ pattern = r"(?:postgresql|postgres)://(?:([^:]+):([^@]+)@)?([^:/]+)(?::(\d+))?/(.+)"
90
+ match = re.match(pattern, url)
91
+
92
+ if not match:
93
+ raise ValueError(f"Invalid PostgreSQL URL: {url}")
94
+
95
+ user, password, host, port, database = match.groups()
96
+
97
+ return cls(
98
+ host=host or "localhost",
99
+ port=int(port) if port else 5432,
100
+ database=database,
101
+ user=user or "postgres",
102
+ password=password or "",
103
+ )
104
+
105
+ def to_dict(self) -> dict[str, Any]:
106
+ """Convert to dictionary for use with create_connection."""
107
+ return {
108
+ "database": {
109
+ "host": self.host,
110
+ "port": self.port,
111
+ "database": self.database,
112
+ "user": self.user,
113
+ "password": self.password,
114
+ }
115
+ }
116
+
117
+
118
+ class Environment(BaseModel):
119
+ """Environment configuration
120
+
121
+ Loaded from db/environments/{env_name}.yaml files.
122
+
123
+ Attributes:
124
+ name: Environment name (e.g., "local", "production")
125
+ database_url: PostgreSQL connection URL
126
+ include_dirs: Directories to include when building schema (supports both string and dict formats)
127
+ exclude_dirs: Directories to exclude from schema build
128
+ migration_table: Table name for tracking migrations
129
+ auto_backup: Whether to automatically backup before migrations
130
+ require_confirmation: Whether to require user confirmation for risky operations
131
+ build: Build configuration options
132
+ """
133
+
134
+ name: str
135
+ database_url: str
136
+ include_dirs: list[str | DirectoryConfig]
137
+ exclude_dirs: list[str] = Field(default_factory=list)
138
+ migration_table: str = "confiture_migrations"
139
+ auto_backup: bool = True
140
+ require_confirmation: bool = True
141
+ build: BuildConfig = Field(default_factory=BuildConfig)
142
+ migration: MigrationConfig = Field(default_factory=MigrationConfig)
143
+
144
+ @property
145
+ def database(self) -> DatabaseConfig:
146
+ """Get database configuration from database_url.
147
+
148
+ Returns:
149
+ DatabaseConfig instance
150
+ """
151
+ return DatabaseConfig.from_url(self.database_url)
152
+
153
+ @field_validator("database_url")
154
+ @classmethod
155
+ def validate_database_url(cls, v: str) -> str:
156
+ """Validate PostgreSQL connection URL format"""
157
+ if not v.startswith(("postgresql://", "postgres://")):
158
+ raise ValueError(
159
+ f"Invalid database_url: must start with postgresql:// or postgres://, got: {v}"
160
+ )
161
+ return v
162
+
163
+ @classmethod
164
+ def load(cls, env_name: str, project_dir: Path | None = None) -> "Environment":
165
+ """Load environment configuration from YAML file
166
+
167
+ Args:
168
+ env_name: Environment name (e.g., "local", "production")
169
+ project_dir: Project root directory. If None, uses current directory.
170
+
171
+ Returns:
172
+ Environment configuration object
173
+
174
+ Raises:
175
+ ConfigurationError: If config file not found, invalid, or missing required fields
176
+
177
+ Example:
178
+ >>> env = Environment.load("local")
179
+ >>> print(env.database_url)
180
+ postgresql://localhost/myapp_local
181
+ """
182
+ if project_dir is None:
183
+ project_dir = Path.cwd()
184
+
185
+ # Find config file
186
+ config_path = project_dir / "db" / "environments" / f"{env_name}.yaml"
187
+
188
+ if not config_path.exists():
189
+ raise ConfigurationError(
190
+ f"Environment config not found: {config_path}\n"
191
+ f"Expected: db/environments/{env_name}.yaml"
192
+ )
193
+
194
+ # Load YAML
195
+ try:
196
+ with open(config_path) as f:
197
+ data = yaml.safe_load(f)
198
+ except yaml.YAMLError as e:
199
+ raise ConfigurationError(f"Invalid YAML in {config_path}: {e}") from e
200
+
201
+ if not isinstance(data, dict):
202
+ raise ConfigurationError(
203
+ f"Invalid config format in {config_path}: expected dictionary, got {type(data)}"
204
+ )
205
+
206
+ # Validate required fields
207
+ if "database_url" not in data:
208
+ raise ConfigurationError(f"Missing required field 'database_url' in {config_path}")
209
+
210
+ if "include_dirs" not in data:
211
+ raise ConfigurationError(f"Missing required field 'include_dirs' in {config_path}")
212
+
213
+ # Resolve include_dirs paths to absolute
214
+ resolved_include_dirs: list[str | dict[str, Any]] = []
215
+ for include_item in data["include_dirs"]:
216
+ if isinstance(include_item, str):
217
+ # Simple string format - resolve to absolute path
218
+ abs_path = (project_dir / include_item).resolve()
219
+ if not abs_path.exists():
220
+ raise ConfigurationError(
221
+ f"Include directory does not exist: {abs_path}\nSpecified in {config_path}"
222
+ )
223
+ resolved_include_dirs.append(str(abs_path))
224
+ elif isinstance(include_item, dict):
225
+ # Dict format - resolve the path field and keep as dict
226
+ path_str = include_item.get("path")
227
+ if not path_str:
228
+ raise ConfigurationError(
229
+ f"Missing 'path' field in include_dirs item: {include_item}\nIn {config_path}"
230
+ )
231
+ abs_path = (project_dir / path_str).resolve()
232
+ auto_discover = include_item.get("auto_discover", True)
233
+ if not abs_path.exists() and not auto_discover:
234
+ raise ConfigurationError(
235
+ f"Include directory does not exist: {abs_path}\nSpecified in {config_path}"
236
+ )
237
+ # Keep the dict format but with resolved path
238
+ resolved_item = include_item.copy()
239
+ resolved_item["path"] = str(abs_path)
240
+ resolved_include_dirs.append(resolved_item)
241
+ else:
242
+ raise ConfigurationError(
243
+ f"Invalid include_dirs item type: {type(include_item)}. Expected str or dict.\nIn {config_path}"
244
+ )
245
+
246
+ data["include_dirs"] = resolved_include_dirs
247
+
248
+ # Resolve exclude_dirs if present
249
+ if "exclude_dirs" in data:
250
+ exclude_dirs = []
251
+ for dir_path in data["exclude_dirs"]:
252
+ abs_path = (project_dir / dir_path).resolve()
253
+ exclude_dirs.append(str(abs_path))
254
+ data["exclude_dirs"] = exclude_dirs
255
+
256
+ # Set environment name
257
+ data["name"] = env_name
258
+
259
+ # Create Environment instance
260
+ try:
261
+ return cls(**data)
262
+ except Exception as e:
263
+ raise ConfigurationError(f"Invalid configuration in {config_path}: {e}") from e
@@ -0,0 +1,51 @@
1
+ """Core migration execution and schema building components."""
2
+
3
+ from confiture.core.dry_run import (
4
+ DryRunError,
5
+ DryRunExecutor,
6
+ DryRunResult,
7
+ )
8
+ from confiture.core.hooks import (
9
+ CircuitBreaker,
10
+ CircuitBreakerState,
11
+ ExecutionDAG,
12
+ Hook,
13
+ HookContext,
14
+ HookErrorStrategy,
15
+ HookExecutionEvent,
16
+ HookExecutionResult,
17
+ HookExecutionStatus,
18
+ HookExecutionStrategy,
19
+ HookExecutionTracer,
20
+ HookPhase,
21
+ HookRegistry,
22
+ HookResult,
23
+ PerformanceTrace,
24
+ RetryConfig,
25
+ )
26
+
27
+ __all__ = [
28
+ # Dry-run mode
29
+ "DryRunError",
30
+ "DryRunExecutor",
31
+ "DryRunResult",
32
+ # Hook system - Base
33
+ "Hook",
34
+ "HookContext",
35
+ "HookResult",
36
+ "HookPhase",
37
+ "HookRegistry",
38
+ # Hook system - Execution strategies
39
+ "HookExecutionStrategy",
40
+ "HookErrorStrategy",
41
+ "RetryConfig",
42
+ # Hook system - Observability
43
+ "HookExecutionStatus",
44
+ "HookExecutionEvent",
45
+ "HookExecutionResult",
46
+ "CircuitBreaker",
47
+ "CircuitBreakerState",
48
+ "HookExecutionTracer",
49
+ "ExecutionDAG",
50
+ "PerformanceTrace",
51
+ ]
File without changes