From 2c8949cc3f118a4f82cc9e2b699200d2695ddb91 Mon Sep 17 00:00:00 2001 From: Jeremy Carroll Date: Sun, 26 Aug 2018 19:44:29 -0700 Subject: [PATCH 1/6] Use explicit cast to str for environment (for py2 on windows) --- bumpversion/__init__.py | 2 +- tests/test_cli.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bumpversion/__init__.py b/bumpversion/__init__.py index 57972aa..7c0a07b 100644 --- a/bumpversion/__init__.py +++ b/bumpversion/__init__.py @@ -72,7 +72,7 @@ def commit(cls, message): f.write(message.encode('utf-8')) f.close() env = os.environ.copy() - env['HGENCODING'] = 'utf-8' + env[str('HGENCODING')] = str('utf-8') try: subprocess.check_output(cls._COMMIT_COMMAND + [f.name], env=env) except subprocess.CalledProcessError as exc: diff --git a/tests/test_cli.py b/tests/test_cli.py index 7b798d3..488dc1c 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -21,10 +21,14 @@ from bumpversion import main, DESCRIPTION, WorkingDirectoryIsDirtyException, \ split_args_in_optional_and_positional + def _get_subprocess_env(): env = os.environ.copy() - env['HGENCODING'] = 'utf-8' + # In python2 cast to str from unicode (note the future import). + # In python3 does nothing. + env[str('HGENCODING')] = str('utf-8') return env + SUBPROCESS_ENV = _get_subprocess_env() call = partial(subprocess.call, env=SUBPROCESS_ENV) From 24e3046baa219ce015c1470918692c5222253448 Mon Sep 17 00:00:00 2001 From: Jeremy Carroll Date: Sun, 26 Aug 2018 19:47:21 -0700 Subject: [PATCH 2/6] Use encoding for command line args Windows Py2 --- bumpversion/__init__.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/bumpversion/__init__.py b/bumpversion/__init__.py index 7c0a07b..c36add9 100644 --- a/bumpversion/__init__.py +++ b/bumpversion/__init__.py @@ -15,6 +15,7 @@ import argparse import os +import platform import re import sre_constants import subprocess @@ -30,6 +31,15 @@ from bumpversion.version_part import VersionPart, NumericVersionPartConfiguration, ConfiguredVersionPartConfiguration + +if platform.system() == 'Windows' and sys.version_info[0] == 2: + def _command_args(args): + return [a.encode("utf-8") for a in args] +else: + def _command_args(args): + return args + + if sys.version_info[0] == 2: sys.stdout = codecs.getwriter('utf-8')(sys.stdout) @@ -153,7 +163,7 @@ def latest_tag_info(cls): @classmethod def add_path(cls, path): - subprocess.check_output(["git", "add", "--update", path]) + subprocess.check_output(_command_args(["git", "add", "--update", path])) @classmethod def tag(cls, sign, name, message): @@ -162,7 +172,7 @@ def tag(cls, sign, name, message): command += ['-s'] if message: command += ['--message', message] - subprocess.check_output(command) + subprocess.check_output(_command_args(command)) class Mercurial(BaseVCS): @@ -201,7 +211,7 @@ def tag(cls, sign, name, message): ) if message: command += ['--message', message] - subprocess.check_output(command) + subprocess.check_output(_command_args(command)) VCS = [Git, Mercurial] From 3ac443f8d9410927ce3ae4abb8247bf8ee2464d3 Mon Sep 17 00:00:00 2001 From: Jeremy Carroll Date: Sun, 26 Aug 2018 19:48:03 -0700 Subject: [PATCH 3/6] Use text mode utf-8 for most I/O, not binary --- bumpversion/__init__.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/bumpversion/__init__.py b/bumpversion/__init__.py index c36add9..e0e5099 100644 --- a/bumpversion/__init__.py +++ b/bumpversion/__init__.py @@ -12,23 +12,21 @@ except: from io import StringIO - import argparse +import codecs +import io import os import platform import re import sre_constants import subprocess +import sys import warnings -import io from string import Formatter from datetime import datetime from difflib import unified_diff from tempfile import NamedTemporaryFile -import sys -import codecs - from bumpversion.version_part import VersionPart, NumericVersionPartConfiguration, ConfiguredVersionPartConfiguration @@ -74,6 +72,7 @@ def __call__(self, parser, namespace, values, option_string=None): 'utcnow': datetime.utcnow(), } + class BaseVCS(object): @classmethod @@ -243,12 +242,12 @@ def should_contain_version(self, version, context): assert False, msg def contains(self, search): - with io.open(self.path, 'rb') as f: + with io.open(self.path, 'rt', encoding='utf-8') as f: search_lines = search.splitlines() lookbehind = [] for lineno, line in enumerate(f.readlines()): - lookbehind.append(line.decode('utf-8').rstrip("\n")) + lookbehind.append(line.rstrip("\n")) if len(lookbehind) > len(search_lines): lookbehind = lookbehind[1:] @@ -257,14 +256,14 @@ def contains(self, search): search_lines[-1] in lookbehind[-1] and search_lines[1:-1] == lookbehind[1:-1]): logger.info("Found '{}' in {} at line {}: {}".format( - search, self.path, lineno - (len(lookbehind) - 1), line.decode('utf-8').rstrip())) + search, self.path, lineno - (len(lookbehind) - 1), line.rstrip())) return True return False def replace(self, current_version, new_version, context, dry_run): - with io.open(self.path, 'rb') as f: - file_content_before = f.read().decode('utf-8') + with io.open(self.path, 'rt', encoding='utf-8') as f: + file_content_before = f.read() context['current_version'] = self._versionconfig.serialize(current_version, context) context['new_version'] = self._versionconfig.serialize(new_version, context) @@ -302,8 +301,8 @@ def replace(self, current_version, new_version, context, dry_run): )) if not dry_run: - with io.open(self.path, 'wb') as f: - f.write(file_content_after.encode('utf-8')) + with io.open(self.path, 'wt', encoding='utf-8') as f: + f.write(file_content_after) def __str__(self): return self.path @@ -902,8 +901,8 @@ def main(original_args=None): logger.info(new_config.getvalue()) if write_to_config_file: - with io.open(config_file, 'wb') as f: - f.write(new_config.getvalue().encode('utf-8')) + with io.open(config_file, 'wt', encoding='utf-8') as f: + f.write(new_config.getvalue()) except UnicodeEncodeError: warnings.warn( From 3cf47a694c7db1195af4c38b47c224b0133b00de Mon Sep 17 00:00:00 2001 From: Jeremy Carroll Date: Sun, 26 Aug 2018 19:48:20 -0700 Subject: [PATCH 4/6] Extra git ignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index ebe387d..8c7cb9c 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,8 @@ venv.bak/ .spyderproject .spyproject +.idea + # Rope project settings .ropeproject @@ -129,3 +131,4 @@ Session.vim # Auto-generated tag files tags +.pytest_cache From e457552b2d070c58365a69850a2deef1f7d7dee3 Mon Sep 17 00:00:00 2001 From: Jeremy Carroll Date: Sun, 26 Aug 2018 19:48:52 -0700 Subject: [PATCH 5/6] Fix tests when hg or git absent --- tests/test_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 488dc1c..17fb045 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -31,7 +31,7 @@ def _get_subprocess_env(): SUBPROCESS_ENV = _get_subprocess_env() -call = partial(subprocess.call, env=SUBPROCESS_ENV) +call = partial(subprocess.call, env=SUBPROCESS_ENV, shell=True) check_call = partial(subprocess.check_call, env=SUBPROCESS_ENV) check_output = partial(subprocess.check_output, env=SUBPROCESS_ENV) From 0b9d7caaa9e0704dbf883b16de2695b875273c17 Mon Sep 17 00:00:00 2001 From: Jeremy Carroll Date: Sun, 26 Aug 2018 19:49:29 -0700 Subject: [PATCH 6/6] Rework test_usage_string_fork --- tests/test_cli.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 17fb045..d8e1049 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,21 +2,20 @@ from __future__ import unicode_literals, print_function +import argparse +import bumpversion +import mock import os +import platform import pytest -import sys -import logging -import mock -import argparse +import six import subprocess -from os import curdir, makedirs, chdir, environ -from os.path import join, curdir, dirname +from os import environ from shlex import split as shlex_split from textwrap import dedent from functools import partial -import bumpversion from bumpversion import main, DESCRIPTION, WorkingDirectoryIsDirtyException, \ split_args_in_optional_and_positional @@ -154,18 +153,26 @@ def test_usage_string(tmpdir, capsys): assert EXPECTED_USAGE in out + def test_usage_string_fork(tmpdir, capsys): tmpdir.chdir() + if platform.system() == "Windows" and six.PY3: + # There are encoding problems on Windows with the encoding of → + tmpdir.join(".bumpversion.cfg").write("""[bumpversion] + message: Bump version: {current_version} to {new_version} + tag_message: 'Bump version: {current_version} to {new_version}""") + try: - out = check_output('bumpversion --help', shell=True, stderr=subprocess.STDOUT).decode('utf-8') + out = check_output('bumpversion --help', shell=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: out = e.output - if not 'usage: bumpversion [-h]' in out: + if not b'usage: bumpversion [-h]' in out: print(out) - assert 'usage: bumpversion [-h]' in out + assert b'usage: bumpversion [-h]' in out + @pytest.mark.parametrize(("vcs"), [xfail_if_no_git("git"), xfail_if_no_hg("hg")]) def test_regression_help_in_workdir(tmpdir, capsys, vcs):