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 diff --git a/bumpversion/__init__.py b/bumpversion/__init__.py index 57972aa..e0e5099 100644 --- a/bumpversion/__init__.py +++ b/bumpversion/__init__.py @@ -12,24 +12,32 @@ 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 + +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) @@ -64,6 +72,7 @@ def __call__(self, parser, namespace, values, option_string=None): 'utcnow': datetime.utcnow(), } + class BaseVCS(object): @classmethod @@ -72,7 +81,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: @@ -153,7 +162,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 +171,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 +210,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] @@ -233,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:] @@ -247,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) @@ -292,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 @@ -892,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( diff --git a/tests/test_cli.py b/tests/test_cli.py index 7b798d3..d8e1049 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,32 +2,35 @@ 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 + 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) +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) @@ -150,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):