sqlalchemy-upsert-kit 0.1.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.
@@ -0,0 +1,148 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import os
4
+ import sys
5
+ import contextlib
6
+ import subprocess
7
+ from pathlib import Path
8
+
9
+ __version__ = "0.2.1"
10
+
11
+
12
+ @contextlib.contextmanager
13
+ def temp_cwd(path: Path):
14
+ """
15
+ Temporarily set the current working directory (CWD) and automatically
16
+ switch back when it's done.
17
+ """
18
+ cwd = os.getcwd()
19
+ os.chdir(str(path))
20
+ try:
21
+ yield path
22
+ finally:
23
+ os.chdir(cwd)
24
+
25
+
26
+ def run_unit_test(
27
+ script: str,
28
+ root_dir: str,
29
+ ):
30
+ """
31
+ Run ``pytest -s --tb=native /path/to/script.py`` Command.
32
+
33
+ :param script: the path to test script
34
+ :param root_dir: the dir you want to temporarily set as cwd
35
+ """
36
+ bin_pytest = Path(sys.executable).parent / "pytest"
37
+ args = [
38
+ f"{bin_pytest}",
39
+ "-s",
40
+ "--tb=native",
41
+ script,
42
+ ]
43
+ with temp_cwd(Path(root_dir)):
44
+ subprocess.run(args)
45
+
46
+
47
+ def run_cov_test(
48
+ script: str,
49
+ module: str,
50
+ root_dir: str,
51
+ htmlcov_dir: str,
52
+ preview: bool = False,
53
+ is_folder: bool = False,
54
+ ):
55
+ """
56
+ The pytest-cov plugin gives you the coverage for entire project. What if
57
+ I want run per-module test independently and get per-module coverage?
58
+
59
+ This is a simple wrapper around pytest + coverage cli command. Allow you to run
60
+ coverage test from Python script and set the code coverage measurement scope.
61
+
62
+ Usage example:
63
+
64
+ suppose you have a source code folder structure like this::
65
+
66
+ /dir_git_repo/
67
+ /dir_git_repo/my_library
68
+ /dir_git_repo/my_library/__init__.py
69
+ /dir_git_repo/my_library/module1.py
70
+ /dir_git_repo/my_library/module2.py
71
+
72
+ In your module 1 unit test script, you can do this:
73
+
74
+ .. code-block:: python
75
+
76
+ from my_library.module1 import func1, func2
77
+
78
+ def test_func1():
79
+ pass
80
+
81
+ def test_func2():
82
+ pass
83
+
84
+ if __name__ == "__main__":
85
+ from fixa.pytest_cov_helper import run_cov_test
86
+
87
+ run_cov_test(
88
+ script=__file__,
89
+ module="my_library.module1", # test scope is the module1.py
90
+ root_dir="/path/to/dir_git_repo",
91
+ htmlcov_dir="/path/to/dir_git_repo/htmlcov",
92
+ )
93
+
94
+ In your all modules unit test script, you can do this:
95
+
96
+ .. code-block:: python
97
+
98
+ if __name__ == "__main__":
99
+ from fixa.pytest_cov_helper import run_cov_test
100
+
101
+ run_cov_test(
102
+ script=__file__,
103
+ module="my_library", # test scope is the my_library/
104
+ root_dir="/path/to/dir_git_repo",
105
+ htmlcov_dir="/path/to/dir_git_repo/htmlcov",
106
+ is_folder=True, # my_library is a folder
107
+ )
108
+
109
+ :param script: the test script absolute path
110
+ :param module: the dot notation to the python module you want to calculate
111
+ coverage
112
+ :param root_dir: the dir to dump coverage results binary file
113
+ :param htmlcov_dir: the dir to dump HTML output
114
+ :param preview: whether to open the HTML output in web browser after the test
115
+ :param is_folder: whether the module is a folder
116
+
117
+ Reference:
118
+
119
+ - https://pypi.org/project/pytest-cov/
120
+ """
121
+ bin_pytest = Path(sys.executable).parent / "pytest"
122
+ if is_folder:
123
+ script = f"{Path(script).parent}"
124
+ if module.endswith(".py"): # pragma: no cover
125
+ module = module[:-3]
126
+ args = [
127
+ f"{bin_pytest}",
128
+ "-s",
129
+ "--tb=native",
130
+ f"--rootdir={root_dir}",
131
+ f"--cov={module}",
132
+ "--cov-report",
133
+ "term-missing",
134
+ "--cov-report",
135
+ f"html:{htmlcov_dir}",
136
+ script,
137
+ ]
138
+ with temp_cwd(Path(root_dir)):
139
+ subprocess.run(args)
140
+ if preview: # pragma: no cover
141
+ platform = sys.platform
142
+ if platform in ["win32", "cygwin"]:
143
+ open_command = "start"
144
+ elif platform in ["darwin", "linux"]:
145
+ open_command = "open"
146
+ else:
147
+ raise NotImplementedError
148
+ subprocess.run([open_command, f"{Path(htmlcov_dir).joinpath('index.html')}"])
@@ -0,0 +1,15 @@
1
+ .. _about_author:
2
+
3
+ About the Author
4
+ ------------------------------------------------------------------------------
5
+ ::
6
+
7
+ (\ (\
8
+ ( -.-)o
9
+ o_(")(")
10
+
11
+ **Sanhe Hu** is a seasoned software engineer with a deep passion for Python development since 2010. As an author and maintainer of `150+ open-source Python projects <https://pypi.org/user/machugwu/>`_, with over `15 million monthly downloads <https://github.com/MacHu-GWU>`_, I bring a wealth of experience to the table. As a Senior Solution Architect and Subject Matter Expert in AI, Data, Amazon Web Services, Cloud Engineering, DevOps, I thrive on helping clients with platform design, enterprise architecture, and strategic roadmaps.
12
+
13
+ Talk is cheap, show me the code:
14
+
15
+ - My Github: https://github.com/MacHu-GWU
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sanhe Hu <husanhe@email.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.3
2
+ Name: sqlalchemy_upsert_kit
3
+ Version: 0.1.1
4
+ Summary: High-performance SQLAlchemy bulk upsert operations using temporary table staging - 20x faster than row-by-row approaches.
5
+ License: MIT
6
+ Author: Sanhe Hu
7
+ Author-email: husanhe@email.com
8
+ Maintainer: Sanhe Hu
9
+ Maintainer-email: husanhe@email.com
10
+ Requires-Python: >=3.9,<4.0
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Provides-Extra: auto
19
+ Provides-Extra: dev
20
+ Provides-Extra: doc
21
+ Provides-Extra: test
22
+ Requires-Dist: Sphinx (>=7.4.7,<8.0.0) ; extra == "doc"
23
+ Requires-Dist: build (>=1.2.1,<2.0.0) ; extra == "dev"
24
+ Requires-Dist: docfly (==3.0.0) ; extra == "doc"
25
+ Requires-Dist: furo (==2024.8.6) ; extra == "doc"
26
+ Requires-Dist: ipython (>=8.18.1,<8.19.0) ; extra == "doc"
27
+ Requires-Dist: nbsphinx (>=0.8.12,<1.0.0) ; extra == "doc"
28
+ Requires-Dist: prettytable (>=3.0.0,<4.0.0) ; extra == "test"
29
+ Requires-Dist: pygments (>=2.18.0,<3.0.0) ; extra == "doc"
30
+ Requires-Dist: pytest (>=8.2.2,<9.0.0) ; extra == "test"
31
+ Requires-Dist: pytest-cov (>=6.0.0,<7.0.0) ; extra == "test"
32
+ Requires-Dist: rich (>=13.8.1,<14.0.0) ; extra == "dev"
33
+ Requires-Dist: rstobj (==1.2.1) ; extra == "doc"
34
+ Requires-Dist: sphinx-copybutton (>=0.5.2,<1.0.0) ; extra == "doc"
35
+ Requires-Dist: sphinx-design (>=0.6.1,<1.0.0) ; extra == "doc"
36
+ Requires-Dist: sphinx-jinja (>=2.0.2,<3.0.0) ; extra == "doc"
37
+ Requires-Dist: sqlalchemy (>=2.0.0,<3.0.0)
38
+ Requires-Dist: twine (>=6.0.0,<7.0.0) ; extra == "dev"
39
+ Requires-Dist: wheel (>=0.45.0,<1.0.0) ; extra == "dev"
40
+ Project-URL: Changelog, https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project/blob/main/release-history.rst
41
+ Project-URL: Documentation, https://sqlalchemy-upsert-kit.readthedocs.io/en/latest/
42
+ Project-URL: Download, https://pypi.org/pypi/sqlalchemy-upsert-kit#files
43
+ Project-URL: Homepage, https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project
44
+ Project-URL: Issues, https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project/issues
45
+ Project-URL: Repository, https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project
46
+ Description-Content-Type: text/x-rst
47
+
48
+
49
+ .. image:: https://readthedocs.org/projects/sqlalchemy-upsert-kit/badge/?version=latest
50
+ :target: https://sqlalchemy-upsert-kit.readthedocs.io/en/latest/
51
+ :alt: Documentation Status
52
+
53
+ .. image:: https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project/actions/workflows/main.yml/badge.svg
54
+ :target: https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project/actions?query=workflow:CI
55
+
56
+ .. image:: https://codecov.io/gh/MacHu-GWU/sqlalchemy_upsert_kit-project/branch/main/graph/badge.svg
57
+ :target: https://codecov.io/gh/MacHu-GWU/sqlalchemy_upsert_kit-project
58
+
59
+ .. image:: https://img.shields.io/pypi/v/sqlalchemy-upsert-kit.svg
60
+ :target: https://pypi.python.org/pypi/sqlalchemy-upsert-kit
61
+
62
+ .. image:: https://img.shields.io/pypi/l/sqlalchemy-upsert-kit.svg
63
+ :target: https://pypi.python.org/pypi/sqlalchemy-upsert-kit
64
+
65
+ .. image:: https://img.shields.io/pypi/pyversions/sqlalchemy-upsert-kit.svg
66
+ :target: https://pypi.python.org/pypi/sqlalchemy-upsert-kit
67
+
68
+ .. image:: https://img.shields.io/badge/✍️_Release_History!--None.svg?style=social&logo=github
69
+ :target: https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project/blob/main/release-history.rst
70
+
71
+ .. image:: https://img.shields.io/badge/⭐_Star_me_on_GitHub!--None.svg?style=social&logo=github
72
+ :target: https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project
73
+
74
+ ------
75
+
76
+ .. image:: https://img.shields.io/badge/Link-API-blue.svg
77
+ :target: https://sqlalchemy-upsert-kit.readthedocs.io/en/latest/py-modindex.html
78
+
79
+ .. image:: https://img.shields.io/badge/Link-Install-blue.svg
80
+ :target: `install`_
81
+
82
+ .. image:: https://img.shields.io/badge/Link-GitHub-blue.svg
83
+ :target: https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project
84
+
85
+ .. image:: https://img.shields.io/badge/Link-Submit_Issue-blue.svg
86
+ :target: https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project/issues
87
+
88
+ .. image:: https://img.shields.io/badge/Link-Request_Feature-blue.svg
89
+ :target: https://github.com/MacHu-GWU/sqlalchemy_upsert_kit-project/issues
90
+
91
+ .. image:: https://img.shields.io/badge/Link-Download-blue.svg
92
+ :target: https://pypi.org/pypi/sqlalchemy-upsert-kit#files
93
+
94
+
95
+ Welcome to ``sqlalchemy_upsert_kit`` Documentation
96
+ ==============================================================================
97
+ .. image:: https://sqlalchemy-upsert-kit.readthedocs.io/en/latest/_static/sqlalchemy_upsert_kit-logo.png
98
+ :target: https://sqlalchemy-upsert-kit.readthedocs.io/en/latest/
99
+
100
+ ``sqlalchemy_upsert_kit`` provides high-performance bulk upsert operations for SQLAlchemy applications using temporary table staging. It offers three optimized strategies: ``insert_or_ignore`` for conflict-free insertion, ``insert_or_replace`` for complete record replacement, and ``insert_or_merge`` for selective column updates while preserving existing data. Each strategy leverages efficient SQL JOIN operations and temporary tables to achieve ~20x performance improvements over traditional row-by-row upserts, making it ideal for ETL processes, data synchronization, and bulk data operations where speed and reliability are critical.
101
+
102
+
103
+ .. _install:
104
+
105
+ Install
106
+ ------------------------------------------------------------------------------
107
+
108
+ ``sqlalchemy_upsert_kit`` is released on PyPI, so all you need is to:
109
+
110
+ .. code-block:: console
111
+
112
+ $ pip install sqlalchemy-upsert-kit
113
+
114
+ To upgrade to latest version:
115
+
116
+ .. code-block:: console
117
+
118
+ $ pip install --upgrade sqlalchemy-upsert-kit
119
+
@@ -0,0 +1,19 @@
1
+ sqlalchemy_upsert_kit/__init__.py,sha256=91jqwa6UIinipz7S0hF5_qpjjB9MUONExbvbLt7UTys,289
2
+ sqlalchemy_upsert_kit/_version.py,sha256=P4PUbSbDBSFuAsxGoCW3y9fB7gt2D1K03MlkhIvg0vI,568
3
+ sqlalchemy_upsert_kit/api.py,sha256=v8H6ctTExDFcXeYKYPCSOU-rpZkQAvjEWTNsaXTELWk,59
4
+ sqlalchemy_upsert_kit/exc.py,sha256=bERofark5mlacbuWLq1nCkJmjRiOzKM_sYqlzztWLZM,459
5
+ sqlalchemy_upsert_kit/paths.py,sha256=JH9o7s__FexYpUMuwV7SArCUCdatooOQA1gUYtBm7UY,1226
6
+ sqlalchemy_upsert_kit/sqlite/__init__.py,sha256=ALdw6W36ti0qIHhyD7vMCV9WE9cyDlOdhFrxqnnP8IA,1488
7
+ sqlalchemy_upsert_kit/sqlite/api.py,sha256=q5gTmoHNg_G_FZYVBFuK7yNEnYB6fUlR5Fs-BIeg5fA,166
8
+ sqlalchemy_upsert_kit/sqlite/executor.py,sha256=1BuQSFIK-5X0CYwRIJhsZBoMbMaRar1rTwomu0cqd6s,22437
9
+ sqlalchemy_upsert_kit/sqlite/insert_or_ignore.py,sha256=sUhjJxbkVGGlym_Qmy6dOOb25w3fhZt2mT3yuJHQ6sk,5976
10
+ sqlalchemy_upsert_kit/sqlite/insert_or_merge.py,sha256=m7QCOMHG47_rN1XOmkTu5qNB4yYDnXvK7Cb6SrM-QTY,10632
11
+ sqlalchemy_upsert_kit/sqlite/insert_or_replace.py,sha256=wHz_-5sJjPi_nHkuZ8oetfUWu196C0YSWxlA34FJJMg,7086
12
+ sqlalchemy_upsert_kit/utils.py,sha256=OeVaSUgalT9YSW8Sp8YmeLB3yYPQ8M2RbbKDkkYCsTA,2887
13
+ sqlalchemy_upsert_kit/vendor/__init__.py,sha256=O9CT1B2F-cVB8elT0EoCJbgkcffjvlmqteqavs4giDg,25
14
+ sqlalchemy_upsert_kit/vendor/pytest_cov_helper.py,sha256=H2BJtFq3bwJHZtt-bxZ1yF9LnXHVDRZZ0NH-OvkRJdE,4063
15
+ sqlalchemy_upsert_kit-0.1.1.dist-info/AUTHORS.rst,sha256=oo38V9AD_y57Ac69mKmiItWquV29SRi2aTCdKQo531A,767
16
+ sqlalchemy_upsert_kit-0.1.1.dist-info/LICENSE.txt,sha256=Vc2EgX0hR-6Qw_RxS1IR-2qllqh4PiqSWFvrdnqZSDU,1084
17
+ sqlalchemy_upsert_kit-0.1.1.dist-info/METADATA,sha256=FcmOtb8ug1KSQkXYAkAhzciMgBCkSodZ2QN9WF76c_g,5798
18
+ sqlalchemy_upsert_kit-0.1.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
19
+ sqlalchemy_upsert_kit-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.1.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any