AsyncIO rewrite

This commit is contained in:
Felix Yan 2017-07-04 17:04:29 +08:00
parent c423bdee0a
commit 84df2716b5
46 changed files with 352 additions and 503 deletions

View file

@ -1,17 +1,15 @@
sudo: false sudo: false
language: python language: python
python: python:
- "3.4"
- "3.5" - "3.5"
- "3.6" - "3.6"
- "nightly" - "nightly"
# ptr requires Python >= 3.3 but pypy3 reports 3.2 # ptr requires Python >= 3.3 but pypy3 reports 3.2
# - "pypy3" # - "pypy3"
install: pip install -U pytest pytest-runner install: pip install -U aiohttp pytest pytest-asyncio pytest-xdist flaky
script: python setup.py pytest script: pytest
env: env:
global: global:
- ASYNC_TEST_TIMEOUT=20
# github # github
- secure: "JNuxbHbO+Qj88r0So+FKp8GBVmobGlBNi0hkZIyOH4cBXtuiM1Jo6FtRYInfTUH5TcgfMQml1a8p9g8n1fbRcTsxPt3kkT0ZleW1fJNudOHJFOmDooM4gC2/A+6aMl3xdnLCQ9cXxqsXjIUBie3GhqC4ufInU7VshxOn7KZADbI3zDuLuw9gdsBQf/OADY4oO3y1URxdnWjssP8pwfDFRSEkuLKNDtsYrhkmp3jRAq5DMtMXTEyHly9CJHow7yMyoBHa6Q/J7+C57pI4JsO8c0nJWy/wQUnqw9EeLE/9gAHY1sHlEpjZtJrV45kRd+KC6x4FtoFjvngxymK2A0zmecBI3DRTWBAZedPPVatAD9nlDmwAacBtwvuZJkt6fMUBWMY1I1NEiwdYxceBiqrnvU48FfNOylXE6KuarCQZik/VWk8olIQjXIukMu8EQ58pnEuLZB7wbwNzMLheomuVMEK1nfLOltKaytztl/7cKlsx6SmxY5rQI/x7QInd+rq9OxDDwCo+jEofPKvAcCbUJj6SqfB7QAUxJwwD/ER4/Bji9KSz3BoCu+x7h/ILcskNqLlg4LDCcpxqMOyxePk7A30sSop1E5YLWo0lmS9s88mEz89tzCWSDVIzwQrdMghNBe6JFMzOoKDRDhEkMrs3MAK+FUJkbteGhHrdC86EidU=" - secure: "JNuxbHbO+Qj88r0So+FKp8GBVmobGlBNi0hkZIyOH4cBXtuiM1Jo6FtRYInfTUH5TcgfMQml1a8p9g8n1fbRcTsxPt3kkT0ZleW1fJNudOHJFOmDooM4gC2/A+6aMl3xdnLCQ9cXxqsXjIUBie3GhqC4ufInU7VshxOn7KZADbI3zDuLuw9gdsBQf/OADY4oO3y1URxdnWjssP8pwfDFRSEkuLKNDtsYrhkmp3jRAq5DMtMXTEyHly9CJHow7yMyoBHa6Q/J7+C57pI4JsO8c0nJWy/wQUnqw9EeLE/9gAHY1sHlEpjZtJrV45kRd+KC6x4FtoFjvngxymK2A0zmecBI3DRTWBAZedPPVatAD9nlDmwAacBtwvuZJkt6fMUBWMY1I1NEiwdYxceBiqrnvU48FfNOylXE6KuarCQZik/VWk8olIQjXIukMu8EQ58pnEuLZB7wbwNzMLheomuVMEK1nfLOltKaytztl/7cKlsx6SmxY5rQI/x7QInd+rq9OxDDwCo+jEofPKvAcCbUJj6SqfB7QAUxJwwD/ER4/Bji9KSz3BoCu+x7h/ILcskNqLlg4LDCcpxqMOyxePk7A30sSop1E5YLWo0lmS9s88mEz89tzCWSDVIzwQrdMghNBe6JFMzOoKDRDhEkMrs3MAK+FUJkbteGhHrdC86EidU="
# gitlab # gitlab

View file

@ -45,9 +45,8 @@ Contents
Dependency Dependency
========== ==========
- Python 3 - Python 3.5+
- Tornado - aiohttp
- Optional pycurl
- All commands used in your version source files - All commands used in your version source files
Running Running
@ -132,7 +131,7 @@ regex
When multiple version strings are found, the maximum of those is chosen. When multiple version strings are found, the maximum of those is chosen.
proxy proxy
The HTTP proxy to use. The format is ``host:port``, e.g. ``localhost:8087``. This requires `pycurl <http://pycurl.sourceforge.net/>`_. The HTTP proxy to use. The format is ``host:port``, e.g. ``localhost:8087``.
user_agent user_agent
The ``User-Agent`` header value to use. Use something more like a tool (e.g. ``curl/7.40.0``) in Europe or the real web page won't get through because cookie policies (SourceForge has this issue). The ``User-Agent`` header value to use. Use something more like a tool (e.g. ``curl/7.40.0``) in Europe or the real web page won't get through because cookie policies (SourceForge has this issue).
@ -191,7 +190,7 @@ sort_version_key
``pkg_resources.parse_version``. ``vercmp`` use ``pyalpm.vercmp``. ``pkg_resources.parse_version``. ``vercmp`` use ``pyalpm.vercmp``.
proxy proxy
The HTTP proxy to use. The format is ``host:port``, e.g. ``localhost:8087``. This requires `pycurl <http://pycurl.sourceforge.net/>`_. The HTTP proxy to use. The format is ``host:port``, e.g. ``localhost:8087``.
An environment variable ``NVCHECKER_GITHUB_TOKEN`` can be set to a GitHub OAuth token in order to request more frequently than anonymously. An environment variable ``NVCHECKER_GITHUB_TOKEN`` can be set to a GitHub OAuth token in order to request more frequently than anonymously.
@ -289,7 +288,7 @@ cpan
The name used on CPAN, e.g. ``YAML``. The name used on CPAN, e.g. ``YAML``.
proxy proxy
The HTTP proxy to use. The format is ``host:port``, e.g. ``localhost:8087``. This requires `pycurl <http://pycurl.sourceforge.net/>`_. The HTTP proxy to use. The format is ``host:port``, e.g. ``localhost:8087``.
Check Packagist Check Packagist
--------------- ---------------

View file

@ -1,4 +1,4 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
__version__ = '0.4.5dev' __version__ = '0.5dev'

View file

