pum 1.2.2__py3-none-any.whl → 1.3.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.
pum/__init__.py CHANGED
@@ -1,21 +1,44 @@
1
- from .changelog import Changelog
2
- from .dependency_handler import DependencyHandler
3
- from .dumper import Dumper, DumpFormat
4
- from .pum_config import PumConfig
5
- from .hook import HookHandler, HookBase
6
- from .parameter import ParameterDefinition, ParameterType
7
- from .role_manager import RoleManager, Role, Permission, PermissionType
8
- from .schema_migrations import SchemaMigrations
9
- from .sql_content import SqlContent
10
- from .upgrader import Upgrader
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
11
30
 
12
31
  __all__ = [
32
+ "Checker",
13
33
  "Changelog",
34
+ "CursorResult",
14
35
  "DependencyHandler",
15
36
  "Dumper",
16
37
  "DumpFormat",
38
+ "Feedback",
17
39
  "HookBase",
18
40
  "HookHandler",
41
+ "LogFeedback",
19
42
  "ParameterDefinition",
20
43
  "ParameterType",
21
44
  "Permission",
@@ -24,6 +47,44 @@ __all__ = [
24
47
  "Role",
25
48
  "RoleManager",
26
49
  "SchemaMigrations",
50
+ "SilentFeedback",
51
+ "SQL",
27
52
  "SqlContent",
28
53
  "Upgrader",
29
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())))
pum/changelog.py CHANGED
@@ -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=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