fraiseql-confiture 0.3.7__cp311-cp311-macosx_11_0_arm64.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 (124) hide show
  1. confiture/__init__.py +48 -0
  2. confiture/_core.cpython-311-darwin.so +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 +1893 -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 +184 -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 +882 -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 +24 -0
  95. confiture/models/lint.py +193 -0
  96. confiture/models/migration.py +265 -0
  97. confiture/models/schema.py +203 -0
  98. confiture/models/sql_file_migration.py +225 -0
  99. confiture/scenarios/__init__.py +36 -0
  100. confiture/scenarios/compliance.py +586 -0
  101. confiture/scenarios/ecommerce.py +199 -0
  102. confiture/scenarios/financial.py +253 -0
  103. confiture/scenarios/healthcare.py +315 -0
  104. confiture/scenarios/multi_tenant.py +340 -0
  105. confiture/scenarios/saas.py +295 -0
  106. confiture/testing/FRAMEWORK_API.md +722 -0
  107. confiture/testing/__init__.py +100 -0
  108. confiture/testing/fixtures/__init__.py +11 -0
  109. confiture/testing/fixtures/data_validator.py +229 -0
  110. confiture/testing/fixtures/migration_runner.py +167 -0
  111. confiture/testing/fixtures/schema_snapshotter.py +352 -0
  112. confiture/testing/frameworks/__init__.py +10 -0
  113. confiture/testing/frameworks/mutation.py +587 -0
  114. confiture/testing/frameworks/performance.py +479 -0
  115. confiture/testing/loader.py +225 -0
  116. confiture/testing/pytest/__init__.py +38 -0
  117. confiture/testing/pytest_plugin.py +190 -0
  118. confiture/testing/sandbox.py +304 -0
  119. confiture/testing/utils/__init__.py +0 -0
  120. fraiseql_confiture-0.3.7.dist-info/METADATA +438 -0
  121. fraiseql_confiture-0.3.7.dist-info/RECORD +124 -0
  122. fraiseql_confiture-0.3.7.dist-info/WHEEL +4 -0
  123. fraiseql_confiture-0.3.7.dist-info/entry_points.txt +4 -0
  124. fraiseql_confiture-0.3.7.dist-info/licenses/LICENSE +21 -0
