pum 1.2.2__tar.gz → 1.3.0__tar.gz
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.
- {pum-1.2.2 → pum-1.3.0}/PKG-INFO +6 -2
- {pum-1.2.2 → pum-1.3.0}/README.md +3 -1
- pum-1.3.0/pum/__init__.py +90 -0
- {pum-1.2.2 → pum-1.3.0}/pum/changelog.py +61 -1
- pum-1.3.0/pum/checker.py +661 -0
- pum-1.3.0/pum/cli.py +571 -0
- {pum-1.2.2 → pum-1.3.0}/pum/config_model.py +57 -34
- pum-1.3.0/pum/connection.py +30 -0
- {pum-1.2.2 → pum-1.3.0}/pum/dependency_handler.py +69 -4
- {pum-1.2.2 → pum-1.3.0}/pum/dumper.py +14 -4
- {pum-1.2.2 → pum-1.3.0}/pum/exceptions.py +9 -0
- pum-1.3.0/pum/feedback.py +119 -0
- {pum-1.2.2 → pum-1.3.0}/pum/hook.py +95 -29
- {pum-1.2.2 → pum-1.3.0}/pum/info.py +0 -2
- {pum-1.2.2 → pum-1.3.0}/pum/parameter.py +4 -0
- {pum-1.2.2 → pum-1.3.0}/pum/pum_config.py +103 -20
- pum-1.3.0/pum/report_generator.py +1043 -0
- {pum-1.2.2 → pum-1.3.0}/pum/role_manager.py +151 -23
- {pum-1.2.2 → pum-1.3.0}/pum/schema_migrations.py +173 -36
- {pum-1.2.2 → pum-1.3.0}/pum/sql_content.py +83 -21
- pum-1.3.0/pum/upgrader.py +441 -0
- {pum-1.2.2 → pum-1.3.0}/pum.egg-info/PKG-INFO +6 -2
- {pum-1.2.2 → pum-1.3.0}/pum.egg-info/SOURCES.txt +9 -0
- {pum-1.2.2 → pum-1.3.0}/pum.egg-info/requires.txt +3 -0
- {pum-1.2.2 → pum-1.3.0}/pyproject.toml +1 -0
- pum-1.3.0/requirements/html.txt +1 -0
- {pum-1.2.2 → pum-1.3.0}/test/test_changelog.py +5 -1
- pum-1.3.0/test/test_checker.py +577 -0
- pum-1.3.0/test/test_config.py +298 -0
- {pum-1.2.2 → pum-1.3.0}/test/test_dumper.py +2 -2
- pum-1.3.0/test/test_feedback.py +317 -0
- pum-1.3.0/test/test_hooks.py +202 -0
- pum-1.3.0/test/test_roles.py +309 -0
- {pum-1.2.2 → pum-1.3.0}/test/test_schema_migrations.py +57 -6
- pum-1.3.0/test/test_sql_content.py +193 -0
- pum-1.3.0/test/test_transaction_fix.py +100 -0
- {pum-1.2.2 → pum-1.3.0}/test/test_upgrader.py +162 -16
- pum-1.2.2/pum/__init__.py +0 -29
- pum-1.2.2/pum/checker.py +0 -431
- pum-1.2.2/pum/cli.py +0 -408
- pum-1.2.2/pum/upgrader.py +0 -177
- pum-1.2.2/test/test_config.py +0 -152
- pum-1.2.2/test/test_roles.py +0 -117
- {pum-1.2.2 → pum-1.3.0}/LICENSE +0 -0
- {pum-1.2.2 → pum-1.3.0}/pum.egg-info/dependency_links.txt +0 -0
- {pum-1.2.2 → pum-1.3.0}/pum.egg-info/entry_points.txt +0 -0
- {pum-1.2.2 → pum-1.3.0}/pum.egg-info/top_level.txt +0 -0
- {pum-1.2.2 → pum-1.3.0}/requirements/base.txt +0 -0
- {pum-1.2.2 → pum-1.3.0}/requirements/development.txt +0 -0
- {pum-1.2.2 → pum-1.3.0}/setup.cfg +0 -0
{pum-1.2.2 → pum-1.3.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pum
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Pum stands for "Postgres Upgrades Manager". It is a Database migration management tool very similar to flyway-db or Liquibase, based on metadata tables.
|
|
5
5
|
Author-email: Denis Rouzaud <denis@opengis.ch>
|
|
6
6
|
License-Expression: GPL-2.0-or-later
|
|
@@ -32,6 +32,8 @@ Requires-Dist: flake8-isort; extra == "dev"
|
|
|
32
32
|
Requires-Dist: flake8-print; extra == "dev"
|
|
33
33
|
Requires-Dist: pre-commit; extra == "dev"
|
|
34
34
|
Requires-Dist: nose2; extra == "dev"
|
|
35
|
+
Provides-Extra: html
|
|
36
|
+
Requires-Dist: Jinja2; extra == "html"
|
|
35
37
|
Dynamic: license-file
|
|
36
38
|
|
|
37
39
|
# PostgreSQL Upgrades Manager (PUM)
|
|
@@ -49,10 +51,12 @@ PUM (PostgreSQL Upgrades Manager) is a robust database migration management tool
|
|
|
49
51
|
|
|
50
52
|
## Key Features
|
|
51
53
|
|
|
54
|
+
- **Flexible Database Connections**: Connect using PostgreSQL service names or direct connection strings (URI or parameters).
|
|
52
55
|
- **Command-line and Python Integration**: Use PUM as a standalone CLI tool or integrate it into your Python project.
|
|
53
56
|
- **Database Versioning**: Automatically manage database versioning with a metadata table.
|
|
54
57
|
- **Changelog Management**: Apply and track SQL delta files for database upgrades.
|
|
55
|
-
- **
|
|
58
|
+
- **Droppable & recreatable app with data isolation**: PUM supports a clean rebuild workflow where an application environment can be dropped and recreated deterministically using hooks (pre and post migration).
|
|
59
|
+
|
|
56
60
|
|
|
57
61
|
## Why PUM?
|
|
58
62
|
|
|
@@ -13,10 +13,12 @@ PUM (PostgreSQL Upgrades Manager) is a robust database migration management tool
|
|
|
13
13
|
|
|
14
14
|
## Key Features
|
|
15
15
|
|
|
16
|
+
- **Flexible Database Connections**: Connect using PostgreSQL service names or direct connection strings (URI or parameters).
|
|
16
17
|
- **Command-line and Python Integration**: Use PUM as a standalone CLI tool or integrate it into your Python project.
|
|
17
18
|
- **Database Versioning**: Automatically manage database versioning with a metadata table.
|
|
18
19
|
- **Changelog Management**: Apply and track SQL delta files for database upgrades.
|
|
19
|
-
- **
|
|
20
|
+
- **Droppable & recreatable app with data isolation**: PUM supports a clean rebuild workflow where an application environment can be dropped and recreated deterministically using hooks (pre and post migration).
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
## Why PUM?
|
|
22
24
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
# Custom SQL logging level (more verbose than DEBUG)
|
|
6
|
+
# Register with: logging.addLevelName(SQL, 'SQL')
|
|
7
|
+
SQL = 5
|
|
8
|
+
|
|
9
|
+
# Configure default logging for API usage (not CLI)
|
|
10
|
+
# CLI will override this with its own configuration
|
|
11
|
+
if not logging.getLogger().handlers:
|
|
12
|
+
logging.basicConfig(
|
|
13
|
+
level=logging.INFO,
|
|
14
|
+
format="%(message)s",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .changelog import Changelog
|
|
19
|
+
from .checker import Checker
|
|
20
|
+
from .dependency_handler import DependencyHandler
|
|
21
|
+
from .dumper import Dumper, DumpFormat
|
|
22
|
+
from .feedback import Feedback, LogFeedback, SilentFeedback
|
|
23
|
+
from .hook import HookBase, HookHandler
|
|
24
|
+
from .parameter import ParameterDefinition, ParameterType
|
|
25
|
+
from .pum_config import PumConfig
|
|
26
|
+
from .role_manager import Permission, PermissionType, Role, RoleManager
|
|
27
|
+
from .schema_migrations import SchemaMigrations
|
|
28
|
+
from .sql_content import SqlContent, CursorResult
|
|
29
|
+
from .upgrader import Upgrader
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"Checker",
|
|
33
|
+
"Changelog",
|
|
34
|
+
"CursorResult",
|
|
35
|
+
"DependencyHandler",
|
|
36
|
+
"Dumper",
|
|
37
|
+
"DumpFormat",
|
|
38
|
+
"Feedback",
|
|
39
|
+
"HookBase",
|
|
40
|
+
"HookHandler",
|
|
41
|
+
"LogFeedback",
|
|
42
|
+
"ParameterDefinition",
|
|
43
|
+
"ParameterType",
|
|
44
|
+
"Permission",
|
|
45
|
+
"PermissionType",
|
|
46
|
+
"PumConfig",
|
|
47
|
+
"Role",
|
|
48
|
+
"RoleManager",
|
|
49
|
+
"SchemaMigrations",
|
|
50
|
+
"SilentFeedback",
|
|
51
|
+
"SQL",
|
|
52
|
+
"SqlContent",
|
|
53
|
+
"Upgrader",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
|
|
58
|
+
"Checker": ("pum.checker", "Checker"),
|
|
59
|
+
"Changelog": ("pum.changelog", "Changelog"),
|
|
60
|
+
"CursorResult": ("pum.sql_content", "CursorResult"),
|
|
61
|
+
"DependencyHandler": ("pum.dependency_handler", "DependencyHandler"),
|
|
62
|
+
"Dumper": ("pum.dumper", "Dumper"),
|
|
63
|
+
"DumpFormat": ("pum.dumper", "DumpFormat"),
|
|
64
|
+
"HookBase": ("pum.hook", "HookBase"),
|
|
65
|
+
"HookHandler": ("pum.hook", "HookHandler"),
|
|
66
|
+
"ParameterDefinition": ("pum.parameter", "ParameterDefinition"),
|
|
67
|
+
"ParameterType": ("pum.parameter", "ParameterType"),
|
|
68
|
+
"Permission": ("pum.role_manager", "Permission"),
|
|
69
|
+
"PermissionType": ("pum.role_manager", "PermissionType"),
|
|
70
|
+
"PumConfig": ("pum.pum_config", "PumConfig"),
|
|
71
|
+
"Role": ("pum.role_manager", "Role"),
|
|
72
|
+
"RoleManager": ("pum.role_manager", "RoleManager"),
|
|
73
|
+
"SchemaMigrations": ("pum.schema_migrations", "SchemaMigrations"),
|
|
74
|
+
"SqlContent": ("pum.sql_content", "SqlContent"),
|
|
75
|
+
"Upgrader": ("pum.upgrader", "Upgrader"),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def __getattr__(name: str) -> Any:
|
|
80
|
+
if name not in _LAZY_IMPORTS:
|
|
81
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
82
|
+
module_name, symbol_name = _LAZY_IMPORTS[name]
|
|
83
|
+
module = importlib.import_module(module_name)
|
|
84
|
+
value = getattr(module, symbol_name)
|
|
85
|
+
globals()[name] = value
|
|
86
|
+
return value
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def __dir__() -> list[str]:
|
|
90
|
+
return sorted(set(list(globals().keys()) + list(_LAZY_IMPORTS.keys())))
|
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from os import listdir
|
|
2
3
|
from os.path import basename
|
|
3
4
|
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
4
6
|
|
|
5
7
|
from packaging.version import parse as parse_version
|
|
6
8
|
import psycopg
|
|
7
9
|
|
|
10
|
+
from .schema_migrations import SchemaMigrations
|
|
8
11
|
from .exceptions import PumInvalidChangelog, PumSqlError
|
|
9
12
|
from .sql_content import SqlContent
|
|
10
13
|
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .feedback import Feedback
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
11
19
|
|
|
12
20
|
class Changelog:
|
|
13
21
|
"""This class represent a changelog directory.
|
|
@@ -82,6 +90,9 @@ class Changelog:
|
|
|
82
90
|
connection: psycopg.Connection,
|
|
83
91
|
parameters: dict | None = None,
|
|
84
92
|
commit: bool = True,
|
|
93
|
+
schema_migrations: SchemaMigrations | None = None,
|
|
94
|
+
beta_testing: bool = False,
|
|
95
|
+
feedback: "Feedback | None" = None,
|
|
85
96
|
) -> list[Path]:
|
|
86
97
|
"""Apply a changelog
|
|
87
98
|
This will execute all the files in the changelog directory.
|
|
@@ -94,18 +105,67 @@ class Changelog:
|
|
|
94
105
|
The parameters to pass to the SQL files
|
|
95
106
|
commit: bool
|
|
96
107
|
If true, the transaction is committed. The default is true.
|
|
108
|
+
schema_migrations: SchemaMigrations | None
|
|
109
|
+
The SchemaMigrations instance to use to record the applied changelog.
|
|
110
|
+
If None, the changelog will not be recorded.
|
|
111
|
+
beta_testing: bool
|
|
112
|
+
If true, the changelog will be recorded as a beta testing version.
|
|
113
|
+
feedback: Feedback | None
|
|
114
|
+
Optional feedback object for progress reporting.
|
|
97
115
|
|
|
98
116
|
Returns:
|
|
99
117
|
list[Path]
|
|
100
118
|
The list of changelogs that were executed
|
|
101
119
|
|
|
102
120
|
"""
|
|
121
|
+
logger.info(f"Applying changelog version {self.version} from {self.dir}")
|
|
122
|
+
|
|
123
|
+
parameters_literals = SqlContent.prepare_parameters(parameters)
|
|
103
124
|
files = self.files()
|
|
104
125
|
for file in files:
|
|
126
|
+
if feedback:
|
|
127
|
+
feedback.increment_step()
|
|
128
|
+
feedback.report_progress(f"Executing {file.name}")
|
|
105
129
|
try:
|
|
106
130
|
SqlContent(file).execute(
|
|
107
|
-
connection=connection, commit=commit, parameters=
|
|
131
|
+
connection=connection, commit=commit, parameters=parameters_literals
|
|
108
132
|
)
|
|
109
133
|
except PumSqlError as e:
|
|
110
134
|
raise PumSqlError(f"Error applying changelog {file}: {e}") from e
|
|
135
|
+
if schema_migrations:
|
|
136
|
+
schema_migrations.set_baseline(
|
|
137
|
+
connection=connection,
|
|
138
|
+
version=self.version,
|
|
139
|
+
beta_testing=beta_testing,
|
|
140
|
+
commit=commit,
|
|
141
|
+
changelog_files=[str(f) for f in files],
|
|
142
|
+
parameters=parameters,
|
|
143
|
+
)
|
|
111
144
|
return files
|
|
145
|
+
|
|
146
|
+
def is_applied(
|
|
147
|
+
self,
|
|
148
|
+
connection: psycopg.Connection,
|
|
149
|
+
schema_migrations: SchemaMigrations,
|
|
150
|
+
) -> bool:
|
|
151
|
+
"""Check if the changelog has been applied.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
connection: The database connection to use.
|
|
155
|
+
Returns:
|
|
156
|
+
bool: True if the changelog has been applied, False otherwise.
|
|
157
|
+
"""
|
|
158
|
+
query = psycopg.sql.SQL("""
|
|
159
|
+
SELECT EXISTS (
|
|
160
|
+
SELECT 1
|
|
161
|
+
FROM {table}
|
|
162
|
+
WHERE version = {version}
|
|
163
|
+
)
|
|
164
|
+
""")
|
|
165
|
+
parameters = {
|
|
166
|
+
"version": psycopg.sql.Literal(str(self.version)),
|
|
167
|
+
"table": schema_migrations.migration_table_identifier,
|
|
168
|
+
}
|
|
169
|
+
cursor = SqlContent(query).execute(connection, parameters=parameters)
|
|
170
|
+
result = cursor._pum_results[0] if cursor._pum_results else None
|
|
171
|
+
return result[0] if result else False
|