@ -6,11 +6,11 @@ import os
import sys import sys
import logging import logging
import configparser import configparser
import asyncio
from tornado.stack_context import ExceptionStackContext
from .lib import nicelogger from .lib import nicelogger
from .get_version import get_version from .get_version import get_version
from .source import session
from . import __version__ from . import __version__
@ -60,9 +60,8 @@ def write_verfile(file, versions):
safe_overwrite(file, data, method='writelines') safe_overwrite(file, data, method='writelines')
class Source: class Source:
started = False
tasks = 0
oldver = newver = None oldver = newver = None
def __init__(self, file): def __init__(self, file):
self.config = config = configparser.ConfigParser( self.config = config = configparser.ConfigParser(
dict_type=dict, allow_no_value=True dict_type=dict, allow_no_value=True
@ -72,62 +71,51 @@ class Source:
if '__config__' in config: if '__config__' in config:
c = config['__config__'] c = config['__config__']
d = os.path.dirname(file.name) d = os.path.dirname(file.name)
self.oldver = os.path.expandvars(os.path.expanduser(os.path.join(d, c.get('oldver')))) self.oldver = os.path.expandvars(os.path.expanduser(
self.newver = os.path.expandvars(os.path.expanduser(os.path.join(d, c.get('newver')))) os.path.join(d, c.get('oldver'))))
self.newver = os.path.expandvars(os.path.expanduser(
os.path.join(d, c.get('newver'))))
def check(self): session.nv_config = config["__config__"]
self.started = True
async def check(self):
if self.oldver: if self.oldver:
self.oldvers = read_verfile(self.oldver) self.oldvers = read_verfile(self.oldver)
else: else:
self.oldvers = {} self.oldvers = {}
self.curvers = self.oldvers.copy() self.curvers = self.oldvers.copy()
futures = []
config = self.config config = self.config
for name in config.sections(): for name in config.sections():
if name == '__config__': if name == '__config__':
continue continue
self.task_inc()
conf = config[name] conf = config[name]
conf['oldver'] = self.oldvers.get(name, None) conf['oldver'] = self.oldvers.get(name, None)
with ExceptionStackContext(self._handle_exception): futures.append(get_version(name, conf))
get_version(name, conf, self.print_version_update)
def _handle_exception(self, type, value, traceback): for fu in asyncio.as_completed(futures):
self.task_dec() try:
raise value.with_traceback(traceback) name, version = await fu
if version is not None:
self.print_version_update(name, version)
except Exception:
logger.exception('error happened dealing with %s', name)
def task_inc(self): if self.newver:
self.tasks += 1 write_verfile(self.newver, self.curvers)
def task_dec(self):
self.tasks -= 1
if self.tasks == 0 and self.started:
if self.newver:
write_verfile(self.newver, self.curvers)
self.on_finish()
def print_version_update(self, name, version): def print_version_update(self, name, version):
try: oldver = self.oldvers.get(name, None)
if version is None: if not oldver or oldver != version:
return logger.info('%s updated to %s', name, version)
self.curvers[name] = version
oldver = self.oldvers.get(name, None) self.on_update(name, version, oldver)
if not oldver or oldver != version: else:
logger.info('%s updated version %s', name, version) logger.debug('%s current %s', name, version)
self.curvers[name] = version
self.on_update(name, version, oldver)
else:
logger.debug('%s current version %s', name, version)
finally:
self.task_dec()
def on_update(self, name, version, oldver): def on_update(self, name, version, oldver):
pass pass
def on_finish(self):
pass
def __repr__(self): def __repr__(self):
return '<Source from %r>' % self.name return '<Source from %r>' % self.name

View file

@ -11,12 +11,11 @@ handler_precedence = (
'cratesio', 'npm', 'hackage', 'cpan', 'gitlab', 'packagist' 'cratesio', 'npm', 'hackage', 'cpan', 'gitlab', 'packagist'
) )
def get_version(name, conf, callback): async def get_version(name, conf):
for key in handler_precedence: for key in handler_precedence:
if key in conf: if key in conf:
func = import_module('.source.' + key, __package__).get_version func = import_module('.source.' + key, __package__).get_version
func(name, conf, callback) return await func(name, conf)
break
else: else:
logger.error('%s: no idea to get version info.', name) logger.error('%s: no idea to get version info.', name)
callback(name, None) return name, None

View file

@ -5,7 +5,7 @@
import logging import logging
import argparse import argparse
from tornado.ioloop import IOLoop import asyncio
from .lib import notify from .lib import notify
from . import core from . import core
@ -21,9 +21,6 @@ class Source(core.Source):
notifications.append(msg) notifications.append(msg)
notify.update('nvchecker', '\n'.join(notifications)) notify.update('nvchecker', '\n'.join(notifications))
def on_finish(self):
IOLoop.instance().stop()
def main(): def main():
global args global args
@ -37,11 +34,11 @@ def main():
if not args.file: if not args.file:
return return
s = Source(args.file) s = Source(args.file)
ioloop = IOLoop.instance() ioloop = asyncio.get_event_loop()
ioloop.add_callback(s.check) ioloop.run_until_complete(s.check())
ioloop.start()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -1,4 +1,18 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from .base import * import atexit
import aiohttp
connector = aiohttp.TCPConnector(limit=20)
config = None
class BetterClientSession(aiohttp.ClientSession):
async def _request(self, *args, **kwargs):
if hasattr(self, "nv_config") and self.nv_config.get("proxy"):
kwargs.setdefault("proxy", self.nv_config.get("proxy"))
return await super(BetterClientSession, self)._request(*args, **kwargs)
session = BetterClientSession(connector=connector, read_timeout=10, conn_timeout=5)
atexit.register(session.close)

View file

@ -1,33 +1,22 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from functools import partial
import logging import logging
import json from . import session
from tornado.httpclient import AsyncHTTPClient
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
URL = 'https://www.archlinux.org/packages/search/json/?name=' URL = 'https://www.archlinux.org/packages/search/json/'
def get_version(name, conf, callback): async def get_version(name, conf):
pkg = conf.get('archpkg') or name pkg = conf.get('archpkg') or name
strip_release = conf.getboolean('strip-release', False) strip_release = conf.getboolean('strip-release', False)
url = URL + pkg async with session.get(URL, params={"name": pkg}) as res:
AsyncHTTPClient().fetch( data = await res.json()
url, partial(_pkg_done, name, strip_release, callback))
def _pkg_done(name, strip_release, callback, res):
if res.error:
raise res.error
data = json.loads(res.body.decode('utf-8'))
if not data['results']: if not data['results']:
logger.error('Arch package not found: %s', name) logger.error('Arch package not found: %s', name)
callback(name, None) return name, None
return
r = [r for r in data['results'] if r['repo'] != 'testing'][0] r = [r for r in data['results'] if r['repo'] != 'testing'][0]
if strip_release: if strip_release:
@ -35,4 +24,4 @@ def _pkg_done(name, strip_release, callback, res):
else: else:
version = r['pkgver'] + '-' + r['pkgrel'] version = r['pkgver'] + '-' + r['pkgrel']
callback(name, version) return name, version

View file

@ -1,36 +1,24 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from functools import partial
import json
import logging import logging
from . import session
from tornado.httpclient import AsyncHTTPClient
from tornado.escape import url_escape
AUR_URL = 'https://aur.archlinux.org/rpc/?v=5&type=info&arg[]=' AUR_URL = 'https://aur.archlinux.org/rpc/?v=5&type=info&arg[]='
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_version(name, conf, callback): async def get_version(name, conf):
aurname = conf.get('aur') or name aurname = conf.get('aur') or name
strip_release = conf.getboolean('strip-release', False) strip_release = conf.getboolean('strip-release', False)
url = AUR_URL + url_escape(aurname) async with session.get(AUR_URL, params={"v": 5, "type": "info", "arg[]": aurname}) as res:
AsyncHTTPClient().fetch( data = await res.json()
url, partial(_aur_done, name, strip_release, callback))
def _aur_done(name, strip_release, callback, res):
if res.error:
raise res.error
data = json.loads(res.body.decode('utf-8'))
if not data['results']: if not data['results']:
logger.error('AUR upstream not found for %s', name) logger.error('AUR upstream not found for %s', name)
callback(name, None) return name, None
return
version = data['results'][0]['Version'] version = data['results'][0]['Version']
if strip_release and '-' in version: if strip_release and '-' in version:
version = version.rsplit('-', 1)[0] version = version.rsplit('-', 1)[0]
callback(name, version) return name, version

View file

@ -1,10 +0,0 @@
# MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tornado.httpclient import AsyncHTTPClient
try:
import pycurl
AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
except ImportError:
pycurl = None

View file

@ -1,11 +1,7 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import os from aiohttp import request
import json
from functools import partial
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from ..sortversion import sort_version_keys from ..sortversion import sort_version_keys
@ -13,7 +9,7 @@ from ..sortversion import sort_version_keys
BITBUCKET_URL = 'https://bitbucket.org/api/2.0/repositories/%s/commits/%s' BITBUCKET_URL = 'https://bitbucket.org/api/2.0/repositories/%s/commits/%s'
BITBUCKET_MAX_TAG = 'https://bitbucket.org/api/1.0/repositories/%s/tags' BITBUCKET_MAX_TAG = 'https://bitbucket.org/api/1.0/repositories/%s/tags'
def get_version(name, conf, callback): async def get_version(name, conf):
repo = conf.get('bitbucket') repo = conf.get('bitbucket')
br = conf.get('branch', '') br = conf.get('branch', '')
use_max_tag = conf.getboolean('use_max_tag', False) use_max_tag = conf.getboolean('use_max_tag', False)
@ -23,16 +19,12 @@ def get_version(name, conf, callback):
url = BITBUCKET_MAX_TAG % repo url = BITBUCKET_MAX_TAG % repo
else: else:
url = BITBUCKET_URL % (repo, br) url = BITBUCKET_URL % (repo, br)
request = HTTPRequest(url, user_agent='lilydjwg/nvchecker') async with request("GET", url) as res:
AsyncHTTPClient().fetch(request, data = await res.json()
callback=partial(_bitbucket_done, name, use_max_tag, ignored_tags, sort_version_key, callback))
def _bitbucket_done(name, use_max_tag, ignored_tags, sort_version_key, callback, res):
data = json.loads(res.body.decode('utf-8'))
if use_max_tag: if use_max_tag:
data = [tag for tag in data if tag not in ignored_tags] data = [tag for tag in data if tag not in ignored_tags]
data.sort(key=sort_version_key) data.sort(key=sort_version_key)
version = data[-1] version = data[-1]
else: else:
version = data['values'][0]['date'].split('T', 1)[0].replace('-', '') version = data['values'][0]['date'].split('T', 1)[0].replace('-', '')
callback(name, version) return name, version

View file

@ -1,44 +1,18 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import queue
import logging import logging
from functools import partial import asyncio
import tornado.process
from tornado.ioloop import IOLoop
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
cmd_q = queue.Queue()
cmd_q.running = False
def get_version(name, conf, callback): async def get_version(name, conf):
cmd = conf['cmd'] cmd = conf['cmd']
cmd_q.put((name, cmd, callback)) p = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE)
if not cmd_q.running:
_run_command()
def _run_command(): output = (await p.communicate())[0].strip().decode('latin1')
cmd_q.running = True if p.returncode != 0:
try: logger.error('%s: command exited with %d.', name, p.returncode)
name, cmd, callback = cmd_q.get_nowait() return name, None
except queue.Empty:
cmd_q.running = False
return
p = tornado.process.Subprocess(cmd, shell=True,
stdout=tornado.process.Subprocess.STREAM)
p.set_exit_callback(partial(_command_done, name, callback, p))
def _command_done(name, callback, process, status):
if status != 0:
logger.error('%s: command exited with %d.', name, status)
callback(name, None)
else:
process.stdout.read_until_close(partial(_got_version_from_cmd, callback, name))
_run_command()
def _got_version_from_cmd(callback, name, output):
output = output.strip().decode('latin1')
callback(name, output)
return name, output

