Skip to content

Commit 997b453

Browse files
authored
Fix #100: strip lines before parsing with Requirement() (#101)
* Add failing test for issue #100 packaging 26.3 rejects requirement strings with trailing whitespace. process_line() passes the raw line including its newline to Requirement(), the resulting InvalidRequirement is silently swallowed, and source packages are no longer commented out in the generated constraints. * Fix #100: strip lines before parsing with Requirement() packaging 26.3 made requirement parsing strict: a trailing newline now raises InvalidRequirement. process_line() parsed the raw line straight from file iteration, the error was silently swallowed, and source packages stayed pinned in the generated constraints. Parse the stripped line instead, and log unparseable lines at debug level so parser-behavior changes surface under -v instead of silently producing downstream resolver errors. * Update CHANGES.md for #100
1 parent 6c5840c commit 997b453

3 files changed

Lines changed: 66 additions & 2 deletions

File tree

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
<!-- Add future changes here -->
66

7+
- Fix #100: With packaging >= 26.3, packages configured as sources were no longer
8+
commented out in the generated requirements/constraints, because `Requirement()`
9+
now rejects the trailing newline and the error was silently swallowed. Lines are
10+
now stripped before parsing and unparseable lines are logged at debug level. [jensens]
11+
712

813
## 5.4.0 (2026-06-01)
914

src/mxdev/processing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ def process_line(
122122
cache_dir=cache_dir,
123123
)
124124
try:
125-
parsed = Requirement(line)
125+
parsed = Requirement(line.strip())
126126
except Exception:
127-
pass
127+
logger.debug(f"Line is not a requirement specifier: {line.strip()!r}")
128128
else:
129129
parsed_name_lower = parsed.name.lower()
130130
if parsed_name_lower in [k.lower() for k in package_keys]:

tests/test_processing.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,65 @@ def test_process_line_blank():
109109
assert constraints == []
110110

111111

112+
@pytest.mark.parametrize("eol", ["\n", "\r\n"])
113+
def test_process_line_source_package_with_trailing_newline(eol):
114+
"""Regression test for #100: lines read from files carry a trailing newline.
115+
116+
packaging >= 26.3 rejects requirement strings with trailing whitespace, so
117+
process_line must strip before parsing.
118+
"""
119+
from mxdev.processing import process_line
120+
121+
requirements, constraints = process_line(
122+
f"my.package==1.0.0{eol}",
123+
package_keys=["my.package"],
124+
override_keys=[],
125+
ignore_keys=[],
126+
variety="r",
127+
)
128+
assert requirements == ["# my.package==1.0.0 -> mxdev disabled (source)\n"]
129+
assert constraints == []
130+
131+
132+
@pytest.mark.parametrize("eol", ["\n", "\r\n"])
133+
def test_process_line_source_constraint_with_trailing_newline(eol):
134+
"""Regression test for #100: constraints variety must also strip before parsing."""
135+
from mxdev.processing import process_line
136+
137+
requirements, constraints = process_line(
138+
f"my.package==1.0.0{eol}",
139+
package_keys=["my.package"],
140+
override_keys=[],
141+
ignore_keys=[],
142+
variety="c",
143+
)
144+
assert requirements == []
145+
assert constraints == ["# my.package==1.0.0 -> mxdev disabled (source)\n"]
146+
147+
148+
def test_resolve_dependencies_source_package_commented(tmp_path):
149+
"""Regression test for #100 through the real file-reading path."""
150+
from mxdev.processing import resolve_dependencies
151+
152+
req_file = tmp_path / "requirements.txt"
153+
req_file.write_text("my.package==1.0.0\nrequests>=2.28.0\n")
154+
155+
old_cwd = os.getcwd()
156+
os.chdir(tmp_path)
157+
try:
158+
requirements, constraints = resolve_dependencies(
159+
"requirements.txt",
160+
package_keys=["my.package"],
161+
override_keys=[],
162+
ignore_keys=[],
163+
variety="r",
164+
)
165+
assert "# my.package==1.0.0 -> mxdev disabled (source)\n" in requirements
166+
assert any("requests>=2.28.0" in line for line in requirements)
167+
finally:
168+
os.chdir(old_cwd)
169+
170+
112171
def test_resolve_dependencies_missing_file(tmp_path):
113172
"""Test resolve_dependencies with a missing requirements file."""
114173
from mxdev.processing import resolve_dependencies

0 commit comments

Comments
 (0)