mirror of
https://github.com/lilydjwg/nvchecker.git
synced 2025-03-10 06:14:02 +00:00
record rich results in verfile and get rid of Result & VersData types
Use RichResult to replace Result; Result was RichResult plus entry name.
This commit is contained in:
parent
f29bdee6a3
commit
de1a3c6fc2
5 changed files with 56 additions and 37 deletions
|
@ -88,7 +88,7 @@ def main() -> None:
|
||||||
if options.ver_files is not None:
|
if options.ver_files is not None:
|
||||||
newverf = options.ver_files[1]
|
newverf = options.ver_files[1]
|
||||||
vers = core.read_verfile(newverf)
|
vers = core.read_verfile(newverf)
|
||||||
vers.update({k: r.version for k, r in results.items()})
|
vers.update(results)
|
||||||
core.write_verfile(newverf, vers)
|
core.write_verfile(newverf, vers)
|
||||||
|
|
||||||
if args.failures and has_failures:
|
if args.failures and has_failures:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MIT licensed
|
# MIT licensed
|
||||||
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
|
# Copyright (c) 2013-2020, 2024 lilydjwg <lilydjwg@gmail.com>, et al.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ from importlib import import_module
|
||||||
import re
|
import re
|
||||||
import contextvars
|
import contextvars
|
||||||
import json
|
import json
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ import platformdirs
|
||||||
from .lib import nicelogger
|
from .lib import nicelogger
|
||||||
from . import slogconf
|
from . import slogconf
|
||||||
from .util import (
|
from .util import (
|
||||||
Entry, Entries, KeyManager, RawResult, RichResult, Result, VersData,
|
Entry, Entries, KeyManager, RawResult, RichResult, ResultData,
|
||||||
FunctionWorker, GetVersionError,
|
FunctionWorker, GetVersionError,
|
||||||
FileLoadError, EntryWaiter,
|
FileLoadError, EntryWaiter,
|
||||||
)
|
)
|
||||||
|
@ -126,7 +127,7 @@ def safe_overwrite(file: Path, data: Union[bytes, str], *,
|
||||||
# if the above write failed (because disk is full etc), the old data should be kept
|
# if the above write failed (because disk is full etc), the old data should be kept
|
||||||
os.rename(tmpname, resolved_path)
|
os.rename(tmpname, resolved_path)
|
||||||
|
|
||||||
def read_verfile(file: Path) -> VersData:
|
def read_verfile(file: Path) -> ResultData:
|
||||||
try:
|
try:
|
||||||
with open(file) as f:
|
with open(file) as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
|
@ -142,17 +143,35 @@ def read_verfile(file: Path) -> VersData:
|
||||||
name, ver = l.rstrip().split(None, 1)
|
name, ver = l.rstrip().split(None, 1)
|
||||||
v[name] = ver
|
v[name] = ver
|
||||||
|
|
||||||
|
if v.get('version') is None:
|
||||||
|
v = {k: RichResult(version=a) for k, a in v.items()}
|
||||||
|
elif v['version'] == 2:
|
||||||
|
v = {k: RichResult(**a) for k, a in v['data'].items()}
|
||||||
|
else:
|
||||||
|
raise Exception('unknown verfile version', v['version'])
|
||||||
|
|
||||||
return v
|
return v
|
||||||
|
|
||||||
def write_verfile(file: Path, versions: VersData) -> None:
|
def write_verfile(file: Path, versions: ResultData) -> None:
|
||||||
# sort and indent to make it friendly to human and git
|
d = {
|
||||||
|
'version': 2,
|
||||||
|
# sort and indent to make it friendly to human and git
|
||||||
|
'data': dict(sorted(versions.items())),
|
||||||
|
}
|
||||||
data = json.dumps(
|
data = json.dumps(
|
||||||
dict(sorted(versions.items())),
|
d,
|
||||||
indent=2,
|
indent = 2,
|
||||||
ensure_ascii=False,
|
ensure_ascii = False,
|
||||||
|
default = json_encode,
|
||||||
) + '\n'
|
) + '\n'
|
||||||
safe_overwrite(file, data)
|
safe_overwrite(file, data)
|
||||||
|
|
||||||
|
def json_encode(obj):
|
||||||
|
if isinstance(obj, RichResult):
|
||||||
|
d = {k: v for k, v in dataclasses.asdict(obj).items() if v is not None}
|
||||||
|
return d
|
||||||
|
raise TypeError(obj)
|
||||||
|
|
||||||
class Options(NamedTuple):
|
class Options(NamedTuple):
|
||||||
ver_files: Optional[Tuple[Path, Path]]
|
ver_files: Optional[Tuple[Path, Path]]
|
||||||
max_concurrency: int
|
max_concurrency: int
|
||||||
|
@ -325,7 +344,7 @@ def apply_list_options(
|
||||||
|
|
||||||
return versions[-1]
|
return versions[-1]
|
||||||
|
|
||||||
def _process_result(r: RawResult) -> Union[Result, Exception]:
|
def _process_result(r: RawResult) -> Union[RichResult, Exception]:
|
||||||
version = r.version
|
version = r.version
|
||||||
conf = r.conf
|
conf = r.conf
|
||||||
name = r.name
|
name = r.name
|
||||||
|
@ -362,7 +381,12 @@ def _process_result(r: RawResult) -> Union[Result, Exception]:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
version_str = substitute_version(version_str, conf)
|
version_str = substitute_version(version_str, conf)
|
||||||
return Result(name, version_str, conf, url, gitref, revision)
|
return RichResult(
|
||||||
|
version = version_str,
|
||||||
|
url = url,
|
||||||
|
gitref = gitref,
|
||||||
|
revision = revision,
|
||||||
|
)
|
||||||
except (ValueError, re.error) as e:
|
except (ValueError, re.error) as e:
|
||||||
logger.exception('error occurred in version substitutions', name=name)
|
logger.exception('error occurred in version substitutions', name=name)
|
||||||
return e
|
return e
|
||||||
|
@ -371,15 +395,19 @@ def _process_result(r: RawResult) -> Union[Result, Exception]:
|
||||||
return ValueError('no version returned')
|
return ValueError('no version returned')
|
||||||
|
|
||||||
def check_version_update(
|
def check_version_update(
|
||||||
oldvers: VersData,
|
oldvers: ResultData,
|
||||||
r: Result,
|
name: str,
|
||||||
|
r: RichResult,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
oldver = oldvers.get(r.name, None)
|
if old_result := oldvers.get(name):
|
||||||
|
oldver = old_result.version
|
||||||
|
else:
|
||||||
|
oldver = None
|
||||||
if not oldver or oldver != r.version:
|
if not oldver or oldver != r.version:
|
||||||
logger.info(
|
logger.info(
|
||||||
'updated',
|
'updated',
|
||||||
name = r.name,
|
name = name,
|
||||||
version = r.version,
|
version = r.version,
|
||||||
old_version = oldver,
|
old_version = oldver,
|
||||||
url = r.url,
|
url = r.url,
|
||||||
|
@ -387,10 +415,10 @@ def check_version_update(
|
||||||
else:
|
else:
|
||||||
# provide visible user feedback if it was the only entry
|
# provide visible user feedback if it was the only entry
|
||||||
level = logging.INFO if verbose else logging.DEBUG
|
level = logging.INFO if verbose else logging.DEBUG
|
||||||
logger.log(level, 'up-to-date', name=r.name, version=r.version, url=r.url)
|
logger.log(level, 'up-to-date', name=name, version=r.version, url=r.url)
|
||||||
|
|
||||||
async def process_result(
|
async def process_result(
|
||||||
oldvers: VersData,
|
oldvers: ResultData,
|
||||||
result_q: Queue[RawResult],
|
result_q: Queue[RawResult],
|
||||||
entry_waiter: EntryWaiter,
|
entry_waiter: EntryWaiter,
|
||||||
verbose: bool = False,
|
verbose: bool = False,
|
||||||
|
@ -409,9 +437,9 @@ async def process_result(
|
||||||
entry_waiter.set_exception(r.name, r1)
|
entry_waiter.set_exception(r.name, r1)
|
||||||
has_failures = True
|
has_failures = True
|
||||||
continue
|
continue
|
||||||
check_version_update(oldvers, r1, verbose)
|
check_version_update(oldvers, r.name, r1, verbose)
|
||||||
entry_waiter.set_result(r1.name, r1.version)
|
entry_waiter.set_result(r.name, r1.version)
|
||||||
ret[r1.name] = r1
|
ret[r.name] = r1
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
return ret, has_failures
|
return ret, has_failures
|
||||||
|
|
||||||
|
|
|
@ -106,8 +106,8 @@ def cmp() -> None:
|
||||||
oldverf = opt.ver_files[0]
|
oldverf = opt.ver_files[0]
|
||||||
newverf = opt.ver_files[1]
|
newverf = opt.ver_files[1]
|
||||||
|
|
||||||
oldvers = core.read_verfile(oldverf)
|
oldvers = {k: v.version for k, v in core.read_verfile(oldverf).items()}
|
||||||
newvers = core.read_verfile(newverf)
|
newvers = {k: v.version for k, v in core.read_verfile(newverf).items()}
|
||||||
|
|
||||||
differences = []
|
differences = []
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ logger = structlog.get_logger(logger_name=__name__)
|
||||||
Entry = Dict[str, Any]
|
Entry = Dict[str, Any]
|
||||||
Entry.__doc__ = '''The configuration `dict` for an entry.'''
|
Entry.__doc__ = '''The configuration `dict` for an entry.'''
|
||||||
Entries = Dict[str, Entry]
|
Entries = Dict[str, Entry]
|
||||||
VersData = Dict[str, str]
|
|
||||||
|
|
||||||
if sys.version_info[:2] >= (3, 11):
|
if sys.version_info[:2] >= (3, 11):
|
||||||
from typing import LiteralString
|
from typing import LiteralString
|
||||||
|
@ -145,15 +144,7 @@ RawResult.name.__doc__ = 'The name (table name) of the entry.'
|
||||||
RawResult.version.__doc__ = 'The result from the check.'
|
RawResult.version.__doc__ = 'The result from the check.'
|
||||||
RawResult.conf.__doc__ = 'The entry configuration (table content) of the entry.'
|
RawResult.conf.__doc__ = 'The entry configuration (table content) of the entry.'
|
||||||
|
|
||||||
class Result(NamedTuple):
|
ResultData = Dict[str, RichResult]
|
||||||
name: str
|
|
||||||
version: str
|
|
||||||
conf: Entry
|
|
||||||
url: Optional[str]
|
|
||||||
gitref: Optional[str]
|
|
||||||
revision: Optional[str]
|
|
||||||
|
|
||||||
ResultData = Dict[str, Result]
|
|
||||||
|
|
||||||
class BaseWorker:
|
class BaseWorker:
|
||||||
'''The base class for defining `Worker` classes for source plugins.
|
'''The base class for defining `Worker` classes for source plugins.
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# MIT licensed
|
# MIT licensed
|
||||||
# Copyright (c) 2020 lilydjwg <lilydjwg@gmail.com>, et al.
|
# Copyright (c) 2020, 2024 lilydjwg <lilydjwg@gmail.com>, et al.
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import structlog
|
import structlog
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Dict
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import tomli as tomllib
|
import tomli as tomllib
|
||||||
|
@ -20,13 +20,13 @@ import pytest_asyncio
|
||||||
|
|
||||||
from nvchecker import core
|
from nvchecker import core
|
||||||
from nvchecker import __main__ as main
|
from nvchecker import __main__ as main
|
||||||
from nvchecker.util import Entries, VersData, RawResult
|
from nvchecker.util import Entries, ResultData, RawResult
|
||||||
|
|
||||||
use_keyfile = False
|
use_keyfile = False
|
||||||
|
|
||||||
async def run(
|
async def run(
|
||||||
entries: Entries, max_concurrency: int = 20,
|
entries: Entries, max_concurrency: int = 20,
|
||||||
) -> VersData:
|
) -> Dict[str, str]:
|
||||||
task_sem = asyncio.Semaphore(max_concurrency)
|
task_sem = asyncio.Semaphore(max_concurrency)
|
||||||
result_q: asyncio.Queue[RawResult] = asyncio.Queue()
|
result_q: asyncio.Queue[RawResult] = asyncio.Queue()
|
||||||
keyfile = os.environ.get('KEYFILE')
|
keyfile = os.environ.get('KEYFILE')
|
||||||
|
@ -43,7 +43,7 @@ async def run(
|
||||||
keymanager, entry_waiter, 1, {},
|
keymanager, entry_waiter, 1, {},
|
||||||
)
|
)
|
||||||
|
|
||||||
oldvers: VersData = {}
|
oldvers: ResultData = {}
|
||||||
result_coro = core.process_result(oldvers, result_q, entry_waiter)
|
result_coro = core.process_result(oldvers, result_q, entry_waiter)
|
||||||
runner_coro = core.run_tasks(futures)
|
runner_coro = core.run_tasks(futures)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue