sqlite-database 0.7.7__tar.gz → 0.7.8__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.
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.gitignore +1 -0
- {sqlite_database-0.7.7/sqlite_database.egg-info → sqlite_database-0.7.8}/PKG-INFO +24 -1
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/README.md +23 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/TODO.md +5 -5
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/__init__.py +2 -1
- sqlite_database-0.7.8/sqlite_database/asyncs/__init__.py +3 -0
- sqlite_database-0.7.8/sqlite_database/asyncs/database.py +37 -0
- sqlite_database-0.7.8/sqlite_database/asyncs/table.py +95 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/csv.py +13 -1
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/database.py +3 -30
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/models/__init__.py +3 -2
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/table.py +73 -16
- {sqlite_database-0.7.7 → sqlite_database-0.7.8/sqlite_database.egg-info}/PKG-INFO +24 -1
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database.egg-info/SOURCES.txt +3 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.editorconfig +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.github/dependabot.yml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.github/workflows/pylint.yml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.github/workflows/pytest.yml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.github/workflows/python-publish.yml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.readthedocs.yaml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/.vscode/settings.json +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/Features.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/History.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/LICENSE +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/SimpleGuide.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/bin/activate +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/bin/check.bat +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/bin/check.sh +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/bin/include/utility.bash +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/bin/install.bash +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/bin/need-installed/activate +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/bin/need-installed/pre-commit +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/bin/summarize-pylint.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/dev-config/black.toml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/dev-config/pylint.toml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/dev-config/pytest.ini +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/dev-requirements.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/Makefile +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/ModelAPI.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/SimpleGuide.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/_.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/api_reference.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/conf.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/index.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/make.bat +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/modules.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.column.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.config.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.csv.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.database.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.errors.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.functions.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.locals.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.model.errors.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.model.helpers.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.model.query_builder.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.model.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.operators.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.query_builder.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.signature.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.subexp.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.table.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.typings.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.utils.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs-requirements.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/project-init.bash +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/pyproject.toml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/setup.cfg +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/setup.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/_debug.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/_utils.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/column.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/errors.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/functions.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/locals.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/models/errors.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/models/helpers.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/models/mixin.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/models/query_builder.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/models/type_checkers.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/operators.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/query_builder.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/signature.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/subquery.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/typings.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database/utils.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database.egg-info/dependency_links.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database.egg-info/requires.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database.egg-info/top_level.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database.egg-info/zip-safe +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/__init__.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/__init__.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/model_api/__init__.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/model_api/test_model_api.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/setup.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/table_api/__init__.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/table_api/test_csv.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/table_api/test_delete.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/table_api/test_insert.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/table_api/test_others.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/table_api/test_select.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/table_api/test_update.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/database/test_custom.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/manual_test_performances.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/test_internals.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/user_benchmark.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/tests/user_helpers.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.8}/transient/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_database
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.8
|
|
4
4
|
Summary: A weird wrapper for SQLite Connection
|
|
5
5
|
Home-page: https://github.com/RimuEirnarn/sqlite_database
|
|
6
6
|
Author: RimuEirnarn
|
|
@@ -79,6 +79,29 @@ You can read why this library exists by reading [the history](History.md). The p
|
|
|
79
79
|
|
|
80
80
|
You can submit any issue if you found a good issue. You can submit a pull request as long as the thing you want complies with what this project aims for.
|
|
81
81
|
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
When pulling this repository with latest commit, make sure to install all `dev-requirements.txt` depending on your intentions. It's mostly pylint and pytest.
|
|
85
|
+
|
|
86
|
+
When using `pytest`, do this: `pytest --config-file=./dev-config/pytest.ini` or check any check scripts in `bin/`
|
|
87
|
+
|
|
88
|
+
### How to install?
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
git clone https://github.com/RimuEirnarn/sqlite_database
|
|
92
|
+
cd sqlite_database
|
|
93
|
+
|
|
94
|
+
python -m venv .venv
|
|
95
|
+
.venv/bin/activate
|
|
96
|
+
|
|
97
|
+
pip install -r ./dev-requirements.txt
|
|
98
|
+
./bin/check.sh
|
|
99
|
+
|
|
100
|
+
# Above script is equivalent to:
|
|
101
|
+
pylint --rcfile ./dev-config/pylint.toml sqlite_database
|
|
102
|
+
pytest --config-file ./dev-config/pytest.ini
|
|
103
|
+
```
|
|
104
|
+
|
|
82
105
|
## License
|
|
83
106
|
|
|
84
107
|
This library/wrapper/repo/project is licensed with/in BSD 3-Clause "New" or "Revised" License.
|
|
@@ -53,6 +53,29 @@ You can read why this library exists by reading [the history](History.md). The p
|
|
|
53
53
|
|
|
54
54
|
You can submit any issue if you found a good issue. You can submit a pull request as long as the thing you want complies with what this project aims for.
|
|
55
55
|
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
When pulling this repository with latest commit, make sure to install all `dev-requirements.txt` depending on your intentions. It's mostly pylint and pytest.
|
|
59
|
+
|
|
60
|
+
When using `pytest`, do this: `pytest --config-file=./dev-config/pytest.ini` or check any check scripts in `bin/`
|
|
61
|
+
|
|
62
|
+
### How to install?
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
git clone https://github.com/RimuEirnarn/sqlite_database
|
|
66
|
+
cd sqlite_database
|
|
67
|
+
|
|
68
|
+
python -m venv .venv
|
|
69
|
+
.venv/bin/activate
|
|
70
|
+
|
|
71
|
+
pip install -r ./dev-requirements.txt
|
|
72
|
+
./bin/check.sh
|
|
73
|
+
|
|
74
|
+
# Above script is equivalent to:
|
|
75
|
+
pylint --rcfile ./dev-config/pylint.toml sqlite_database
|
|
76
|
+
pytest --config-file ./dev-config/pytest.ini
|
|
77
|
+
```
|
|
78
|
+
|
|
56
79
|
## License
|
|
57
80
|
|
|
58
81
|
This library/wrapper/repo/project is licensed with/in BSD 3-Clause "New" or "Revised" License.
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
What API should we bring? These todo's is for simplification; you can do anything with `Database.sql` property
|
|
10
10
|
|
|
11
11
|
- [x] sqlite function support
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
12
|
+
- [x] sqlite aggregate function support
|
|
13
|
+
- [x] sqlite window function support
|
|
14
|
+
- [x] sqlite collation support
|
|
15
15
|
- [x] sqlite pragma
|
|
16
|
-
- [
|
|
16
|
+
- [x] sqlite branching subquery (??)
|
|
17
17
|
- [ ] sqlite 'as' keyword (??)
|
|
18
18
|
- [x] sqlite select data crunching[^1] by using Database config option (crunch=True)
|
|
19
19
|
- [x] sqlite select 'only' data. Instead of using `select *`, we should also have `only`. Bring up few data than select everything.[^2]
|
|
@@ -25,7 +25,7 @@ The functionality here is outside of sqlite features such as export and import.
|
|
|
25
25
|
|
|
26
26
|
- [ ] YAML/JSON/TOML/CUSTOM scheme/table include
|
|
27
27
|
- [x] CSV Export
|
|
28
|
-
- [
|
|
28
|
+
- [x] CSV Import
|
|
29
29
|
|
|
30
30
|
1. abc
|
|
31
31
|
- a
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Database"""
|
|
2
2
|
|
|
3
|
+
from . import models
|
|
3
4
|
from .models import BaseModel, model, Foreign, Primary, Unique, hook, validate
|
|
4
5
|
from .database import Database
|
|
5
6
|
from ._utils import Row, Null
|
|
@@ -14,7 +15,7 @@ def test_installed():
|
|
|
14
15
|
return True
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
__version__ = "0.7.
|
|
18
|
+
__version__ = "0.7.8"
|
|
18
19
|
__all__ = [
|
|
19
20
|
"Database",
|
|
20
21
|
"Table",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""AsyncDatabase"""
|
|
2
|
+
|
|
3
|
+
from sqlite3 import connect, Connection
|
|
4
|
+
from threading import local
|
|
5
|
+
|
|
6
|
+
from .table import AsyncTable
|
|
7
|
+
from ..database import Database
|
|
8
|
+
from .._utils import dict_factory
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AsyncDatabase(Database):
|
|
12
|
+
"""Async (threads, subprocess) ready"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, path: str, **kwargs) -> None:
|
|
15
|
+
self._local = local()
|
|
16
|
+
self._table_class = AsyncTable
|
|
17
|
+
super().__init__(path, **kwargs)
|
|
18
|
+
|
|
19
|
+
def _create_connection(self):
|
|
20
|
+
conn = getattr(self._local, "conn", None)
|
|
21
|
+
if conn is None:
|
|
22
|
+
timeout = self._kwargs.pop('timeout', 30)
|
|
23
|
+
conn = connect(
|
|
24
|
+
self._path,
|
|
25
|
+
timeout=timeout,
|
|
26
|
+
isolation_level=self._kwargs.pop("isolation_level", None),
|
|
27
|
+
check_same_thread=self._kwargs.pop("check_same_thread", False)
|
|
28
|
+
)
|
|
29
|
+
conn.row_factory = dict_factory
|
|
30
|
+
conn.execute("PRAGMA journal_mode=WAL;")
|
|
31
|
+
if isinstance(timeout, int):
|
|
32
|
+
conn.execute(f'PRAGMA busy_timeout={timeout * 1000};')
|
|
33
|
+
self._local.conn = conn
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def _database(self) -> Connection:
|
|
37
|
+
return self._local.conn
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Async Table"""
|
|
2
|
+
|
|
3
|
+
# pylint: disable=invalid-overridden-method
|
|
4
|
+
import asyncio
|
|
5
|
+
from contextvars import ContextVar
|
|
6
|
+
|
|
7
|
+
from ..table import Table
|
|
8
|
+
from ..query_builder import Condition
|
|
9
|
+
from ..typings import Orders
|
|
10
|
+
|
|
11
|
+
# Context-local transaction depth stack per async task
|
|
12
|
+
_tx_stack = ContextVar("_tx_stack", default=[])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AsyncTable(Table):
|
|
16
|
+
"""Async (threading/multiprocess ready) Table"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, parent, table: str, columns=None, aggressive_select=False):
|
|
19
|
+
super().__init__(parent, table, columns, False)
|
|
20
|
+
if (self._columns is None and aggressive_select) and table != "sqlite_master":
|
|
21
|
+
asyncio.get_event_loop().run_until_complete(
|
|
22
|
+
asyncio.to_thread(self._fetch_columns)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
async def _begin_transaction(self):
|
|
26
|
+
"""Start a transaction or savepoint depending on depth."""
|
|
27
|
+
stack = list(_tx_stack.get()) # copy since ContextVar values are immutable
|
|
28
|
+
depth = len(stack)
|
|
29
|
+
|
|
30
|
+
if depth == 0:
|
|
31
|
+
await asyncio.to_thread(self._sql.execute, "BEGIN TRANSACTION")
|
|
32
|
+
else:
|
|
33
|
+
savepoint_name = f"sp_{depth}"
|
|
34
|
+
await asyncio.to_thread(self._sql.execute, f"SAVEPOINT {savepoint_name}")
|
|
35
|
+
|
|
36
|
+
stack.append(True)
|
|
37
|
+
_tx_stack.set(stack)
|
|
38
|
+
|
|
39
|
+
async def _commit_transaction(self):
|
|
40
|
+
"""Commit or release savepoint depending on depth."""
|
|
41
|
+
stack = list(_tx_stack.get())
|
|
42
|
+
depth = len(stack)
|
|
43
|
+
|
|
44
|
+
if depth == 1:
|
|
45
|
+
await asyncio.to_thread(self._sql.commit)
|
|
46
|
+
elif depth > 1:
|
|
47
|
+
savepoint_name = f"sp_{depth-1}"
|
|
48
|
+
await asyncio.to_thread(
|
|
49
|
+
self._sql.execute, f"RELEASE SAVEPOINT {savepoint_name}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
stack.pop()
|
|
53
|
+
_tx_stack.set(stack)
|
|
54
|
+
|
|
55
|
+
async def _rollback_transaction(self):
|
|
56
|
+
"""Rollback or rollback to savepoint depending on depth."""
|
|
57
|
+
stack = list(_tx_stack.get())
|
|
58
|
+
depth = len(stack)
|
|
59
|
+
|
|
60
|
+
if depth == 1:
|
|
61
|
+
await asyncio.to_thread(self._sql.rollback)
|
|
62
|
+
elif depth > 1:
|
|
63
|
+
savepoint_name = f"sp_{depth-1}"
|
|
64
|
+
await asyncio.to_thread(
|
|
65
|
+
self._sql.execute, f"ROLLBACK TO SAVEPOINT {savepoint_name}"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
stack.pop()
|
|
69
|
+
_tx_stack.set(stack)
|
|
70
|
+
|
|
71
|
+
# ✅ async context manager compatible with your original Table
|
|
72
|
+
async def __aenter__(self):
|
|
73
|
+
await self._begin_transaction()
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
async def __aexit__(self, exc_type, *_):
|
|
77
|
+
if exc_type:
|
|
78
|
+
await self._rollback_transaction()
|
|
79
|
+
else:
|
|
80
|
+
await self._commit_transaction()
|
|
81
|
+
|
|
82
|
+
async def begin(self):
|
|
83
|
+
"""Begin"""
|
|
84
|
+
await self._begin_transaction()
|
|
85
|
+
|
|
86
|
+
async def commit(self):
|
|
87
|
+
await self._commit_transaction()
|
|
88
|
+
|
|
89
|
+
async def rollback(self):
|
|
90
|
+
await self._rollback_transaction()
|
|
91
|
+
|
|
92
|
+
async def delete(
|
|
93
|
+
self, where: Condition = None, limit: int = 0, order: Orders | None = None
|
|
94
|
+
):
|
|
95
|
+
await asyncio.to_thread(self.delete, where, limit, order)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""CSV module, used to export database/table to csv"""
|
|
2
2
|
|
|
3
|
-
from csv import DictWriter
|
|
3
|
+
from csv import DictWriter, DictReader
|
|
4
4
|
from io import StringIO
|
|
5
5
|
from os import mkdir
|
|
6
6
|
from os.path import join as join_path, isfile, exists
|
|
@@ -65,3 +65,15 @@ def to_csv_file(table_or_database: Table | Database, file: str):
|
|
|
65
65
|
with open(file, "w", encoding="utf-8") as fio:
|
|
66
66
|
fio.write(readed)
|
|
67
67
|
return True
|
|
68
|
+
|
|
69
|
+
def from_csv_string(table: Table, data: str):
|
|
70
|
+
"""Insert from CSV data"""
|
|
71
|
+
reader = DictReader(data)
|
|
72
|
+
for entry in reader:
|
|
73
|
+
table.insert(entry)
|
|
74
|
+
|
|
75
|
+
def from_csv_file(table: Table, data: str):
|
|
76
|
+
"""Insert from CSV file"""
|
|
77
|
+
with open(data, encoding='utf-8') as f:
|
|
78
|
+
fdata = f.read()
|
|
79
|
+
return from_csv_string(table, fdata)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"""SQLite Database"""
|
|
2
2
|
|
|
3
3
|
from atexit import register as finalize
|
|
4
|
-
from sqlite3 import OperationalError, connect
|
|
5
|
-
from threading import local
|
|
4
|
+
from sqlite3 import OperationalError, connect
|
|
6
5
|
from typing import Iterable, Literal, Optional
|
|
7
6
|
|
|
8
7
|
from sqlite_database._debug import if_debug_print
|
|
@@ -46,6 +45,7 @@ class Database: # pylint: disable=too-many-instance-attributes
|
|
|
46
45
|
del kwargs['strict']
|
|
47
46
|
self._config = None
|
|
48
47
|
self._closed = False
|
|
48
|
+
self._table_class = Table
|
|
49
49
|
if not self._closed or self.__dict__.get("_initiated", False) is False:
|
|
50
50
|
finalize(self._finalizer)
|
|
51
51
|
self._initiated = True
|
|
@@ -119,7 +119,7 @@ class Database: # pylint: disable=too-many-instance-attributes
|
|
|
119
119
|
raise DatabaseMissingError(f"table {table} does not exists.")
|
|
120
120
|
|
|
121
121
|
try:
|
|
122
|
-
this_table =
|
|
122
|
+
this_table = self._table_class(self, table, __columns)
|
|
123
123
|
except OperationalError as exc:
|
|
124
124
|
dberror = DatabaseMissingError(f"table {table} does not exists")
|
|
125
125
|
dberror.add_note(f"{type(exc).__name__}: {exc!s}")
|
|
@@ -228,30 +228,3 @@ class Database: # pylint: disable=too-many-instance-attributes
|
|
|
228
228
|
def sql(self):
|
|
229
229
|
"""SQL Connection"""
|
|
230
230
|
return self._database
|
|
231
|
-
|
|
232
|
-
class AsyncDatabase(Database):
|
|
233
|
-
"""Async (threads, subprocess) ready"""
|
|
234
|
-
|
|
235
|
-
def __init__(self, path: str, **kwargs) -> None:
|
|
236
|
-
super().__init__(path, **kwargs)
|
|
237
|
-
self._local = local()
|
|
238
|
-
|
|
239
|
-
def _create_connection(self):
|
|
240
|
-
conn = getattr(self._local, "conn", None)
|
|
241
|
-
if conn is None:
|
|
242
|
-
timeout = self._kwargs.pop('timeout', 30)
|
|
243
|
-
conn = connect(
|
|
244
|
-
self._path,
|
|
245
|
-
timeout=timeout,
|
|
246
|
-
isolation_level=self._kwargs.pop("isolation_level", None),
|
|
247
|
-
check_same_thread=self._kwargs.pop("check_same_thread", False)
|
|
248
|
-
)
|
|
249
|
-
conn.row_factory = dict_factory
|
|
250
|
-
conn.execute("PRAGMA journal_mode=WAL;")
|
|
251
|
-
if isinstance(timeout, int):
|
|
252
|
-
conn.execute(f'PRAGMA busy_timeout={timeout * 1000};')
|
|
253
|
-
self._local.conn = conn
|
|
254
|
-
|
|
255
|
-
@property
|
|
256
|
-
def _database(self) -> Connection:
|
|
257
|
-
return self._local.conn
|
|
@@ -363,9 +363,10 @@ class BaseModel: # pylint: disable=too-few-public-methods,too-many-public-metho
|
|
|
363
363
|
f"with {self.__class__.__name__}"
|
|
364
364
|
)
|
|
365
365
|
|
|
366
|
-
|
|
366
|
+
@classmethod
|
|
367
|
+
def get_table(cls):
|
|
367
368
|
"""Return table instance"""
|
|
368
|
-
return
|
|
369
|
+
return cls._tbl
|
|
369
370
|
|
|
370
371
|
@classmethod
|
|
371
372
|
def where(cls, **kwargs):
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Table"""
|
|
2
2
|
|
|
3
|
-
# pylint: disable=too-many-arguments,too-many-public-methods
|
|
3
|
+
# pylint: disable=too-many-arguments,too-many-public-methods,R0801
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from contextvars import ContextVar
|
|
6
|
+
from sqlite3 import Connection, Error, OperationalError
|
|
6
7
|
from typing import (
|
|
7
8
|
Any,
|
|
8
9
|
Generator,
|
|
@@ -44,6 +45,7 @@ from .typings import (
|
|
|
44
45
|
|
|
45
46
|
# Let's add a little bit of 'black' magic here.
|
|
46
47
|
_null = Function("__NULL__")()
|
|
48
|
+
_tx_stack = ContextVar("_tx_stack", default=[])
|
|
47
49
|
|
|
48
50
|
class Table: # pylint: disable=too-many-instance-attributes
|
|
49
51
|
"""Table. Make sure you remember how the table goes."""
|
|
@@ -74,20 +76,40 @@ class Table: # pylint: disable=too-many-instance-attributes
|
|
|
74
76
|
if (self._columns is None and aggresive_select) and table != "sqlite_master":
|
|
75
77
|
self._fetch_columns()
|
|
76
78
|
|
|
79
|
+
# def __enter__(self):
|
|
80
|
+
# self._prev_auto = self._auto
|
|
81
|
+
# self._prev_autocommit = self._sql.isolation_level
|
|
82
|
+
|
|
83
|
+
# self._sql.isolation_level = None
|
|
84
|
+
# self._auto = False
|
|
85
|
+
# self._sql.execute("BEGIN TRANSACTION")
|
|
86
|
+
# return self
|
|
87
|
+
|
|
88
|
+
# def __exit__(self, exc_type, _, __):
|
|
89
|
+
# if exc_type is None:
|
|
90
|
+
# self._sql.commit()
|
|
91
|
+
# else:
|
|
92
|
+
# self._sql.rollback()
|
|
93
|
+
# self._sql.isolation_level = self._prev_autocommit
|
|
94
|
+
# self._auto = self._prev_auto
|
|
95
|
+
|
|
77
96
|
def __enter__(self):
|
|
78
97
|
self._prev_auto = self._auto
|
|
79
98
|
self._prev_autocommit = self._sql.isolation_level
|
|
80
99
|
|
|
81
|
-
self._sql.isolation_level = None
|
|
82
100
|
self._auto = False
|
|
83
|
-
self._sql.
|
|
101
|
+
self._sql.isolation_level = None
|
|
102
|
+
self._begin_transaction()
|
|
84
103
|
return self
|
|
85
104
|
|
|
86
105
|
def __exit__(self, exc_type, _, __):
|
|
87
106
|
if exc_type is None:
|
|
88
|
-
self.
|
|
107
|
+
self._commit_transaction()
|
|
108
|
+
self._dirty = False
|
|
89
109
|
else:
|
|
90
|
-
self.
|
|
110
|
+
self._rollback_transaction()
|
|
111
|
+
self._dirty = False
|
|
112
|
+
|
|
91
113
|
self._sql.isolation_level = self._prev_autocommit
|
|
92
114
|
self._auto = self._prev_auto
|
|
93
115
|
|
|
@@ -167,10 +189,11 @@ class Table: # pylint: disable=too-many-instance-attributes
|
|
|
167
189
|
fn = cursor.execute if which == "execute" else cursor.executemany
|
|
168
190
|
try:
|
|
169
191
|
fn(query, data)
|
|
170
|
-
except
|
|
192
|
+
except Error as exc:
|
|
171
193
|
if str(exc).startswith("no such table:"):
|
|
172
194
|
raise TableRemovedError(f"Table {self._table} doesn't exists anymore") from None
|
|
173
195
|
exc.add_note(f"SQL query: {query}")
|
|
196
|
+
exc.add_note(f"Arguments: {data}")
|
|
174
197
|
exc.add_note(
|
|
175
198
|
f"There's about {1 if isinstance(data, dict) else len(data)} value(s) inserted"
|
|
176
199
|
)
|
|
@@ -587,12 +610,55 @@ constraint is enabled."
|
|
|
587
610
|
def commit(self):
|
|
588
611
|
"""Commit changes"""
|
|
589
612
|
self._sql.commit()
|
|
613
|
+
self._dirty = False
|
|
590
614
|
|
|
591
615
|
def rollback(self):
|
|
592
616
|
"""Rollback"""
|
|
593
617
|
self._sql.rollback()
|
|
594
618
|
self._dirty = False
|
|
595
619
|
|
|
620
|
+
def _begin_transaction(self):
|
|
621
|
+
"""Start a transaction or savepoint depending on depth."""
|
|
622
|
+
stack = list(_tx_stack.get()) # copy since ContextVar values are immutable
|
|
623
|
+
depth = len(stack)
|
|
624
|
+
|
|
625
|
+
if depth == 0:
|
|
626
|
+
self._sql.execute("BEGIN TRANSACTION")
|
|
627
|
+
else:
|
|
628
|
+
savepoint_name = f"sp_{depth}"
|
|
629
|
+
self._sql.execute(f"SAVEPOINT {savepoint_name}")
|
|
630
|
+
|
|
631
|
+
stack.append(True)
|
|
632
|
+
_tx_stack.set(stack)
|
|
633
|
+
|
|
634
|
+
def _commit_transaction(self):
|
|
635
|
+
"""Commit or release savepoint depending on depth."""
|
|
636
|
+
stack = list(_tx_stack.get())
|
|
637
|
+
depth = len(stack)
|
|
638
|
+
|
|
639
|
+
if depth == 1:
|
|
640
|
+
self._sql.commit()
|
|
641
|
+
elif depth > 1:
|
|
642
|
+
savepoint_name = f"sp_{depth-1}"
|
|
643
|
+
self._sql.execute(f"RELEASE SAVEPOINT {savepoint_name}")
|
|
644
|
+
stack.pop()
|
|
645
|
+
_tx_stack.set(stack)
|
|
646
|
+
|
|
647
|
+
def _rollback_transaction(self):
|
|
648
|
+
"""Rollback or rollback to savepoint depending on depth."""
|
|
649
|
+
stack = list(_tx_stack.get())
|
|
650
|
+
depth = len(stack)
|
|
651
|
+
|
|
652
|
+
if depth == 1:
|
|
653
|
+
self._sql.rollback()
|
|
654
|
+
elif depth > 1:
|
|
655
|
+
savepoint_name = f"sp_{depth-1}"
|
|
656
|
+
self._sql.execute(f"ROLLBACK TO SAVEPOINT {savepoint_name}"
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
stack.pop()
|
|
660
|
+
_tx_stack.set(stack)
|
|
661
|
+
|
|
596
662
|
def count(self):
|
|
597
663
|
"""Count how much objects/rows stored in this table"""
|
|
598
664
|
# ? Might as well uses __len__? But it's quite expensive.
|
|
@@ -601,13 +667,4 @@ constraint is enabled."
|
|
|
601
667
|
def __repr__(self) -> str:
|
|
602
668
|
return f"<Table({self._table}) -> {self._parent_repr}>"
|
|
603
669
|
|
|
604
|
-
class AsyncTable(Table):
|
|
605
|
-
"""Async (threads, subprocess) ready"""
|
|
606
|
-
|
|
607
|
-
def __enter__(self):
|
|
608
|
-
return self
|
|
609
|
-
|
|
610
|
-
def __exit__(self, exc_type, _, __):
|
|
611
|
-
pass
|
|
612
|
-
|
|
613
670
|
__all__ = ["Table"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_database
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.8
|
|
4
4
|
Summary: A weird wrapper for SQLite Connection
|
|
5
5
|
Home-page: https://github.com/RimuEirnarn/sqlite_database
|
|
6
6
|
Author: RimuEirnarn
|
|
@@ -79,6 +79,29 @@ You can read why this library exists by reading [the history](History.md). The p
|
|
|
79
79
|
|
|
80
80
|
You can submit any issue if you found a good issue. You can submit a pull request as long as the thing you want complies with what this project aims for.
|
|
81
81
|
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
When pulling this repository with latest commit, make sure to install all `dev-requirements.txt` depending on your intentions. It's mostly pylint and pytest.
|
|
85
|
+
|
|
86
|
+
When using `pytest`, do this: `pytest --config-file=./dev-config/pytest.ini` or check any check scripts in `bin/`
|
|
87
|
+
|
|
88
|
+
### How to install?
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
git clone https://github.com/RimuEirnarn/sqlite_database
|
|
92
|
+
cd sqlite_database
|
|
93
|
+
|
|
94
|
+
python -m venv .venv
|
|
95
|
+
.venv/bin/activate
|
|
96
|
+
|
|
97
|
+
pip install -r ./dev-requirements.txt
|
|
98
|
+
./bin/check.sh
|
|
99
|
+
|
|
100
|
+
# Above script is equivalent to:
|
|
101
|
+
pylint --rcfile ./dev-config/pylint.toml sqlite_database
|
|
102
|
+
pytest --config-file ./dev-config/pytest.ini
|
|
103
|
+
```
|
|
104
|
+
|
|
82
105
|
## License
|
|
83
106
|
|
|
84
107
|
This library/wrapper/repo/project is licensed with/in BSD 3-Clause "New" or "Revised" License.
|
|
@@ -82,6 +82,9 @@ sqlite_database.egg-info/dependency_links.txt
|
|
|
82
82
|
sqlite_database.egg-info/requires.txt
|
|
83
83
|
sqlite_database.egg-info/top_level.txt
|
|
84
84
|
sqlite_database.egg-info/zip-safe
|
|
85
|
+
sqlite_database/asyncs/__init__.py
|
|
86
|
+
sqlite_database/asyncs/database.py
|
|
87
|
+
sqlite_database/asyncs/table.py
|
|
85
88
|
sqlite_database/models/__init__.py
|
|
86
89
|
sqlite_database/models/errors.py
|
|
87
90
|
sqlite_database/models/helpers.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlite_database-0.7.7 → sqlite_database-0.7.8}/docs/sqlite_database.model.query_builder.rst
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlite_database-0.7.7 → sqlite_database-0.7.8}/sqlite_database.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|