SQLDbWrpr 5.0.1__tar.gz → 5.1.1__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.
@@ -0,0 +1,363 @@
1
+ Metadata-Version: 2.4
2
+ Name: SQLDbWrpr
3
+ Version: 5.1.1
4
+ Summary: Wrapper for importing CSV and Text files into MySQL and Postgress
5
+ License-Expression: MIT
6
+ License-File: LICENSE.txt
7
+ Author: Hendrik du Toit
8
+ Author-email: hendrik@brightedge.co.za
9
+ Maintainer: Hendrik du Toit
10
+ Maintainer-email: hendrikdt@citiqprepaid.co.za
11
+ Requires-Python: >=3.10,<3.15
12
+ Classifier: Development Status :: 6 - Mature
13
+ Classifier: Framework :: Pydantic :: 2
14
+ Classifier: Framework :: Pytest
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Operating System :: iOS
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Dist: csvwrpr (>=1.16.1,<2.0.0)
26
+ Requires-Dist: fixdate (>=2.1.0,<3.0.0)
27
+ Requires-Dist: mysql-connector (>=2.2.9,<3.0.0)
28
+ Requires-Dist: psycopg[binary] (>=3.2.0,<4.0.0)
29
+ Requires-Dist: pydantic-settings (>=2.12.0,<3.0.0)
30
+ Requires-Dist: sqlalchemy (>=2.0.0,<3.0.0)
31
+ Project-URL: Homepage, https://github.com/BrightEdgeeServices/SqlDbWrpr
32
+ Project-URL: Issues, https://github.com/BrightEdgeeServices/SqlDbWrpr/issues
33
+ Project-URL: Repository, https://github.com/BrightEdgeeServices/SqlDbWrpr.git
34
+ Project-URL: changelog, https://github.com/BrightEdgeeServices/SqlDbWrpr/releases
35
+ Description-Content-Type: text/markdown
36
+
37
+ # SqlDbWrpr
38
+
39
+ | **Category** | **Status' and Links** |
40
+ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | General | [![][general_maintenance_y_img]][general_maintenance_y_lnk] [![][general_semver_pic]][general_semver_link] [![][general_license_img]][general_license_lnk] |
42
+ | CD/CI | [![][gh_tests_img]][gh_tests_lnk] [![][cicd_codestyle_img]][cicd_codestyle_lnk] [![][cicd_pre_commit_img]][cicd_pre_commit_lnk] [![][codecov_img]][codecov_lnk] [![][gh_doc_img]][gh_doc_lnk] |
43
+ | PyPI | [![][pypi_release_img]][pypi_release_lnk] [![][pypi_py_versions_img]][pypi_py_versions_lnk] [![][pypi_format_img]][pypi_format_lnk] [![][pypi_downloads_img]][pypi_downloads_lnk] |
44
+ | Github | [![][gh_issues_img]][gh_issues_lnk] [![][gh_language_img]][gh_language_lnk] [![][gh_last_commit_img]][gh_last_commit_lnk] [![][gh_deployment_img]][gh_deployment_lnk] |
45
+
46
+ ## Short description
47
+
48
+ SqlDbWrpr is a Python wrapper that streamlines schema creation plus CSV import/export workflows for SQL backends.
49
+
50
+ ## Module Overview
51
+
52
+ SqlDbWrpr is a Python utility for creating SQL database schemas and moving CSV data into and out of those schemas. It currently provides wrappers for MySQL and PostgreSQL.
53
+
54
+ Schemas can be supplied in two ways:
55
+
56
+ 1. A legacy `db_structure` dictionary.
57
+ 2. SQLAlchemy metadata, either directly through `p_sqlalchemy_metadata` or through `p_sqlalchemy_base.metadata`.
58
+
59
+ When both are supplied, `p_db_structure` takes precedence for backward compatibility. If no supported schema source is supplied, `SchemaSourceError` is raised.
60
+
61
+ ### Key Features
62
+
63
+ - **Schema Management**: Create databases, tables, primary keys, foreign keys, and indexes from a legacy dictionary or SQLAlchemy metadata.
64
+ - **Data Import/Export**:
65
+ - Import CSV data from files or in-memory rows, including single-volume and numbered multi-volume files.
66
+ - Export full tables or custom SQL query results to CSV, including optional multi-volume exports.
67
+ - **Database Support**: Includes MySQL and PostgreSQL wrappers with dialect-specific SQL rendering.
68
+ - **User and Permission Management**: Create and delete MySQL users or PostgreSQL login roles, and grant backend-specific database or table rights.
69
+ - **Batch Processing**: Configure import batch sizes for larger CSV loads.
70
+
71
+ ### Project Structure
72
+
73
+ - `src/sqldbwrpr/`: Core library implementation, including MySQL and PostgreSQL wrappers.
74
+ - `tests/`: Unit and integration-oriented test coverage for wrapper behaviour.
75
+ - `scripts/`: SQL setup/bootstrap assets used for database initialization.
76
+ - Root automation scripts (`*.ps1`) and CI configuration under `.github/workflows/` support setup and delivery.
77
+
78
+ ## Getting Started
79
+
80
+ ### Installation
81
+
82
+ ```bash
83
+ pip install SqlDbWrpr
84
+ ```
85
+
86
+ ### Running the Tests
87
+
88
+ The focused database tests use temporary MySQL 8.0 and PostgreSQL 16 Docker containers. Ensure Docker is running, then install the development dependencies and run the test suite:
89
+
90
+ ```powershell
91
+ poetry install --with dev
92
+ poetry run pytest
93
+ ```
94
+
95
+ The test fixtures wait for each database to become ready and remove the temporary containers after the test session.
96
+
97
+ ### Quick Start With A Legacy Structure
98
+
99
+ ```python
100
+ from sqldbwrpr.sqldbwrpr import MySQL
101
+
102
+ field_defaults = {
103
+ "PrimaryKey": ["", ""],
104
+ "FKey": [],
105
+ "Index": [],
106
+ "NN": "",
107
+ "B": "",
108
+ "UN": "",
109
+ "ZF": "",
110
+ "AI": "",
111
+ "G": "",
112
+ "DEF": "",
113
+ }
114
+
115
+ db_structure = {
116
+ "Users": {
117
+ "ID": {
118
+ "Type": ["int"],
119
+ "Params": {
120
+ **field_defaults,
121
+ "PrimaryKey": ["Y", "A"],
122
+ "NN": "Y",
123
+ "AI": "Y",
124
+ },
125
+ "Possible Values": "",
126
+ "Comment": "",
127
+ },
128
+ "Username": {
129
+ "Type": ["varchar", 50],
130
+ "Params": {**field_defaults, "NN": "Y"},
131
+ "Possible Values": "",
132
+ "Comment": "",
133
+ },
134
+ }
135
+ }
136
+
137
+ db = MySQL(
138
+ p_host_name="localhost",
139
+ p_user_name="root",
140
+ p_password="yourpassword",
141
+ p_db_name="my_database",
142
+ p_db_structure=db_structure,
143
+ p_recreate_db=True,
144
+ )
145
+
146
+ db.import_csv("Users", p_csv_db=[("Username",), ("alice",)], p_vol_type="Single")
147
+ db.export_to_csv("exported_users.csv", "Users")
148
+ ```
149
+
150
+ ### Quick Start With SQLAlchemy Metadata
151
+
152
+ ```python
153
+ from sqlalchemy import Column
154
+ from sqlalchemy import Integer
155
+ from sqlalchemy import MetaData
156
+ from sqlalchemy import String
157
+ from sqlalchemy import Table
158
+
159
+ from sqldbwrpr.sqldbwrpr import PostgreSQL
160
+
161
+ metadata = MetaData()
162
+ Table(
163
+ "Users",
164
+ metadata,
165
+ Column("ID", Integer, primary_key=True, autoincrement=True),
166
+ Column("Username", String(50), nullable=False),
167
+ )
168
+
169
+ db = PostgreSQL(
170
+ p_host_name="localhost",
171
+ p_user_name="postgres",
172
+ p_password="yourpassword",
173
+ p_db_name="my_database",
174
+ p_sqlalchemy_metadata=metadata,
175
+ p_recreate_db=True,
176
+ )
177
+
178
+ db.import_csv("Users", p_csv_db=[("Username",), ("alice",)], p_vol_type="Single")
179
+ ```
180
+
181
+ ## Function Reference
182
+
183
+ This section documents the callable API in `src/sqldbwrpr/sqldbwrpr.py`.
184
+
185
+ ### `SchemaSourceError`
186
+
187
+ - `SchemaSourceError(ValueError)`: Raised when no usable schema source is provided to initialize a wrapper (`p_db_structure`, `p_sqlalchemy_metadata`, or `p_sqlalchemy_base.metadata`).
188
+
189
+ ### `SQLDbWrpr` (base wrapper)
190
+
191
+ - `__init__(p_host_name, p_user_name, p_password, p_recreate_db, p_db_name, p_db_structure, p_sqlalchemy_base, p_sqlalchemy_metadata, p_batch_size, p_bar_len, p_msg_width, p_verbose, p_db_port, p_ssl_ca, p_ssl_key, p_ssl_cert)`
192
+ - Initializes shared wrapper state, resolves schema source, configures import/export behavior, and precomputes char vs non-char field maps.
193
+ - Schema resolution order: explicit `p_db_structure` first, then SQLAlchemy metadata, else raises `SchemaSourceError`.
194
+ - `close()`
195
+ - Closes the active DB connection if present.
196
+ - `build_column_sql(p_field_name, p_field_type, p_field_params, p_field_comment)`
197
+ - Builds the column fragment for `CREATE TABLE` in the active dialect (base implementation is MySQL-oriented).
198
+ - Applies `AUTO_INCREMENT`, `UNSIGNED`, `NOT NULL`, `ZEROFILL`, defaults, and comments based on field parameters.
199
+ - `build_index_sql(p_table_name, p_idx_name, p_idx_fields, p_unique=False)`
200
+ - Builds an index clause with sort directions from legacy index metadata.
201
+ - `build_insert_sql(p_table_name, p_header, p_replace=False)`
202
+ - Builds an `INSERT` or `REPLACE` statement with backend parameter placeholders.
203
+ - `param_placeholder()`
204
+ - Returns the DB-API parameter token used by execution (`%s`).
205
+ - `quote_identifier(p_identifier)`
206
+ - Quotes SQL identifiers when a backend quote character is configured; otherwise returns plain identifier text.
207
+ - `quote_identifier_list(p_identifiers)`
208
+ - Quotes and joins a list of identifiers using commas.
209
+ - `render_default_sql(p_field_type, p_default_value)`
210
+ - Renders a `DEFAULT` clause, quoting string-like defaults for char/varchar types.
211
+ - `render_field_type(p_field_type, p_field_params)`
212
+ - Renders legacy type arrays into SQL type declarations (e.g., `varchar(n)`, `decimal(p,s)`).
213
+ - `create_db()`
214
+ - Base database recreation flow: drops an existing DB and creates/selects a fresh one.
215
+ - `create_tables()`
216
+ - Validates legacy schema metadata and creates tables, keys, indexes, and constraints in dependency order.
217
+ - Splits table creation and post-create operations where backend rules require deferred statements.
218
+ - `create_users(p_admin_user, p_new_users)`
219
+ - Creates MySQL users if they do not already exist.
220
+ - `delete_users(p_admin_user, p_del_users)`
221
+ - Drops MySQL users that exist in `mysql.user`.
222
+ - `_err_broken_rec(p_sql_str, p_csv_db_slice)`
223
+ - Retry helper used during imports to isolate/log failing rows and stop on first unrecoverable record.
224
+ - `export_to_csv(p_csv_path, p_table_name, p_delimiter="|", p_strip_chars="", p__vol_size=0, p_sql_query="")`
225
+ - Exports table/query results to CSV in single-file or multi-volume mode.
226
+ - Multi-volume mode chunks output by record count and generates numbered files.
227
+ - `get_db_field_types()`
228
+ - Populates per-table lists of character vs non-character columns for type-sensitive import handling.
229
+ - `grant_rights(p_admin_user, p_user_rights)`
230
+ - Grants MySQL rights and corresponding grant-option privileges according to supplied rights tuples.
231
+ - `import_csv(p_table_name, p_csv_file_name="", p_key="", p_header="", p_del_head=False, p_csv_db="", p_csv_corr_str_file_name="", p_vol_type="Multi", p_verbose=False, p_replace=False)`
232
+ - Imports CSV data from file(s) or in-memory rows.
233
+ - Supports single- and multi-volume input, optional header override/removal, correction-string preprocessing, type conversion, date normalization, and batched insert/replace.
234
+ - `import_and_split_csv(p_split_struct, p_data, p_header="", p_insert_header=False, p_verbose=False, p_debug=False)`
235
+ - Splits an input dataset into multiple destination table payloads using declarative field mappings and transform commands, then imports each generated dataset.
236
+ - `from_sqlalchemy_metadata(p_sqlalchemy_metadata)` (`@classmethod`)
237
+ - Converts SQLAlchemy `MetaData` (sorted tables) to the legacy `db_structure` dictionary.
238
+ - `resolve_db_structure(p_db_structure=None, p_sqlalchemy_base=None, p_sqlalchemy_metadata=None)` (`@staticmethod`)
239
+ - Central schema source resolver used by constructors; raises `SchemaSourceError` when none are provided.
240
+ - `_action_to_legacy_code(p_action)` (`@staticmethod`)
241
+ - Maps SQLAlchemy FK action text (e.g., `CASCADE`, `SET NULL`) to legacy action codes (`C`, `N`, etc.).
242
+ - `_build_default_field_params()` (`@staticmethod`)
243
+ - Produces the baseline legacy field-parameter block used during SQLAlchemy conversion.
244
+ - `_column_type_to_legacy(p_column)` (`@staticmethod`)
245
+ - Converts a SQLAlchemy column type object into the legacy type-array format.
246
+ - `_column_to_legacy_field(p_column)` (`@staticmethod`)
247
+ - Converts one SQLAlchemy column into a legacy field definition, including nullability, autoincrement, defaults, and comments.
248
+ - `_set_foreign_keys(p_table, p_table_structure)` (`@staticmethod`)
249
+ - Writes legacy foreign-key metadata onto converted table fields.
250
+ - `_set_indexes(p_table, p_table_structure)` (`@staticmethod`)
251
+ - Writes legacy index metadata for converted table fields.
252
+ - `_set_primary_key(p_table, p_table_structure)` (`@staticmethod`)
253
+ - Marks primary-key columns in converted legacy table definitions.
254
+ - `_table_to_legacy_structure(p_table)` (`@staticmethod`)
255
+ - Converts an entire SQLAlchemy table into a legacy table structure and enriches it with keys/indexes.
256
+ - `_print_err_msg(p_err, p_msg="")` (`@staticmethod`)
257
+ - Formats and prints database error details before termination paths.
258
+
259
+ ### `MySQL(SQLDbWrpr)`
260
+
261
+ - `__init__(p_host_name, p_user_name, p_password, p_user_rights, p_recreate_db, p_db_name, p_db_structure, p_sqlalchemy_base, p_sqlalchemy_metadata, p_batch_size, p_bar_len, p_msg_width, p_verbose, p_admin_username, p_admin_user_password, p_db_port, **kwargs)`
262
+ - Opens a MySQL connection and cursor, optionally recreates database/tables, or selects the configured DB.
263
+ - Includes fallback logic to create and grant rights to missing users when valid admin credentials and rights data are supplied.
264
+
265
+ ### `PostgreSQL(SQLDbWrpr)`
266
+
267
+ - `__init__(p_host_name, p_user_name, p_password, p_recreate_db, p_db_name, p_db_structure, p_sqlalchemy_base, p_sqlalchemy_metadata, p_batch_size, p_bar_len, p_msg_width, p_verbose, p_db_port, p_maintenance_db, **kwargs)`
268
+ - Configures PostgreSQL-specific behavior (quoted identifiers, non-inline indexes), opens connection, and optionally recreates DB/tables.
269
+ - `build_column_sql(p_field_name, p_field_type, p_field_params, p_field_comment)`
270
+ - PostgreSQL-specific column SQL renderer; handles non-AI `NOT NULL` and defaults.
271
+ - `build_index_sql(p_table_name, p_idx_name, p_idx_fields, p_unique=False)`
272
+ - Builds PostgreSQL `CREATE INDEX`/`CREATE UNIQUE INDEX` statements.
273
+ - `build_insert_sql(p_table_name, p_header, p_replace=False)`
274
+ - Builds PostgreSQL `INSERT`; when `p_replace=True`, builds `ON CONFLICT` upsert/no-op behavior from primary-key metadata.
275
+ - `create_db()`
276
+ - PostgreSQL DB recreation flow via `pg_database` lookup, optional forced drop, create, and reconnect.
277
+ - `create_users(p_admin_user, p_new_users)`
278
+ - Creates PostgreSQL login roles that do not already exist, using the supplied passwords.
279
+ - `delete_users(p_admin_user, p_del_users)`
280
+ - Drops PostgreSQL roles that currently exist.
281
+ - `grant_rights(p_admin_user, p_user_rights)`
282
+ - Grants database-level rights when the table target is `*`; otherwise grants table-level rights. PostgreSQL ignores the host field in each rights entry.
283
+ - `render_default_sql(p_field_type, p_default_value)`
284
+ - Renders PostgreSQL defaults with proper escaping for string values.
285
+ - `render_field_type(p_field_type, p_field_params)`
286
+ - Maps legacy types to PostgreSQL types, including `SERIAL`/`BIGSERIAL` for autoincrement fields.
287
+ - `_connect(p_db_name)`
288
+ - Internal connector helper returning a `psycopg` connection for the requested database.
289
+
290
+ ______________________________________________________________________
291
+
292
+ ## Updating ReleaseNotes Instructions
293
+
294
+ 1. Run the `pushpy.ps1` script or manually commit the current changes.
295
+ 2. Generate the release notes
296
+ 3. Use one of the following AI propmpts in Notion to generate the release notes.
297
+
298
+ - [Release - Update - General](https://www.notion.so/Release-Update-General-2c0bc8e6c6f38076b4cee82e3cf243fa?v=2c0bc8e6c6f3806e85db000c395f94ce&source=copy_link)
299
+ - [Release - Update - VenvIt](https://www.notion.so/Release-Update-VenvIt-2c0bc8e6c6f380de84a0f3fbb8b5dda5?v=2c0bc8e6c6f3806e85db000c395f94ce&source=copy_link)
300
+
301
+ or
302
+
303
+ 1. Use the following template and manually update the ReleaseNotes.md file.
304
+
305
+ ```
306
+ # Release ?.?.?
307
+ ## Summary of Changes
308
+ - bla, bla, bla
309
+ ## Next Heading
310
+ - bla, bla, bla
311
+ ---
312
+ ```
313
+
314
+ 2. You can repeat step 1 multiple times.
315
+
316
+ 3. You can repeat step 2 multiple times but update the ReleaseNotes that has not been published.
317
+
318
+ 4. Run the `pushpr.ps1` script once you are ready to create the PR to publish the release. TOy can also manually create
319
+ the tag, touch a file, commit and push the changes.
320
+
321
+ 5. Merge the PR in GitHub.
322
+
323
+ 6. Confirm the following:
324
+
325
+ 7. The release update reflects in GitHub
326
+
327
+ 8. The release update notification was sent
328
+
329
+ ______________________________________________________________________
330
+
331
+ [cicd_codestyle_img]: https://img.shields.io/badge/code%20style-black-000000.svg "Black"
332
+ [cicd_codestyle_lnk]: https://github.com/psf/black "Black"
333
+ [cicd_pre_commit_img]: https://img.shields.io/github/actions/workflow/status/BrightEdgeeServices/SqlDbWrpr/pre-commit.yml?label=pre-commit "Pre-Commit"
334
+ [cicd_pre_commit_lnk]: https://github.com/BrightEdgeeServices/SqlDbWrpr/blob/master/.github/workflows/pre-commit.yml "Pre-Commit"
335
+ [codecov_img]: https://img.shields.io/codecov/c/gh/BrightEdgeeServices/SqlDbWrpr "CodeCov"
336
+ [codecov_lnk]: https://app.codecov.io/gh/BrightEdgeeServices/SqlDbWrpr "CodeCov"
337
+ [general_license_img]: https://img.shields.io/pypi/l/SqlDbWrpr "License"
338
+ [general_license_lnk]: https://github.com/BrightEdgeeServices/SqlDbWrpr/blob/master/LICENSE "License"
339
+ [general_maintenance_y_img]: https://img.shields.io/badge/Maintenance%20Intended-%E2%9C%94-green.svg?style=flat-square "Maintenance - intended"
340
+ [general_maintenance_y_lnk]: http://unmaintained.tech/ "Maintenance - intended"
341
+ [general_semver_link]: https://semver.org/ "Sentic Versioning - 2.0.0"
342
+ [general_semver_pic]: https://img.shields.io/badge/Semantic%20Versioning-2.0.0-brightgreen.svg?style=flat-square "Sentic Versioning - 2.0.0"
343
+ [gh_deployment_img]: https://img.shields.io/github/deployments/BrightEdgeeServices/SqlDbWrpr/pypi "GitHub - PiPy Deployment"
344
+ [gh_deployment_lnk]: https://github.com/BrightEdgeeServices/SqlDbWrpr/deployments/pypi "GitHub - PiPy Deployment"
345
+ [gh_doc_img]: https://img.shields.io/readthedocs/SqlDbWrpr "Read the Docs"
346
+ [gh_doc_lnk]: https://github.com/BrightEdgeeServices/SqlDbWrpr/blob/master/.github/workflows/check-rst-documentation.yml "Read the Docs"
347
+ [gh_issues_img]: https://img.shields.io/github/issues-raw/BrightEdgeeServices/SqlDbWrpr "GitHub - Issue Counter"
348
+ [gh_issues_lnk]: https://github.com/BrightEdgeeServices/SqlDbWrpr/issues "GitHub - Issue Counter"
349
+ [gh_language_img]: https://img.shields.io/github/languages/top/BrightEdgeeServices/SqlDbWrpr "GitHub - Top Language"
350
+ [gh_language_lnk]: https://github.com/BrightEdgeeServices/SqlDbWrpr "GitHub - Top Language"
351
+ [gh_last_commit_img]: https://img.shields.io/github/last-commit/BrightEdgeeServices/SqlDbWrpr/master "GitHub - Last Commit"
352
+ [gh_last_commit_lnk]: https://github.com/BrightEdgeeServices/SqlDbWrpr/commit/master "GitHub - Last Commit"
353
+ [gh_tests_img]: https://img.shields.io/github/actions/workflow/status/BrightEdgeeServices/SqlDbWrpr/ci.yml?label=ci "Test status"
354
+ [gh_tests_lnk]: https://github.com/BrightEdgeeServices/SqlDbWrpr/blob/master/.github/workflows/ci.yml "Test status"
355
+ [pypi_downloads_img]: https://img.shields.io/pypi/dm/SqlDbWrpr "Monthly downloads"
356
+ [pypi_downloads_lnk]: https://pypi.org/project/SqlDbWrpr/ "Monthly downloads"
357
+ [pypi_format_img]: https://img.shields.io/pypi/wheel/SqlDbWrpr "PyPI - Format"
358
+ [pypi_format_lnk]: https://pypi.org/project/SqlDbWrpr/ "PyPI - Format"
359
+ [pypi_py_versions_img]: https://img.shields.io/pypi/pyversions/SqlDbWrpr "PyPI - Supported Python Versions"
360
+ [pypi_py_versions_lnk]: https://pypi.org/project/SqlDbWrpr/ "PyPI - Supported Python Versions"
361
+ [pypi_release_img]: https://img.shields.io/pypi/v/SqlDbWrpr "Test status"
362
+ [pypi_release_lnk]: https://pypi.org/project/SqlDbWrpr/ "Test status"
363
+