Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support files with encoding other than UTF-8. #188

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ General configuration is grouped in a ``[bumpversion]`` section.
Also available as ``--message`` (e.g.: ``bumpversion --message
'[{now:%Y-%m-%d}] Jenkins Build {$BUILD_NUMBER}: {new_version}' patch``)

``encoding =``
**default:** ``utf-8``

The encoding of the files to modify, unless overridden for a specific
file. This can be any encoding supported by Python's ``codecs`` module.


Part specific configuration
---------------------------
Expand Down Expand Up @@ -304,6 +310,12 @@ File specific configuration
Can be multiple lines, templated using `Python Format String Syntax
<http://docs.python.org/2/library/string.html#format-string-syntax>`_.

``encoding =``
**default:** ``utf-8``

The encoding of the file. This can be any encoding supported by
Python's ``codecs`` module.

Options
=======

Expand Down
30 changes: 20 additions & 10 deletions bumpversion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,9 @@ def prefixed_environ():

class ConfiguredFile(object):

def __init__(self, path, versionconfig):
def __init__(self, path, encoding, versionconfig):
self.path = path
self.encoding = encoding
self._versionconfig = versionconfig

def should_contain_version(self, version, context):
Expand All @@ -213,12 +214,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=self.encoding) 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:]
Expand All @@ -227,14 +228,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=self.encoding) 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)
Expand Down Expand Up @@ -272,8 +273,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=self.encoding) as f:
f.write(file_content_after)

def __str__(self):
return self.path
Expand Down Expand Up @@ -483,6 +484,7 @@ def serialize(self, version, context):
OPTIONAL_ARGUMENTS_THAT_TAKE_VALUES = [
'--config-file',
'--current-version',
'--encoding',
'--message',
'--new-version',
'--parse',
Expand Down Expand Up @@ -677,7 +679,12 @@ def main(original_args=None):
if not 'replace' in section_config:
section_config['replace'] = defaults.get("replace", '{new_version}')

files.append(ConfiguredFile(filename, VersionConfig(**section_config)))
if 'encoding' in section_config:
encoding = section_config.pop("encoding")
else:
encoding = defaults.get("encoding", 'utf-8')

files.append(ConfiguredFile(filename, encoding, VersionConfig(**section_config)))

else:
message = "Could not read config file at {}".format(config_file)
Expand All @@ -704,6 +711,9 @@ def main(original_args=None):
parser2.add_argument('--replace', metavar='REPLACE',
help='Template for complete string to replace',
default=defaults.get("replace", '{new_version}'))
parser2.add_argument('--encoding',
help='File encoding',
default=defaults.get("encoding", 'utf-8'))

known_args, remaining_argv = parser2.parse_known_args(args)

Expand Down Expand Up @@ -808,7 +818,7 @@ def main(original_args=None):
file_names = file_names or positionals[1:]

for file_name in file_names:
files.append(ConfiguredFile(file_name, vc))
files.append(ConfiguredFile(file_name, defaults.get("encoding", 'utf-8'), vc))

for vcs in VCS:
if vcs.is_usable():
Expand Down
9 changes: 9 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def _mock_calls_to_string(called_mock):
{current_version})
--replace REPLACE Template for complete string to replace (default:
{new_version})
--encoding ENCODING File encoding (default: utf-8)
--current-version VERSION
Version that needs to be updated (default: None)
--dry-run, -n Don't write any files, just pretend. (default: False)
Expand Down Expand Up @@ -215,6 +216,14 @@ def test_simple_replacement_in_utf8_file(tmpdir):
assert "'Kr\\xc3\\xb6t1.3.1'" in repr(out)


def test_simple_replacement_in_utf16le_file(tmpdir):
tmpdir.join("VERSION").write("Kröt1.3.0".encode('utf-16le'), 'wb')
tmpdir.chdir()
main(shlex_split("patch --encoding utf-16le --current-version 1.3.0 --new-version 1.3.1 VERSION"))
out = tmpdir.join("VERSION").read('rb')
assert "'K\\x00r\\x00\\xf6\\x00t\\x001\\x00.\\x003\\x00.\\x001\\x00'" in repr(out)


def test_config_file(tmpdir):
tmpdir.join("file1").write("0.9.34")
tmpdir.join("mybumpconfig.cfg").write("""[bumpversion]
Expand Down