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
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Unreleased
==========

* preserve inline ``#`` and ``;`` in values unless preceded by whitespace when
using ``IniConfig.parse()``, including continuation lines (regarding #83)

2.3.1
=====

Expand Down
4 changes: 3 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ If you have an ini file like this:
line1
line2

use ``IniConfig.parse()`` to strip inline comments from values:
use ``IniConfig.parse()`` to strip inline comments from values. Inline ``#`` and
``;`` start comments only when preceded by whitespace, so values such as URL
fragments and semicolon-separated text remain intact:

.. code-block:: pycon

Expand Down
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 ; preceded by whitespace starts
a comment. Other occurrences remain part of the value.
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
19 changes: 11 additions & 8 deletions src/iniconfig/_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,24 @@ def _parseline(
key_name = name.strip()

# Strip value
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_comment(value)

return key_name, value
return key_name, value.strip()
# 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()
return None, line
line = _strip_inline_comment(line)
return None, line.strip()


def _strip_inline_comment(value: str) -> str:
for index, char in enumerate(value):
if index and char in COMMENTCHARS and value[index - 1].isspace():
return value[:index]
return value


def iscommentline(line: str) -> bool:
Expand Down
31 changes: 30 additions & 1 deletion testing/test_iniconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,35 @@ def test_iscommentline_true(line: str) -> None:
assert iscommentline(line)


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


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


def test_parse_strips_inline_comments() -> None:
"""Test that IniConfig.parse() strips inline comments from values by default."""
config = IniConfig.parse(
Expand All @@ -332,7 +361,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 Down