View file

@ -2,24 +2,13 @@
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import os import os
import json from . import session
from functools import partial
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from ..sortversion import sort_version_keys
API_URL = 'https://crates.io/api/v1/crates/%s' API_URL = 'https://crates.io/api/v1/crates/%s'
def get_version(name, conf, callback): async def get_version(name, conf):
name = conf.get('cratesio') or name name = conf.get('cratesio') or name
request = HTTPRequest(API_URL % name, user_agent='lilydjwg/nvchecker') async with session.get(API_URL % name) as res:
AsyncHTTPClient().fetch( data = await res.json()
request,
callback = partial(_cratesio_done, name, callback),
)
def _cratesio_done(name, callback, res):
data = json.loads(res.body.decode('utf-8'))
version = [v['num'] for v in data['versions'] if not v['yanked']][0] version = [v['num'] for v in data['versions'] if not v['yanked']][0]
callback(name, version) return name, version

View file

@ -1,34 +1,24 @@
# MIT licensed # MIT licensed
# Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al. # Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al.
from functools import partial
import logging import logging
import json from . import session
from tornado.httpclient import AsyncHTTPClient
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
URL = 'https://sources.debian.net/api/src/%(pkgname)s/?suite=%(suite)s' URL = 'https://sources.debian.net/api/src/%(pkgname)s/?suite=%(suite)s'
def get_version(name, conf, callback): async def get_version(name, conf):
pkg = conf.get('debianpkg') or name pkg = conf.get('debianpkg') or name
strip_release = conf.getboolean('strip-release', False) strip_release = conf.getboolean('strip-release', False)
suite = conf.get('suite') or "sid" suite = conf.get('suite') or "sid"
url = URL % {"pkgname": pkg, "suite": suite} url = URL % {"pkgname": pkg, "suite": suite}
AsyncHTTPClient().fetch( async with session.get(url) as res:
url, partial(_pkg_done, name, strip_release, callback)) data = await res.json()
def _pkg_done(name, strip_release, callback, res):
if res.error:
raise res.error
data = json.loads(res.body.decode('utf-8'))
if not data.get('versions'): if not data.get('versions'):
logger.error('Debian package not found: %s', name) logger.error('Debian package not found: %s', name)
callback(name, None) return name, None
return
r = data['versions'][0] r = data['versions'][0]
if strip_release: if strip_release:
@ -36,4 +26,4 @@ def _pkg_done(name, strip_release, callback, res):
else: else:
version = r['version'] version = r['version']
callback(name, version) return name, version

