sqlite-database 0.7.7__tar.gz → 0.7.9__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.9}/.gitignore +1 -0
- {sqlite_database-0.7.7/sqlite_database.egg-info → sqlite_database-0.7.9}/PKG-INFO +24 -1
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/README.md +23 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/TODO.md +5 -5
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/__init__.py +2 -1
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/_utils.py +8 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/csv.py +13 -1
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/database.py +3 -30
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/errors.py +11 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/models/__init__.py +3 -2
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/table.py +74 -17
- sqlite_database-0.7.9/sqlite_database/workers/__init__.py +5 -0
- sqlite_database-0.7.9/sqlite_database/workers/connection.py +214 -0
- sqlite_database-0.7.9/sqlite_database/workers/database.py +62 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9/sqlite_database.egg-info}/PKG-INFO +24 -1
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database.egg-info/SOURCES.txt +3 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/table_api/test_others.py +14 -1
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.editorconfig +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.github/dependabot.yml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.github/workflows/pylint.yml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.github/workflows/pytest.yml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.github/workflows/python-publish.yml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.readthedocs.yaml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/.vscode/settings.json +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/Features.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/History.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/LICENSE +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/SimpleGuide.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/bin/activate +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/bin/check.bat +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/bin/check.sh +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/bin/include/utility.bash +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/bin/install.bash +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/bin/need-installed/activate +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/bin/need-installed/pre-commit +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/bin/summarize-pylint.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/dev-config/black.toml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/dev-config/pylint.toml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/dev-config/pytest.ini +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/dev-requirements.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/Makefile +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/ModelAPI.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/SimpleGuide.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/_.md +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/api_reference.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/conf.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/index.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/make.bat +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/modules.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.column.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.config.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.csv.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.database.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.errors.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.functions.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.locals.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.model.errors.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.model.helpers.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.model.query_builder.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.model.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.operators.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.query_builder.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.signature.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.subexp.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.table.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.typings.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs/sqlite_database.utils.rst +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/docs-requirements.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/project-init.bash +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/pyproject.toml +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/setup.cfg +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/setup.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/_debug.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/column.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/functions.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/locals.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/models/errors.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/models/helpers.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/models/mixin.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/models/query_builder.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/models/type_checkers.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/operators.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/query_builder.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/signature.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/subquery.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/typings.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database/utils.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database.egg-info/dependency_links.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database.egg-info/requires.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database.egg-info/top_level.txt +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/sqlite_database.egg-info/zip-safe +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/__init__.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/__init__.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/model_api/__init__.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/model_api/test_model_api.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/setup.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/table_api/__init__.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/table_api/test_csv.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/table_api/test_delete.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/table_api/test_insert.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/table_api/test_select.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/table_api/test_update.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/database/test_custom.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/manual_test_performances.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/test_internals.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/user_benchmark.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/tests/user_helpers.py +0 -0
- {sqlite_database-0.7.7 → sqlite_database-0.7.9}/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.9
|
|
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.9"
|
|
18
19
|
__all__ = [
|
|
19
20
|
"Database",
|
|
20
21
|
"Table",
|
|
@@ -139,6 +139,13 @@ class WithCursor(Cursor):
|
|
|
139
139
|
def __repr__(self) -> str:
|
|
140
140
|
return type(self).__name__
|
|
141
141
|
|
|
142
|
+
class NoopResource:
|
|
143
|
+
"""No-op resource control"""
|
|
144
|
+
def close(self):
|
|
145
|
+
"""Close this resource"""
|
|
146
|
+
|
|
147
|
+
def open(self, *_, **__):
|
|
148
|
+
"""open something"""
|
|
142
149
|
|
|
143
150
|
|
|
144
151
|
def test_installed():
|
|
@@ -162,4 +169,5 @@ __all__ = [
|
|
|
162
169
|
"AttrDict",
|
|
163
170
|
"NullObject",
|
|
164
171
|
"sqlite_multithread_check",
|
|
172
|
+
"NoopResource"
|
|
165
173
|
]
|
|
@@ -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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Errors"""
|
|
2
2
|
|
|
3
|
+
class Rejection(RuntimeError):
|
|
4
|
+
"""Cannot push into worker db"""
|
|
3
5
|
|
|
4
6
|
class DependencyError(ImportError):
|
|
5
7
|
"""Specific dependency is missing"""
|
|
@@ -30,3 +32,12 @@ class ObjectRemovedError(BaseException):
|
|
|
30
32
|
|
|
31
33
|
class CuteDemonLordException(Exception):
|
|
32
34
|
"""A demon lord (cute one, somehow a dude) aborts an SQL statement"""
|
|
35
|
+
|
|
36
|
+
class FeatureGatekeep(ValueError):
|
|
37
|
+
"""A feature is gatekeeped"""
|
|
38
|
+
|
|
39
|
+
class VersionError(FeatureGatekeep):
|
|
40
|
+
"""Current version cannot use this feature"""
|
|
41
|
+
|
|
42
|
+
class ImplementationWarning(FutureWarning):
|
|
43
|
+
"""The implementation of specific feature is not supported"""
|
|
@@ -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,27 +610,61 @@ 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.
|
|
599
665
|
return self.select(what=count("*"))
|
|
600
666
|
|
|
601
667
|
def __repr__(self) -> str:
|
|
602
|
-
return f"<
|
|
603
|
-
|
|
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
|
|
668
|
+
return f"<{type(self).__name__}({self._table}) -> {self._parent_repr}>"
|
|
612
669
|
|
|
613
670
|
__all__ = ["Table"]
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Worker Connection"""
|
|
2
|
+
|
|
3
|
+
# pylint: disable=ungrouped-imports,possibly-used-before-assignment,too-few-public-methods,no-name-in-module,no-member
|
|
4
|
+
|
|
5
|
+
# import os
|
|
6
|
+
from sys import version_info
|
|
7
|
+
from concurrent.futures import Future
|
|
8
|
+
from queue import Empty, Queue
|
|
9
|
+
from sqlite3 import Connection, Cursor
|
|
10
|
+
# from multiprocessing import Event as EventProcess, Process, freeze_support
|
|
11
|
+
from threading import Thread, Event as EventThread
|
|
12
|
+
from typing import Any, Literal, TypeAlias
|
|
13
|
+
from atexit import register as finalize
|
|
14
|
+
import warnings
|
|
15
|
+
|
|
16
|
+
from ..errors import Rejection, ImplementationWarning
|
|
17
|
+
|
|
18
|
+
WorkerType: TypeAlias = Literal["thread"] | Literal["process"]
|
|
19
|
+
FEATURE_MIN_VERSION = (3, 13)
|
|
20
|
+
POSSIBLE_STACKTRACE_COUNT = 6
|
|
21
|
+
|
|
22
|
+
if version_info > FEATURE_MIN_VERSION:
|
|
23
|
+
from queue import ShutDown # type: ignore
|
|
24
|
+
else:
|
|
25
|
+
|
|
26
|
+
class ShutDown(RuntimeError):
|
|
27
|
+
"""Raised when put/get with shut-down queue."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def is_shutdown(worker: "Worker"):
|
|
31
|
+
"""Is shutdown?"""
|
|
32
|
+
if worker.is_closed:
|
|
33
|
+
return lambda *a, **kw: None
|
|
34
|
+
return lambda *a, **kw: worker.push(worker.conn.close, "connection", *a, **kw)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Worker:
|
|
38
|
+
"""Worker"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, *args, worker_type: WorkerType = "thread", **kwargs):
|
|
41
|
+
self.conn = Connection(*args, **kwargs)
|
|
42
|
+
self.queue = Queue()
|
|
43
|
+
self.name = f"WorkerDB[{worker_type}]"
|
|
44
|
+
self.daemon = False
|
|
45
|
+
if worker_type in ("thread", "process"):
|
|
46
|
+
self.event = EventThread()
|
|
47
|
+
self.worker = Thread(target=self._run, name=self.name, daemon=self.daemon)
|
|
48
|
+
# elif worker_type == "process":
|
|
49
|
+
# freeze_support()
|
|
50
|
+
# self.event = EventProcess()
|
|
51
|
+
# self.worker = Process(target=self._run, name=self.name, daemon=self.daemon)
|
|
52
|
+
if worker_type == 'process':
|
|
53
|
+
warnings.warn(
|
|
54
|
+
"Worker Process implementation is not supported, reverting back to threading.",
|
|
55
|
+
ImplementationWarning,
|
|
56
|
+
POSSIBLE_STACKTRACE_COUNT)
|
|
57
|
+
self.accepting = self.event
|
|
58
|
+
self._closing = False
|
|
59
|
+
self.event.set()
|
|
60
|
+
self.worker.start()
|
|
61
|
+
finalize(self.close)
|
|
62
|
+
|
|
63
|
+
def recall(self):
|
|
64
|
+
"""Recall remainding queues"""
|
|
65
|
+
while True:
|
|
66
|
+
try:
|
|
67
|
+
fn, _, args, kwargs, fut = self.queue.get_nowait()
|
|
68
|
+
except Empty:
|
|
69
|
+
break
|
|
70
|
+
try:
|
|
71
|
+
if callable(fn):
|
|
72
|
+
res = fn(*args, **kwargs)
|
|
73
|
+
fut.set_result(res)
|
|
74
|
+
else:
|
|
75
|
+
fut.set_result(None)
|
|
76
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
77
|
+
fut.set_exception(e)
|
|
78
|
+
finally:
|
|
79
|
+
self.queue.task_done()
|
|
80
|
+
|
|
81
|
+
def _run(self):
|
|
82
|
+
while True:
|
|
83
|
+
try:
|
|
84
|
+
fn, owner, args, kwargs, fut = self.queue.get(timeout=0.01)
|
|
85
|
+
except Empty:
|
|
86
|
+
if self._closing:
|
|
87
|
+
break
|
|
88
|
+
continue
|
|
89
|
+
except ShutDown:
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
# If close signal is received, finish all tasks and quit
|
|
93
|
+
if fn is None or (
|
|
94
|
+
getattr(fn, "__name__", "") == "close" and owner == "connection"
|
|
95
|
+
):
|
|
96
|
+
fut.set_result(None)
|
|
97
|
+
self.queue.task_done()
|
|
98
|
+
self._closing = True
|
|
99
|
+
self.event.clear()
|
|
100
|
+
self.recall() # Finish all remaining tasks
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
if callable(fn):
|
|
105
|
+
res = fn(*args, **kwargs)
|
|
106
|
+
fut.set_result(res)
|
|
107
|
+
else:
|
|
108
|
+
setattr(self.conn, fn, kwargs["value"])
|
|
109
|
+
fut.set_result(None)
|
|
110
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
111
|
+
fut.set_exception(e)
|
|
112
|
+
finally:
|
|
113
|
+
self.queue.task_done()
|
|
114
|
+
# Ensure connection is closed
|
|
115
|
+
self.conn.close()
|
|
116
|
+
|
|
117
|
+
def push(self, fn, owner: str, *args, **kwargs):
|
|
118
|
+
"""Push to worker"""
|
|
119
|
+
if not self.accepting.is_set() or self._closing:
|
|
120
|
+
exc = Rejection("Cannot push during shutdown")
|
|
121
|
+
exc.add_note(f"Caller: {fn}")
|
|
122
|
+
raise exc
|
|
123
|
+
fut = Future()
|
|
124
|
+
try:
|
|
125
|
+
self.queue.put((fn, owner, args, kwargs, fut))
|
|
126
|
+
except ShutDown:
|
|
127
|
+
fut.set_result(None)
|
|
128
|
+
return fut.result()
|
|
129
|
+
return fut.result() # blocks until worker finishes
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def is_closed(self):
|
|
133
|
+
"""Return true if worker is closed"""
|
|
134
|
+
return self._closing
|
|
135
|
+
|
|
136
|
+
def close(self, push=True):
|
|
137
|
+
"""Close this worker"""
|
|
138
|
+
if self._closing:
|
|
139
|
+
return
|
|
140
|
+
self._closing = True
|
|
141
|
+
self.event.clear()
|
|
142
|
+
if push:
|
|
143
|
+
# Push a close signal to the queue
|
|
144
|
+
fut = Future()
|
|
145
|
+
self.queue.put((lambda: None, "connection", (), {}, fut))
|
|
146
|
+
fut.result()
|
|
147
|
+
self.worker.join()
|
|
148
|
+
|
|
149
|
+
def join(self, timeout: float = 0):
|
|
150
|
+
"""Join this worker"""
|
|
151
|
+
self.worker.join(timeout)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class WorkerCursor:
|
|
155
|
+
"""Worker cursor"""
|
|
156
|
+
|
|
157
|
+
__slots__ = ("_worker", "_cursor")
|
|
158
|
+
|
|
159
|
+
def __init__(self, worker: Worker, real_cursor: Cursor):
|
|
160
|
+
self._worker: Worker = worker
|
|
161
|
+
self._cursor: Cursor = real_cursor
|
|
162
|
+
|
|
163
|
+
def __getattr__(self, item):
|
|
164
|
+
# print(self, item)
|
|
165
|
+
if item in self.__slots__:
|
|
166
|
+
return super().__getattribute__(item)
|
|
167
|
+
|
|
168
|
+
attr = getattr(self._cursor, item)
|
|
169
|
+
if callable(attr):
|
|
170
|
+
return lambda *a, **kw: self._worker.push(attr, "cursor", *a, **kw)
|
|
171
|
+
return attr
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class WorkerConnection:
|
|
175
|
+
"""Worker connection"""
|
|
176
|
+
|
|
177
|
+
def __init__(self, *args, worker_type: WorkerType = "thread", **kwargs):
|
|
178
|
+
self._real: Worker = Worker(*args, worker_type=worker_type, **kwargs)
|
|
179
|
+
|
|
180
|
+
def __getattr__(self, item):
|
|
181
|
+
# print(self, item)
|
|
182
|
+
if item in ("_real", "cursor"):
|
|
183
|
+
return super().__getattribute__(item)
|
|
184
|
+
|
|
185
|
+
if item in ("join",):
|
|
186
|
+
return getattr(self._real, item)
|
|
187
|
+
|
|
188
|
+
conn = self._real.conn
|
|
189
|
+
attr = getattr(conn, item)
|
|
190
|
+
if item == "close":
|
|
191
|
+
return is_shutdown(self._real)
|
|
192
|
+
|
|
193
|
+
if callable(attr):
|
|
194
|
+
return lambda *a, **kw: self._real.push(attr, "connection", *a, **kw)
|
|
195
|
+
return attr
|
|
196
|
+
|
|
197
|
+
def cursor(self, *args, **kwargs):
|
|
198
|
+
"""Return cursor object"""
|
|
199
|
+
real = self._real.push(
|
|
200
|
+
getattr(self._real.conn, "cursor"), "connection", *args, **kwargs
|
|
201
|
+
)
|
|
202
|
+
return WorkerCursor(self._real, real)
|
|
203
|
+
|
|
204
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
205
|
+
if name == "_real":
|
|
206
|
+
super().__setattr__(name, value)
|
|
207
|
+
return
|
|
208
|
+
self._real.push(name, "connection", value=value)
|
|
209
|
+
|
|
210
|
+
def __enter__(self):
|
|
211
|
+
return self.cursor()
|
|
212
|
+
|
|
213
|
+
def __exit__(self, _, exc, __):
|
|
214
|
+
pass
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Database Worker"""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
from sqlite_database._utils import dict_factory, NoopResource
|
|
6
|
+
from sqlite_database.database import Database
|
|
7
|
+
from sqlite_database.workers.connection import WorkerConnection, WorkerType
|
|
8
|
+
from sqlite_database.errors import VersionError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DatabaseWorker(Database):
|
|
12
|
+
"""Database Worker"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, path: str, worker_type: WorkerType = "thread", **kwargs) -> None:
|
|
15
|
+
self._worker_type: WorkerType = worker_type
|
|
16
|
+
super().__init__(path, **kwargs)
|
|
17
|
+
|
|
18
|
+
def _create_connection(self):
|
|
19
|
+
timeout = self._kwargs.pop("timeout", 30)
|
|
20
|
+
if not isinstance(timeout, int):
|
|
21
|
+
timeout = 30
|
|
22
|
+
# conn = connect(
|
|
23
|
+
# self._path,
|
|
24
|
+
# timeout=timeout,
|
|
25
|
+
# isolation_level=self._kwargs.pop("isolation_level", None),
|
|
26
|
+
# check_same_thread=self._kwargs.pop("check_same_thread", False)
|
|
27
|
+
# )
|
|
28
|
+
# conn.row_factory = dict_factory
|
|
29
|
+
try:
|
|
30
|
+
self._database = WorkerConnection(
|
|
31
|
+
self._path,
|
|
32
|
+
worker_type=self._worker_type,
|
|
33
|
+
timeout=timeout,
|
|
34
|
+
isolation_level=self._kwargs.pop("isolation_level", None),
|
|
35
|
+
check_same_thread=self._kwargs.pop("check_same_thread", False)
|
|
36
|
+
)
|
|
37
|
+
self._database.row_factory = dict_factory
|
|
38
|
+
self._database.execute("PRAGMA journal_mode=WAL;")
|
|
39
|
+
self._database.execute(f'PRAGMA busy_timeout={timeout * 1000};')
|
|
40
|
+
except VersionError:
|
|
41
|
+
self._database = NoopResource()
|
|
42
|
+
raise
|
|
43
|
+
|
|
44
|
+
def close(self):
|
|
45
|
+
self._database.close()
|
|
46
|
+
self._database.join() # type: ignore
|
|
47
|
+
|
|
48
|
+
def join(self):
|
|
49
|
+
"""Join the worker thread/process"""
|
|
50
|
+
self._database.join() # type: ignore
|
|
51
|
+
|
|
52
|
+
def __del__(self):
|
|
53
|
+
warnings.warn(
|
|
54
|
+
(f"Instance of {self} was not properly closed." # type: ignore
|
|
55
|
+
"The process is threathened to be stalled indefinitely"
|
|
56
|
+
),
|
|
57
|
+
ResourceWarning,
|
|
58
|
+
stacklevel=2
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def __repr__(self) -> str:
|
|
62
|
+
return f"<{type(self).__name__} {self._database._real.name}>" # type: ignore
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_database
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.9
|
|
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.
|
|
@@ -88,6 +88,9 @@ sqlite_database/models/helpers.py
|
|
|
88
88
|
sqlite_database/models/mixin.py
|
|
89
89
|
sqlite_database/models/query_builder.py
|
|
90
90
|
sqlite_database/models/type_checkers.py
|
|
91
|
+
sqlite_database/workers/__init__.py
|
|
92
|
+
sqlite_database/workers/connection.py
|
|
93
|
+
sqlite_database/workers/database.py
|
|
91
94
|
tests/__init__.py
|
|
92
95
|
tests/manual_test_performances.py
|
|
93
96
|
tests/test_internals.py
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from sqlite3 import OperationalError
|
|
4
4
|
from random import randint
|
|
5
|
+
import sys
|
|
5
6
|
|
|
6
|
-
from pytest import raises
|
|
7
|
+
from pytest import mark, raises
|
|
7
8
|
from sqlite_database import Database, integer
|
|
9
|
+
from sqlite_database.workers import DatabaseWorker
|
|
8
10
|
|
|
9
11
|
from ..setup import setup_database_fns, setup_database, count
|
|
10
12
|
|
|
@@ -63,3 +65,14 @@ def test_vacuum():
|
|
|
63
65
|
_ = [t.delete_one({"a": randint(0, 1000)})]
|
|
64
66
|
t.commit()
|
|
65
67
|
db.vacuum()
|
|
68
|
+
|
|
69
|
+
# @mark.skipif(sys.version_info < (3, 13), reason="Worker feature is exclusive in 3.13")
|
|
70
|
+
def test_worker():
|
|
71
|
+
"""Test worker"""
|
|
72
|
+
|
|
73
|
+
db = DatabaseWorker(":memory:")
|
|
74
|
+
t = db.create_table("t", [integer('a')])
|
|
75
|
+
t.insert({'a': 1})
|
|
76
|
+
t.commit()
|
|
77
|
+
assert t.select_one({'a': 1}) is not None
|
|
78
|
+
db.close()
|
|
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.9}/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
|
{sqlite_database-0.7.7 → sqlite_database-0.7.9}/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
|