mirror of
https://github.com/saltstack/salt.git
synced 2025-04-10 14:51:40 +00:00
Add an option to ini.set_option
Adds an new option named ``no_spaces`` to the ini.set_option function that allows the user to deteremine whether the separator (``=``) should be wrapped with spaces or not.
This commit is contained in:
parent
3ae69b9691
commit
7faee48ef3
3 changed files with 108 additions and 59 deletions
3
changelog/33669.added.md
Normal file
3
changelog/33669.added.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
Issue #33669: Fixes an issue with the ``ini_managed`` execution module
|
||||
where it would always wrap the separator with spaces. Adds a new parameter
|
||||
named ``no_spaces`` that will not warp the separator with spaces.
|
|
@ -37,7 +37,7 @@ COM_REGX = re.compile(r"^\s*(#|;)\s*(.*)")
|
|||
INDENTED_REGX = re.compile(r"(\s+)(.*)")
|
||||
|
||||
|
||||
def set_option(file_name, sections=None, separator="=", encoding=None):
|
||||
def set_option(file_name, sections=None, separator="=", encoding=None, no_spaces=False):
|
||||
"""
|
||||
Edit an ini file, replacing one or more sections. Returns a dictionary
|
||||
containing the changes made.
|
||||
|
@ -66,6 +66,14 @@ def set_option(file_name, sections=None, separator="=", encoding=None):
|
|||
|
||||
.. versionadded:: 3006.6
|
||||
|
||||
no_spaces (bool):
|
||||
A bool value that specifies if the separator will be wrapped with
|
||||
spaces. This parameter was added to have the ability to not wrap the
|
||||
separator with spaces. Default is ``False``, which maintains
|
||||
backwards compatibility.
|
||||
|
||||
.. versionadded:: 3006.10
|
||||
|
||||
Returns:
|
||||
dict: A dictionary representing the changes made to the ini file
|
||||
|
||||
|
@ -88,7 +96,9 @@ def set_option(file_name, sections=None, separator="=", encoding=None):
|
|||
"""
|
||||
|
||||
sections = sections or {}
|
||||
inifile = _Ini.get_ini_file(file_name, separator=separator, encoding=encoding)
|
||||
inifile = _Ini.get_ini_file(
|
||||
file_name, separator=separator, encoding=encoding, no_spaces=no_spaces
|
||||
)
|
||||
changes = inifile.update(sections)
|
||||
inifile.flush()
|
||||
return changes
|
||||
|
@ -388,20 +398,19 @@ def get_ini(file_name, separator="=", encoding=None):
|
|||
|
||||
|
||||
class _Section(OrderedDict):
|
||||
def __init__(self, name, inicontents="", separator="=", commenter="#", no_spaces=False):
|
||||
def __init__(
|
||||
self, name, inicontents="", separator="=", commenter="#", no_spaces=False
|
||||
):
|
||||
super().__init__(self)
|
||||
self.name = name
|
||||
self.inicontents = inicontents
|
||||
self.sep = separator
|
||||
self.com = commenter
|
||||
if not no_spaces =
|
||||
self.sep = ' ' + self.sep + ' '
|
||||
self.no_spaces = no_spaces
|
||||
|
||||
opt_regx_prefix = r"(\s*)(.+?)\s*"
|
||||
opt_regx_suffix = r"\s*(.*)\s*"
|
||||
self.opt_regx_str = r"{}(\{}){}".format(
|
||||
opt_regx_prefix, self.sep, opt_regx_suffix
|
||||
)
|
||||
self.opt_regx_str = rf"{opt_regx_prefix}(\{self.sep}){opt_regx_suffix}"
|
||||
self.opt_regx = re.compile(self.opt_regx_str)
|
||||
|
||||
def refresh(self, inicontents=None):
|
||||
|
@ -477,7 +486,11 @@ class _Section(OrderedDict):
|
|||
# Ensure the value is either a _Section or a string
|
||||
if isinstance(value, (dict, OrderedDict)):
|
||||
sect = _Section(
|
||||
name=key, inicontents="", separator=self.sep, commenter=self.com
|
||||
name=key,
|
||||
inicontents="",
|
||||
separator=self.sep,
|
||||
commenter=self.com,
|
||||
no_spaces=self.no_spaces,
|
||||
)
|
||||
sect.update(value)
|
||||
value = sect
|
||||
|
@ -509,7 +522,7 @@ class _Section(OrderedDict):
|
|||
return changes
|
||||
|
||||
def gen_ini(self):
|
||||
yield "{0}[{1}]{0}".format(os.linesep, self.name)
|
||||
yield f"{os.linesep}[{self.name}]{os.linesep}"
|
||||
sections_dict = OrderedDict()
|
||||
for name, value in self.items():
|
||||
# Handle Comment Lines
|
||||
|
@ -520,12 +533,19 @@ class _Section(OrderedDict):
|
|||
sections_dict.update({name: value})
|
||||
# Key / Value pairs
|
||||
else:
|
||||
yield "{}{}{}{}".format(
|
||||
name,
|
||||
self.sep,
|
||||
value,
|
||||
os.linesep,
|
||||
)
|
||||
# multiple spaces will be a single space
|
||||
if all(c == " " for c in self.sep):
|
||||
self.sep = " "
|
||||
# Default is to add spaces
|
||||
if self.no_spaces:
|
||||
if self.sep != " ":
|
||||
# We only strip whitespace if the delimiter is not a space
|
||||
self.sep = self.sep.strip()
|
||||
else:
|
||||
if self.sep != " ":
|
||||
# We only add spaces if the delimiter itself is not a space
|
||||
self.sep = f" {self.sep.strip()} "
|
||||
yield f"{name}{self.sep}{value}{os.linesep}"
|
||||
for name, value in sections_dict.items():
|
||||
yield from value.gen_ini()
|
||||
|
||||
|
@ -558,15 +578,26 @@ class _Section(OrderedDict):
|
|||
|
||||
class _Ini(_Section):
|
||||
def __init__(
|
||||
self, name, inicontents="", separator="=", commenter="#", encoding=None
|
||||
self,
|
||||
name,
|
||||
inicontents="",
|
||||
separator="=",
|
||||
commenter="#",
|
||||
encoding=None,
|
||||
no_spaces=False,
|
||||
):
|
||||
super().__init__(
|
||||
self, inicontents=inicontents, separator=separator, commenter=commenter
|
||||
self,
|
||||
inicontents=inicontents,
|
||||
separator=separator,
|
||||
commenter=commenter,
|
||||
no_spaces=no_spaces,
|
||||
)
|
||||
self.name = name
|
||||
if encoding is None:
|
||||
encoding = __salt_system_encoding__
|
||||
self.encoding = encoding
|
||||
self.no_spaces = no_spaces
|
||||
|
||||
def refresh(self, inicontents=None):
|
||||
if inicontents is None:
|
||||
|
@ -613,7 +644,7 @@ class _Ini(_Section):
|
|||
self.name, "w", encoding=self.encoding
|
||||
) as outfile:
|
||||
ini_gen = self.gen_ini()
|
||||
next(ini_gen)
|
||||
next(ini_gen) # Next to skip the file name
|
||||
ini_gen_list = list(ini_gen)
|
||||
# Avoid writing an initial line separator.
|
||||
if ini_gen_list:
|
||||
|
@ -625,8 +656,10 @@ class _Ini(_Section):
|
|||
)
|
||||
|
||||
@staticmethod
|
||||
def get_ini_file(file_name, separator="=", encoding=None):
|
||||
inifile = _Ini(file_name, separator=separator, encoding=encoding)
|
||||
def get_ini_file(file_name, separator="=", encoding=None, no_spaces=False):
|
||||
inifile = _Ini(
|
||||
file_name, separator=separator, encoding=encoding, no_spaces=no_spaces
|
||||
)
|
||||
inifile.refresh()
|
||||
return inifile
|
||||
|
||||
|
|
|
@ -94,24 +94,22 @@ def test_get_option(encoding, linesep, ini_file, ini_content):
|
|||
)
|
||||
ini_file.write_bytes(content)
|
||||
|
||||
assert (
|
||||
ini.get_option(str(ini_file), "main", "test1", encoding=encoding) == "value 1"
|
||||
)
|
||||
assert (
|
||||
ini.get_option(str(ini_file), "main", "test2", encoding=encoding) == "value 2"
|
||||
)
|
||||
assert (
|
||||
ini.get_option(str(ini_file), "SectionB", "test1", encoding=encoding)
|
||||
== "value 1B"
|
||||
)
|
||||
assert (
|
||||
ini.get_option(str(ini_file), "SectionB", "test3", encoding=encoding)
|
||||
== "value 3B"
|
||||
)
|
||||
assert (
|
||||
ini.get_option(str(ini_file), "SectionC", "empty_option", encoding=encoding)
|
||||
== ""
|
||||
option = ini.get_option(str(ini_file), "main", "test1", encoding=encoding)
|
||||
assert option == "value 1"
|
||||
|
||||
option = ini.get_option(str(ini_file), "main", "test2", encoding=encoding)
|
||||
assert option == "value 2"
|
||||
|
||||
option = ini.get_option(str(ini_file), "SectionB", "test1", encoding=encoding)
|
||||
assert option == "value 1B"
|
||||
|
||||
option = ini.get_option(str(ini_file), "SectionB", "test3", encoding=encoding)
|
||||
assert option == "value 3B"
|
||||
|
||||
option = ini.get_option(
|
||||
str(ini_file), "SectionC", "empty_option", encoding=encoding
|
||||
)
|
||||
assert option == ""
|
||||
|
||||
|
||||
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
|
||||
|
@ -249,11 +247,12 @@ def test_set_option(encoding, linesep, ini_file, ini_content):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("no_spaces", [True, False])
|
||||
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
|
||||
@pytest.mark.parametrize(
|
||||
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
|
||||
)
|
||||
def test_empty_value(encoding, linesep, ini_file, ini_content):
|
||||
def test_empty_value(encoding, linesep, no_spaces, ini_file, ini_content):
|
||||
"""
|
||||
Test empty value preserved after edit
|
||||
"""
|
||||
|
@ -263,19 +262,23 @@ def test_empty_value(encoding, linesep, ini_file, ini_content):
|
|||
ini_file.write_bytes(content)
|
||||
|
||||
ini.set_option(
|
||||
str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding
|
||||
str(ini_file),
|
||||
{"SectionB": {"test3": "new value 3B"}},
|
||||
encoding=encoding,
|
||||
no_spaces=no_spaces,
|
||||
)
|
||||
with salt.utils.files.fopen(str(ini_file), "r") as fp_:
|
||||
file_content = salt.utils.stringutils.to_unicode(fp_.read(), encoding=encoding)
|
||||
expected = "{0}{1}{0}".format(os.linesep, "empty_option = ")
|
||||
expected = f"{os.linesep}empty_option{'=' if no_spaces else ' = '}{os.linesep}"
|
||||
assert expected in file_content, "empty_option was not preserved"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("no_spaces", [True, False])
|
||||
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
|
||||
@pytest.mark.parametrize(
|
||||
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
|
||||
)
|
||||
def test_empty_lines(encoding, linesep, ini_file, ini_content):
|
||||
def test_empty_lines(encoding, linesep, no_spaces, ini_file, ini_content):
|
||||
"""
|
||||
Test empty lines preserved after edit
|
||||
"""
|
||||
|
@ -289,42 +292,48 @@ def test_empty_lines(encoding, linesep, ini_file, ini_content):
|
|||
"# Comment on the first line",
|
||||
"",
|
||||
"# First main option",
|
||||
"option1 = main1",
|
||||
f"option1{'=' if no_spaces else ' = '}main1",
|
||||
"",
|
||||
"# Second main option",
|
||||
"option2 = main2",
|
||||
f"option2{'=' if no_spaces else ' = '}main2",
|
||||
"",
|
||||
"[main]",
|
||||
"# Another comment",
|
||||
"test1 = value 1",
|
||||
f"test1{'=' if no_spaces else ' = '}value 1",
|
||||
"",
|
||||
"test2 = value 2",
|
||||
f"test2{'=' if no_spaces else ' = '}value 2",
|
||||
"",
|
||||
"[SectionB]",
|
||||
"test1 = value 1B",
|
||||
f"test1{'=' if no_spaces else ' = '}value 1B",
|
||||
"",
|
||||
"# Blank line should be above",
|
||||
"test3 = new value 3B",
|
||||
f"test3{'=' if no_spaces else ' = '}new value 3B",
|
||||
"",
|
||||
"[SectionC]",
|
||||
"# The following option is empty",
|
||||
"empty_option = ",
|
||||
f"empty_option{'=' if no_spaces else ' = '}",
|
||||
"",
|
||||
]
|
||||
)
|
||||
ini.set_option(
|
||||
str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding
|
||||
str(ini_file),
|
||||
{"SectionB": {"test3": "new value 3B"}},
|
||||
encoding=encoding,
|
||||
no_spaces=no_spaces,
|
||||
)
|
||||
with salt.utils.files.fopen(str(ini_file), "r") as fp_:
|
||||
file_content = fp_.read()
|
||||
assert expected == file_content
|
||||
|
||||
|
||||
@pytest.mark.parametrize("no_spaces", [True, False])
|
||||
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
|
||||
@pytest.mark.parametrize(
|
||||
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
|
||||
)
|
||||
def test_empty_lines_multiple_edits(encoding, linesep, ini_file, ini_content):
|
||||
def test_empty_lines_multiple_edits(
|
||||
encoding, linesep, no_spaces, ini_file, ini_content
|
||||
):
|
||||
"""
|
||||
Test empty lines preserved after multiple edits
|
||||
"""
|
||||
|
@ -337,6 +346,7 @@ def test_empty_lines_multiple_edits(encoding, linesep, ini_file, ini_content):
|
|||
str(ini_file),
|
||||
{"SectionB": {"test3": "this value will be edited two times"}},
|
||||
encoding=encoding,
|
||||
no_spaces=no_spaces,
|
||||
)
|
||||
|
||||
expected = os.linesep.join(
|
||||
|
@ -344,31 +354,34 @@ def test_empty_lines_multiple_edits(encoding, linesep, ini_file, ini_content):
|
|||
"# Comment on the first line",
|
||||
"",
|
||||
"# First main option",
|
||||
"option1 = main1",
|
||||
f"option1{'=' if no_spaces else ' = '}main1",
|
||||
"",
|
||||
"# Second main option",
|
||||
"option2 = main2",
|
||||
f"option2{'=' if no_spaces else ' = '}main2",
|
||||
"",
|
||||
"[main]",
|
||||
"# Another comment",
|
||||
"test1 = value 1",
|
||||
f"test1{'=' if no_spaces else ' = '}value 1",
|
||||
"",
|
||||
"test2 = value 2",
|
||||
f"test2{'=' if no_spaces else ' = '}value 2",
|
||||
"",
|
||||
"[SectionB]",
|
||||
"test1 = value 1B",
|
||||
f"test1{'=' if no_spaces else ' = '}value 1B",
|
||||
"",
|
||||
"# Blank line should be above",
|
||||
"test3 = new value 3B",
|
||||
f"test3{'=' if no_spaces else ' = '}new value 3B",
|
||||
"",
|
||||
"[SectionC]",
|
||||
"# The following option is empty",
|
||||
"empty_option = ",
|
||||
f"empty_option{'=' if no_spaces else ' = '}",
|
||||
"",
|
||||
]
|
||||
)
|
||||
ini.set_option(
|
||||
str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding
|
||||
str(ini_file),
|
||||
{"SectionB": {"test3": "new value 3B"}},
|
||||
encoding=encoding,
|
||||
no_spaces=no_spaces,
|
||||
)
|
||||
with salt.utils.files.fopen(str(ini_file), "r") as fp_:
|
||||
file_content = fp_.read()
|
||||
|
|
Loading…
Add table
Reference in a new issue