pum 1.2.3__py3-none-any.whl → 1.3.1__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/sql_content.py CHANGED
@@ -1,14 +1,62 @@
1
1
  import logging
2
2
  import re
3
3
  from pathlib import Path
4
+ from typing import Any, Optional
4
5
 
5
6
  import psycopg
6
7
 
7
8
  from .exceptions import PumSqlError
9
+ from . import SQL
8
10
 
9
11
  logger = logging.getLogger(__name__)
10
12
 
11
13
 
14
+ class CursorResult:
15
+ """A simple wrapper to hold cursor results after the cursor is closed.
16
+
17
+ This class provides a cursor-compatible interface for accessing query results
18
+ after the actual database cursor has been closed.
19
+ """
20
+
21
+ def __init__(
22
+ self, results: list | None = None, description: Any | None = None, rowcount: int = 0
23
+ ):
24
+ self._pum_results = results
25
+ self._pum_description = description
26
+ self._pum_rowcount = rowcount
27
+ self._pum_index = 0
28
+
29
+ @property
30
+ def description(self):
31
+ """Return the column description (compatible with cursor.description)."""
32
+ return self._pum_description
33
+
34
+ @property
35
+ def rowcount(self):
36
+ """Return the number of rows (compatible with cursor.rowcount)."""
37
+ return self._pum_rowcount
38
+
39
+ def fetchall(self):
40
+ """Return all results (compatible with cursor.fetchall())."""
41
+ return self._pum_results if self._pum_results is not None else []
42
+
43
+ def fetchone(self):
44
+ """Return the next result (compatible with cursor.fetchone())."""
45
+ if self._pum_results is None or self._pum_index >= len(self._pum_results):
46
+ return None
47
+ result = self._pum_results[self._pum_index]
48
+ self._pum_index += 1
49
+ return result
50
+
51
+ def fetchmany(self, size: int = 1):
52
+ """Return the next `size` results (compatible with cursor.fetchmany())."""
53
+ if self._pum_results is None:
54
+ return []
55
+ results = self._pum_results[self._pum_index : self._pum_index + size]
56
+ self._pum_index += len(results)
57
+ return results
58
+
59
+
12
60
  def sql_chunks_from_file(file: str | Path) -> list[psycopg.sql.SQL]:
13
61
  """Read SQL from a file, remove comments, and split into chunks.
14
62
 
@@ -161,7 +209,7 @@ class SqlContent:
161
209
  sql: The SQL statement to execute or a path to a SQL file.
162
210
 
163
211
  """
164
- if not isinstance(sql, (str, psycopg.sql.SQL, Path)):
212
+ if not isinstance(sql, str | psycopg.sql.SQL | Path):
165
213
  raise PumSqlError(
166
214
  f"SQL must be a string, psycopg.sql.SQL object or a Path object, not {type(sql)}."
167
215
  )
@@ -189,7 +237,7 @@ class SqlContent:
189
237
  *,
190
238
  parameters: dict | None = None,
191
239
  commit: bool = False,
192
- ) -> psycopg.Cursor:
240
+ ) -> CursorResult:
193
241
  """Execute a SQL statement with optional parameters.
194
242
 
195
243
  Args:
@@ -197,27 +245,41 @@ class SqlContent:
197
245
  parameters: Parameters to bind to the SQL statement. Defaults to ().
198
246
  commit: Whether to commit the transaction. Defaults to False.
199
247
 
248
+ Returns:
249
+ CursorResult: Object containing results, description and other cursor info.
250
+
200
251
  """
201
- cursor = connection.cursor()
252
+ with connection.cursor() as cursor:
253
+ for sql_code in self._prepare_sql(parameters):
254
+ try:
255
+ statement = sql_code.as_string(connection)
256
+ except (psycopg.errors.SyntaxError, psycopg.errors.ProgrammingError) as e:
257
+ raise PumSqlError(
258
+ f"SQL preparation failed for the following code: {statement} {e}"
259
+ ) from e
260
+ try:
261
+ logger.log(SQL, f"Executing SQL: {statement}")
262
+ cursor.execute(statement)
263
+ except (psycopg.errors.SyntaxError, psycopg.errors.ProgrammingError) as e:
264
+ raise PumSqlError(
265
+ f"SQL execution failed for the following code: {statement} {e}"
266
+ ) from e
267
+ if commit:
268
+ connection.commit()
202
269
 
