Revert "Revert "Allow ini plugin to load file using other encoding than utf8." (#27407)"

This reverts commit 520696fb39.
This commit is contained in:
Matt Clay 2017-07-27 18:15:56 -07:00
parent 520696fb39
commit 51bd07204b
9 changed files with 78 additions and 35 deletions

View file

@ -253,6 +253,7 @@ type ini Type of the file. Can be ini or properties (for java
file ansible.ini Name of the file to load
section global Default section where to lookup for key.
re False The key is a regexp.
encoding utf-8 Text encoding to use.
default empty string return value if the key is not in the ini file
========== ============ =========================================================================================

View file

@ -179,7 +179,7 @@ class DataLoader:
except AttributeError:
pass # older versions of yaml don't have dispose function, ignore
def _get_file_contents(self, file_name):
def _get_file_contents(self, file_name, encoding='utf-8'):
'''
Reads the file contents from the given file name, and will decrypt them
if they are found to be vault-encrypted.
@ -194,7 +194,7 @@ class DataLoader:
show_content = True
try:
with open(b_file_name, 'rb') as f:
data = f.read()
data = to_text(f.read(), encoding=encoding)
if is_encrypted(data):
data = self._vault.decrypt(data, filename=b_file_name)
show_content = False

View file

@ -31,7 +31,7 @@ from ansible.plugins.lookup import LookupBase
def _parse_params(term):
'''Safely split parameter term to preserve spaces'''
keys = ['key', 'type', 'section', 'file', 're', 'default']
keys = ['key', 'type', 'section', 'file', 're', 'default', 'encoding']
params = {}
for k in keys:
params[k] = ''
@ -52,19 +52,6 @@ def _parse_params(term):
class LookupModule(LookupBase):
def read_properties(self, filename, key, dflt, is_regexp):
config = StringIO()
current_cfg_file = open(to_bytes(filename, errors='surrogate_or_strict'), 'rb')
config.write(u'[java_properties]\n' + to_text(current_cfg_file.read(), errors='surrogate_or_strict'))
config.seek(0, os.SEEK_SET)
self.cp.readfp(config)
return self.get_value(key, 'java_properties', dflt, is_regexp)
def read_ini(self, filename, key, section, dflt, is_regexp):
self.cp.readfp(open(to_bytes(filename, errors='surrogate_or_strict')))
return self.get_value(key, section, dflt, is_regexp)
def get_value(self, key, section, dflt, is_regexp):
# Retrieve all values from a section using a regexp
if is_regexp:
@ -79,8 +66,6 @@ class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs):
basedir = self.get_basedir(variables)
self.basedir = basedir
self.cp = configparser.ConfigParser()
ret = []
@ -94,6 +79,7 @@ class LookupModule(LookupBase):
'default': None,
'section': "global",
'type': "ini",
'encoding': 'utf-8',
}
# parameters specified?
@ -105,11 +91,23 @@ class LookupModule(LookupBase):
except (ValueError, AssertionError) as e:
raise AnsibleError(e)
# Retrieve file path
path = self.find_file_in_search_path(variables, 'files', paramvals['file'])
# Create StringIO later used to parse ini
config = StringIO()
# Special case for java properties
if paramvals['type'] == "properties":
var = self.read_properties(path, key, paramvals['default'], paramvals['re'])
else:
var = self.read_ini(path, key, paramvals['section'], paramvals['default'], paramvals['re'])
config.write(u'[java_properties]\n')
paramvals['section'] = 'java_properties'
# Open file using encoding
contents, show_data = self._loader._get_file_contents(path, encoding=paramvals['encoding'])
config.write(contents)
config.seek(0, os.SEEK_SET)
self.cp.readfp(config)
var = self.get_value(key, paramvals['section'], paramvals['default'], paramvals['re'])
if var is not None:
if isinstance(var, MutableSequence):
for v in var:

View file

@ -0,0 +1,7 @@
[global]
# A comment
value1=Text associated with value1 and global section
value2=Same for value2 and global section
value.dot=Properties with dot
field.with.space = another space
field_with_unicode=été indien où à château français ïîôû

View file

@ -4,6 +4,7 @@ value1=Text associated with value1 and global section
value2=Same for value2 and global section
value.dot=Properties with dot
field.with.space = another space
unicode=été indien où à château français ïîôû
[section1]
value1=section1/value1

View file

@ -3,3 +3,4 @@ value1=Text associated with value1
value2=Same for value2
value.dot=Properties with dot
field.with.space = another space
field.with.unicode = été indien où à château français ïîôû

View file

@ -9,32 +9,63 @@
test2: "{{lookup('ini', 'value2 type=properties file=lookup.properties')}}"
test_dot: "{{lookup('ini', 'value.dot type=properties file=lookup.properties')}}"
field_with_space: "{{lookup('ini', 'field.with.space type=properties file=lookup.properties')}}"
- debug: var={{item}}
- assert:
that: "{{item}} is defined"
with_items: [ 'test1', 'test2', 'test_dot', 'field_with_space' ]
- name: "read ini value"
set_fact:
value1_global: "{{lookup('ini', 'value1 section=global file=lookup.ini')}}"
value2_global: "{{lookup('ini', 'value2 section=global file=lookup.ini')}}"
value1_section1: "{{lookup('ini', 'value1 section=section1 file=lookup.ini')}}"
field_with_unicode: "{{lookup('ini', 'unicode section=global file=lookup.ini')}}"
- debug: var={{item}}
with_items: [ 'value1_global', 'value2_global', 'value1_section1' ]
with_items: [ 'value1_global', 'value2_global', 'value1_section1', 'field_with_unicode' ]
- assert:
that:
- "field_with_unicode == 'été indien où à château français ïîôû'"
- name: "read ini value from iso8859-15 file"
set_fact:
field_with_unicode: "{{lookup('ini', 'field_with_unicode section=global encoding=iso8859-1 file=lookup-8859-15.ini')}}"
- assert:
that:
- "field_with_unicode == 'été indien où à château français ïîôû'"
- name: "read ini value with section and regexp"
set_fact:
value_section: "{{lookup('ini', 'value[1-2] section=value_section file=lookup.ini re=true')}}"
other_section: "{{lookup('ini', 'other[1-2] section=other_section file=lookup.ini re=true')}}"
- debug: var={{item}}
with_items: [ 'value_section', 'other_section' ]
- assert:
that:
- "value_section == '1,2'"
- "other_section == '4,5'"
- name: "Reading unknown value"
set_fact:
unknown: "{{lookup('ini', 'value2 default=unknown section=section1 file=lookup.ini')}}"
unknown: "{{lookup('ini', 'unknown default=unknown section=section1 file=lookup.ini')}}"
- debug: var=unknown
- assert:
that:
- 'unknown == "unknown"'
- name: "Looping over section section1"
debug: msg="{{item}}"
with_ini: value[1-2] section=section1 file=lookup.ini re=true
register: _
- assert:
that:
- '_.results.0.item == "section1/value1"'
- '_.results.1.item == "section1/value2"'
- name: "Looping over section value_section"
debug: msg="{{item}}"
with_ini: value[1-2] section=value_section file=lookup.ini re=true
register: _
- assert:
that:
- '_.results.0.item == "1"'
- '_.results.1.item == "2"'
- debug: msg="{{item}}"
with_ini: value[1-2] section=section1 file=lookup.ini re=true
register: _
- debug: var=_
- assert:
that:
- '_.results.0.item == "section1/value1"'
- '_.results.1.item == "section1/value2"'

View file

@ -24,7 +24,7 @@ class Git(object):
:rtype: list[str]
"""
cmd = ['diff'] + args
return self.run_git_split(cmd, '\n')
return self.run_git_split(cmd, '\n', str_errors='replace')
def get_diff_names(self, args):
"""
@ -76,22 +76,24 @@ class Git(object):
except SubprocessError:
return False
def run_git_split(self, cmd, separator=None):
def run_git_split(self, cmd, separator=None, str_errors='strict'):
"""
:type cmd: list[str]
:param separator: str | None
:type str_errors: 'strict' | 'replace'
:rtype: list[str]
"""
output = self.run_git(cmd).strip(separator)
output = self.run_git(cmd, str_errors=str_errors).strip(separator)
if not output:
return []
return output.split(separator)
def run_git(self, cmd):
def run_git(self, cmd, str_errors='strict'):
"""
:type cmd: list[str]
:type str_errors: 'strict' | 'replace'
:rtype: str
"""
return run_command(self.args, [self.git] + cmd, capture=True, always=True)[0]
return run_command(self.args, [self.git] + cmd, capture=True, always=True, str_errors=str_errors)[0]

View file

@ -81,7 +81,7 @@ def find_executable(executable, cwd=None, path=None, required=True):
def run_command(args, cmd, capture=False, env=None, data=None, cwd=None, always=False, stdin=None, stdout=None,
cmd_verbosity=1):
cmd_verbosity=1, str_errors='strict'):
"""
:type args: CommonConfig
:type cmd: collections.Iterable[str]
@ -93,15 +93,16 @@ def run_command(args, cmd, capture=False, env=None, data=None, cwd=None, always=
:type stdin: file | None
:type stdout: file | None
:type cmd_verbosity: int
:type str_errors: 'strict' | 'replace'
:rtype: str | None, str | None
"""
explain = args.explain and not always
return raw_command(cmd, capture=capture, env=env, data=data, cwd=cwd, explain=explain, stdin=stdin, stdout=stdout,
cmd_verbosity=cmd_verbosity)
cmd_verbosity=cmd_verbosity, str_errors=str_errors)
def raw_command(cmd, capture=False, env=None, data=None, cwd=None, explain=False, stdin=None, stdout=None,
cmd_verbosity=1):
cmd_verbosity=1, str_errors='strict'):
"""
:type cmd: collections.Iterable[str]
:type capture: bool
@ -112,6 +113,7 @@ def raw_command(cmd, capture=False, env=None, data=None, cwd=None, explain=False
:type stdin: file | None
:type stdout: file | None
:type cmd_verbosity: int
:type str_errors: 'strict' | 'replace'
:rtype: str | None, str | None
"""
if not cwd:
@ -170,8 +172,8 @@ def raw_command(cmd, capture=False, env=None, data=None, cwd=None, explain=False
encoding = 'utf-8'
data_bytes = data.encode(encoding) if data else None
stdout_bytes, stderr_bytes = process.communicate(data_bytes)
stdout_text = stdout_bytes.decode(encoding) if stdout_bytes else u''
stderr_text = stderr_bytes.decode(encoding) if stderr_bytes else u''
stdout_text = stdout_bytes.decode(encoding, str_errors) if stdout_bytes else u''
stderr_text = stderr_bytes.decode(encoding, str_errors) if stderr_bytes else u''
else:
process.wait()
stdout_text, stderr_text = None, None