Skip to content
Closed
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
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,8 @@ use ``IniConfig.parse()`` to strip inline comments from values:
The ``IniConfig()`` constructor preserves inline comments in values for backward
compatibility. Use ``IniConfig.parse(..., strip_inline_comments=False)`` to
preserve them when using the parsing method.

When inline comment stripping is enabled, ``#`` and ``;`` start a comment only
at the beginning of a value or after whitespace. Characters within a value,
such as the ``#`` in ``https://example.com/#fragment`` or the ``;`` in ``a;b``,
are preserved. The same rule applies to continuation lines.
4 changes: 2 additions & 2 deletions src/iniconfig/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ def parse(
data: Optional INI content as string. If None, reads from path.
encoding: Encoding to use when reading the file (default: utf-8)
strip_inline_comments: Whether to strip inline comments from values
(default: True). When True, comments starting with # or ; are
removed from values, matching the behavior for section comments.
(default: True). When True, # or ; at the beginning of a value
or after whitespace starts a comment, including on continuation lines.
strip_section_whitespace: Whether to strip whitespace from section and key names
(default: False). When True, strips Unicode whitespace from section and key names,
addressing issue #4. When False, preserves existing behavior for backward compatibility.
Expand Down
13 changes: 9 additions & 4 deletions src/iniconfig/_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,25 @@ def _parseline(
value = value.strip()
# Strip inline comments from values if requested (issue #55)
if strip_inline_comments:
for c in COMMENTCHARS:
value = value.split(c)[0].rstrip()
value = _strip_inline_comments(value)

return key_name, value
# continuation
else:
line = line.strip()
# Strip inline comments from continuations if requested (issue #55)
if strip_inline_comments:
for c in COMMENTCHARS:
line = line.split(c)[0].rstrip()
line = _strip_inline_comments(line)
return None, line


def _strip_inline_comments(value: str) -> str:
for index, char in enumerate(value):
if char in COMMENTCHARS and (index == 0 or value[index - 1].isspace()):
return value[:index].rstrip()
return value


def iscommentline(line: str) -> bool:
c = line.lstrip()[:1]
return c in COMMENTCHARS
36 changes: 35 additions & 1 deletion testing/test_iniconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ def test_parse_strips_inline_comments() -> None:
)
assert config["section1"]["name1"] == "value1"
assert config["section1"]["name2"] == "value2"
assert config["section1"]["name3"] == "value3"
assert config["section1"]["name3"] == "value3# no space before comment"
assert config["section1"]["list"] == "a, b, c"


Expand All @@ -353,6 +353,40 @@ def test_parse_strips_inline_comments_from_continuations() -> None:
assert config["section"]["names"] == "Alice\nBob\nCharlie"


@pytest.mark.parametrize("continuation", [False, True])
@pytest.mark.parametrize(
("value", "expected"),
[
("http://example.com/#fragment", "http://example.com/#fragment"),
("a#b", "a#b"),
("d;e", "d;e"),
("a#b ; comment", "a#b"),
("d;e # comment", "d;e"),
("value # comment;more", "value"),
("value ; comment#more", "value"),
("value\t# comment", "value"),
("value\u00a0; comment", "value"),
("value#", "value#"),
("value;", "value;"),
],
)
def test_parse_inline_comment_boundaries(
value: str, expected: str, continuation: bool
) -> None:
if continuation:
data = f"[section]\nkey =\n {value}\n"
else:
data = f"[section]\nkey = {value}\n"
config = IniConfig.parse("test.ini", data=data)
assert config["section"]["key"] == expected


@pytest.mark.parametrize("value", ["# comment", "; comment"])
def test_parse_comment_only_value(value: str) -> None:
config = IniConfig.parse("test.ini", data=f"[section]\nkey = {value}\n")
assert config["section"]["key"] == ""


def test_parse_preserves_inline_comments_when_disabled() -> None:
"""Test that IniConfig.parse(strip_inline_comments=False) preserves comments."""
config = IniConfig.parse(
Expand Down