203
- for sql_code in self._prepare_sql(parameters):
204
- try:
205
- statement = sql_code.as_string(connection)
206
- except (psycopg.errors.SyntaxError, psycopg.errors.ProgrammingError) as e:
207
- raise PumSqlError(
208
- f"SQL preparation failed for the following code: {statement} {e}"
209
- ) from e
270
+ # Store results before cursor closes
210
271
  try:
211
- logger.debug(f"Executing SQL statement: {statement}")
212
- cursor.execute(statement)
213
- except (psycopg.errors.SyntaxError, psycopg.errors.ProgrammingError) as e:
214
- raise PumSqlError(
215
- f"SQL execution failed for the following code: {statement} {e}"
216
- ) from e
217
- if commit:
218
- connection.commit()
219
-
220
- return cursor
272
+ results = cursor.fetchall()
273
+ description = cursor.description
274
+ rowcount = cursor.rowcount
275
+ except psycopg.ProgrammingError:
276
+ # No results to fetch (e.g., DDL statements)
277
+ results = None
278
+ description = None
279
+ rowcount = 0
280
+
281
+ # Return a CursorResult object with the fetched data
282
+ return CursorResult(results, description, rowcount)
221
283
 
222
284
  def _prepare_sql(self, parameters: dict | None) -> list[psycopg.sql.SQL]:
223
285
  """Prepare SQL for execution.
@@ -234,7 +296,7 @@ class SqlContent:
234
296
 
235
297
  """
236
298
  if isinstance(self.sql, Path):
