style fix
This commit is contained in:
parent
56e6160e6a
commit
a103364f1a
@ -3,21 +3,21 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'VDELETE',
|
"VDELETE",
|
||||||
'KVProtocol',
|
"KVProtocol",
|
||||||
'KVFactory',
|
"KVFactory",
|
||||||
'KVJson',
|
"KVJson",
|
||||||
'VirtualConnection',
|
"VirtualConnection",
|
||||||
'ExtendedVirtualConnection',
|
"ExtendedVirtualConnection",
|
||||||
'DbInterface',
|
"DbInterface",
|
||||||
'AbstractDbConnection',
|
"AbstractDbConnection",
|
||||||
'DbConnection',
|
"DbConnection",
|
||||||
'DbManager',
|
"DbManager",
|
||||||
'DbFactory',
|
"DbFactory",
|
||||||
'Db',
|
"Db",
|
||||||
'Transaction',
|
"Transaction",
|
||||||
'TransactionView',
|
"TransactionView",
|
||||||
'FutureContext',
|
"FutureContext",
|
||||||
)
|
)
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
@ -33,9 +33,7 @@ from typing import IO, Any, Protocol, TypeAlias
|
|||||||
|
|
||||||
|
|
||||||
class Request:
|
class Request:
|
||||||
__slots__ = (
|
__slots__ = ("__future",)
|
||||||
'__future',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, future: asyncio.Future | None, /) -> None:
|
def __init__(self, future: asyncio.Future | None, /) -> None:
|
||||||
self.__future = future
|
self.__future = future
|
||||||
@ -57,9 +55,7 @@ class Request:
|
|||||||
|
|
||||||
|
|
||||||
class LineRequest(Request):
|
class LineRequest(Request):
|
||||||
__slots__ = (
|
__slots__ = ("line",)
|
||||||
'line',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, line: str, /, *, future: asyncio.Future | None) -> None:
|
def __init__(self, line: str, /, *, future: asyncio.Future | None) -> None:
|
||||||
super().__init__(future)
|
super().__init__(future)
|
||||||
@ -76,7 +72,8 @@ VDELETE = object()
|
|||||||
|
|
||||||
|
|
||||||
class KVFactory(KVProtocol):
|
class KVFactory(KVProtocol):
|
||||||
"""this class is for working with already normalised data values, not for data transformation (e.g. reducing keys to a common form).
|
"""\
|
||||||
|
this class is for working with already normalised data values, not for data transformation (e.g. reducing keys to a common form).
|
||||||
that functionality may be added in the future, though, probably, only for custom DbConnection implementations.
|
that functionality may be added in the future, though, probably, only for custom DbConnection implementations.
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
|
|
||||||
@ -84,18 +81,21 @@ note: unstable signature."""
|
|||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def line(self, key: Any, value: Any, /) -> str:
|
def line(self, key: Any, value: Any, /) -> str:
|
||||||
"""line must contain exactly one '\\n' at exactly the end if the line is not empty.
|
"""\
|
||||||
|
line must contain exactly one '\\n' at exactly the end if the line is not empty.
|
||||||
note: other forms of requests will later be represented by different methods or by instances of Action class."""
|
note: other forms of requests will later be represented by different methods or by instances of Action class."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def fromline(self, line: str, /) -> tuple[Any, Any]:
|
def fromline(self, line: str, /) -> tuple[Any, Any]:
|
||||||
"""inverse of line().
|
"""\
|
||||||
|
inverse of line().
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def run(self, line: str, db: dict, reduce: bool, /) -> None:
|
def run(self, line: str, db: dict, reduce: bool, /) -> None:
|
||||||
"""run request against the db.
|
"""\
|
||||||
|
run request against the db.
|
||||||
extensible to allow forms of requests other than set.
|
extensible to allow forms of requests other than set.
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
key, value = self.fromline(line)
|
key, value = self.fromline(line)
|
||||||
@ -121,18 +121,21 @@ note: unstable signature."""
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
def request(self, key: Any, value: Any, /, *, future: asyncio.Future | None) -> KVRequest:
|
def request(self, key: Any, value: Any, /, *, future: asyncio.Future | None) -> KVRequest:
|
||||||
"""form request with Future.
|
"""\
|
||||||
|
form request with Future.
|
||||||
low-level API.
|
low-level API.
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
return KVRequest(key, value, future=future, factory=self)
|
return KVRequest(key, value, future=future, factory=self)
|
||||||
|
|
||||||
def free(self, key: Any, value: Any, /) -> KVRequest:
|
def free(self, key: Any, value: Any, /) -> KVRequest:
|
||||||
"""result free from Future.
|
"""\
|
||||||
|
result free from Future.
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
return self.request(key, value, future=None)
|
return self.request(key, value, future=None)
|
||||||
|
|
||||||
def io2db(self, io: IO[str], db: dict, reduce: bool, /) -> int:
|
def io2db(self, io: IO[str], db: dict, reduce: bool, /) -> int:
|
||||||
"""note: unstable signature."""
|
"""\
|
||||||
|
note: unstable signature."""
|
||||||
size = 0
|
size = 0
|
||||||
for line in io:
|
for line in io:
|
||||||
self.run(line, db, reduce)
|
self.run(line, db, reduce)
|
||||||
@ -140,7 +143,8 @@ note: unstable signature."""
|
|||||||
return size
|
return size
|
||||||
|
|
||||||
def db2io(self, db: dict, io: IO[str], /) -> int:
|
def db2io(self, db: dict, io: IO[str], /) -> int:
|
||||||
"""note: unstable signature."""
|
"""\
|
||||||
|
note: unstable signature."""
|
||||||
size = 0
|
size = 0
|
||||||
for key, value in db.items():
|
for key, value in db.items():
|
||||||
size += io.write(self.line(key, value))
|
size += io.write(self.line(key, value))
|
||||||
@ -148,11 +152,11 @@ note: unstable signature."""
|
|||||||
|
|
||||||
def path2db_sync(self, path: pathlib.Path, db: dict, /) -> int:
|
def path2db_sync(self, path: pathlib.Path, db: dict, /) -> int:
|
||||||
path.touch()
|
path.touch()
|
||||||
with path.open('r') as file:
|
with path.open("r") as file:
|
||||||
return self.io2db(file, db, True)
|
return self.io2db(file, db, True)
|
||||||
|
|
||||||
def db2path_sync(self, db: dict, path: pathlib.Path, /) -> int:
|
def db2path_sync(self, db: dict, path: pathlib.Path, /) -> int:
|
||||||
with path.open('w') as file:
|
with path.open("w") as file:
|
||||||
initial_size = self.db2io(db, file)
|
initial_size = self.db2io(db, file)
|
||||||
os.fsync(file.fileno())
|
os.fsync(file.fileno())
|
||||||
return initial_size
|
return initial_size
|
||||||
@ -160,9 +164,9 @@ note: unstable signature."""
|
|||||||
|
|
||||||
class KVRequest(LineRequest):
|
class KVRequest(LineRequest):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__factory',
|
"__factory",
|
||||||
'key',
|
"key",
|
||||||
'value',
|
"value",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, key: Any, value: Any, /, *, future: asyncio.Future | None, factory: KVFactory) -> None:
|
def __init__(self, key: Any, value: Any, /, *, future: asyncio.Future | None, factory: KVFactory) -> None:
|
||||||
@ -187,10 +191,10 @@ class KVJson(KVFactory):
|
|||||||
|
|
||||||
def line(self, key: Any, value: Any, /) -> str:
|
def line(self, key: Any, value: Any, /) -> str:
|
||||||
if value is VDELETE:
|
if value is VDELETE:
|
||||||
obj = {'key': key}
|
obj = {"key": key}
|
||||||
else:
|
else:
|
||||||
obj = {'key': key, 'value': value}
|
obj = {"key": key, "value": value}
|
||||||
return json.dumps(obj) + '\n'
|
return json.dumps(obj) + "\n"
|
||||||
|
|
||||||
def _load_key(self, key: Any, /) -> Hashable:
|
def _load_key(self, key: Any, /) -> Hashable:
|
||||||
"""note: unstable signature."""
|
"""note: unstable signature."""
|
||||||
@ -202,17 +206,15 @@ class KVJson(KVFactory):
|
|||||||
case dict():
|
case dict():
|
||||||
return tuple((self._load_key(k), self._load_key(v)) for k, v in key.items())
|
return tuple((self._load_key(k), self._load_key(v)) for k, v in key.items())
|
||||||
case _:
|
case _:
|
||||||
raise TypeError('unknown KVJson key type, cannot convert to hashable')
|
raise TypeError("unknown KVJson key type, cannot convert to hashable")
|
||||||
|
|
||||||
def fromline(self, line: str, /) -> tuple[Any, Any]:
|
def fromline(self, line: str, /) -> tuple[Any, Any]:
|
||||||
d = json.loads(line)
|
d = json.loads(line)
|
||||||
return self._load_key(d['key']), d.get('value', VDELETE)
|
return self._load_key(d["key"]), d.get("value", VDELETE)
|
||||||
|
|
||||||
|
|
||||||
class TransactionRequest(LineRequest):
|
class TransactionRequest(LineRequest):
|
||||||
__slots__ = (
|
__slots__ = ("buffer",)
|
||||||
'buffer',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, buffer: StringIO, /, *, future: asyncio.Future | None) -> None:
|
def __init__(self, buffer: StringIO, /, *, future: asyncio.Future | None) -> None:
|
||||||
super().__init__(buffer.getvalue(), future=future)
|
super().__init__(buffer.getvalue(), future=future)
|
||||||
@ -221,9 +223,9 @@ class TransactionRequest(LineRequest):
|
|||||||
|
|
||||||
class DbParameters:
|
class DbParameters:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'path',
|
"path",
|
||||||
'kvfactory',
|
"kvfactory",
|
||||||
'buffersize',
|
"buffersize",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, path: pathlib.Path, /, *, kvfactory: KVFactory, buffersize: int) -> None:
|
def __init__(self, path: pathlib.Path, /, *, kvfactory: KVFactory, buffersize: int) -> None:
|
||||||
@ -239,9 +241,7 @@ class RequestToClosedConnection(asyncio.InvalidStateError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class VirtualConnection(
|
class VirtualConnection(abc.ABC):
|
||||||
abc.ABC
|
|
||||||
):
|
|
||||||
"""minimal intersection of DbConnection and TransactionView functionality"""
|
"""minimal intersection of DbConnection and TransactionView functionality"""
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
@ -270,9 +270,7 @@ class VirtualConnection(
|
|||||||
return Transaction(self)
|
return Transaction(self)
|
||||||
|
|
||||||
|
|
||||||
class ExtendedVirtualConnection(
|
class ExtendedVirtualConnection(VirtualConnection, abc.ABC):
|
||||||
VirtualConnection, abc.ABC
|
|
||||||
):
|
|
||||||
"""maximal intersection of DbConnection and TransactionView functionality"""
|
"""maximal intersection of DbConnection and TransactionView functionality"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
@ -288,9 +286,7 @@ class ExtendedVirtualConnection(
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class DbInterface(
|
class DbInterface(ExtendedVirtualConnection, abc.ABC):
|
||||||
ExtendedVirtualConnection, abc.ABC
|
|
||||||
):
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def set(self, key: Any, value: Any, /) -> None:
|
async def set(self, key: Any, value: Any, /) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -302,17 +298,20 @@ class AbstractDbConnection(Protocol):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def set(self, key: Any, value: Any, /) -> None:
|
async def set(self, key: Any, value: Any, /) -> None:
|
||||||
"""this method may take time to run.
|
"""\
|
||||||
|
this method may take time to run.
|
||||||
ordering may not be guaranteed (depends on event loop implementation)."""
|
ordering may not be guaranteed (depends on event loop implementation)."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def set_nowait(self, key: Any, value: Any, /) -> None:
|
def set_nowait(self, key: Any, value: Any, /) -> None:
|
||||||
"""this method is instant.
|
"""\
|
||||||
|
this method is instant.
|
||||||
ordering is guaranteed."""
|
ordering is guaranteed."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def commit(self, /) -> None:
|
async def commit(self, /) -> None:
|
||||||
"""this method may take time to run.
|
"""\
|
||||||
|
this method may take time to run.
|
||||||
respects the ordering of previously called :meth:`~ptvp35.AbstractDbConnection.set_nowait` methods.
|
respects the ordering of previously called :meth:`~ptvp35.AbstractDbConnection.set_nowait` methods.
|
||||||
will, depending on event loop implementation, also execute later changes."""
|
will, depending on event loop implementation, also execute later changes."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -322,9 +321,7 @@ will, depending on event loop implementation, also execute later changes."""
|
|||||||
|
|
||||||
|
|
||||||
class _Loop:
|
class _Loop:
|
||||||
__slots__ = (
|
__slots__ = ("__loop",)
|
||||||
'__loop',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, loop: asyncio.AbstractEventLoop, /) -> None:
|
def __init__(self, loop: asyncio.AbstractEventLoop, /) -> None:
|
||||||
self.__loop = loop
|
self.__loop = loop
|
||||||
@ -336,7 +333,8 @@ class _Loop:
|
|||||||
return self.__loop
|
return self.__loop
|
||||||
|
|
||||||
def run_in_thread(self, name: str, fn, /, *args, **kwargs) -> asyncio.Future:
|
def run_in_thread(self, name: str, fn, /, *args, **kwargs) -> asyncio.Future:
|
||||||
"""we are using our own thread to guarantee as much of autonomy and control as possible.
|
"""\
|
||||||
|
we are using our own thread to guarantee as much of autonomy and control as possible.
|
||||||
intended for heavy tasks."""
|
intended for heavy tasks."""
|
||||||
future = self.create_future()
|
future = self.create_future()
|
||||||
|
|
||||||
@ -344,45 +342,37 @@ intended for heavy tasks."""
|
|||||||
try:
|
try:
|
||||||
result = fn(*args, **kwargs)
|
result = fn(*args, **kwargs)
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
self.__loop.call_soon_threadsafe(
|
self.__loop.call_soon_threadsafe(future.set_exception, exception)
|
||||||
future.set_exception, exception
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.__loop.call_soon_threadsafe(
|
self.__loop.call_soon_threadsafe(future.set_result, result)
|
||||||
future.set_result, result
|
|
||||||
)
|
fname = getattr(fn, "__name__", "?")
|
||||||
fname = getattr(fn, '__name__', '?')
|
threading.Thread(target=wrap, name=f"persistence5-{name}-{fname}").start()
|
||||||
threading.Thread(
|
|
||||||
target=wrap,
|
|
||||||
name=f'persistence5-{name}-{fname}'
|
|
||||||
).start()
|
|
||||||
|
|
||||||
return future
|
return future
|
||||||
|
|
||||||
|
|
||||||
class _Errors:
|
class _Errors:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__path',
|
"__path",
|
||||||
'__loop',
|
"__loop",
|
||||||
'__event_loop',
|
"__event_loop",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, path: pathlib.Path, loop: _Loop, /) -> None:
|
def __init__(self, path: pathlib.Path, loop: _Loop, /) -> None:
|
||||||
self.__path = path.with_name(path.name + '.error')
|
self.__path = path.with_name(path.name + ".error")
|
||||||
self.__loop = loop
|
self.__loop = loop
|
||||||
self.__event_loop = loop.loop()
|
self.__event_loop = loop.loop()
|
||||||
|
|
||||||
def _save_sync(self, line: str, /) -> None:
|
def _save_sync(self, line: str, /) -> None:
|
||||||
with self.__path.open('a') as file:
|
with self.__path.open("a") as file:
|
||||||
file.write(line.strip() + '\n')
|
file.write(line.strip() + "\n")
|
||||||
|
|
||||||
async def _save(self, line: str, /) -> None:
|
async def _save(self, line: str, /) -> None:
|
||||||
await self.__event_loop.run_in_executor(None, self._save_sync, line)
|
await self.__event_loop.run_in_executor(None, self._save_sync, line)
|
||||||
|
|
||||||
def _schedule(self, line: str, /) -> concurrent.futures.Future:
|
def _schedule(self, line: str, /) -> concurrent.futures.Future:
|
||||||
return asyncio.run_coroutine_threadsafe(
|
return asyncio.run_coroutine_threadsafe(self._save(line), self.__event_loop)
|
||||||
self._save(line), self.__event_loop
|
|
||||||
)
|
|
||||||
|
|
||||||
def save_from_thread(self, line: str, /) -> None:
|
def save_from_thread(self, line: str, /) -> None:
|
||||||
self._schedule(line).result()
|
self._schedule(line).result()
|
||||||
@ -390,8 +380,8 @@ class _Errors:
|
|||||||
|
|
||||||
class _File:
|
class _File:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__path',
|
"__path",
|
||||||
'__file',
|
"__file",
|
||||||
)
|
)
|
||||||
|
|
||||||
__file: IO[str]
|
__file: IO[str]
|
||||||
@ -414,7 +404,7 @@ class _File:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def open_sync(self, /) -> None:
|
def open_sync(self, /) -> None:
|
||||||
self.__file = self.__path.open('a')
|
self.__file = self.__path.open("a")
|
||||||
|
|
||||||
def close_sync(self, /) -> None:
|
def close_sync(self, /) -> None:
|
||||||
self.__file.close()
|
self.__file.close()
|
||||||
@ -423,13 +413,13 @@ class _File:
|
|||||||
|
|
||||||
class _Backup:
|
class _Backup:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__file',
|
"__file",
|
||||||
'__kvfactory',
|
"__kvfactory",
|
||||||
'__loop',
|
"__loop",
|
||||||
'__path',
|
"__path",
|
||||||
'__backup',
|
"__backup",
|
||||||
'__recover',
|
"__recover",
|
||||||
'__initial_size',
|
"__initial_size",
|
||||||
)
|
)
|
||||||
|
|
||||||
__initial_size: int
|
__initial_size: int
|
||||||
@ -439,8 +429,8 @@ class _Backup:
|
|||||||
self.__kvfactory = kvfactory
|
self.__kvfactory = kvfactory
|
||||||
self.__loop = loop
|
self.__loop = loop
|
||||||
self.__path = path
|
self.__path = path
|
||||||
self.__backup = path.with_name(path.name + '.backup')
|
self.__backup = path.with_name(path.name + ".backup")
|
||||||
self.__recover = path.with_name(path.name + '.recover')
|
self.__recover = path.with_name(path.name + ".recover")
|
||||||
|
|
||||||
def file(self, /) -> _File:
|
def file(self, /) -> _File:
|
||||||
return self.__file
|
return self.__file
|
||||||
@ -502,12 +492,12 @@ class _Backup:
|
|||||||
|
|
||||||
class _Guard:
|
class _Guard:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__backup',
|
"__backup",
|
||||||
'__error',
|
"__error",
|
||||||
'__file',
|
"__file",
|
||||||
'__path',
|
"__path",
|
||||||
'__truncate',
|
"__truncate",
|
||||||
'__flag',
|
"__flag",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, backup: _Backup, error: _Errors, /) -> None:
|
def __init__(self, backup: _Backup, error: _Errors, /) -> None:
|
||||||
@ -515,8 +505,8 @@ class _Guard:
|
|||||||
self.__error = error
|
self.__error = error
|
||||||
self.__file = backup.file()
|
self.__file = backup.file()
|
||||||
self.__path = path = self.__file.path()
|
self.__path = path = self.__file.path()
|
||||||
self.__truncate = path.with_name(path.name + '.truncate')
|
self.__truncate = path.with_name(path.name + ".truncate")
|
||||||
self.__flag = path.with_name(path.name + '.truncate_flag')
|
self.__flag = path.with_name(path.name + ".truncate_flag")
|
||||||
|
|
||||||
def backup(self, /) -> _Backup:
|
def backup(self, /) -> _Backup:
|
||||||
return self.__backup
|
return self.__backup
|
||||||
@ -526,13 +516,13 @@ class _Guard:
|
|||||||
self.__truncate.write_bytes(s)
|
self.__truncate.write_bytes(s)
|
||||||
|
|
||||||
def _write_value_sync(self, value: int, /) -> None:
|
def _write_value_sync(self, value: int, /) -> None:
|
||||||
self._write_bytes_sync(value.to_bytes(16, 'little'))
|
self._write_bytes_sync(value.to_bytes(16, "little"))
|
||||||
|
|
||||||
def _read_bytes_sync(self, /) -> bytes:
|
def _read_bytes_sync(self, /) -> bytes:
|
||||||
return self.__truncate.read_bytes()
|
return self.__truncate.read_bytes()
|
||||||
|
|
||||||
def _read_value_sync(self, /) -> int:
|
def _read_value_sync(self, /) -> int:
|
||||||
return int.from_bytes(self._read_bytes_sync(), 'little')
|
return int.from_bytes(self._read_bytes_sync(), "little")
|
||||||
|
|
||||||
def _set_sync(self, /) -> None:
|
def _set_sync(self, /) -> None:
|
||||||
self._write_value_sync(self.__file.tell())
|
self._write_value_sync(self.__file.tell())
|
||||||
@ -543,7 +533,7 @@ class _Guard:
|
|||||||
self.__truncate.unlink(missing_ok=True)
|
self.__truncate.unlink(missing_ok=True)
|
||||||
|
|
||||||
def _truncate_sync(self, /) -> None:
|
def _truncate_sync(self, /) -> None:
|
||||||
with self.__path.open('r+') as file:
|
with self.__path.open("r+") as file:
|
||||||
self._file_truncate_sync(file, self._read_value_sync())
|
self._file_truncate_sync(file, self._read_value_sync())
|
||||||
|
|
||||||
def assure_sync(self, /) -> None:
|
def assure_sync(self, /) -> None:
|
||||||
@ -563,9 +553,7 @@ class _Guard:
|
|||||||
|
|
||||||
|
|
||||||
class _ReceivingQueue:
|
class _ReceivingQueue:
|
||||||
__all__ = (
|
__all__ = ("__queue",)
|
||||||
'__queue',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, queue: asyncio.Queue[Request], /) -> None:
|
def __init__(self, queue: asyncio.Queue[Request], /) -> None:
|
||||||
self.__queue: asyncio.Queue[Request] = queue
|
self.__queue: asyncio.Queue[Request] = queue
|
||||||
@ -576,25 +564,23 @@ class _ReceivingQueue:
|
|||||||
|
|
||||||
class _WriteableBuffer:
|
class _WriteableBuffer:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__buffersize',
|
"__buffersize",
|
||||||
'__guard',
|
"__guard",
|
||||||
'__queue',
|
"__queue",
|
||||||
'__backup',
|
"__backup",
|
||||||
'__kvfactory',
|
"__kvfactory",
|
||||||
'__loop',
|
"__loop",
|
||||||
'__event_loop',
|
"__event_loop",
|
||||||
'__buffer',
|
"__buffer",
|
||||||
'__buffer_future',
|
"__buffer_future",
|
||||||
'__buffer_requested',
|
"__buffer_requested",
|
||||||
)
|
)
|
||||||
|
|
||||||
__buffer: StringIO
|
__buffer: StringIO
|
||||||
__buffer_future: asyncio.Future
|
__buffer_future: asyncio.Future
|
||||||
__buffer_requested: bool
|
__buffer_requested: bool
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, buffersize: int, guard: _Guard, queue: _ReceivingQueue, loop: _Loop, /) -> None:
|
||||||
self, buffersize: int, guard: _Guard, queue: _ReceivingQueue, loop: _Loop, /
|
|
||||||
) -> None:
|
|
||||||
self.__buffersize = buffersize
|
self.__buffersize = buffersize
|
||||||
self.__guard = guard
|
self.__guard = guard
|
||||||
self.__queue = queue
|
self.__queue = queue
|
||||||
@ -647,16 +633,14 @@ class _WriteableBuffer:
|
|||||||
|
|
||||||
def _request_buffer(self, request: Request, /) -> None:
|
def _request_buffer(self, request: Request, /) -> None:
|
||||||
if request.waiting():
|
if request.waiting():
|
||||||
|
|
||||||
def callback(bf: asyncio.Future) -> None:
|
def callback(bf: asyncio.Future) -> None:
|
||||||
if (e := bf.exception()) is not None:
|
if (e := bf.exception()) is not None:
|
||||||
request.set_exception(e)
|
request.set_exception(e)
|
||||||
else:
|
else:
|
||||||
request.set_result(None)
|
request.set_result(None)
|
||||||
|
|
||||||
self.__buffer_future.exception
|
self.__buffer_future.add_done_callback(callback)
|
||||||
self.__buffer_future.add_done_callback(
|
|
||||||
callback
|
|
||||||
)
|
|
||||||
if not self.__buffer_requested:
|
if not self.__buffer_requested:
|
||||||
self.__buffer_requested = True
|
self.__buffer_requested = True
|
||||||
self.__queue.submit(CommitRequest(None))
|
self.__queue.submit(CommitRequest(None))
|
||||||
@ -702,12 +686,12 @@ class _WriteableBuffer:
|
|||||||
|
|
||||||
class _Memory:
|
class _Memory:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__backup',
|
"__backup",
|
||||||
'__guard',
|
"__guard",
|
||||||
'__file',
|
"__file",
|
||||||
'__kvfactory',
|
"__kvfactory",
|
||||||
'__loop',
|
"__loop",
|
||||||
'__mmdb',
|
"__mmdb",
|
||||||
)
|
)
|
||||||
|
|
||||||
__mmdb: dict
|
__mmdb: dict
|
||||||
@ -754,10 +738,10 @@ class _Memory:
|
|||||||
|
|
||||||
class _QueueTask:
|
class _QueueTask:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__queue',
|
"__queue",
|
||||||
'__buffer',
|
"__buffer",
|
||||||
'__event_loop',
|
"__event_loop",
|
||||||
'__task',
|
"__task",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, queue: asyncio.Queue[Request], buffer: _WriteableBuffer, /) -> None:
|
def __init__(self, queue: asyncio.Queue[Request], buffer: _WriteableBuffer, /) -> None:
|
||||||
@ -792,22 +776,20 @@ class _QueueTask:
|
|||||||
self.__task = self.__event_loop.create_task(self._background_task())
|
self.__task = self.__event_loop.create_task(self._background_task())
|
||||||
|
|
||||||
|
|
||||||
class _DbConnection(
|
class _DbConnection(DbInterface):
|
||||||
DbInterface
|
|
||||||
):
|
|
||||||
"""note: unstable constructor signature."""
|
"""note: unstable constructor signature."""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__kvfactory',
|
"__kvfactory",
|
||||||
'__buffersize',
|
"__buffersize",
|
||||||
'__path',
|
"__path",
|
||||||
'__error',
|
"__error",
|
||||||
'__not_running',
|
"__not_running",
|
||||||
'__mmdb',
|
"__mmdb",
|
||||||
'__loop',
|
"__loop",
|
||||||
'__queue',
|
"__queue",
|
||||||
'__file',
|
"__file",
|
||||||
'__task',
|
"__task",
|
||||||
)
|
)
|
||||||
|
|
||||||
__mmdb: _Memory
|
__mmdb: _Memory
|
||||||
@ -852,10 +834,7 @@ class _DbConnection(
|
|||||||
self.__queue = _ReceivingQueue(queue)
|
self.__queue = _ReceivingQueue(queue)
|
||||||
self.__mmdb = _Memory(guard)
|
self.__mmdb = _Memory(guard)
|
||||||
await self.__mmdb._load_from_file()
|
await self.__mmdb._load_from_file()
|
||||||
self.__task = _QueueTask(
|
self.__task = _QueueTask(queue, _WriteableBuffer(self.__buffersize, guard, self.__queue, self.__loop))
|
||||||
queue,
|
|
||||||
_WriteableBuffer(self.__buffersize, guard, self.__queue, self.__loop)
|
|
||||||
)
|
|
||||||
self.__task.start()
|
self.__task.start()
|
||||||
|
|
||||||
async def _initialize(self, /) -> None:
|
async def _initialize(self, /) -> None:
|
||||||
@ -866,7 +845,8 @@ class _DbConnection(
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create(cls, parameters: DbParameters, /) -> _DbConnection:
|
async def create(cls, parameters: DbParameters, /) -> _DbConnection:
|
||||||
"""connect to the factory.
|
"""\
|
||||||
|
connect to the factory.
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
dbconnection = _DbConnection(parameters)
|
dbconnection = _DbConnection(parameters)
|
||||||
await dbconnection._initialize()
|
await dbconnection._initialize()
|
||||||
@ -882,13 +862,15 @@ note: unstable signature."""
|
|||||||
await mmdb._close()
|
await mmdb._close()
|
||||||
|
|
||||||
async def aclose(self, /) -> None:
|
async def aclose(self, /) -> None:
|
||||||
"""close the connection.
|
"""\
|
||||||
|
close the connection.
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
await self._close_running()
|
await self._close_running()
|
||||||
self.__running = False
|
self.__running = False
|
||||||
|
|
||||||
async def commit_transaction(self, delta: dict, /) -> None:
|
async def commit_transaction(self, delta: dict, /) -> None:
|
||||||
"""hybrid of set() and dict.update().
|
"""\
|
||||||
|
hybrid of set() and dict.update().
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
if not delta:
|
if not delta:
|
||||||
return
|
return
|
||||||
@ -898,7 +880,8 @@ note: unstable signature."""
|
|||||||
await future
|
await future
|
||||||
|
|
||||||
def submit_transaction(self, delta: dict, /) -> None:
|
def submit_transaction(self, delta: dict, /) -> None:
|
||||||
"""hybrid of set_nowait() and dict.update().
|
"""\
|
||||||
|
hybrid of set_nowait() and dict.update().
|
||||||
_nowait analogue of commit_transaction().
|
_nowait analogue of commit_transaction().
|
||||||
note: this method was added only for async-sync symmetry with commit_transaction().
|
note: this method was added only for async-sync symmetry with commit_transaction().
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
@ -908,7 +891,8 @@ note: unstable signature."""
|
|||||||
self.__queue.submit(TransactionRequest(buffer, future=None))
|
self.__queue.submit(TransactionRequest(buffer, future=None))
|
||||||
|
|
||||||
def submit_transaction_request(self, delta: dict, future: asyncio.Future | None, /) -> None:
|
def submit_transaction_request(self, delta: dict, future: asyncio.Future | None, /) -> None:
|
||||||
"""low-level API.
|
"""\
|
||||||
|
low-level API.
|
||||||
for high-level synchronisation use transaction() instead.
|
for high-level synchronisation use transaction() instead.
|
||||||
note: unstable signature."""
|
note: unstable signature."""
|
||||||
if not delta:
|
if not delta:
|
||||||
@ -933,14 +917,12 @@ DbConnection: TypeAlias = DbInterface
|
|||||||
|
|
||||||
class DbManager:
|
class DbManager:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__parameters',
|
"__parameters",
|
||||||
'__db',
|
"__db",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, path: pathlib.Path, /, *, kvfactory: KVFactory, buffersize=1048576) -> None:
|
def __init__(self, path: pathlib.Path, /, *, kvfactory: KVFactory, buffersize=1048576) -> None:
|
||||||
self.__parameters = DbParameters(
|
self.__parameters = DbParameters(path, kvfactory=kvfactory, buffersize=buffersize)
|
||||||
path, kvfactory=kvfactory, buffersize=buffersize
|
|
||||||
)
|
|
||||||
|
|
||||||
async def __aenter__(self) -> DbInterface:
|
async def __aenter__(self) -> DbInterface:
|
||||||
self.__db = await _DbConnection.create(self.__parameters)
|
self.__db = await _DbConnection.create(self.__parameters)
|
||||||
@ -959,12 +941,7 @@ class Db(_DbConnection):
|
|||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def __init__(self, path: str | pathlib.Path, /, *, kvfactory: KVFactory, buffersize=1048576) -> None:
|
def __init__(self, path: str | pathlib.Path, /, *, kvfactory: KVFactory, buffersize=1048576) -> None:
|
||||||
_DbConnection.__init__(
|
_DbConnection.__init__(self, DbParameters(pathlib.Path(path), kvfactory=kvfactory, buffersize=buffersize))
|
||||||
self,
|
|
||||||
DbParameters(
|
|
||||||
pathlib.Path(path), kvfactory=kvfactory, buffersize=buffersize
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
async def __aenter__(self) -> _DbConnection:
|
async def __aenter__(self) -> _DbConnection:
|
||||||
await self._initialize()
|
await self._initialize()
|
||||||
@ -989,18 +966,16 @@ class FutureContext:
|
|||||||
await self.__future
|
await self.__future
|
||||||
|
|
||||||
|
|
||||||
class TransactionView(
|
class TransactionView(ExtendedVirtualConnection):
|
||||||
ExtendedVirtualConnection
|
|
||||||
):
|
|
||||||
"""note: unstable constructor signature."""
|
"""note: unstable constructor signature."""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__delta',
|
"__delta",
|
||||||
'__shadow',
|
"__shadow",
|
||||||
'__connection',
|
"__connection",
|
||||||
'__loop',
|
"__loop",
|
||||||
'__kvprotocol',
|
"__kvprotocol",
|
||||||
'__subfuture',
|
"__subfuture",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, delta: dict, connection: VirtualConnection, /) -> None:
|
def __init__(self, delta: dict, connection: VirtualConnection, /) -> None:
|
||||||
@ -1098,7 +1073,8 @@ class TransactionView(
|
|||||||
await self.__connection.commit_transaction(delta)
|
await self.__connection.commit_transaction(delta)
|
||||||
|
|
||||||
def submit(self, /) -> None:
|
def submit(self, /) -> None:
|
||||||
"""submit changes.
|
"""\
|
||||||
|
submit changes.
|
||||||
_nowait analogue of commit().
|
_nowait analogue of commit().
|
||||||
bulk analogue of DbConnection.set_nowait()."""
|
bulk analogue of DbConnection.set_nowait()."""
|
||||||
# for persistence5('s forks) developers:
|
# for persistence5('s forks) developers:
|
||||||
@ -1185,9 +1161,9 @@ class Transaction:
|
|||||||
"""note: unstable signature."""
|
"""note: unstable signature."""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__connection',
|
"__connection",
|
||||||
'__view',
|
"__view",
|
||||||
'__running',
|
"__running",
|
||||||
)
|
)
|
||||||
|
|
||||||
__view: TransactionView
|
__view: TransactionView
|
||||||
|
16
setup.py
16
setup.py
@ -1,12 +1,12 @@
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='ptvp35',
|
name="ptvp35",
|
||||||
version='1.1.0',
|
version="1.1.0",
|
||||||
packages=['ptvp35'],
|
packages=["ptvp35"],
|
||||||
url='https://gitea.ongoteam.net/PTV/ptvp35',
|
url="https://gitea.ongoteam.net/PTV/ptvp35",
|
||||||
license='MIT',
|
license="MIT",
|
||||||
author='PARRRATE TNV',
|
author="PARRRATE TNV",
|
||||||
author_email='',
|
author_email="",
|
||||||
description='',
|
description="",
|
||||||
)
|
)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from ptvp35 import DbFactory, KVJson, VDELETE
|
from ptvp35 import VDELETE, DbFactory, KVJson
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
path = pathlib.Path('test_delete.db')
|
path = pathlib.Path("test_delete.db")
|
||||||
path.unlink(missing_ok=True)
|
path.unlink(missing_ok=True)
|
||||||
async with DbFactory(path, kvfactory=KVJson()) as connection:
|
async with DbFactory(path, kvfactory=KVJson()) as connection:
|
||||||
connection.set_nowait(0, 0)
|
connection.set_nowait(0, 0)
|
||||||
@ -19,4 +19,5 @@ async def main():
|
|||||||
print(connection.get(0, 1))
|
print(connection.get(0, 1))
|
||||||
# path.unlink(missing_ok=True)
|
# path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
Loading…
Reference in New Issue
Block a user