Add config option to set default character encoding
This commit adds a new config option 'encoding' which when set will take a codec string from the stdlib codec library and use it as the encoding kwarg for all open() calls that are part of the reno commands. This will let a project assert a specific encoding for all release note files, which can be important if the project is using special characters and have contributors using multiple platforms or locales. Change-Id: If90422fa95cb4eabc4c1722104cef3b28b7fca2d Story: #2007757 Task: #39959
This commit is contained in:
parent
1d503fa8ca
commit
984bcba17e
@ -210,6 +210,7 @@ the most convenient way to manage the values consistently.
|
||||
template: |
|
||||
<template-used-to-create-new-notes>
|
||||
...
|
||||
encoding: utf8
|
||||
|
||||
Many of the settings in the configuration file can be overridden by
|
||||
using command-line switches. For example:
|
||||
|
@ -79,17 +79,18 @@ def write_cache_db(conf, versions_to_include,
|
||||
Return the name of the file created, if any.
|
||||
|
||||
"""
|
||||
encoding = conf.options['encoding']
|
||||
if outfilename == '-':
|
||||
stream = sys.stdout
|
||||
close_stream = False
|
||||
elif outfilename:
|
||||
stream = open(outfilename, 'w')
|
||||
stream = open(outfilename, 'w', encoding=encoding)
|
||||
close_stream = True
|
||||
else:
|
||||
outfilename = loader.get_cache_filename(conf)
|
||||
if not os.path.exists(os.path.dirname(outfilename)):
|
||||
os.makedirs(os.path.dirname(outfilename))
|
||||
stream = open(outfilename, 'w')
|
||||
stream = open(outfilename, 'w', encoding=encoding)
|
||||
close_stream = True
|
||||
try:
|
||||
cache = build_cache_db(
|
||||
|
@ -180,6 +180,14 @@ _OPTIONS = [
|
||||
released version. If this option is unset, the development
|
||||
version number is used (for example, ``3.0.0-3``).
|
||||
""")),
|
||||
Opt('encoding', None,
|
||||
textwrap.dedent("""\
|
||||
The character encoding to use when opening note files. If not
|
||||
specified it will be dependent on the system running reno (whatever
|
||||
'locale.getpreferredencoding()' returns. This takes in a string
|
||||
name that will be passed to the encoding kwarg for open(), so any
|
||||
codec or alias from stdlib's codec module is valid.
|
||||
""")),
|
||||
]
|
||||
|
||||
|
||||
|
@ -30,11 +30,11 @@ def _pick_note_file_name(notesdir, slug):
|
||||
)
|
||||
|
||||
|
||||
def _make_note_file(filename, template):
|
||||
def _make_note_file(filename, template, encoding=None):
|
||||
notesdir = os.path.dirname(filename)
|
||||
if not os.path.exists(notesdir):
|
||||
os.makedirs(notesdir)
|
||||
with open(filename, 'w') as f:
|
||||
with open(filename, 'w', encoding=encoding) as f:
|
||||
f.write(template)
|
||||
|
||||
|
||||
@ -45,13 +45,13 @@ def _edit_file(filename):
|
||||
return True
|
||||
|
||||
|
||||
def _get_user_template(template_file):
|
||||
def _get_user_template(template_file, encoding=None):
|
||||
if not os.path.exists(template_file):
|
||||
raise ValueError(
|
||||
'The provided template file %s doesn\'t '
|
||||
'exist' % template_file,
|
||||
)
|
||||
with open(template_file, 'r') as f:
|
||||
with open(template_file, 'r', encoding=encoding) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
@ -65,11 +65,12 @@ def create_cmd(args, conf):
|
||||
# concern.
|
||||
slug = args.slug.replace(' ', '-')
|
||||
filename = _pick_note_file_name(conf.notespath, slug)
|
||||
encoding = conf.options['encoding']
|
||||
if args.from_template:
|
||||
template = _get_user_template(args.from_template)
|
||||
template = _get_user_template(args.from_template, encoding=encoding)
|
||||
else:
|
||||
template = conf.template
|
||||
_make_note_file(filename, template)
|
||||
_make_note_file(filename, template, encoding=encoding)
|
||||
if args.edit and not _edit_file(filename):
|
||||
print('Was unable to edit the new note. EDITOR environment variable '
|
||||
'is missing!')
|
||||
|
@ -58,6 +58,7 @@ class Loader(object):
|
||||
self._scanner_output = None
|
||||
self._tags_to_dates = None
|
||||
self._cache_filename = get_cache_filename(conf)
|
||||
self._encoding = conf.options['encoding']
|
||||
|
||||
self._load_data()
|
||||
|
||||
@ -69,7 +70,7 @@ class Loader(object):
|
||||
|
||||
if (not self._ignore_cache) and cache_file_exists:
|
||||
LOG.debug('loading cache file %s', self._cache_filename)
|
||||
with open(self._cache_filename, 'r') as f:
|
||||
with open(self._cache_filename, 'r', encoding=self._encoding) as f:
|
||||
self._cache = yaml.safe_load(f.read())
|
||||
# Save the cached scanner output to the same attribute
|
||||
# it would be in if we had loaded it "live". This
|
||||
|
@ -17,6 +17,7 @@ from reno import loader
|
||||
def report_cmd(args, conf):
|
||||
"Generates a release notes report"
|
||||
ldr = loader.Loader(conf)
|
||||
encoding = conf.options['encoding']
|
||||
if args.version:
|
||||
versions = args.version
|
||||
else:
|
||||
@ -30,7 +31,7 @@ def report_cmd(args, conf):
|
||||
branch=args.branch,
|
||||
)
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
with open(args.output, 'w', encoding=encoding) as f:
|
||||
f.write(text)
|
||||
else:
|
||||
print(text)
|
||||
|
@ -460,7 +460,7 @@ class RenoRepo(repo.Repo):
|
||||
tree = self[tree_sha]
|
||||
return tree
|
||||
|
||||
def get_file_at_commit(self, filename, sha):
|
||||
def get_file_at_commit(self, filename, sha, encoding=None):
|
||||
"""Return the contents of the file.
|
||||
|
||||
If sha is None, return the working copy of the file. If the
|
||||
@ -474,7 +474,8 @@ class RenoRepo(repo.Repo):
|
||||
if sha is None:
|
||||
# Get the copy from the working directory.
|
||||
try:
|
||||
with open(os.path.join(self.path, filename), 'r') as f:
|
||||
with open(os.path.join(self.path, filename), 'r',
|
||||
encoding=encoding) as f:
|
||||
return f.read()
|
||||
except IOError:
|
||||
return None
|
||||
@ -530,6 +531,7 @@ class Scanner(object):
|
||||
_get_unique_id(fn)
|
||||
for fn in self.conf.ignore_notes
|
||||
)
|
||||
self._encoding = conf.options['encoding']
|
||||
|
||||
def _get_ref(self, name):
|
||||
if name:
|
||||
@ -812,11 +814,13 @@ class Scanner(object):
|
||||
|
||||
def get_file_at_commit(self, filename, sha):
|
||||
"Return the contents of the file if it exists at the commit, or None."
|
||||
return self._repo.get_file_at_commit(filename, sha)
|
||||
return self._repo.get_file_at_commit(filename, sha,
|
||||
encoding=self._encoding)
|
||||
|
||||
def _file_exists_at_commit(self, filename, sha):
|
||||
"Return true if the file exists at the given commit."
|
||||
return bool(self.get_file_at_commit(filename, sha))
|
||||
return bool(self.get_file_at_commit(filename, sha,
|
||||
encoding=self._encoding))
|
||||
|
||||
def get_series_branches(self):
|
||||
"Get branches matching the branch_name_re config option."
|
||||
|
@ -17,6 +17,7 @@ from unittest import mock
|
||||
import fixtures
|
||||
import io
|
||||
|
||||
from reno import config
|
||||
from reno import create
|
||||
from reno.tests import base
|
||||
|
||||
@ -69,8 +70,9 @@ class TestCreate(base.TestCase):
|
||||
args.from_template = self._create_user_template('i-am-a-user-template')
|
||||
args.slug = 'theslug'
|
||||
args.edit = False
|
||||
conf = mock.Mock()
|
||||
conf = mock.create_autospec(config.Config)
|
||||
conf.notespath = self.tmpdir
|
||||
conf.options = {'encoding': None}
|
||||
with mock.patch('sys.stdout', new=io.StringIO()) as fake_out:
|
||||
create.create_cmd(args, conf)
|
||||
filename = self._get_file_path_from_output(fake_out.getvalue())
|
||||
@ -83,7 +85,7 @@ class TestCreate(base.TestCase):
|
||||
args.from_template = 'some-unexistent-file.yaml'
|
||||
args.slug = 'theslug'
|
||||
args.edit = False
|
||||
conf = mock.Mock()
|
||||
conf = mock.create_autospec(config.Config)
|
||||
conf.notespath = self.tmpdir
|
||||
self.assertRaises(ValueError, create.create_cmd, args, conf)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user