237
- logger.info(
299
+ logger.debug(
238
300
  f"Checking SQL from file: {self.sql} with parameters: {parameters}",
239
301
  )
240
302
  sql_code = sql_chunks_from_file(self.sql)
pum/upgrader.py CHANGED
@@ -9,6 +9,7 @@ from .pum_config import PumConfig
9
9
  from .exceptions import PumException
10
10
  from .schema_migrations import SchemaMigrations
11
11
  from .sql_content import SqlContent
12
+ from .feedback import Feedback, LogFeedback
12
13
 
13
14
 
14
15
  logger = logging.getLogger(__name__)
@@ -40,7 +41,7 @@ class Upgrader:
40
41
 
41
42
  """
42
43
  self.config = config
43
- self.max_version = packaging.parse(max_version) if max_version else None
44
+ self.max_version = packaging.version.parse(max_version) if max_version else None
44
45
  self.schema_migrations = SchemaMigrations(self.config)
45
46
 
46
47
  def install(
@@ -52,7 +53,10 @@ class Upgrader:
52
53
  roles: bool = False,
53
54
  grant: bool = False,
54
55
  beta_testing: bool = False,
56
+ skip_drop_app: bool = False,
57
+ skip_create_app: bool = False,
55
58
  commit: bool = False,
59
+ feedback: Feedback | None = None,
56
60
  ) -> None:
57
61
  """Installs the given module
58
62
  This will create the schema_migrations table if it does not exist.
@@ -72,11 +76,21 @@ class Upgrader:
72
76
  If True, permissions will be granted to the roles.
73
77
  beta_testing:
74
78
  If True, the module is installed in beta testing mode.
75
- This means that the module will not be able to receive any future updates.
79
+ This means that the module will not be allowed to receive any future updates.
76
80
  We strongly discourage using this for production.
81
+ skip_drop_app:
82
+ If True, drop app handlers will be skipped.
83
+ skip_create_app:
84
+ If True, create app handlers will be skipped.
77
85
  commit:
78
86
  If True, the changes will be committed to the database.
87
+ feedback:
88
+ A Feedback instance to report progress and check for cancellation.
89
+ If None, a LogFeedback instance will be used.
79
90
  """
91
+ if feedback is None:
92
+ feedback = LogFeedback()
93
+
80
94
  if self.schema_migrations.exists(connection):
81
95
  msg = (
82
96
  f"Schema migrations table {self.config.config.pum.migration_table_schema}.pum_migrations already exists. "
@@ -84,35 +98,70 @@ class Upgrader:
84
98
  "Use upgrade() to upgrade the db or start with a clean db."
85
99
  )
86
100
  raise PumException(msg)
101
+
102
+ feedback.report_progress("Creating migrations table...")
87
103
  self.schema_migrations.create(connection, commit=False)
88
104
 
105
+ logger.info("Installing module...")
106
+ feedback.report_progress("Installing module...")
107
+
108
+ # Calculate total steps: drop handlers + all SQL files in changelogs + create handlers + role operations
109
+ drop_handlers = self.config.drop_app_handlers() if not skip_drop_app else []
110
+ changelogs = list(self.config.changelogs(max_version=max_version))
111
+ create_handlers = self.config.create_app_handlers() if not skip_create_app else []
112
+
113
+ total_changelog_files = sum(len(changelog.files()) for changelog in changelogs)
114
+
115
+ # Count role operations
116
+ role_steps = 0
117
+ if roles or grant:
118
+ role_manager = self.config.role_manager()
119
+ role_steps += len(role_manager.roles) # create roles
120
+ if grant:
121
+ role_steps += len(role_manager.roles) # grant permissions
122
+
123
+ total_steps = len(drop_handlers) + total_changelog_files + len(create_handlers) + role_steps
124
+ feedback.set_total_steps(total_steps)
125
+
89
126
  if roles or grant:
127
+ feedback.report_progress("Creating roles...")
90
128
  self.config.role_manager().create_roles(
91
- connection=connection, grant=False, commit=False
129
+ connection=connection, grant=False, commit=False, feedback=feedback
92
130
  )
93
131
 
94
- for pre_hook in self.config.pre_hook_handlers():
95
- pre_hook.execute(connection=connection, commit=False, parameters=parameters)
132
+ if not skip_drop_app:
133
+ for drop_app_hook in drop_handlers:
134
+ if feedback.is_cancelled():
135
+ raise PumException("Installation cancelled by user")
136
+ feedback.increment_step()
137
+ feedback.report_progress(
138
+ f"Executing drop app handler: {drop_app_hook.file or 'SQL code'}"
139
+ )
140
+ drop_app_hook.execute(connection=connection, commit=False, parameters=parameters)
96
141
 
97
- parameters_literals = SqlContent.prepare_parameters(parameters)
98
142
  last_changelog = None
99
- for changelog in self.config.changelogs(max_version=max_version):
143
+ for changelog in changelogs:
144
+ if feedback.is_cancelled():
145
+ raise PumException("Installation cancelled by user")
100
146
  last_changelog = changelog
101
- changelog_files = changelog.apply(
102
- connection, commit=False, parameters=parameters_literals
103
- )
104
- changelog_files = [str(f) for f in changelog_files]
105
- self.schema_migrations.set_baseline(
106
- connection=connection,
107
- version=changelog.version,
108
- beta_testing=beta_testing,
147
+ changelog.apply(
148
+ connection,
109
149
  commit=False,
110
- changelog_files=changelog_files,
111
150
  parameters=parameters,
151
+ schema_migrations=self.schema_migrations,
152
+ beta_testing=beta_testing,
153
+ feedback=feedback,
112
154
  )
113
155
 
114
- for post_hook in self.config.post_hook_handlers():
115
- post_hook.execute(connection=connection, commit=False, parameters=parameters)
156
+ if not skip_create_app:
157
+ for create_app_hook in create_handlers:
158
+ if feedback.is_cancelled():
159
+ raise PumException("Installation cancelled by user")
160
+ feedback.increment_step()
161
+ feedback.report_progress(
162
+ f"Executing create app handler: {create_app_hook.file or 'SQL code'}"
163
+ )
164
+ create_app_hook.execute(connection=connection, commit=False, parameters=parameters)
116
165
 
117
166
  logger.info(
118
167
  "Installed %s.pum_migrations table and applied changelogs up to version %s",
@@ -121,9 +170,14 @@ class Upgrader:
121
170
  )
122
171
 
123
172
  if grant:
124
- self.config.role_manager().grant_permissions(connection=connection, commit=False)
173
+ feedback.report_progress("Granting permissions...")
174
+ self.config.role_manager().grant_permissions(
175
+ connection=connection, commit=False, feedback=feedback
176
+ )
125
177
 
126
178
  if commit:
179
+ feedback.lock_cancellation()
180
+ feedback.report_progress("Committing changes...")
127
181
  connection.commit()
128
182
  logger.info("Changes committed to the database.")
129
183
 
@@ -134,6 +188,8 @@ class Upgrader:
134
188
  *,
135
189
  parameters: dict | None = None,
136
190
  grant: bool = True,
191
+ skip_drop_app: bool = False,
192
+ skip_create_app: bool = False,
137
193
  ) -> None:
138
194
  """Install demo data for the module.
139
195
 
@@ -142,14 +198,17 @@ class Upgrader:
142
198
  name: The name of the demo data to install.
143
199
  parameters: The parameters to pass to the demo data SQL.
144
200
  grant: If True, grant permissions to the roles after installing the demo data. Default is True.
201
+ skip_drop_app: If True, skip drop app handlers during demo data installation. Default is False.
202
+ skip_create_app: If True, skip create app handlers during demo data installation. Default is False.
145
203
  """
146
204
  if name not in self.config.demo_data():
147
205
  raise PumException(f"Demo data '{name}' not found in the configuration.")
148
206
 
149
207
  logger.info(f"Installing demo data {name}")
150
208
 
151
- for pre_hook in self.config.pre_hook_handlers():
152
- pre_hook.execute(connection=connection, commit=False, parameters=parameters)
209
+ if not skip_drop_app:
210
+ for drop_app_hook in self.config.drop_app_handlers():
211
+ drop_app_hook.execute(connection=connection, commit=False, parameters=parameters)
153
212
 
154
213
  connection.commit()
155
214
 
@@ -164,8 +223,9 @@ class Upgrader:
164
223
 
165
224
  connection.commit()
166
225
 
167
- for post_hook in self.config.post_hook_handlers():
168
- post_hook.execute(connection=connection, commit=False, parameters=parameters)
226
+ if not skip_create_app:
227
+ for create_app_hook in self.config.create_app_handlers():
228
+ create_app_hook.execute(connection=connection, commit=False, parameters=parameters)
169
229
 
170
230
  connection.commit()
171
231
 
@@ -175,3 +235,207 @@ class Upgrader:
175
235
  connection.commit()
176
236
 
177
237
  logger.info("Demo data '%s' installed successfully.", name)
238
+
239
+ def upgrade(
240
+ self,
241
+ connection: psycopg.Connection,
242
+ *,
243
+ parameters: dict | None = None,
244
+ max_version: str | packaging.version.Version | None = None,
245
+ beta_testing: bool = False,
246
+ force: bool = False,
247
+ skip_drop_app: bool = False,
248
+ skip_create_app: bool = False,
249
+ roles: bool = False,
250
+ grant: bool = False,
251
+ feedback: Feedback | None = None,
252
+ ) -> None:
253
+ """Upgrades the given module
254
+ The changelogs are applied in the order they are found in the directory.
255
+
256
+ Args:
257
+ connection:
258
+ The database connection to use for the upgrade.
259
+ parameters:
260
+ The parameters to pass for the migration.
261
+ max_version:
262
+ The maximum version to apply. If None, all versions are applied.
263
+ beta_testing:
264
+ If True, the module is upgraded in beta testing mode.
265
+ This means that the module will not be allowed to receive any future updates.
266
+ We strongly discourage using this for production.
267
+ force:
268
+ If True, allow upgrading a module that is installed in beta testing mode.
269
+ skip_drop_app:
270
+ If True, drop app handlers will be skipped.
271
+ skip_create_app:
272
+ If True, create app handlers will be skipped.
273
+ roles:
274
+ If True, roles will be created.
275
+ grant:
276
+ If True, permissions will be granted to the roles.
277
+ feedback:
278
+ A Feedback instance to report progress and check for cancellation.
279
+ If None, a LogFeedback instance will be used.
280
+ """
281
+ if feedback is None:
282
+ feedback = LogFeedback()
283
+
284
+ if not self.schema_migrations.exists(connection):
285
+ msg = (
286
+ f"Schema migrations table {self.config.config.pum.migration_table_schema}.pum_migrations does not exist. "
287
+ "This means that the module is not installed yet. Use install() to install the module."
288
+ )
289
+ raise PumException(msg)
290
+
291
+ migration_details = self.schema_migrations.migration_details(connection)
292
+ installed_beta_testing = bool(migration_details.get("beta_testing", False))
293
+ if installed_beta_testing and not force:
294
+ msg = (
295
+ "This module is installed in beta testing mode, upgrades are disabled. "
296
+ "Re-run with force=True (or --force in the CLI) if you really want to upgrade anyway."
297
+ )
298
+ raise PumException(msg)
299
+
300
+ effective_beta_testing = beta_testing or installed_beta_testing
301
+
302
+ logger.info("Starting upgrade process...")
303
+ feedback.report_progress("Starting upgrade...")
304
+
305
+ # Calculate total steps: drop handlers + applicable changelog files + create handlers
306
+ drop_handlers = self.config.drop_app_handlers() if not skip_drop_app else []
307
+ changelogs = list(self.config.changelogs(max_version=max_version))
308
+ create_handlers = self.config.create_app_handlers() if not skip_create_app else []
309
+
310
+ # First pass: determine applicable changelogs
311
+ applicable_changelogs = []
312
+ for changelog in changelogs:
313
+ if changelog.version <= self.schema_migrations.baseline(connection):
314
+ if not changelog.is_applied(
315
+ connection=connection, schema_migrations=self.schema_migrations
316
+ ):
317
+ msg = (
318
+ f"Changelog version {changelog.version} is lower than or equal to the current version "
319
+ f"{self.schema_migrations.current_version(connection)} but not applied. "
320
+ "This indicates a problem with the database state."
321
+ )
322
+ logger.error(msg)
323
+ raise PumException(msg)
324
+ logger.debug("Changelog version %s already applied, skipping.", changelog.version)
325
+ continue
326
+ applicable_changelogs.append(changelog)
327
+
328
+ total_changelog_files = sum(len(changelog.files()) for changelog in applicable_changelogs)
329
+
330
+ # Count role operations
331
+ role_steps = 0
332
+ if roles or grant:
333
+ role_manager = self.config.role_manager()
334
+ role_steps += len(role_manager.roles) # create roles
335
+ if grant:
336
+ role_steps += len(role_manager.roles) # grant permissions
337
+
338
+ total_steps = len(drop_handlers) + total_changelog_files + len(create_handlers) + role_steps
339
+ feedback.set_total_steps(total_steps)
340
+
341
+ if not skip_drop_app:
342
+ for drop_app_hook in drop_handlers:
343
+ if feedback.is_cancelled():
344
+ raise PumException("Upgrade cancelled by user")
345
+ feedback.increment_step()
346
+ feedback.report_progress(
347
+ f"Executing drop app handler: {drop_app_hook.file or 'SQL code'}"
348
+ )
349
+ drop_app_hook.execute(connection=connection, commit=False, parameters=parameters)
350
+
351
+ for changelog in applicable_changelogs:
352
+ if feedback.is_cancelled():
353
+ raise PumException("Upgrade cancelled by user")
354
+ changelog.apply(
355
+ connection,
356
+ commit=False,
357
+ parameters=parameters,
358
+ schema_migrations=self.schema_migrations,
359
+ beta_testing=effective_beta_testing,
360
+ feedback=feedback,
361
+ )
362
+
363
+ if not skip_create_app:
364
+ for create_app_hook in create_handlers:
365
+ if feedback.is_cancelled():
366
+ raise PumException("Upgrade cancelled by user")
367
+ feedback.increment_step()
368
+ feedback.report_progress(
369
+ f"Executing create app handler: {create_app_hook.file or 'SQL code'}"
370
+ )
371
+ create_app_hook.execute(connection=connection, commit=False, parameters=parameters)
372
+
373
+ if roles or grant:
374
+ feedback.report_progress("Creating roles...")
375
+ self.config.role_manager().create_roles(
376
+ connection=connection, grant=False, commit=False, feedback=feedback
377
+ )
378
+ if grant:
379
+ feedback.report_progress("Granting permissions...")
380
+ self.config.role_manager().grant_permissions(
381
+ connection=connection, commit=False, feedback=feedback
382
+ )
383
+
384
+ feedback.lock_cancellation()
385
+ feedback.report_progress("Committing changes...")
386
+ connection.commit()
387
+ logger.info("Upgrade completed and changes committed to the database.")
388
+
389
+ def uninstall(
390
+ self,
391
+ connection: psycopg.Connection,
392
+ *,
393
+ parameters: dict | None = None,
394
+ commit: bool = True,
395
+ feedback: Feedback | None = None,
396
+ ) -> None:
397
+ """Uninstall the module by executing uninstall hooks.
398
+
399
+ Args:
400
+ connection: The database connection to use for the uninstall.
401
+ parameters: The parameters to pass to the uninstall hooks.
402
+ commit: If True, the changes will be committed to the database. Default is True.
403
+ feedback: A Feedback instance to report progress and check for cancellation.
404
+ If None, a LogFeedback instance will be used.
405
+
406
+ Raises:
407
+ PumException: If no uninstall hooks are defined in the configuration.
408
+ """
409
+ if feedback is None:
410
+ feedback = LogFeedback()
411
+
412
+ uninstall_hooks = self.config.uninstall_handlers()
413
+
414
+ if not uninstall_hooks:
415
+ raise PumException(
416
+ "No uninstall hooks defined in the configuration. "
417
+ "Add 'uninstall' section to your .pum.yaml file to define uninstall hooks."
418
+ )
419
+
420
+ logger.info("Uninstalling module...")
421
+ feedback.report_progress("Starting uninstall...")
422
+
423
+ # Set total steps for progress tracking
424
+ total_steps = len(uninstall_hooks)
425
+ feedback.set_total_steps(total_steps)
426
+
427
+ for uninstall_hook in uninstall_hooks:
428
+ if feedback.is_cancelled():
429
+ raise PumException("Uninstall cancelled by user")
430
+ feedback.increment_step()
431
+ feedback.report_progress(
432
+ f"Executing uninstall handler: {uninstall_hook.file or 'SQL code'}"
433
+ )
434
+ uninstall_hook.execute(connection=connection, commit=False, parameters=parameters)
435
+
436
+ if commit:
437
+ feedback.lock_cancellation()
438
+ feedback.report_progress("Committing changes...")
439
+ connection.commit()
440
+ logger.info("Uninstall completed and changes committed to the database.")
441
+ logger.info("Uninstall completed and changes committed to the database.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pum
3
- Version: 1.2.3
3
+ Version: 1.3.1
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
- - **Migration Hooks**: Define custom hooks to execute additional SQL or Python code before or after migrations. This feature allows you to isolate data (table) code from application code (such as views and triggers), ensuring a clear separation of concerns and more maintainable database structures.
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
 
@@ -0,0 +1,25 @@
1
+ pum/__init__.py,sha256=XJWOk-gUJZgh68L1Sttg9AcnnYN0Ig_nbptufVbvtuU,2899
2
+ pum/changelog.py,sha256=UikQ3KtV8e5BZXttjWmvR3nL3-uoLEzZ489k4AtHfR0,6008
3
+ pum/checker.py,sha256=s6mYLXgugooX1ii8O21jp5oAaafY8bStEh4lKvwr1mc,25091
4
+ pum/cli.py,sha256=skNG8xvcAderJmzxnuzAfnJmi7CEYASwvagvltn6j8o,19563
5
+ pum/config_model.py,sha256=2zmT8QP8W1_NOLll7Dp9_nCQa_RHiGGedeWlumVogRI,7944
6
+ pum/connection.py,sha256=lGW8vUyeh_HS6xX0kYeEANbUJ9Aw3cufz46fGjvuNvk,1119
7
+ pum/dependency_handler.py,sha256=1A6720Tv0rBME-gJVLn9PYK46YPXLMD-r9hpNNX8ApY,6965
8
+ pum/dumper.py,sha256=FFRidaOg3ENePNZ61TGbnAdp0TItvWbA4j292Xlf9bA,3878
9
+ pum/exceptions.py,sha256=h1TEPHI_hSbLLQc6zYfTpFexczv1iKIN7OuTsLTN590,1439
10
+ pum/feedback.py,sha256=pbZYdhsocjmJbjcdUpvYhI8XLio15htyVg-Cay6HHdE,3810
11
+ pum/hook.py,sha256=G-qI1J4erWPWw5mRa-BHFVtA9yYTGcTK3aYmqaoKep8,12528
12
+ pum/info.py,sha256=75Fr4Bn-ARe36aK8KV31MSCNWDkAdiMgJ-4IvMF73zU,1346
13
+ pum/parameter.py,sha256=e7Lm5fp2Xg4SEkIDmbxSyNTsYvELCkeyPUeS6CCs6EA,2646
14
+ pum/pum_config.py,sha256=k-YF2nL73HcfybjV09G042Xuiu8nRWSxAI43JyiCTsg,14952
15
+ pum/report_generator.py,sha256=upv6gpVZ_kk7O6IhcO7LWMlPHg_u_V0wx_LxQebMp-c,38908
16
+ pum/role_manager.py,sha256=_LG5LR8osc5bVQVb-AKGU3wvXBrIe9J3IV1ECzhiwnA,15991
17
+ pum/schema_migrations.py,sha256=-yR84KkG1mLY5yeEWzidNPam1hrE_1mMC5KJaHOUxiA,16304
18
+ pum/sql_content.py,sha256=BY5XMS713sIOUT4xLHByOzQhxYItucvkCFEyzmwSTR4,12969
19
+ pum/upgrader.py,sha256=skGfwfuAb0TLMqCPjRbcJNf1VKmekaaQLm_C-abs61E,18353
20
+ pum-1.3.1.dist-info/licenses/LICENSE,sha256=2ylvL381vKOhdO-w6zkrOxe9lLNBhRQpo9_0EbHC_HM,18046
21
+ pum-1.3.1.dist-info/METADATA,sha256=xcbQ-mlS84gzT_6xj2tzxj4JeA2LPf6PowfHY83CfYA,3236
22
+ pum-1.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
23
+ pum-1.3.1.dist-info/entry_points.txt,sha256=U6dmxSpKs1Pe9vWiR29VPhJMDjrmZeJCSxvfLGR8BD4,36
24
+ pum-1.3.1.dist-info/top_level.txt,sha256=ddiI4HLBhY6ql-NNm0Ez0JhoOHdWDIzrHeCdHmmagcc,4
25
+ pum-1.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,22 +0,0 @@
1
- pum/__init__.py,sha256=P-NHd6_SYpk9aypefLI62QCZ3f5APOMCwSzrFFCKAew,759
2
- pum/changelog.py,sha256=yDc5swmMd5gb2vCEAlenoq5gs-ZEGc4uXicBtiGxkOk,3692
3
- pum/checker.py,sha256=GT2v7793HP1g94dv0mL6CHtQfblQwAyeFHEWCy44lkc,14379
4
- pum/cli.py,sha256=HGt5_aqSk8hKdV3xYKNJFMEcZc_3BULWhvX-6wM_2EM,15046
5
- pum/config_model.py,sha256=9jGuk8PC3JLS-VT7SNLF-WiVtaLZQi_24rPxt0zekTQ,6933
6
- pum/dependency_handler.py,sha256=AVeemsh6zUumRJKbRLRwP_FRC0x3K16TJiK2i9ogvn0,3861
7
- pum/dumper.py,sha256=EJZ8T44JM0GKgdqw1ENOfhZ-RI89OQ4DNdoTZKtLdEw,3404
8
- pum/exceptions.py,sha256=xyzzY4ht1nKfrVt59Giulflpmu83nJhxoTygrqiqPlw,1137
9
- pum/hook.py,sha256=5MrVa6Xr0o28RfsXylGDatlM_vOKfKtGJmhYx8crC94,9541
10
- pum/info.py,sha256=CGj-Lt4Y2l2ymAl3OFqCWfJD5xZF4aaUSztAiSKwgE4,1395
11
- pum/parameter.py,sha256=qdbWk3WZc419AW-qwGMxlgc-7GEhdwIoPBnDk6UsVZU,2485
12
- pum/pum_config.py,sha256=NkvmlnqLQVbrfnmGkbPAveWyjlyxyFdRSW2zwpup9fI,11349
13
- pum/role_manager.py,sha256=iEmznB09YAmmkIFh1Ar8nNdy-ZofO7R3J0VdjtLVyp8,10293
14
- pum/schema_migrations.py,sha256=rUn14aNkt8nw-69cCgklbtveanq6CrNdKKrc458cGIc,10491
15
- pum/sql_content.py,sha256=-0h3caJlvkyEjZwjPVrKh5ZYaDctC-5lklBvZ-zgRzA,10620
16
- pum/upgrader.py,sha256=h6a3Jeem6PQX-abWf7cBqYSaoSUXhuNBj6aYKrBxscM,6665
17
- pum-1.2.3.dist-info/licenses/LICENSE,sha256=2ylvL381vKOhdO-w6zkrOxe9lLNBhRQpo9_0EbHC_HM,18046
18
- pum-1.2.3.dist-info/METADATA,sha256=GaGg7gKTmj86gLLDE6JBmYN2_a03kUMbVJWsdpTTP3g,3138
19
- pum-1.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
- pum-1.2.3.dist-info/entry_points.txt,sha256=U6dmxSpKs1Pe9vWiR29VPhJMDjrmZeJCSxvfLGR8BD4,36
21
- pum-1.2.3.dist-info/top_level.txt,sha256=ddiI4HLBhY6ql-NNm0Ez0JhoOHdWDIzrHeCdHmmagcc,4
22
- pum-1.2.3.dist-info/RECORD,,