Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
b05851d667
|
|||
|
a417c7484b
|
|||
|
04aae1f976
|
|||
|
79246f70c4
|
|||
|
36f7031fea
|
|||
|
37591f78d9
|
+57
-50
@@ -1,95 +1,103 @@
|
|||||||
#
|
#
|
||||||
# This file is autogenerated by pip-compile with Python 3.12
|
# This file is autogenerated by pip-compile with Python 3.14
|
||||||
# by the following command:
|
# by the following command:
|
||||||
#
|
#
|
||||||
# pip-compile --extra=dev --output-file=requirements.txt
|
# pip-compile --allow-unsafe --extra=dev --output-file=requirements.txt pyproject.toml
|
||||||
#
|
#
|
||||||
--index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple
|
--index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple
|
||||||
--extra-index-url https://pypi.org/simple
|
--extra-index-url https://pypi.org/simple
|
||||||
|
|
||||||
asttokens==2.4.1
|
asttokens==3.0.1
|
||||||
# via stack-data
|
# via stack-data
|
||||||
build==1.2.2.post1
|
build==1.4.3
|
||||||
# via
|
# via
|
||||||
# pip-tools
|
# pip-tools
|
||||||
# pwo (pyproject.toml)
|
# pwo (pyproject.toml)
|
||||||
certifi==2024.8.30
|
certifi==2026.2.25
|
||||||
# via requests
|
# via requests
|
||||||
cffi==1.17.1
|
cffi==2.0.0
|
||||||
# via cryptography
|
# via cryptography
|
||||||
charset-normalizer==3.4.0
|
charset-normalizer==3.4.7
|
||||||
# via requests
|
# via requests
|
||||||
click==8.1.7
|
click==8.3.2
|
||||||
# via pip-tools
|
# via pip-tools
|
||||||
cryptography==43.0.3
|
cryptography==46.0.7
|
||||||
# via secretstorage
|
# via secretstorage
|
||||||
decorator==5.1.1
|
decorator==5.2.1
|
||||||
# via
|
# via
|
||||||
# ipdb
|
# ipdb
|
||||||
# ipython
|
# ipython
|
||||||
docutils==0.21.2
|
docutils==0.22.4
|
||||||
# via readme-renderer
|
# via readme-renderer
|
||||||
executing==2.1.0
|
executing==2.2.1
|
||||||
# via stack-data
|
# via stack-data
|
||||||
idna==3.10
|
id==1.6.1
|
||||||
# via requests
|
|
||||||
importlib-metadata==8.5.0
|
|
||||||
# via twine
|
# via twine
|
||||||
|
idna==3.11
|
||||||
|
# via requests
|
||||||
ipdb==0.13.13
|
ipdb==0.13.13
|
||||||
# via pwo (pyproject.toml)
|
# via pwo (pyproject.toml)
|
||||||
ipython==8.29.0
|
ipython==9.12.0
|
||||||
# via ipdb
|
# via ipdb
|
||||||
|
ipython-pygments-lexers==1.1.1
|
||||||
|
# via ipython
|
||||||
jaraco-classes==3.4.0
|
jaraco-classes==3.4.0
|
||||||
# via keyring
|
# via keyring
|
||||||
jaraco-context==6.0.1
|
jaraco-context==6.1.2
|
||||||
# via keyring
|
# via keyring
|
||||||
jaraco-functools==4.1.0
|
jaraco-functools==4.4.0
|
||||||
# via keyring
|
# via keyring
|
||||||
jedi==0.19.1
|
jedi==0.19.2
|
||||||
# via ipython
|
# via ipython
|
||||||
jeepney==0.8.0
|
jeepney==0.9.0
|
||||||
# via
|
# via
|
||||||
# keyring
|
# keyring
|
||||||
# secretstorage
|
# secretstorage
|
||||||
keyring==25.5.0
|
keyring==25.7.0
|
||||||
# via twine
|
# via twine
|
||||||
markdown-it-py==3.0.0
|
librt==0.9.0
|
||||||
|
# via mypy
|
||||||
|
markdown-it-py==4.0.0
|
||||||
# via rich
|
# via rich
|
||||||
matplotlib-inline==0.1.7
|
matplotlib-inline==0.2.1
|
||||||
# via ipython
|
# via ipython
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
# via markdown-it-py
|
# via markdown-it-py
|
||||||
more-itertools==10.5.0
|
more-itertools==11.0.2
|
||||||
# via
|
# via
|
||||||
# jaraco-classes
|
# jaraco-classes
|
||||||
# jaraco-functools
|
# jaraco-functools
|
||||||
mypy==1.13.0
|
mypy==1.20.1
|
||||||
# via pwo (pyproject.toml)
|
# via pwo (pyproject.toml)
|
||||||
mypy-extensions==1.0.0
|
mypy-extensions==1.1.0
|
||||||
# via mypy
|
# via mypy
|
||||||
nh3==0.2.18
|
nh3==0.3.4
|
||||||
# via readme-renderer
|
# via readme-renderer
|
||||||
packaging==24.1
|
packaging==26.1
|
||||||
# via build
|
# via
|
||||||
parso==0.8.4
|
# build
|
||||||
|
# twine
|
||||||
|
# wheel
|
||||||
|
parso==0.8.6
|
||||||
# via jedi
|
# via jedi
|
||||||
|
pathspec==1.0.4
|
||||||
|
# via mypy
|
||||||
pexpect==4.9.0
|
pexpect==4.9.0
|
||||||
# via ipython
|
# via ipython
|
||||||
pip-tools==7.4.1
|
pip-tools==7.5.3
|
||||||
# via pwo (pyproject.toml)
|
# via pwo (pyproject.toml)
|
||||||
pkginfo==1.10.0
|
prompt-toolkit==3.0.52
|
||||||
# via twine
|
|
||||||
prompt-toolkit==3.0.48
|
|
||||||
# via ipython
|
# via ipython
|
||||||
ptyprocess==0.7.0
|
ptyprocess==0.7.0
|
||||||
# via pexpect
|
# via pexpect
|
||||||
pure-eval==0.2.3
|
pure-eval==0.2.3
|
||||||
# via stack-data
|
# via stack-data
|
||||||
pycparser==2.22
|
pycparser==3.0
|
||||||
# via cffi
|
# via cffi
|
||||||
pygments==2.18.0
|
pygments==2.20.0
|
||||||
# via
|
# via
|
||||||
# ipython
|
# ipython
|
||||||
|
# ipython-pygments-lexers
|
||||||
# readme-renderer
|
# readme-renderer
|
||||||
# rich
|
# rich
|
||||||
pyproject-hooks==1.2.0
|
pyproject-hooks==1.2.0
|
||||||
@@ -98,7 +106,7 @@ pyproject-hooks==1.2.0
|
|||||||
# pip-tools
|
# pip-tools
|
||||||
readme-renderer==44.0
|
readme-renderer==44.0
|
||||||
# via twine
|
# via twine
|
||||||
requests==2.32.3
|
requests==2.33.1
|
||||||
# via
|
# via
|
||||||
# requests-toolbelt
|
# requests-toolbelt
|
||||||
# twine
|
# twine
|
||||||
@@ -106,35 +114,34 @@ requests-toolbelt==1.0.0
|
|||||||
# via twine
|
# via twine
|
||||||
rfc3986==2.0.0
|
rfc3986==2.0.0
|
||||||
# via twine
|
# via twine
|
||||||
rich==13.9.4
|
rich==15.0.0
|
||||||
# via twine
|
# via twine
|
||||||
secretstorage==3.3.3
|
secretstorage==3.5.0
|
||||||
# via keyring
|
# via keyring
|
||||||
six==1.16.0
|
|
||||||
# via asttokens
|
|
||||||
stack-data==0.6.3
|
stack-data==0.6.3
|
||||||
# via ipython
|
# via ipython
|
||||||
traitlets==5.14.3
|
traitlets==5.14.3
|
||||||
# via
|
# via
|
||||||
# ipython
|
# ipython
|
||||||
# matplotlib-inline
|
# matplotlib-inline
|
||||||
twine==5.1.1
|
twine==6.2.0
|
||||||
# via pwo (pyproject.toml)
|
# via pwo (pyproject.toml)
|
||||||
typing-extensions==4.12.2
|
typing-extensions==4.15.0
|
||||||
# via
|
# via
|
||||||
# mypy
|
# mypy
|
||||||
# pwo (pyproject.toml)
|
# pwo (pyproject.toml)
|
||||||
urllib3==2.2.3
|
urllib3==2.6.3
|
||||||
# via
|
# via
|
||||||
|
# id
|
||||||
# requests
|
# requests
|
||||||
# twine
|
# twine
|
||||||
wcwidth==0.2.13
|
wcwidth==0.6.0
|
||||||
# via prompt-toolkit
|
# via prompt-toolkit
|
||||||
wheel==0.44.0
|
wheel==0.46.3
|
||||||
# via pip-tools
|
# via pip-tools
|
||||||
zipp==3.20.2
|
|
||||||
# via importlib-metadata
|
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
# pip
|
pip==26.0.1
|
||||||
# setuptools
|
# via pip-tools
|
||||||
|
setuptools==82.0.1
|
||||||
|
# via pip-tools
|
||||||
|
|||||||
+3
-1
@@ -9,6 +9,7 @@ from .private import (
|
|||||||
classproperty,
|
classproperty,
|
||||||
AsyncQueueIterator,
|
AsyncQueueIterator,
|
||||||
aenumerate,
|
aenumerate,
|
||||||
|
index_of_with_escape
|
||||||
)
|
)
|
||||||
from .maybe import Maybe
|
from .maybe import Maybe
|
||||||
from .notification import TopicManager, Subscriber
|
from .notification import TopicManager, Subscriber
|
||||||
@@ -28,5 +29,6 @@ __all__ = [
|
|||||||
'Subscriber',
|
'Subscriber',
|
||||||
'AsyncQueueIterator',
|
'AsyncQueueIterator',
|
||||||
'aenumerate',
|
'aenumerate',
|
||||||
'Try'
|
'Try',
|
||||||
|
'index_of_with_escape'
|
||||||
]
|
]
|
||||||
|
|||||||
+8
-17
@@ -1,5 +1,5 @@
|
|||||||
from .private import AsyncQueueIterator
|
from .private import AsyncQueueIterator
|
||||||
from asyncio import Queue, AbstractEventLoop, Future, CancelledError
|
from asyncio import Queue, AbstractEventLoop, Future, CancelledError, timeout
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
@@ -23,25 +23,16 @@ class Subscriber:
|
|||||||
self._unsubscribe_callback(self)
|
self._unsubscribe_callback(self)
|
||||||
log.debug('Deleted subscriber %s', id(self))
|
log.debug('Deleted subscriber %s', id(self))
|
||||||
|
|
||||||
async def wait(self, tout: float) -> bool:
|
async def wait(self, tout: Optional[float]) -> bool:
|
||||||
self._event = self._loop.create_future()
|
|
||||||
|
|
||||||
def callback() -> None:
|
future: Future[bool] = self._loop.create_future()
|
||||||
evt = self._event
|
self._event = future
|
||||||
if evt is None:
|
|
||||||
raise ValueError('Event is None')
|
|
||||||
evt.cancel()
|
|
||||||
if not evt.done():
|
|
||||||
evt.set_result(False)
|
|
||||||
|
|
||||||
handle = self._loop.call_later(tout, callback)
|
|
||||||
try:
|
try:
|
||||||
log.debug('Subscriber %s is waiting for an event', id(self))
|
async with timeout(tout):
|
||||||
return await self._event
|
log.debug('Subscriber %s is waiting for an event', id(self))
|
||||||
except CancelledError:
|
return await future
|
||||||
|
except TimeoutError:
|
||||||
return False
|
return False
|
||||||
finally:
|
|
||||||
handle.cancel()
|
|
||||||
|
|
||||||
def notify(self) -> None:
|
def notify(self) -> None:
|
||||||
log.debug('Subscriber %s notified', id(self))
|
log.debug('Subscriber %s notified', id(self))
|
||||||
|
|||||||
+39
-5
@@ -16,7 +16,8 @@ from typing import (
|
|||||||
Any,
|
Any,
|
||||||
Coroutine,
|
Coroutine,
|
||||||
Never,
|
Never,
|
||||||
Tuple
|
Tuple,
|
||||||
|
no_type_check
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -88,14 +89,14 @@ def decorator_with_kwargs(decorator: Callable[..., Any]) -> Callable[..., Any]:
|
|||||||
_size_uoms = ('B', 'KiB', 'MiB', 'GiB', 'KiB')
|
_size_uoms = ('B', 'KiB', 'MiB', 'GiB', 'KiB')
|
||||||
|
|
||||||
|
|
||||||
def format_filesize(size: int) -> str:
|
def format_filesize(size: int, width: int = 5, decimals : int = 2) -> str:
|
||||||
counter = 0
|
counter = 0
|
||||||
tmp_size = size
|
tmp_size = size
|
||||||
while tmp_size > 0:
|
while tmp_size > 0:
|
||||||
tmp_size //= 1024
|
tmp_size //= 1024
|
||||||
counter += 1
|
counter += 1
|
||||||
counter -= 1
|
counter -= 1
|
||||||
return '%.2f ' % (size / math.pow(1024, counter)) + _size_uoms[counter]
|
return f'%{width}.{decimals}f ' % (size / math.pow(1024, counter)) + _size_uoms[counter]
|
||||||
|
|
||||||
|
|
||||||
class ExceptionHandlerOutcome(Enum):
|
class ExceptionHandlerOutcome(Enum):
|
||||||
@@ -165,7 +166,8 @@ def async_test(coro: Callable[..., Coroutine[Never, Never, None]]) -> Callable[.
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@decorator_with_kwargs # type: ignore
|
@no_type_check
|
||||||
|
@decorator_with_kwargs
|
||||||
def tmpdir(f,
|
def tmpdir(f,
|
||||||
argument_name='temp_dir',
|
argument_name='temp_dir',
|
||||||
suffix=None,
|
suffix=None,
|
||||||
@@ -173,7 +175,8 @@ def tmpdir(f,
|
|||||||
dir=None,
|
dir=None,
|
||||||
ignore_cleanup_errors=False,
|
ignore_cleanup_errors=False,
|
||||||
delete=True):
|
delete=True):
|
||||||
@wraps(f) # type: ignore
|
@no_type_check
|
||||||
|
@wraps(f)
|
||||||
def result(*args, **kwargs):
|
def result(*args, **kwargs):
|
||||||
with TemporaryDirectory(
|
with TemporaryDirectory(
|
||||||
suffix=suffix,
|
suffix=suffix,
|
||||||
@@ -252,3 +255,34 @@ class aenumerate[T](AsyncIterator[Tuple[int, T]]):
|
|||||||
val = await self._ait.__anext__()
|
val = await self._ait.__anext__()
|
||||||
self._i += 1
|
self._i += 1
|
||||||
return self._i, val
|
return self._i, val
|
||||||
|
|
||||||
|
|
||||||
|
def index_of_with_escape(haystack: str, needle: str, escape: str, begin: int, end: int = 0) -> int:
|
||||||
|
result = -1
|
||||||
|
cursor = begin
|
||||||
|
if end == 0:
|
||||||
|
end = len(haystack)
|
||||||
|
escape_count = 0
|
||||||
|
|
||||||
|
while cursor < end:
|
||||||
|
c = haystack[cursor]
|
||||||
|
|
||||||
|
if escape_count > 0:
|
||||||
|
escape_count -= 1
|
||||||
|
if c[0] == escape:
|
||||||
|
result = -1
|
||||||
|
elif escape_count == 0:
|
||||||
|
if c[0] == escape:
|
||||||
|
escape_count += 1
|
||||||
|
if cursor + len(needle) <= len(haystack):
|
||||||
|
test = haystack[cursor:cursor + len(needle)]
|
||||||
|
if test == needle:
|
||||||
|
result = cursor
|
||||||
|
|
||||||
|
if result >= 0 and escape_count == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
cursor += 1
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|||||||
+35
-1
@@ -1,6 +1,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from pwo import retry, async_retry, async_test, AsyncQueueIterator, aenumerate
|
from pwo import retry, async_retry, async_test, AsyncQueueIterator, aenumerate, index_of_with_escape
|
||||||
from asyncio import Queue
|
from asyncio import Queue
|
||||||
|
|
||||||
|
|
||||||
@@ -93,3 +93,37 @@ class PrivateTest(unittest.TestCase):
|
|||||||
self.assertEqual(queue_size, processed)
|
self.assertEqual(queue_size, processed)
|
||||||
|
|
||||||
|
|
||||||
|
class TestIndexOfWithEscape(unittest.TestCase):
|
||||||
|
|
||||||
|
def run_test_case(self, haystack, needle, escape, expected_solution):
|
||||||
|
solution = []
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
i = index_of_with_escape(haystack, needle, escape, i, len(haystack))
|
||||||
|
if i < 0:
|
||||||
|
break
|
||||||
|
solution.append(i)
|
||||||
|
i += 1
|
||||||
|
self.assertEqual(expected_solution, solution)
|
||||||
|
|
||||||
|
def test_simple(self):
|
||||||
|
self.run_test_case(" dsds $sdsa \\$dfivbdsf \\\\$sdgsga", '$', '\\', [6, 25])
|
||||||
|
|
||||||
|
def test_simple2(self):
|
||||||
|
self.run_test_case("asdasd$$vdfv$", '$', '$', [12])
|
||||||
|
|
||||||
|
def test_no_needle(self):
|
||||||
|
self.run_test_case("asdasd$$vdfv$", '#', '\\', [])
|
||||||
|
|
||||||
|
def test_escaped_needle(self):
|
||||||
|
self.run_test_case("asdasd$$vdfv$#sdfs", '#', '$', [])
|
||||||
|
|
||||||
|
def test_not_escaped_needle(self):
|
||||||
|
self.run_test_case("asdasd$$#vdfv$#sdfs", '#', '$', [8])
|
||||||
|
|
||||||
|
def test_special_case(self):
|
||||||
|
self.run_test_case("\n${sys:user.home}${env:HOME}", ':', '\\', [6, 22])
|
||||||
|
|
||||||
|
def test_wide_needle(self):
|
||||||
|
self.run_test_case("asdasd\\${#vdfv|${#sdfs}", '${', '\\', [15])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user