View file

@ -4,9 +4,7 @@
import re import re
import time import time
import logging import logging
from functools import partial from . import session
from tornado.httpclient import AsyncHTTPClient
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -14,14 +12,11 @@ GCODE_URL = 'https://code.google.com/p/%s/source/list'
GCODE_HG_RE = re.compile( GCODE_HG_RE = re.compile(
r'<a onclick="cancelBubble=true" href="detail\?r=[0-9a-f]+">([^<]+)</a>') r'<a onclick="cancelBubble=true" href="detail\?r=[0-9a-f]+">([^<]+)</a>')
def get_version(name, conf, callback): async def get_version(name, conf):
repo = conf.get('gcode_hg') or name repo = conf.get('gcode_hg') or name
url = GCODE_URL % repo url = GCODE_URL % repo
AsyncHTTPClient().fetch(url, user_agent='lilydjwg/nvchecker', async with session.get(url) as res:
callback=partial(_gcodehg_done, name, callback)) data = await res.text()
def _gcodehg_done(name, callback, res):
data = res.body.decode('utf-8')
m = GCODE_HG_RE.search(data) m = GCODE_HG_RE.search(data)
if m: if m:
t = time.strptime(m.group(1), '%b %d, %Y') t = time.strptime(m.group(1), '%b %d, %Y')
@ -29,4 +24,4 @@ def _gcodehg_done(name, callback, res):
else: else:
logger.error('%s: version not found.', name) logger.error('%s: version not found.', name)
version = None version = None
callback(name, version) return name, version

View file

@ -3,27 +3,22 @@
import re import re
import logging import logging
from functools import partial from . import session
from tornado.httpclient import AsyncHTTPClient
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
GCODE_URL = 'https://code.google.com/p/%s/source/list' GCODE_URL = 'https://code.google.com/p/%s/source/list'
GCODE_SVN_RE = re.compile(r'<a href="detail\?r=\d+">r(\d+)</a>') GCODE_SVN_RE = re.compile(r'<a href="detail\?r=\d+">r(\d+)</a>')
def get_version(name, conf, callback): async def get_version(name, conf):
repo = conf.get('gcode_svn') or name repo = conf.get('gcode_svn') or name
url = GCODE_URL % repo url = GCODE_URL % repo
AsyncHTTPClient().fetch(url, user_agent='lilydjwg/nvchecker', async with session.get(url) as res:
callback=partial(_gcodehg_done, name, callback)) data = await res.text()
def _gcodehg_done(name, callback, res):
data = res.body.decode('utf-8')
m = GCODE_SVN_RE.search(data) m = GCODE_SVN_RE.search(data)
if m: if m:
version = m.group(1) version = m.group(1)
else: else:
logger.error('%s: version not found.', name) logger.error('%s: version not found.', name)
version = None version = None
callback(name, version) return name, version

View file

