From 7ab00d4ddce83c25139cde8d19ca8c44969494f8 Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Mon, 16 Nov 2020 20:10:03 -0500 Subject: [PATCH] Add Python 3.9 to the build matrix --- .appveyor.yml | 2 + .travis.yml | 1 + Makefile | 2 +- bin/verchew | 135 ++++++++++++++++++++++++++------------- gitman/tests/conftest.py | 1 + pyproject.toml | 1 + 6 files changed, 95 insertions(+), 47 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 845b62a5..a4204217 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,6 +6,8 @@ environment: PYTHON_MINOR: 7 - PYTHON_MAJOR: 3 PYTHON_MINOR: 8 + - PYTHON_MAJOR: 3 + PYTHON_MINOR: 9 cache: - .venv -> poetry.lock diff --git a/.travis.yml b/.travis.yml index a599a44c..cbaa1ae5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ language: python python: - 3.7 - 3.8 + - 3.9 cache: pip: true diff --git a/Makefile b/Makefile index 6ee62dfa..26979778 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ $(DEPENDENCIES): poetry.lock ifndef CI poetry.lock: pyproject.toml - poetry lock + poetry lock --no-update @ touch $@ endif diff --git a/bin/verchew b/bin/verchew index df487dc9..9ea5eebf 100755 --- a/bin/verchew +++ b/bin/verchew @@ -38,27 +38,28 @@ from collections import OrderedDict from subprocess import PIPE, STDOUT, Popen -try: - import configparser # Python 3 -except ImportError: - import ConfigParser as configparser # Python 2 +PY2 = sys.version_info[0] == 2 -__version__ = '1.6.2' +if PY2: + import ConfigParser as configparser + from urllib import urlretrieve +else: + import configparser + from urllib.request import urlretrieve -PY2 = sys.version_info[0] == 2 +__version__ = '3.1.1' + +SCRIPT_URL = ( + "https://raw.githubusercontent.com/jacebrowning/verchew/main/verchew/script.py" +) -CONFIG_FILENAMES = [ - 'verchew.ini', - '.verchew.ini', - '.verchewrc', - '.verchew', -] +CONFIG_FILENAMES = ['verchew.ini', '.verchew.ini', '.verchewrc', '.verchew'] SAMPLE_CONFIG = """ [Python] cli = python -versions = Python 3.5 | Python 3.6 +version = Python 3.5 || Python 3.6 [Legacy Python] @@ -81,16 +82,16 @@ optional = true STYLE = { "~": "✔", - "*": "⭑", - "?": "⚠", + "?": "▴", "x": "✘", + "#": "䷉", } COLOR = { - "x": "\033[91m", # red "~": "\033[92m", # green "?": "\033[93m", # yellow - "*": "\033[94m", # cyan + "x": "\033[91m", # red + "#": "\033[96m", # cyan None: "\033[0m", # reset } @@ -110,6 +111,10 @@ def main(): log.debug("PWD: %s", os.getenv('PWD')) log.debug("PATH: %s", os.getenv('PATH')) + if args.vendor: + vendor_script(args.vendor) + sys.exit(0) + path = find_config(args.root, generate=args.init) config = parse_config(path) @@ -118,22 +123,37 @@ def main(): def parse_args(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(description="System dependency version checker.",) version = "%(prog)s v" + __version__ - parser.add_argument('--version', action='version', version=version) - parser.add_argument('-r', '--root', metavar='PATH', - help="specify a custom project root directory") - parser.add_argument('--init', action='store_true', - help="generate a sample configuration file") - parser.add_argument('--exit-code', action='store_true', - help="return a non-zero exit code on failure") - - group = parser.add_mutually_exclusive_group() - group.add_argument('-v', '--verbose', action='count', default=0, - help="enable verbose logging") - group.add_argument('-q', '--quiet', action='store_true', - help="suppress all output on success") + parser.add_argument( + '--version', action='version', version=version, + ) + parser.add_argument( + '-r', '--root', metavar='PATH', help="specify a custom project root directory" + ) + parser.add_argument( + '--exit-code', + action='store_true', + help="return a non-zero exit code on failure", + ) + + group_logging = parser.add_mutually_exclusive_group() + group_logging.add_argument( + '-v', '--verbose', action='count', default=0, help="enable verbose logging" + ) + group_logging.add_argument( + '-q', '--quiet', action='store_true', help="suppress all output on success" + ) + + group_commands = parser.add_argument_group('commands') + group_commands.add_argument( + '--init', action='store_true', help="generate a sample configuration file" + ) + + group_commands.add_argument( + '--vendor', metavar='PATH', help="download the program for offline use" + ) args = parser.parse_args() @@ -151,6 +171,20 @@ def configure_logging(count=0): logging.basicConfig(level=level, format="%(levelname)s: %(message)s") +def vendor_script(path): + root = os.path.abspath(os.path.join(path, os.pardir)) + if not os.path.isdir(root): + log.info("Creating directory %s", root) + os.makedirs(root) + + log.info("Downloading %s to %s", SCRIPT_URL, path) + urlretrieve(SCRIPT_URL, path) + + log.debug("Making %s executable", path) + mode = os.stat(path).st_mode + os.chmod(path, mode | 0o111) + + def find_config(root=None, filenames=None, generate=False): root = root or os.getcwd() filenames = filenames or CONFIG_FILENAMES @@ -186,7 +220,7 @@ def generate_config(root=None, filenames=None): def parse_config(path): - data = OrderedDict() + data = OrderedDict() # type: ignore log.info("Parsing config file: %s", path) config = configparser.ConfigParser() @@ -198,9 +232,9 @@ def parse_config(path): data[section][name] = value for name in data: - versions = data[name].get('versions', data[name].pop('version', "")) - data[name]['versions'] = versions - data[name]['patterns'] = [v.strip() for v in versions.split('|')] + version = data[name].get('version') or "" + data[name]['version'] = version + data[name]['patterns'] = [v.strip() for v in version.split('||')] return data @@ -214,23 +248,28 @@ def check_dependencies(config): for pattern in settings['patterns']: if match_version(pattern, output): - show(_("~") + " MATCHED: {0}".format(pattern)) + show(_("~") + " MATCHED: {0}".format(pattern or "")) success.append(_("~")) break else: if settings.get('optional'): - show(_("?") + " EXPECTED: {0}".format(settings['versions'])) + show(_("?") + " EXPECTED (OPTIONAL): {0}".format(settings['version'])) success.append(_("?")) else: if QUIET: - print("Unmatched {0} version: {1}".format( - name, - settings['versions'], - )) - show(_("x") + " EXPECTED: {0}".format(settings['versions'])) + if "not found" in output: + actual = "Not found" + else: + actual = output.split('\n')[0].strip('.') + expected = settings['version'] or "" + print("{0}: {1}, EXPECTED: {2}".format(name, actual, expected)) + show( + _("x") + + " EXPECTED: {0}".format(settings['version'] or "") + ) success.append(_("x")) if settings.get('message'): - show(_("*") + " MESSAGE: {0}".format(settings['message'])) + show(_("#") + " MESSAGE: {0}".format(settings['message'])) show("Results: " + " ".join(success), head=True) @@ -247,12 +286,16 @@ def get_version(program, argument=None): show("$ {0}".format(" ".join(args))) output = call(args) - show(output.splitlines()[0] if output else "") + lines = output.splitlines() + show(lines[0] if lines else "") return output def match_version(pattern, output): + if "not found" in output.split('\n')[0]: + return False + regex = pattern.replace('.', r'\.') + r'(\b|/)' log.debug("Matching %s: %s", regex, output) @@ -289,7 +332,7 @@ def show(text, start='', end='\n', head=False): if log.getEffectiveLevel() < logging.WARNING: log.info(text) else: - formatted = (start + text + end) + formatted = start + text + end if PY2: formatted = formatted.encode('utf-8') sys.stdout.write(formatted) @@ -303,7 +346,7 @@ def _(word, is_tty=None, supports_utf8=None, supports_ansi=None): if is_tty is None: is_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() if supports_utf8 is None: - supports_utf8 = sys.stdout.encoding == 'UTF-8' + supports_utf8 = str(sys.stdout.encoding).lower() == 'utf-8' if supports_ansi is None: supports_ansi = sys.platform != 'win32' or 'ANSICON' in os.environ diff --git a/gitman/tests/conftest.py b/gitman/tests/conftest.py index bd79934c..9388f0d2 100644 --- a/gitman/tests/conftest.py +++ b/gitman/tests/conftest.py @@ -17,6 +17,7 @@ def pytest_configure(config): terminal = config.pluginmanager.getplugin('terminal') terminal.TerminalReporter.showfspath = False + log.init() log.silence('gitman.shell', allow_info=True) log.silence('datafiles', allow_warning=True) diff --git a/pyproject.toml b/pyproject.toml index 936f67c8..0567fd60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ classifiers = [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Software Development", "Topic :: Software Development :: Build Tools", "Topic :: Software Development :: Version Control",