mirror of
https://github.com/lilydjwg/nvchecker.git
synced 2025-03-10 06:14:02 +00:00
support simpler source with only one get_version funcion
also caching is working now
This commit is contained in:
parent
0232d0fb4f
commit
435edf8589
5 changed files with 123 additions and 44 deletions
19
NEW
19
NEW
|
@ -1,21 +1,4 @@
|
||||||
source file
|
|
||||||
| parse
|
|
||||||
entries: name, conf, global options
|
|
||||||
| dedupe
|
|
||||||
entries: names, conf, global options
|
|
||||||
| dispatch
|
|
||||||
one future per module
|
|
||||||
| run; token queue; result queue
|
|
||||||
result task, runner task
|
|
||||||
| receive
|
|
||||||
(names, result)
|
|
||||||
| transform back
|
|
||||||
(name, result)
|
|
||||||
| runner task done
|
|
||||||
| write record file
|
|
||||||
file
|
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
* dedupe & cache
|
* pass `tries` to `httpclient`
|
||||||
* update tests
|
* update tests
|
||||||
* update README
|
* update README
|
||||||
|
|
|
@ -26,6 +26,7 @@ from .lib import nicelogger
|
||||||
from . import slogconf
|
from . import slogconf
|
||||||
from .util import (
|
from .util import (
|
||||||
Entry, Entries, KeyManager, RawResult, Result, VersData,
|
Entry, Entries, KeyManager, RawResult, Result, VersData,
|
||||||
|
FunctionWorker,
|
||||||
)
|
)
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from .sortversion import sort_version_keys
|
from .sortversion import sort_version_keys
|
||||||
|
@ -194,10 +195,20 @@ def dispatch(
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
for mod, tasks in mods.values():
|
for mod, tasks in mods.values():
|
||||||
worker = mod.Worker( # type: ignore
|
if hasattr(mod, 'Worker'):
|
||||||
|
worker_cls = mod.Worker # type: ignore
|
||||||
|
else:
|
||||||
|
worker_cls = FunctionWorker
|
||||||
|
|
||||||
|
worker = worker_cls(
|
||||||
token_q, result_q, tasks,
|
token_q, result_q, tasks,
|
||||||
tries, keymanager,
|
tries, keymanager,
|
||||||
)
|
)
|
||||||
|
if worker_cls is FunctionWorker:
|
||||||
|
func = mod.get_version # type: ignore
|
||||||
|
cacher = getattr(mod, 'cacher', None)
|
||||||
|
worker.set_func(func, cacher)
|
||||||
|
|
||||||
ret.append(worker.run())
|
ret.append(worker.run())
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from asyncio import Queue
|
from asyncio import Queue
|
||||||
import contextlib
|
import contextlib
|
||||||
from typing import (
|
from typing import (
|
||||||
Dict, Optional, List, AsyncGenerator, NamedTuple, Union,
|
Dict, Optional, List, AsyncGenerator, NamedTuple, Union,
|
||||||
Any, Tuple,
|
Any, Tuple, Coroutine, Callable,
|
||||||
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
@ -16,6 +18,7 @@ import toml
|
||||||
Entry = Dict[str, Any]
|
Entry = Dict[str, Any]
|
||||||
Entries = Dict[str, Entry]
|
Entries = Dict[str, Entry]
|
||||||
VersData = Dict[str, str]
|
VersData = Dict[str, str]
|
||||||
|
VersionResult = Union[None, str, List[str], Exception]
|
||||||
|
|
||||||
class KeyManager:
|
class KeyManager:
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -31,6 +34,16 @@ class KeyManager:
|
||||||
def get_key(self, name: str) -> Optional[str]:
|
def get_key(self, name: str) -> Optional[str]:
|
||||||
return self.keys.get(name)
|
return self.keys.get(name)
|
||||||
|
|
||||||
|
class RawResult(NamedTuple):
|
||||||
|
name: str
|
||||||
|
version: VersionResult
|
||||||
|
conf: Entry
|
||||||
|
|
||||||
|
class Result(NamedTuple):
|
||||||
|
name: str
|
||||||
|
version: str
|
||||||
|
conf: Entry
|
||||||
|
|
||||||
class BaseWorker:
|
class BaseWorker:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -54,19 +67,91 @@ class BaseWorker:
|
||||||
finally:
|
finally:
|
||||||
await self.token_q.put(token)
|
await self.token_q.put(token)
|
||||||
|
|
||||||
class RawResult(NamedTuple):
|
if TYPE_CHECKING:
|
||||||
name: str
|
from typing_extensions import Protocol
|
||||||
version: Union[Exception, List[str], str]
|
class GetVersionFunc(Protocol):
|
||||||
conf: Entry
|
async def __call__(
|
||||||
|
self,
|
||||||
|
name: str, conf: Entry,
|
||||||
|
*,
|
||||||
|
keymanager: KeyManager,
|
||||||
|
) -> VersionResult:
|
||||||
|
...
|
||||||
|
else:
|
||||||
|
GetVersionFunc = Any
|
||||||
|
|
||||||
class Result(NamedTuple):
|
Cacher = Callable[[str, Entry], str]
|
||||||
name: str
|
|
||||||
version: str
|
|
||||||
conf: Entry
|
|
||||||
|
|
||||||
def conf_cacheable_with_name(key):
|
class FunctionWorker(BaseWorker):
|
||||||
def get_cacheable_conf(name, conf):
|
func = None
|
||||||
conf = dict(conf)
|
cacher = None
|
||||||
conf[key] = conf.get(key) or name
|
|
||||||
return conf
|
cache: Dict[str, Union[
|
||||||
return get_cacheable_conf
|
VersionResult,
|
||||||
|
asyncio.Task,
|
||||||
|
]]
|
||||||
|
lock: asyncio.Lock
|
||||||
|
|
||||||
|
def set_func(
|
||||||
|
self,
|
||||||
|
func: GetVersionFunc,
|
||||||
|
cacher: Optional[Cacher],
|
||||||
|
) -> None:
|
||||||
|
self.func = func
|
||||||
|
self.cacher = cacher
|
||||||
|
if cacher:
|
||||||
|
self.cache = {}
|
||||||
|
self.lock = asyncio.Lock()
|
||||||
|
|
||||||
|
async def run(self) -> None:
|
||||||
|
assert self.func is not None
|
||||||
|
futures = [
|
||||||
|
self.run_one(name, entry)
|
||||||
|
for name, entry in self.tasks
|
||||||
|
]
|
||||||
|
for fu in asyncio.as_completed(futures):
|
||||||
|
await fu
|
||||||
|
|
||||||
|
async def run_one(
|
||||||
|
self, name: str, entry: Entry,
|
||||||
|
) -> None:
|
||||||
|
assert self.func is not None
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with self.acquire_token():
|
||||||
|
if self.cacher:
|
||||||
|
version = await self.run_one_may_cache(
|
||||||
|
name, entry)
|
||||||
|
else:
|
||||||
|
version = await self.func(
|
||||||
|
name, entry, keymanager = self.keymanager,
|
||||||
|
)
|
||||||
|
await self.result_q.put(RawResult(name, version, entry))
|
||||||
|
except Exception as e:
|
||||||
|
await self.result_q.put(RawResult(name, e, entry))
|
||||||
|
|
||||||
|
async def run_one_may_cache(
|
||||||
|
self, name: str, entry: Entry,
|
||||||
|
) -> VersionResult:
|
||||||
|
assert self.cacher is not None
|
||||||
|
assert self.func is not None
|
||||||
|
|
||||||
|
key = self.cacher(name, entry)
|
||||||
|
|
||||||
|
async with self.lock:
|
||||||
|
cached = self.cache.get(key)
|
||||||
|
if cached is None:
|
||||||
|
coro = self.func(
|
||||||
|
name, entry, keymanager = self.keymanager,
|
||||||
|
)
|
||||||
|
fu = asyncio.create_task(coro)
|
||||||
|
self.cache[key] = fu
|
||||||
|
|
||||||
|
if asyncio.isfuture(cached): # pending
|
||||||
|
return await cached # type: ignore
|
||||||
|
elif cached is not None: # cached
|
||||||
|
return cached # type: ignore
|
||||||
|
else: # not cached
|
||||||
|
version = await fu
|
||||||
|
self.cache[key] = version
|
||||||
|
return version
|
||||||
|
|
|
@ -4,28 +4,25 @@
|
||||||
import structlog
|
import structlog
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Iterable, Dict, List, Tuple, Any
|
from typing import Iterable, Dict, List, Tuple, Any, Optional
|
||||||
|
|
||||||
from nvchecker.util import (
|
from nvchecker.util import Entry, BaseWorker, RawResult
|
||||||
Entry, BaseWorker, RawResult,
|
|
||||||
conf_cacheable_with_name,
|
|
||||||
)
|
|
||||||
from nvchecker.httpclient import session # type: ignore
|
from nvchecker.httpclient import session # type: ignore
|
||||||
|
|
||||||
get_cacheable_conf = conf_cacheable_with_name('aur')
|
|
||||||
|
|
||||||
logger = structlog.get_logger(logger_name=__name__)
|
logger = structlog.get_logger(logger_name=__name__)
|
||||||
|
|
||||||
AUR_URL = 'https://aur.archlinux.org/rpc/'
|
AUR_URL = 'https://aur.archlinux.org/rpc/'
|
||||||
|
|
||||||
class AurResults:
|
class AurResults:
|
||||||
|
cache: Dict[str, Optional[Dict[str, Any]]]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
|
|
||||||
async def get_multiple(
|
async def get_multiple(
|
||||||
self,
|
self,
|
||||||
aurnames: Iterable[str],
|
aurnames: Iterable[str],
|
||||||
) -> Dict[str, Dict[str, Any]]:
|
) -> Dict[str, Optional[Dict[str, Any]]]:
|
||||||
params = [('v', '5'), ('type', 'info')]
|
params = [('v', '5'), ('type', 'info')]
|
||||||
params.extend(('arg[]', name) for name in aurnames
|
params.extend(('arg[]', name) for name in aurnames
|
||||||
if name not in self.cache)
|
if name not in self.cache)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MIT licensed
|
# MIT licensed
|
||||||
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
|
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
@ -7,7 +7,10 @@ import structlog
|
||||||
|
|
||||||
logger = structlog.get_logger(logger_name=__name__)
|
logger = structlog.get_logger(logger_name=__name__)
|
||||||
|
|
||||||
async def get_version(name, conf, **kwargs):
|
def cacher(name, conf):
|
||||||
|
return conf['cmd']
|
||||||
|
|
||||||
|
async def get_version(name, conf, *, keymanager=None):
|
||||||
cmd = conf['cmd']
|
cmd = conf['cmd']
|
||||||
p = await asyncio.create_subprocess_shell(
|
p = await asyncio.create_subprocess_shell(
|
||||||
cmd,
|
cmd,
|
Loading…
Add table
Reference in a new issue