confiture/__init__.py ADDED
@@ -0,0 +1,48 @@
1
+ """Confiture: PostgreSQL migrations, sweetly done 🍓
2
+
3
+ Confiture is a modern PostgreSQL migration tool with a build-from-scratch
4
+ philosophy and 4 migration strategies.
5
+
6
+ Example:
7
+ >>> from confiture import __version__
8
+ >>> print(__version__)
9
+ 0.3.7
10
+ """
11
+
12
+ from typing import Any
13
+
14
+ from confiture.core.linting import SchemaLinter
15
+
16
+ __version__ = "0.3.7"
17
+ __author__ = "Lionel Hamayon"
18
+ __email__ = "lionel.hamayon@evolution-digitale.fr"
19
+
20
+ __all__ = [
21
+ "__version__",
22
+ "__author__",
23
+ "__email__",
24
+ "SchemaLinter",
25
+ ]
26
+
27
+ # Lazy imports to avoid errors during development
28
+ # These will be enabled as components are implemented:
29
+ # - SchemaBuilder (Milestone 1.3+)
30
+ # - Migrator (Milestone 1.7+)
31
+ # - Environment (Milestone 1.2+)
32
+
33
+
34
+ def __getattr__(name: str) -> Any:
35
+ """Lazy import for not-yet-implemented components"""
36
+ if name == "SchemaBuilder":
37
+ from confiture.core.builder import SchemaBuilder
38
+
39
+ return SchemaBuilder
40
+ elif name == "Migrator":
41
+ from confiture.core.migrator import Migrator
42
+
43
+ return Migrator
44
+ elif name == "Environment":
45
+ from confiture.config.environment import Environment
46
+
47
+ return Environment
48
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
Binary file
File without changes
@@ -0,0 +1,116 @@
1
+ """Dry-run mode helpers for CLI integration.
2
+
3
+ This module provides helper functions for dry-run analysis integration with the CLI.
4
+ """
5
+
6
+ import json
7
+ import logging
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from rich.console import Console
12
+
13
+ logger = logging.getLogger(__name__)
14
+ console = Console()
15
+
16
+
17
+ def save_text_report(report_text: str, filepath: Path) -> None:
18
+ """Save text report to file.
19
+
20
+ Args:
21
+ report_text: Formatted text report
22
+ filepath: Path to save report to
23
+
24
+ Raises:
25
+ IOError: If file write fails
26
+ """
27
+ filepath.parent.mkdir(parents=True, exist_ok=True)
28
+ filepath.write_text(report_text)
29
+
30
+
31
+ def save_json_report(report_data: dict, filepath: Path) -> None:
32
+ """Save JSON report to file.
33
+
34
+ Args:
35
+ report_data: Report dictionary to save
36
+ filepath: Path to save report to
37
+
38
+ Raises:
39
+ IOError: If file write fails
40
+ """
41
+ filepath.parent.mkdir(parents=True, exist_ok=True)
42
+ with filepath.open("w") as f:
43
+ json.dump(report_data, f, indent=2)
44
+
45
+
46
+ def print_json_report(report_data: dict) -> None:
47
+ """Print JSON report to console.
48
+
49
+ Args:
50
+ report_data: Report dictionary to print
51
+ """
52
+ console.print_json(data=report_data)
53
+
54
+
55
+ def show_report_summary(report: Any) -> None:
56
+ """Show a brief summary of the report status.
57
+
58
+ Args:
59
+ report: Report object with has_unsafe_statements, unsafe_count,
60
+ total_estimated_time_ms, and total_estimated_disk_mb attributes
61
+ """
62
+ if not report.has_unsafe_statements:
63
+ console.print("[green]✓ SAFE[/green]", end=" ")
64
+ else:
65
+ unsafe_msg = f"[red]❌ UNSAFE ({report.unsafe_count} statements)[/red]"
66
+ console.print(unsafe_msg, end=" ")
67
+
68
+ time_str = report.total_estimated_time_ms
69
+ disk_str = report.total_estimated_disk_mb
70
+ console.print(f"| Time: {time_str}ms | Disk: {disk_str:.1f}MB")
71
+
72
+
73
+ def ask_dry_run_execute_confirmation() -> bool:
74
+ """Ask user to confirm real execution after dry-run-execute test.
75
+
76
+ Returns:
77
+ True if user confirms, False otherwise
78
+ """
79
+ import typer
80
+
81
+ return typer.confirm("\n🔄 Proceed with real execution?", default=False)
82
+
83
+
84
+ def extract_sql_statements_from_migration(migration_class) -> list[str]:
85
+ """Extract SQL statements from a migration's up() method.
86
+
87
+ This is a helper that attempts to extract SQL statements from migration
88
+ code by inspecting the migration object. This is limited and approximate
89
+ since migrations use self.execute() calls.
90
+
91
+ Args:
92
+ migration_class: Migration class (not instance)
93
+
94
+ Returns:
95
+ List of SQL statement strings (may be approximate/incomplete)
96
+ """
97
+ # This is a placeholder - in Day 2 implementation, we would:
98
+ # 1. Create a migration instance with a mock connection
99
+ # 2. Track calls to self.execute()
100
+ # 3. Extract the SQL statements
101
+ # For now, return empty list - actual implementation in Day 2
102
+ return []
103
+
104
+
105
+ def display_dry_run_header(mode: str) -> None:
106
+ """Display header for dry-run analysis.
107
+
108
+ Args:
109
+ mode: Either "analysis" for --dry-run or "testing" for --dry-run-execute
110
+ """
111
+ if mode == "testing":
112
+ msg = "[cyan]🧪 Executing migrations in SAVEPOINT (guaranteed rollback)...[/cyan]"
113
+ console.print(msg + "\n")
114
+ else:
115
+ msg = "[cyan]🔍 Analyzing migrations without execution...[/cyan]"
116
+ console.print(msg + "\n")
@@ -0,0 +1,193 @@
1
+ """Output formatting for linting results.
2
+
3
+ This module provides functions to format LintReport results in various
4
+ output formats (table, JSON, CSV) for the lint CLI command.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Literal
10
+
11
+ from rich.console import Console
12
+ from rich.table import Table
13
+
14
+ from confiture.models.lint import LintReport, LintSeverity
15
+
16
+
17
+ def format_lint_report(
18
+ report: LintReport,
19
+ format_type: Literal["table", "json", "csv"] = "table",
20
+ console: Console | None = None,
21
+ ) -> str:
22
+ """Format a LintReport in the specified format.
23
+
24
+ Args:
25
+ report: LintReport to format
26
+ format_type: Output format (table, json, or csv)
27
+ console: Rich Console instance for table rendering
28
+
29
+ Returns:
30
+ Formatted report as string
31
+ """
32
+ if format_type == "json":
33
+ return format_json(report)
34
+ elif format_type == "csv":
35
+ return format_csv(report)
36
+ else: # table
37
+ if console is None:
38
+ console = Console()
39
+ format_table(report, console)
40
+ return ""
41
+
42
+
43
+ def _severity_string(severity: LintSeverity) -> str:
44
+ """Format severity level with color.
45
+
46
+ Args:
47
+ severity: Severity level to format
48
+
49
+ Returns:
50
+ Colored severity string for Rich output
51
+ """
52
+ if severity == LintSeverity.ERROR:
53
+ return "[red]ERROR[/red]"
54
+ elif severity == LintSeverity.WARNING:
55
+ return "[yellow]WARNING[/yellow]"
56
+ return "[blue]INFO[/blue]"
57
+
58
+
59
+ def format_table(report: LintReport, console: Console) -> None:
60
+ """Display LintReport as a rich table.
61
+
62
+ Args:
63
+ report: LintReport to display
64
+ console: Rich Console instance for rendering
65
+ """
66
+ # Summary section
67
+ console.print(f"\n[bold]Schema Linting Results[/bold] - {report.schema_name}")
68
+ console.print(f"Tables: {report.tables_checked} checked")
69
+ console.print(f"Columns: {report.columns_checked} checked")
70
+ console.print(f"Time: {report.execution_time_ms}ms\n")
71
+
72
+ if not report.violations:
73
+ console.print("[green]✅ No violations found![/green]\n")
74
+ return
75
+
76
+ # Violations table
77
+ table = Table(title="Violations")
78
+ table.add_column("Severity", style="bold")
79
+ table.add_column("Rule", style="cyan")
80
+ table.add_column("Location", style="yellow")
81
+ table.add_column("Message", style="white")
82
+
83
+ for violation in sorted(
84
+ report.violations,
85
+ key=lambda v: (
86
+ v.severity == LintSeverity.ERROR,
87
+ v.severity == LintSeverity.WARNING,
88
+ ),
89
+ reverse=True,
90
+ ):
91
+ table.add_row(
92
+ _severity_string(violation.severity),
93
+ violation.rule_name,
94
+ violation.location,
95
+ violation.message,
96
+ )
97
+
98
+ console.print(table)
99
+
100
+ # Summary counts
101
+ console.print("\n[bold]Summary:[/bold]")
102
+ console.print(f" {report.errors_count} errors")
103
+ console.print(f" {report.warnings_count} warnings")
104
+ console.print(f" {report.info_count} info")
105
+
106
+ # Suggested fixes (if any)
107
+ fixes = [v for v in report.violations if v.suggested_fix]
108
+ if fixes:
109
+ console.print("\n[bold]Suggested Fixes:[/bold]")
110
+ for violation in fixes:
111
+ console.print(f" {violation.location}: {violation.suggested_fix}")
112
+
113
+
114
+ def format_json(report: LintReport) -> str:
115
+ """Format LintReport as JSON.
116
+
117
+ Args:
118
+ report: LintReport to format
119
+
120
+ Returns:
121
+ JSON string representation
122
+ """
123
+ data = {
124
+ "schema_name": report.schema_name,
125
+ "tables_checked": report.tables_checked,
126
+ "columns_checked": report.columns_checked,
127
+ "execution_time_ms": report.execution_time_ms,
128
+ "violations": {
129
+ "total": len(report.violations),
130
+ "errors": report.errors_count,
131
+ "warnings": report.warnings_count,
132
+ "info": report.info_count,
133
+ "items": [
134
+ {
135
+ "rule": v.rule_name,
136
+ "severity": v.severity.value,
137
+ "location": v.location,
138
+ "message": v.message,
139
+ "suggested_fix": v.suggested_fix,
140
+ }
141
+ for v in report.violations
142
+ ],
143
+ },
144
+ }
145
+ return json.dumps(data, indent=2)
146
+
147
+
148
+ def format_csv(report: LintReport) -> str:
149
+ """Format LintReport as CSV.
150
+
151
+ Args:
152
+ report: LintReport to format
153
+
154
+ Returns:
155
+ CSV string representation
156
+ """
157
+ lines = [
158
+ "rule_name,severity,location,message,suggested_fix",
159
+ ]
160
+
161
+ for violation in report.violations:
162
+ # Escape quotes in fields
163
+ rule = violation.rule_name.replace('"', '""')
164
+ severity = violation.severity.value
165
+ location = violation.location.replace('"', '""')
166
+ message = violation.message.replace('"', '""')
167
+ fix = (violation.suggested_fix or "").replace('"', '""')
168
+
169
+ # Quote fields that contain commas
170
+ rule = f'"{rule}"' if "," in rule else rule
171
+ location = f'"{location}"' if "," in location else location
172
+ message = f'"{message}"' if "," in message else message
173
+ fix = f'"{fix}"' if "," in fix else fix
174
+
175
+ lines.append(f"{rule},{severity},{location},{message},{fix}")
176
+
177
+ return "\n".join(lines)
178
+
179
+
180
+ def save_report(
181
+ report: LintReport,
182
+ output_path: Path,
183
+ format_type: Literal["json", "csv"] = "json",
184
+ ) -> None:
185
+ """Save LintReport to a file.
186
+
187
+ Args:
188
+ report: LintReport to save
189
+ output_path: Path to save to
190
+ format_type: Output format (json or csv)
191
+ """
192
+ content = format_json(report) if format_type == "json" else format_csv(report)
193
+ output_path.write_text(content)