@ -2,19 +2,15 @@
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import os import os
import json
from functools import partial
from tornado.httpclient import AsyncHTTPClient, HTTPRequest from . import session
from .base import pycurl
from ..sortversion import sort_version_keys from ..sortversion import sort_version_keys
GITHUB_URL = 'https://api.github.com/repos/%s/commits?sha=%s' GITHUB_URL = 'https://api.github.com/repos/%s/commits?sha=%s'
GITHUB_LATEST_RELEASE = 'https://api.github.com/repos/%s/releases/latest' GITHUB_LATEST_RELEASE = 'https://api.github.com/repos/%s/releases/latest'
GITHUB_MAX_TAG = 'https://api.github.com/repos/%s/tags' GITHUB_MAX_TAG = 'https://api.github.com/repos/%s/tags'
def get_version(name, conf, callback): async def get_version(name, conf):
repo = conf.get('github') repo = conf.get('github')
br = conf.get('branch', 'master') br = conf.get('branch', 'master')
use_latest_release = conf.getboolean('use_latest_release', False) use_latest_release = conf.getboolean('use_latest_release', False)
@ -33,17 +29,9 @@ def get_version(name, conf, callback):
kwargs = {} kwargs = {}
if conf.get('proxy'): if conf.get('proxy'):
if pycurl: kwargs["proxy"] = conf.get("proxy")
kwargs['proxy_host'] = "".join(conf['proxy'].split(':')[:-1]) async with session.get(url, headers=headers, **kwargs) as res:
kwargs['proxy_port'] = int(conf['proxy'].split(':')[-1]) data = await res.json()
else:
logger.warn('%s: proxy set but not used because pycurl is unavailable.', name)
request = HTTPRequest(url, headers=headers, user_agent='lilydjwg/nvchecker', **kwargs)
AsyncHTTPClient().fetch(request,
callback=partial(_github_done, name, use_latest_release, use_max_tag, ignored_tags, sort_version_key, callback))
def _github_done(name, use_latest_release, use_max_tag, ignored_tags, sort_version_key, callback, res):
data = json.loads(res.body.decode('utf-8'))
if use_latest_release: if use_latest_release:
version = data['tag_name'] version = data['tag_name']
elif use_max_tag: elif use_max_tag:
@ -54,4 +42,4 @@ def _github_done(name, use_latest_release, use_max_tag, ignored_tags, sort_versi
# YYYYMMDD.HHMMSS # YYYYMMDD.HHMMSS
version = data[0]['commit']['committer']['date'] \ version = data[0]['commit']['committer']['date'] \
.rstrip('Z').replace('-', '').replace(':', '').replace('T', '.') .rstrip('Z').replace('-', '').replace(':', '').replace('T', '.')
callback(name, version) return name, version

View file

@ -2,13 +2,10 @@
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import os import os
import json
from functools import partial
import logging import logging
import urllib.parse import urllib.parse
from tornado.httpclient import AsyncHTTPClient, HTTPRequest from . import session
from ..sortversion import sort_version_keys from ..sortversion import sort_version_keys
GITLAB_URL = 'https://%s/api/v3/projects/%s/repository/commits?ref_name=%s' GITLAB_URL = 'https://%s/api/v3/projects/%s/repository/commits?ref_name=%s'
@ -16,7 +13,7 @@ GITLAB_MAX_TAG = 'https://%s/api/v3/projects/%s/repository/tags'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_version(name, conf, callback): async def get_version(name, conf):
repo = urllib.parse.quote_plus(conf.get('gitlab')) repo = urllib.parse.quote_plus(conf.get('gitlab'))
br = conf.get('branch', 'master') br = conf.get('branch', 'master')
host = conf.get('host', "gitlab.com") host = conf.get('host', "gitlab.com")
@ -28,8 +25,7 @@ def get_version(name, conf, callback):
token = conf.get('token', os.environ.get(env_name, None)) token = conf.get('token', os.environ.get(env_name, None))
if token is None: if token is None:
logger.error('%s: No gitlab token specified.', name) logger.error('%s: No gitlab token specified.', name)
callback(name, None) return name, None
return
if use_max_tag: if use_max_tag:
url = GITLAB_MAX_TAG % (host, repo) url = GITLAB_MAX_TAG % (host, repo)
@ -37,16 +33,12 @@ def get_version(name, conf, callback):
url = GITLAB_URL % (host, repo, br) url = GITLAB_URL % (host, repo, br)
headers = {"PRIVATE-TOKEN": token} headers = {"PRIVATE-TOKEN": token}
request = HTTPRequest(url, headers=headers, user_agent='lilydjwg/nvchecker') async with session.get(url, headers=headers) as res:
AsyncHTTPClient().fetch(request, data = await res.json()
callback=partial(_gitlab_done, name, use_max_tag, ignored_tags, sort_version_key, callback))
def _gitlab_done(name, use_max_tag, ignored_tags, sort_version_key, callback, res):
data = json.loads(res.body.decode('utf-8'))
if use_max_tag: if use_max_tag:
data = [tag["name"] for tag in data if tag["name"] not in ignored_tags] data = [tag["name"] for tag in data if tag["name"] not in ignored_tags]
data.sort(key=sort_version_key) data.sort(key=sort_version_key)
version = data[-1] version = data[-1]
else: else:
version = data[0]['created_at'].split('T', 1)[0].replace('-', '') version = data[0]['created_at'].split('T', 1)[0].replace('-', '')
callback(name, version) return name, version

View file

@ -1,5 +1,5 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
def get_version(name, conf, callback): async def get_version(name, conf):
callback(name, conf.get('manual')) return name, conf.get('manual')

View file

@ -3,15 +3,14 @@
from . import cmd from . import cmd
def get_version(name, conf, callback): async def get_version(name, conf):
referree = conf.get('pacman') or name referree = conf.get('pacman') or name
c = "LANG=C pacman -Si %s | grep -F Version | awk '{print $3}'" % referree c = "LANG=C pacman -Si %s | grep -F Version | awk '{print $3}'" % referree
conf['cmd'] = c conf['cmd'] = c
strip_release = conf.getboolean('strip-release', False)
def callback_wrapper(name, version): _, version = await cmd.get_version(name, conf)
strip_release = conf.getboolean('strip-release', False)
if strip_release and '-' in version:
version = version.rsplit('-', 1)[0]
callback(name, version)
cmd.get_version(name, conf, callback_wrapper) if strip_release and '-' in version:
version = version.rsplit('-', 1)[0]
return name, version

View file

@ -4,50 +4,36 @@
import re import re
import sre_constants import sre_constants
import logging import logging
import urllib.parse
from functools import partial
from tornado.httpclient import AsyncHTTPClient from . import session
from .base import pycurl
from ..sortversion import sort_version_keys from ..sortversion import sort_version_keys
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_version(name, conf, callback): async def get_version(name, conf):
try: try:
r = re.compile(conf['regex']) regex = re.compile(conf['regex'])
except sre_constants.error: except sre_constants.error:
logger.warn('%s: bad regex, skipped.', name, exc_info=True) logger.warn('%s: bad regex, skipped.', name, exc_info=True)
callback(name, None) return name, None
return
encoding = conf.get('encoding', 'latin1') encoding = conf.get('encoding', 'latin1')
httpclient = AsyncHTTPClient()
kwargs = {} kwargs = {}
headers = {}
if conf.get('proxy'): if conf.get('proxy'):
if pycurl: kwargs["proxy"] = conf.get("proxy")
host, port = urllib.parse.splitport(conf['proxy'])
kwargs['proxy_host'] = host
kwargs['proxy_port'] = int(port)
else:
logger.warn('%s: proxy set but not used because pycurl is unavailable.', name)
if conf.get('user_agent'): if conf.get('user_agent'):
kwargs['user_agent'] = conf['user_agent'] headers['User-Agent'] = conf['user_agent']
sort_version_key = sort_version_keys[conf.get("sort_version_key", "parse_version")] sort_version_key = sort_version_keys[conf.get("sort_version_key", "parse_version")]
httpclient.fetch(conf['url'], partial( async with session.get(conf['url'], headers=headers, **kwargs) as res:
_got_version, name, r, encoding, sort_version_key, callback version = None
), **kwargs)
def _got_version(name, regex, encoding, sort_version_key, callback, res):
version = None
try:
body = res.body.decode(encoding)
try: try:
version = max(regex.findall(body), key=sort_version_key) body = (await res.read()).decode(encoding)
except ValueError: try:
logger.error('%s: version string not found.', name) version = max(regex.findall(body), key=sort_version_key)
finally: except ValueError:
callback(name, version) logger.error('%s: version string not found.', name)
finally:
return name, version

View file

@ -1,32 +1,20 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import json from . import session
from functools import partial
from tornado.httpclient import AsyncHTTPClient
from .base import pycurl
def simple_json(urlpat, confkey, version_from_json): def simple_json(urlpat, confkey, version_from_json):
def get_version(name, conf, callback): async def get_version(name, conf):
repo = conf.get(confkey) or name repo = conf.get(confkey) or name
url = urlpat % repo url = urlpat % repo
kwargs = {} kwargs = {}
if conf.get('proxy'): if conf.get('proxy'):
if pycurl: kwargs["proxy"] = conf.get('proxy')
kwargs['proxy_host'] = "".join(conf['proxy'].split(':')[:-1])
kwargs['proxy_port'] = int(conf['proxy'].split(':')[-1])
else:
logger.warn('%s: proxy set but not used because pycurl is unavailable.', name)
AsyncHTTPClient().fetch(url, user_agent='lilydjwg/nvchecker', async with session.get(url, **kwargs) as res:
callback=partial(_json_done, name, callback), **kwargs) data = await res.json()
def _json_done(name, callback, res):
data = json.loads(res.body.decode('utf-8'))
version = version_from_json(data) version = version_from_json(data)
callback(name, version) return name, version
return get_version return get_version

View file

@ -2,11 +2,7 @@
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import logging import logging
from functools import partial import asyncio
import tornado.process
from tornado.ioloop import IOLoop
from pkg_resources import parse_version from pkg_resources import parse_version
import os.path as _path import os.path as _path
@ -30,7 +26,7 @@ def _parse_oldver(oldver):
return PROT_VER, 0, ver return PROT_VER, 0, ver
return PROT_VER, count, ver return PROT_VER, count, ver
def get_version(name, conf, callback): async def get_version(name, conf):
vcs = conf['vcs'] vcs = conf['vcs']
use_max_tag = conf.getboolean('use_max_tag', False) use_max_tag = conf.getboolean('use_max_tag', False)
ignored_tags = conf.get("ignored_tags", "").split() ignored_tags = conf.get("ignored_tags", "").split()
@ -38,33 +34,21 @@ def get_version(name, conf, callback):
cmd = _cmd_prefix + [name, vcs] cmd = _cmd_prefix + [name, vcs]
if use_max_tag: if use_max_tag:
cmd += ["get_tags"] cmd += ["get_tags"]
p = tornado.process.Subprocess(cmd, io_loop=IOLoop.instance(), p = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE)
stdout=tornado.process.Subprocess.STREAM)
p.set_exit_callback(partial(_command_done, name, oldver, use_max_tag, ignored_tags, callback, p))
def _command_done(name, oldver, use_max_tag, ignored_tags, callback, process, status): output = (await p.communicate())[0].strip().decode('latin1')
if status != 0: if p.returncode != 0:
logger.error('%s: command exited with %d.', name, status) logger.error('%s: command exited with %d.', name, p.returncode)
callback(name, None) return name, None
else: else:
if use_max_tag: if use_max_tag:
process.stdout.read_until_close(partial(_got_tags_from_cmd, data = [tag for tag in output.split("\n") if tag not in ignored_tags]
callback, name, ignored_tags)) data.sort(key=parse_version)
version = data[-1]
return name, version
else: else:
process.stdout.read_until_close(partial(_got_version_from_cmd, oldver = _parse_oldver(oldver)
callback, name, oldver)) if output == oldver[2]:
return name, None
def _got_tags_from_cmd(callback, name, ignored_tags, output): else:
output = output.strip().decode('latin1') return name, "%d.%d.%s" % (oldver[0], oldver[1] + 1, output)
data = [tag for tag in output.split("\n") if tag not in ignored_tags]
data.sort(key=parse_version)
version = data[-1]
callback(name, version)
def _got_version_from_cmd(callback, name, oldver_str, output):
output = output.strip().decode('latin1')
oldver = _parse_oldver(oldver_str)
if output == oldver[2]:
callback(name, None)
else:
callback(name, "%d.%d.%s" % (oldver[0], oldver[1] + 1, output))

