pl-key-value-sqlite-db 0.0.2__tar.gz → 0.0.4__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.
- {pl_key_value_sqlite_db-0.0.2 → pl_key_value_sqlite_db-0.0.4}/PKG-INFO +3 -1
- {pl_key_value_sqlite_db-0.0.2 → pl_key_value_sqlite_db-0.0.4}/pyproject.toml +4 -2
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/constants.py +31 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/create_key.py +8 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/create_key_value_key_unsafe.py +24 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/create_key_value_table_if_not_exists.py +21 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/delete_key_value.py +8 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/delete_key_value_key_unsafe.py +14 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/execute_key_value_sql.py +16 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/execute_sql.py +33 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/get_all_key_value_keys.py +18 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/key_types.py +25 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/key_value_migration.py +32 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/load_bool.py +6 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/load_date.py +8 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/load_datetime.py +8 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/load_int.py +6 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/load_key_value_unsafe.py +26 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/load_str.py +6 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/load_value_untyped.py +6 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/save_bool.py +6 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/save_date.py +11 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/save_datetime.py +8 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/save_int.py +6 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/save_key_value_value_unsafe.py +20 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/save_str.py +6 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/save_value_untyped.py +8 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/settings.py +28 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/testing/__init__.py +1 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/testing/key_value_sqlite_fake.py +49 -0
- pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/testing/settings_fake.py +12 -0
- pl_key_value_sqlite_db-0.0.2/src/pl_key_value_sqlite_db/placeholder.py +0 -2
- {pl_key_value_sqlite_db-0.0.2 → pl_key_value_sqlite_db-0.0.4}/LICENSE +0 -0
- {pl_key_value_sqlite_db-0.0.2 → pl_key_value_sqlite_db-0.0.4}/README.md +0 -0
- {pl_key_value_sqlite_db-0.0.2 → pl_key_value_sqlite_db-0.0.4}/src/pl_key_value_sqlite_db/__init__.py +0 -0
- /pl_key_value_sqlite_db-0.0.2/src/pl_key_value_sqlite_db/typed.py → /pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pl-key-value-sqlite-db
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A persistent key-value store on top of SQLite.
|
|
5
5
|
Author: Peter Lavigne
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -8,6 +8,8 @@ License-File: LICENSE
|
|
|
8
8
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
9
9
|
Requires-Dist: pl-run-program==0.0.30
|
|
10
10
|
Requires-Dist: pl-mocks-and-fakes==0.0.30
|
|
11
|
+
Requires-Dist: pl-user-io==0.0.17
|
|
12
|
+
Requires-Dist: pl-tiny-clients==0.0.37
|
|
11
13
|
Requires-Dist: pydantic>=2.12.5
|
|
12
14
|
Requires-Dist: pydantic-settings>=2.13.1
|
|
13
15
|
Requires-Python: >=3.12
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pl-key-value-sqlite-db"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.4"
|
|
4
4
|
description = "A persistent key-value store on top of SQLite."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Peter Lavigne" }]
|
|
@@ -8,6 +8,8 @@ requires-python = ">=3.12"
|
|
|
8
8
|
dependencies = [
|
|
9
9
|
"pl-run-program==0.0.30",
|
|
10
10
|
"pl-mocks-and-fakes==0.0.30",
|
|
11
|
+
"pl-user-io==0.0.17",
|
|
12
|
+
"pl-tiny-clients==0.0.37",
|
|
11
13
|
"pydantic>=2.12.5",
|
|
12
14
|
"pydantic-settings>=2.13.1",
|
|
13
15
|
]
|
|
@@ -55,7 +57,7 @@ extend-ignore = ["UP047"]
|
|
|
55
57
|
typeCheckingMode = "strict"
|
|
56
58
|
|
|
57
59
|
[tool.coverage.run]
|
|
58
|
-
omit = ["tests/*", "src/pl_key_value_sqlite_db/testing/*"]
|
|
60
|
+
omit = ["tests/*", "src/pl_key_value_sqlite_db/testing/*", "*_fake.py"]
|
|
59
61
|
|
|
60
62
|
[tool.coverage.report]
|
|
61
63
|
# Ignore all functions mocked in unit tests.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
PYTEST_INTEGRATION_MARKER = pytest.mark.integration
|
|
4
|
+
|
|
5
|
+
PYTEST_SLOW_MARKER = pytest.mark.slow
|
|
6
|
+
PYTEST_NONDETERMINISTIC_MARKER = pytest.mark.nondeterministic
|
|
7
|
+
PYTEST_DEPENDENT_MARKER = pytest.mark.dependent
|
|
8
|
+
PYTEST_EXPENSIVE_MARKER = pytest.mark.expensive
|
|
9
|
+
PYTEST_MANUAL_MARKER = pytest.mark.manual
|
|
10
|
+
|
|
11
|
+
PYTEST_THIRD_PARTY_API_MARKERS = [
|
|
12
|
+
PYTEST_SLOW_MARKER,
|
|
13
|
+
PYTEST_NONDETERMINISTIC_MARKER,
|
|
14
|
+
PYTEST_DEPENDENT_MARKER,
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
PYTEST_MANUAL_MARKERS = [
|
|
18
|
+
PYTEST_SLOW_MARKER,
|
|
19
|
+
PYTEST_NONDETERMINISTIC_MARKER,
|
|
20
|
+
PYTEST_DEPENDENT_MARKER,
|
|
21
|
+
PYTEST_MANUAL_MARKER,
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
PYTEST_INTEGRATION_TEST_MARKERS = [
|
|
25
|
+
PYTEST_INTEGRATION_MARKER,
|
|
26
|
+
PYTEST_SLOW_MARKER,
|
|
27
|
+
PYTEST_NONDETERMINISTIC_MARKER,
|
|
28
|
+
PYTEST_DEPENDENT_MARKER,
|
|
29
|
+
PYTEST_EXPENSIVE_MARKER,
|
|
30
|
+
PYTEST_MANUAL_MARKER,
|
|
31
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
2
|
+
|
|
3
|
+
from pl_key_value_sqlite_db.create_key_value_table_if_not_exists import (
|
|
4
|
+
KEY_COLUMN_NAME,
|
|
5
|
+
TABLE_NAME,
|
|
6
|
+
VALUE_COLUMN_NAME,
|
|
7
|
+
)
|
|
8
|
+
from pl_key_value_sqlite_db.execute_key_value_sql import execute_key_value_sql
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@MockInUnitTests(MockReason.UNINVESTIGATED)
|
|
12
|
+
def create_key_value_key_unsafe(key: str, value: str) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Create a key in the database without type-safety.
|
|
15
|
+
|
|
16
|
+
You should probably use key_value.create_key instead.
|
|
17
|
+
"""
|
|
18
|
+
sql = f"""
|
|
19
|
+
INSERT INTO {TABLE_NAME}
|
|
20
|
+
({KEY_COLUMN_NAME}, {VALUE_COLUMN_NAME})
|
|
21
|
+
VALUES (?, ?)
|
|
22
|
+
"""
|
|
23
|
+
with execute_key_value_sql(sql, (key, value)):
|
|
24
|
+
pass
|
pl_key_value_sqlite_db-0.0.4/src/pl_key_value_sqlite_db/create_key_value_table_if_not_exists.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from types import NoneType
|
|
2
|
+
|
|
3
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
4
|
+
|
|
5
|
+
from pl_key_value_sqlite_db.execute_key_value_sql import execute_key_value_sql
|
|
6
|
+
|
|
7
|
+
TABLE_NAME = "key_value"
|
|
8
|
+
KEY_COLUMN_NAME = "key"
|
|
9
|
+
VALUE_COLUMN_NAME = "value"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@MockInUnitTests(MockReason.UNINVESTIGATED)
|
|
13
|
+
def create_key_value_table_if_not_exists() -> NoneType:
|
|
14
|
+
with execute_key_value_sql(f"""
|
|
15
|
+
CREATE TABLE IF NOT EXISTS {TABLE_NAME} (
|
|
16
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
17
|
+
{KEY_COLUMN_NAME} TEXT NOT NULL UNIQUE,
|
|
18
|
+
{VALUE_COLUMN_NAME} TEXT NOT NULL
|
|
19
|
+
)
|
|
20
|
+
"""):
|
|
21
|
+
pass
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
2
|
+
|
|
3
|
+
from pl_key_value_sqlite_db.create_key_value_table_if_not_exists import TABLE_NAME
|
|
4
|
+
from pl_key_value_sqlite_db.execute_key_value_sql import execute_key_value_sql
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@MockInUnitTests(MockReason.UNINVESTIGATED)
|
|
8
|
+
def delete_key_value_key_unsafe(key: str) -> None:
|
|
9
|
+
sql = f"""
|
|
10
|
+
DELETE FROM {TABLE_NAME}
|
|
11
|
+
WHERE key = ?
|
|
12
|
+
"""
|
|
13
|
+
with execute_key_value_sql(sql, (key,)):
|
|
14
|
+
pass
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
|
|
4
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
5
|
+
|
|
6
|
+
from pl_key_value_sqlite_db.execute_sql import ExecuteSqlResponse, execute_sql
|
|
7
|
+
from pl_key_value_sqlite_db.settings import get_settings
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@MockInUnitTests(MockReason.UNINVESTIGATED)
|
|
11
|
+
@contextmanager
|
|
12
|
+
def execute_key_value_sql(
|
|
13
|
+
sql: str, params: tuple[str, ...] = ()
|
|
14
|
+
) -> Generator[ExecuteSqlResponse]:
|
|
15
|
+
with execute_sql(sql, get_settings().key_value_db_path, params) as cursor:
|
|
16
|
+
yield cursor
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sqlite3
|
|
3
|
+
from collections.abc import Callable, Generator
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Any, NamedTuple
|
|
6
|
+
|
|
7
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ExecuteSqlResponse(NamedTuple):
|
|
11
|
+
fetch_one: Callable[[], tuple[Any, ...] | None]
|
|
12
|
+
fetch_all: Callable[[], list[tuple[Any, ...]]]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@MockInUnitTests(MockReason.UNINVESTIGATED)
|
|
16
|
+
@contextmanager
|
|
17
|
+
def execute_sql(
|
|
18
|
+
sql: str, database_file_path: str, params: tuple[str, ...] = ()
|
|
19
|
+
) -> Generator[ExecuteSqlResponse]:
|
|
20
|
+
conn = sqlite3.connect(
|
|
21
|
+
database_file_path
|
|
22
|
+
) # Creates the DB file if it doesn't exist
|
|
23
|
+
cursor = conn.cursor()
|
|
24
|
+
logging.debug(f"Executing SQL:\n```\n{sql}\n```\nwith params: {params}")
|
|
25
|
+
try:
|
|
26
|
+
cursor.execute(sql, params)
|
|
27
|
+
yield ExecuteSqlResponse(
|
|
28
|
+
fetch_one=cursor.fetchone,
|
|
29
|
+
fetch_all=cursor.fetchall,
|
|
30
|
+
)
|
|
31
|
+
conn.commit()
|
|
32
|
+
finally:
|
|
33
|
+
conn.close()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
4
|
+
|
|
5
|
+
from pl_key_value_sqlite_db.create_key_value_table_if_not_exists import TABLE_NAME
|
|
6
|
+
from pl_key_value_sqlite_db.execute_key_value_sql import execute_key_value_sql
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@MockInUnitTests(MockReason.UNINVESTIGATED)
|
|
10
|
+
def get_all_key_value_keys() -> list[str]:
|
|
11
|
+
sql = f"""
|
|
12
|
+
SELECT key
|
|
13
|
+
FROM {TABLE_NAME}
|
|
14
|
+
"""
|
|
15
|
+
with execute_key_value_sql(sql) as cursor:
|
|
16
|
+
result = [row[0] for row in cursor.fetch_all()]
|
|
17
|
+
logging.debug(f"Returned value: {result}")
|
|
18
|
+
return result
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BaseKey(Enum):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DateKey(BaseKey):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BoolKey(BaseKey):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DatetimeKey(BaseKey):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StrKey(BaseKey):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class IntKey(BaseKey):
|
|
25
|
+
pass
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from pl_key_value_sqlite_db.create_key import create_key
|
|
5
|
+
from pl_key_value_sqlite_db.create_key_value_table_if_not_exists import (
|
|
6
|
+
create_key_value_table_if_not_exists,
|
|
7
|
+
)
|
|
8
|
+
from pl_key_value_sqlite_db.delete_key_value_key_unsafe import (
|
|
9
|
+
delete_key_value_key_unsafe,
|
|
10
|
+
)
|
|
11
|
+
from pl_key_value_sqlite_db.get_all_key_value_keys import get_all_key_value_keys
|
|
12
|
+
from pl_key_value_sqlite_db.key_types import BaseKey
|
|
13
|
+
from pl_key_value_sqlite_db.settings import get_settings
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_migrations(initial_entries: dict[BaseKey, str]) -> None:
|
|
17
|
+
logging.info("Checking for migrations to run.")
|
|
18
|
+
|
|
19
|
+
if not Path(get_settings().key_value_db_path).exists():
|
|
20
|
+
create_key_value_table_if_not_exists()
|
|
21
|
+
|
|
22
|
+
existing_keys = get_all_key_value_keys()
|
|
23
|
+
|
|
24
|
+
for key, value in initial_entries.items():
|
|
25
|
+
if key.value not in existing_keys:
|
|
26
|
+
create_key(key, value)
|
|
27
|
+
|
|
28
|
+
for key in existing_keys:
|
|
29
|
+
if key not in [k.value for k in initial_entries]:
|
|
30
|
+
delete_key_value_key_unsafe(key)
|
|
31
|
+
|
|
32
|
+
logging.info("Finished running migrations.")
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from pl_key_value_sqlite_db.key_types import DatetimeKey
|
|
4
|
+
from pl_key_value_sqlite_db.load_value_untyped import load_value_untyped
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def load_datetime(key: DatetimeKey) -> datetime:
|
|
8
|
+
return datetime.fromisoformat(load_value_untyped(key))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
4
|
+
|
|
5
|
+
from pl_key_value_sqlite_db.create_key_value_table_if_not_exists import TABLE_NAME
|
|
6
|
+
from pl_key_value_sqlite_db.execute_key_value_sql import execute_key_value_sql
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@MockInUnitTests(MockReason.UNINVESTIGATED)
|
|
10
|
+
def load_key_value_unsafe(key: str) -> str:
|
|
11
|
+
"""
|
|
12
|
+
Get a value in the database by key without type-safety.
|
|
13
|
+
|
|
14
|
+
You should probably use key_value.get_value instead.
|
|
15
|
+
"""
|
|
16
|
+
sql = f"""
|
|
17
|
+
SELECT value
|
|
18
|
+
FROM {TABLE_NAME}
|
|
19
|
+
WHERE key = ?
|
|
20
|
+
"""
|
|
21
|
+
with execute_key_value_sql(sql, (key,)) as cursor:
|
|
22
|
+
result = cursor.fetch_one()
|
|
23
|
+
assert result is not None, f"Key '{key}' not found in database."
|
|
24
|
+
result_value = result[0]
|
|
25
|
+
logging.debug(f"Returned value: {result_value}")
|
|
26
|
+
return result_value
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from pl_key_value_sqlite_db.key_types import DateKey
|
|
4
|
+
from pl_key_value_sqlite_db.save_value_untyped import save_value_untyped
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def save_date(key: DateKey, value: date) -> None:
|
|
8
|
+
if value.__class__.__name__ != "date":
|
|
9
|
+
msg = f"Expected date object, got {value.__class__.__name__}"
|
|
10
|
+
raise TypeError(msg)
|
|
11
|
+
save_value_untyped(key, value.isoformat())
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from pl_key_value_sqlite_db.key_types import DatetimeKey
|
|
4
|
+
from pl_key_value_sqlite_db.save_value_untyped import save_value_untyped
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def save_datetime(key: DatetimeKey, value: datetime) -> None:
|
|
8
|
+
save_value_untyped(key, value.isoformat())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
2
|
+
|
|
3
|
+
from pl_key_value_sqlite_db.create_key_value_table_if_not_exists import TABLE_NAME
|
|
4
|
+
from pl_key_value_sqlite_db.execute_key_value_sql import execute_key_value_sql
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@MockInUnitTests(MockReason.UNINVESTIGATED)
|
|
8
|
+
def save_key_value_value_unsafe(key: str, value: str) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Set a value in the database by key without type-safety.
|
|
11
|
+
|
|
12
|
+
You should probably use key_value.set_value instead.
|
|
13
|
+
"""
|
|
14
|
+
sql = f"""
|
|
15
|
+
UPDATE {TABLE_NAME}
|
|
16
|
+
SET value = ?
|
|
17
|
+
WHERE key = ?
|
|
18
|
+
"""
|
|
19
|
+
with execute_key_value_sql(sql, (value, key)):
|
|
20
|
+
pass
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from pl_key_value_sqlite_db.key_types import BaseKey
|
|
2
|
+
from pl_key_value_sqlite_db.save_key_value_value_unsafe import (
|
|
3
|
+
save_key_value_value_unsafe,
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def save_value_untyped(key: BaseKey, value: str) -> None:
|
|
8
|
+
save_key_value_value_unsafe(key.value, value)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from pl_mocks_and_fakes import MockInUnitTests, MockReason
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
from pydantic_settings import (
|
|
6
|
+
BaseSettings,
|
|
7
|
+
SettingsConfigDict,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Settings(BaseSettings):
|
|
12
|
+
model_config = SettingsConfigDict(
|
|
13
|
+
env_file=".env",
|
|
14
|
+
extra="ignore",
|
|
15
|
+
secrets_dir=Path.home() / ".secrets-files",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Env-specific config
|
|
19
|
+
key_value_db_path: str = Field(
|
|
20
|
+
validation_alias="KEY_VALUE_DB_PATH", default="~/bin/key_value.db"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@MockInUnitTests(MockReason.UNMITIGATED_SIDE_EFFECT)
|
|
25
|
+
def get_settings() -> Settings:
|
|
26
|
+
"""Get settings. Raises exception if required settings are missing or invalid."""
|
|
27
|
+
# Required fields are populated from env/.env at runtime; pyright doesn't model that.
|
|
28
|
+
return Settings() # pyright: ignore[reportCallIssue]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__all__ = []
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from pl_mocks_and_fakes import Fake, mock_for
|
|
2
|
+
|
|
3
|
+
from pl_key_value_sqlite_db.create_key_value_key_unsafe import (
|
|
4
|
+
create_key_value_key_unsafe,
|
|
5
|
+
)
|
|
6
|
+
from pl_key_value_sqlite_db.delete_key_value_key_unsafe import (
|
|
7
|
+
delete_key_value_key_unsafe,
|
|
8
|
+
)
|
|
9
|
+
from pl_key_value_sqlite_db.get_all_key_value_keys import get_all_key_value_keys
|
|
10
|
+
from pl_key_value_sqlite_db.load_key_value_unsafe import load_key_value_unsafe
|
|
11
|
+
from pl_key_value_sqlite_db.save_key_value_value_unsafe import (
|
|
12
|
+
save_key_value_value_unsafe,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class KeyValueSqliteFake(Fake):
|
|
17
|
+
def __init__(self) -> None:
|
|
18
|
+
def _get_all_keys_side_effect() -> list[str]:
|
|
19
|
+
return list(self.store.keys())
|
|
20
|
+
|
|
21
|
+
def _create_key_unsafe_side_effect(key: str, value: str) -> None:
|
|
22
|
+
if key not in self.store:
|
|
23
|
+
self.store[key] = value
|
|
24
|
+
|
|
25
|
+
def _delete_key_unsafe_side_effect(key: str) -> None:
|
|
26
|
+
del self.store[key]
|
|
27
|
+
|
|
28
|
+
def _load_value_unsafe_side_effect(key: str) -> str:
|
|
29
|
+
return self.store[key]
|
|
30
|
+
|
|
31
|
+
def _save_value_unsafe_side_effect(key: str, value: str) -> None:
|
|
32
|
+
if key not in self.store:
|
|
33
|
+
msg = f"Key `{key}` does not exist."
|
|
34
|
+
raise Exception(msg)
|
|
35
|
+
self.store[key] = value
|
|
36
|
+
|
|
37
|
+
self.store: dict[str, str] = {}
|
|
38
|
+
|
|
39
|
+
mock_for(get_all_key_value_keys).side_effect = _get_all_keys_side_effect
|
|
40
|
+
mock_for(
|
|
41
|
+
create_key_value_key_unsafe
|
|
42
|
+
).side_effect = _create_key_unsafe_side_effect
|
|
43
|
+
mock_for(
|
|
44
|
+
delete_key_value_key_unsafe
|
|
45
|
+
).side_effect = _delete_key_unsafe_side_effect
|
|
46
|
+
mock_for(load_key_value_unsafe).side_effect = _load_value_unsafe_side_effect
|
|
47
|
+
mock_for(
|
|
48
|
+
save_key_value_value_unsafe
|
|
49
|
+
).side_effect = _save_value_unsafe_side_effect
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from pl_mocks_and_fakes import Fake, stub
|
|
2
|
+
|
|
3
|
+
from pl_key_value_sqlite_db.settings import Settings, get_settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SettingsFake(Fake):
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
self.settings = Settings(
|
|
9
|
+
key_value_db_path="",
|
|
10
|
+
)
|
|
11
|
+
self.settings.key_value_db_path = ""
|
|
12
|
+
stub(get_settings)(self.settings)
|
|
File without changes
|
|
File without changes
|
{pl_key_value_sqlite_db-0.0.2 → pl_key_value_sqlite_db-0.0.4}/src/pl_key_value_sqlite_db/__init__.py
RENAMED
|
File without changes
|