5
setup.cfg Normal file
View file

@ -0,0 +1,5 @@
[flake8]
ignore = E111, E302, E501
[tool:pytest]
# addopts = -n auto

View file

@ -18,9 +18,11 @@ setup(
zip_safe = True, zip_safe = True,
packages = find_packages(exclude=["tests"]), packages = find_packages(exclude=["tests"]),
install_requires = ['tornado>=4.1', 'setuptools'], install_requires = ['aiohttp', 'setuptools'],
tests_require = [ tests_require = [
'pytest', 'pytest',
'pytest-asyncio',
'pytest-xdist',
'flaky', 'flaky',
], ],
entry_points = { entry_points = {
@ -42,8 +44,8 @@ setup(
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Topic :: Internet", "Topic :: Internet",
"Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP",
"Topic :: Software Development", "Topic :: Software Development",

26
tests/conftest.py Normal file
View file

@ -0,0 +1,26 @@
import configparser
import pytest
import asyncio
from nvchecker.get_version import get_version as _get_version
@pytest.fixture(scope="module")
async def get_version():
async def __call__(name, config):
if isinstance(config, dict):
_config = configparser.ConfigParser(dict_type=dict, allow_no_value=True)
_config.read_dict({name: config})
config = _config[name]
return (await _get_version(name, config))[1]
return __call__
@pytest.yield_fixture(scope="module")
def event_loop(request):
"""Override pytest-asyncio's event_loop fixture,
Don't create an instance of the default event loop for each test case.
"""
loop = asyncio.get_event_loop()
yield loop

View file

@ -1,24 +0,0 @@
# MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import configparser
from tornado.ioloop import IOLoop
import tornado.testing
from nvchecker.get_version import get_version
class ExternalVersionTestCase(tornado.testing.AsyncTestCase):
def get_new_ioloop(self):
return IOLoop.instance()
def sync_get_version(self, name, config):
def get_version_callback(name, version):
self.stop(version)
if isinstance(config, dict):
_config = configparser.ConfigParser(dict_type=dict, allow_no_value=True)
_config.read_dict({name: config})
config = _config[name]
get_version(name, config, get_version_callback)
return self.wait()

View file

@ -1,17 +1,14 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
import os from flaky import flaky
import pytest import pytest
pytestmark = pytest.mark.asyncio
from tests.helper import ExternalVersionTestCase @flaky
async def test_archpkg(get_version):
assert await get_version("ipw2100-fw", {"archpkg": None}) == "1.3-8"
@pytest.mark.skipif("TRAVIS" in os.environ, @flaky
reason="Travis-CI has issues connecting to the Arch website") async def test_archpkg_strip_release(get_version):
class ArchPKGTest(ExternalVersionTestCase): assert await get_version("ipw2100-fw", {"archpkg": None, "strip-release": 1}) == "1.3"
def test_archpkg(self):
self.assertEqual(self.sync_get_version("ipw2100-fw", {"archpkg": None}), "1.3-8")
def test_archpkg_strip_release(self):
self.assertEqual(self.sync_get_version("ipw2100-fw", {"archpkg": None, "strip-release": 1}), "1.3")

View file

@ -1,12 +1,14 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase from flaky import flaky
import pytest
pytestmark = pytest.mark.asyncio
@flaky
async def test_aur(get_version):
assert await get_version("asciidoc-fake", {"aur": None}) == "1.0-1"
class AURTest(ExternalVersionTestCase): @flaky
def test_aur(self): async def test_aur_strip_release(get_version):
self.assertEqual(self.sync_get_version("asciidoc-fake", {"aur": None}), "1.0-1") assert await get_version("asciidoc-fake", {"aur": None, "strip-release": 1}) == "1.0"
def test_aur_strip_release(self):
self.assertEqual(self.sync_get_version("asciidoc-fake", {"aur": None, "strip-release": 1}), "1.0")

View file

@ -1,14 +1,14 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
class BitBucketTest(ExternalVersionTestCase): async def test_bitbucket(get_version):
def test_bitbucket(self): assert await get_version("example", {"bitbucket": "prawee/git-tag"}) == "20150303"
self.assertEqual(self.sync_get_version("example", {"bitbucket": "prawee/git-tag"}), "20150303")
def test_bitbucket_max_tag(self): async def test_bitbucket_max_tag(get_version):
self.assertEqual(self.sync_get_version("example", {"bitbucket": "prawee/git-tag", "use_max_tag": 1}), "1.7.0") assert await get_version("example", {"bitbucket": "prawee/git-tag", "use_max_tag": 1}) == "1.7.0"
def test_bitbucket_max_tag_with_ignored_tags(self): async def test_bitbucket_max_tag_with_ignored_tags(get_version):
self.assertEqual(self.sync_get_version("example", {"bitbucket": "prawee/git-tag", "use_max_tag": 1, "ignored_tags": "1.6.0 1.7.0"}), "v1.5") assert await get_version("example", {"bitbucket": "prawee/git-tag", "use_max_tag": 1, "ignored_tags": "1.6.0 1.7.0"}) == "v1.5"

View file

@ -1,9 +1,11 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_cmd(get_version):
assert await get_version("example", {"cmd": "echo Meow"}) == "Meow"
class CMDTest(ExternalVersionTestCase): async def test_cmd_complex(get_version):
def test_cmd(self): assert await get_version("example", {"cmd": "echo Meow | sed 's/meow/woof/i'"}) == "woof"
self.assertEqual(self.sync_get_version("example", {"cmd": "echo Meow"}), "Meow")

View file

@ -1,9 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_cpan(get_version):
class CPANTest(ExternalVersionTestCase): assert await get_version("POE-Component-Server-HTTPServer", {"cpan": None}) == "0.9.2"
def test_cpan(self):
self.assertEqual(self.sync_get_version("POE-Component-Server-HTTPServer", {"cpan": None}), "0.9.2")

View file

@ -1,11 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_cratesio(get_version):
class CratesIOTest(ExternalVersionTestCase): assert await get_version("example", {"cratesio": None}) == "0.1.0"
def test_npm(self):
self.assertEqual(
self.sync_get_version("example", {"cratesio": None}),
"0.1.0")

View file

@ -1,16 +1,14 @@
# MIT licensed # MIT licensed
# Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al. # Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al.
import os import pytest
pytestmark = pytest.mark.asyncio
from tests.helper import ExternalVersionTestCase async def test_debianpkg(get_version):
assert await get_version("sigrok-firmware-fx2lafw", {"debianpkg": None}) == "0.1.3-1"
class DebianPKGTest(ExternalVersionTestCase): async def test_debianpkg_strip_release(get_version):
def test_debianpkg(self): assert await get_version("sigrok-firmware-fx2lafw", {"debianpkg": None, "strip-release": 1}) == "0.1.3"
self.assertEqual(self.sync_get_version("sigrok-firmware-fx2lafw", {"debianpkg": None}), "0.1.3-1")
def test_debianpkg_strip_release(self): async def test_debianpkg_suite(get_version):
self.assertEqual(self.sync_get_version("sigrok-firmware-fx2lafw", {"debianpkg": None, "strip-release": 1}), "0.1.3") assert await get_version("sigrok-firmware-fx2lafw", {"debianpkg": None, "suite": "jessie"}) == "0.1.2-1"
def test_debianpkg_suite(self):
self.assertEqual(self.sync_get_version("sigrok-firmware-fx2lafw", {"debianpkg": None, "suite": "jessie"}), "0.1.2-1")

View file

@ -1,9 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_gems(get_version):
class RubyGemsTest(ExternalVersionTestCase): assert await get_version("example", {"gems": None}) == "1.0.2"
def test_gems(self):
self.assertEqual(self.sync_get_version("example", {"gems": None}), "1.0.2")

View file

@ -3,20 +3,18 @@
import os import os
import pytest import pytest
from tests.helper import ExternalVersionTestCase pytestmark = [pytest.mark.asyncio,
pytest.mark.skipif("NVCHECKER_GITHUB_TOKEN" not in os.environ,
reason="requires NVCHECKER_GITHUB_TOKEN, or it fails too much")]
async def test_github(get_version):
assert await get_version("example", {"github": "harry-sanabria/ReleaseTestRepo"}) == "20140122.012101"
@pytest.mark.skipif("NVCHECKER_GITHUB_TOKEN" not in os.environ, async def test_github_latest_release(get_version):
reason="requires NVCHECKER_GITHUB_TOKEN, or it fails too much") assert await get_version("example", {"github": "harry-sanabria/ReleaseTestRepo", "use_latest_release": 1}) == "release3"
class GitHubTest(ExternalVersionTestCase):
def test_github(self):
self.assertEqual(self.sync_get_version("example", {"github": "harry-sanabria/ReleaseTestRepo"}), "20140122.012101")
def test_github_latest_release(self): async def test_github_max_tag(get_version):
self.assertEqual(self.sync_get_version("example", {"github": "harry-sanabria/ReleaseTestRepo", "use_latest_release": 1}), "release3") assert await get_version("example", {"github": "harry-sanabria/ReleaseTestRepo", "use_max_tag": 1}) == "second_release"
def test_github_max_tag(self): async def test_github_max_tag_with_ignored_tags(get_version):
self.assertEqual(self.sync_get_version("example", {"github": "harry-sanabria/ReleaseTestRepo", "use_max_tag": 1}), "second_release") assert await get_version("example", {"github": "harry-sanabria/ReleaseTestRepo", "use_max_tag": 1, "ignored_tags": "second_release release3"}) == "first_release"
def test_github_max_tag_with_ignored_tags(self):
self.assertEqual(self.sync_get_version("example", {"github": "harry-sanabria/ReleaseTestRepo", "use_max_tag": 1, "ignored_tags": "second_release release3"}), "first_release")

View file

@ -3,20 +3,18 @@
import os import os
import pytest import pytest
from tests.helper import ExternalVersionTestCase pytestmark = [pytest.mark.asyncio,
pytest.mark.skipif("NVCHECKER_GITLAB_TOKEN_GITLAB_COM" not in os.environ,
reason="requires NVCHECKER_GITLAB_TOKEN_GITLAB_COM")]
async def test_gitlab(get_version):
ver = await get_version("example",
{"gitlab": "gitlab-org/gitlab-test"})
assert len(ver) == 8
assert ver.isdigit()
@pytest.mark.skipif("NVCHECKER_GITLAB_TOKEN_GITLAB_COM" not in os.environ, async def test_gitlab_max_tag(get_version):
reason="requires NVCHECKER_GITLAB_TOKEN_GITLAB_COM") assert await get_version("example", {"gitlab": "gitlab-org/gitlab-test", "use_max_tag": 1}) == "v1.1.0"
class GitLabTest(ExternalVersionTestCase):
def test_gitlab(self):
ver = self.sync_get_version("example",
{"gitlab": "gitlab-org/gitlab-test"})
self.assertEqual(len(ver), 8)
self.assertTrue(ver.isdigit())
def test_gitlab_max_tag(self): async def test_gitlab_max_tag_with_ignored_tags(get_version):
self.assertEqual(self.sync_get_version("example", {"gitlab": "gitlab-org/gitlab-test", "use_max_tag": 1}), "v1.1.0") assert await get_version("example", {"gitlab": "gitlab-org/gitlab-test", "use_max_tag": 1, "ignored_tags": "v1.1.0"}) == "v1.0.0"
def test_gitlab_max_tag_with_ignored_tags(self):
self.assertEqual(self.sync_get_version("example", {"gitlab": "gitlab-org/gitlab-test", "use_max_tag": 1, "ignored_tags": "v1.1.0"}), "v1.0.0")

View file

@ -1,9 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_hackage(get_version):
class HackageTest(ExternalVersionTestCase): assert await get_version("sessions", {"hackage": None}) == "2008.7.18"
def test_hackage(self):
self.assertEqual(self.sync_get_version("sessions", {"hackage": None}), "2008.7.18")

View file

@ -1,9 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_manual(get_version):
class ManualTest(ExternalVersionTestCase): assert await get_version("example", {"manual": "Meow"}) == "Meow"
def test_manual(self):
self.assertEqual(self.sync_get_version("example", {"manual": "Meow"}), "Meow")

View file

@ -1,9 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_npm(get_version):
class NPMTest(ExternalVersionTestCase): assert await get_version("example", {"npm": None}) == "0.0.0"
def test_npm(self):
self.assertEqual(self.sync_get_version("example", {"npm": None}), "0.0.0")

View file

@ -1,9 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_packagist(get_version):
class PackagistTest(ExternalVersionTestCase): assert await get_version("butterfly/example-web-application", {"packagist": None}) == "1.2.0"
def test_packagist(self):
self.assertEqual(self.sync_get_version("butterfly/example-web-application", {"packagist": None}), "1.2.0")

View file

@ -3,14 +3,12 @@
import shutil import shutil
import pytest import pytest
from tests.helper import ExternalVersionTestCase pytestmark = [pytest.mark.asyncio,
pytest.mark.skipif(shutil.which("pacman") is None,
reason="requires pacman command")]
async def test_pacman(get_version):
assert await get_version("ipw2100-fw", {"pacman": None}) == "1.3-8"
@pytest.mark.skipif(shutil.which("pacman") is None, async def test_pacman_strip_release(get_version):
reason="requires pacman command") assert await get_version("ipw2100-fw", {"pacman": None, "strip-release": 1}) == "1.3"
class PacmanTest(ExternalVersionTestCase):
def test_pacman(self):
self.assertEqual(self.sync_get_version("ipw2100-fw", {"pacman": None}), "1.3-8")
def test_pacman_strip_release(self):
self.assertEqual(self.sync_get_version("ipw2100-fw", {"pacman": None, "strip-release": 1}), "1.3")

25
tests/test_proxy.py Normal file
View file

@ -0,0 +1,25 @@
# MIT licensed
# Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al.
import aiohttp
import pytest
pytestmark = pytest.mark.asyncio
async def test_proxy(get_version, monkeypatch):
from nvchecker.source import session
async def fake_request(*args, proxy, **kwargs):
class fake_response():
async def read():
return proxy.encode("ascii")
def release():
pass
return fake_response
monkeypatch.setattr(session, "nv_config", {"proxy": "255.255.255.255:65535"}, raising=False)
monkeypatch.setattr(aiohttp.ClientSession, "_request", fake_request)
assert await get_version("example", {"regex": "(.+)", "url": "deadbeef"}) == "255.255.255.255:65535"
assert await get_version("example", {"regex": "(.+)", "url": "deadbeef", "proxy": "0.0.0.0:0"}) == "0.0.0.0:0"

View file

@ -1,9 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_pypi(get_version):
class PyPITest(ExternalVersionTestCase): assert await get_version("example", {"pypi": None}) == "0.1.0"
def test_pypi(self):
self.assertEqual(self.sync_get_version("example", {"pypi": None}), "0.1.0")

View file

@ -1,13 +1,12 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
from tests.helper import ExternalVersionTestCase import pytest
pytestmark = pytest.mark.asyncio
async def test_regex(get_version):
class RegexTest(ExternalVersionTestCase): assert await get_version("example", {
def test_regex(self): "url": "https://httpbin.org/get",
self.assertEqual(self.sync_get_version("example", { "regex": '"User-Agent": "(\w+)"',
"url": "https://httpbin.org/get", "user_agent": "Meow",
"regex": '"User-Agent": "(\w+)"', }) == "Meow"
"user_agent": "Meow",
}), "Meow")

View file

@ -4,30 +4,29 @@
import os import os
import shutil import shutil
import pytest import pytest
from tests.helper import ExternalVersionTestCase pytestmark = pytest.mark.asyncio
class VCSTest(ExternalVersionTestCase): @pytest.mark.skipif(shutil.which("git") is None,
@pytest.mark.skipif(shutil.which("git") is None, reason="requires git command")
reason="requires git command") async def test_git(get_version):
def test_git(self): os.path.exists("example") or os.mkdir("example")
os.path.exists("example") or os.mkdir("example") assert await get_version("example", {"vcs": "git+https://github.com/harry-sanabria/ReleaseTestRepo.git"}) == "1.1.2b3cdf6134b07ae6ac77f11b586dc1ae6d1521db"
self.assertEqual(self.sync_get_version("example", {"vcs": "git+https://github.com/harry-sanabria/ReleaseTestRepo.git"}), "1.1.2b3cdf6134b07ae6ac77f11b586dc1ae6d1521db")
@pytest.mark.skipif(shutil.which("hg") is None, @pytest.mark.skipif(shutil.which("hg") is None,
reason="requires hg command") reason="requires hg command")
def test_mercurial(self): async def test_mercurial(get_version):
os.path.exists("example") or os.mkdir("example") os.path.exists("example") or os.mkdir("example")
self.assertEqual(self.sync_get_version("example", {"vcs": "hg+https://bitbucket.org/pil0t/testrepo"}), "1.1.84679e29c7d9") assert await get_version("example", {"vcs": "hg+https://bitbucket.org/pil0t/testrepo"}) == "1.1.84679e29c7d9"
@pytest.mark.skipif(shutil.which("git") is None, @pytest.mark.skipif(shutil.which("git") is None,
reason="requires git command") reason="requires git command")
def test_git_max_tag(self): async def test_git_max_tag(get_version):
os.path.exists("example") or os.mkdir("example") os.path.exists("example") or os.mkdir("example")
self.assertEqual(self.sync_get_version("example", {"vcs": "git+https://github.com/harry-sanabria/ReleaseTestRepo.git", "use_max_tag": 1}), "second_release") assert await get_version("example", {"vcs": "git+https://github.com/harry-sanabria/ReleaseTestRepo.git", "use_max_tag": 1}) == "second_release"
@pytest.mark.skipif(shutil.which("git") is None, @pytest.mark.skipif(shutil.which("git") is None,
reason="requires git command") reason="requires git command")
def test_git_max_tag_with_ignored_tags(self): async def test_git_max_tag_with_ignored_tags(get_version):
os.path.exists("example") or os.mkdir("example") os.path.exists("example") or os.mkdir("example")
self.assertEqual(self.sync_get_version("example", {"vcs": "git+https://github.com/harry-sanabria/ReleaseTestRepo.git", "use_max_tag": 1, "ignored_tags": "second_release release3"}), "first_release") assert await get_version("example", {"vcs": "git+https://github.com/harry-sanabria/ReleaseTestRepo.git", "use_max_tag": 1, "ignored_tags": "second_release release3"}) == "first_release"