Poetry 1.2+ incorrectly appears to validate 'platform_release' markers against PEP 440 #7418
Description
- Poetry version: 1.3.2
- Python version: 3.11.1
- OS version and name: macOS 13.1
- pyproject.toml: not publicly available pyproject.toml, but doesn't seem relevant.
- I am on the latest stable Poetry version, installed using a recommended method.
- I have searched the issues of this repo and believe that this is not a duplicate.
- I have consulted the FAQ and blog for any relevant entries or release notes.
- If an exception occurs when executing a command, I executed it again in debug mode (
-vvv
option) and have included the output below.
Issue
We have a package with a marker on a platform_constraint. I'm not fully spun up on what it's doing, but I think it's downloading different versions of a dependency depending on which embedded platform Poetry is being run on.
I believe that this is a regression between Poetry 1.1.15 and Poetry 1.2 because Poetry 1.2 stopped supporting Python 3.6 and this project was stuck on that until recently.
The constraint looks something like:
some-package = [
{version="1.0.0", optional = true, source = "source", markers = "platform_release == '1.0.0-platformname'}
{version="1.2.1", optional = true, source = "source", markers = "platform_release == '1.0.0-otherplatformname'}
]
What I'm seeing is that PEP440 constraint validation is failing on the platform_release, and clearly that's not a valid PEP440 constraint. It's also not meant to be, though - PEP508 describes that a platform_release might be something like 3.14.1-x86_64-linode39
. From my read of the code, I have a hunch that... platform_releases are considered to be 'version like' because they could be of the form 13.0 or the like, and at some point, someone started validating version like constraints against PEP 440. But I'm not familiar with this codebase so it's just a hunch.
Stack trace:
20 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/cleo/application.py:327 in run
325│
326│ try:
→ 327│ exit_code = self._run(io)
328│ except BrokenPipeError:
329│ # If we are piped to another process, it may close early and send a
19 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/console/application.py:190 in _run
188│ self._load_plugins(io)
189│
→ 190│ exit_code: int = super()._run(io)
191│ return exit_code
192│
18 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/cleo/application.py:431 in _run
429│ io.input.interactive(interactive)
430│
→ 431│ exit_code = self._run_command(command, io)
432│ self._running_command = None
433│
17 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/cleo/application.py:473 in _run_command
471│
472│ if error is not None:
→ 473│ raise error
474│
475│ return terminate_event.exit_code
16 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/cleo/application.py:457 in _run_command
455│
456│ if command_event.command_should_run():
→ 457│ exit_code = command.run(io)
458│ else:
459│ exit_code = ConsoleCommandEvent.RETURN_CODE_DISABLED
15 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/cleo/commands/base_command.py:119 in run
117│ io.input.validate()
118│
→ 119│ status_code = self.execute(io)
120│
121│ if status_code is None:
14 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/cleo/commands/command.py:62 in execute
60│
61│ try:
→ 62│ return self.handle()
63│ except KeyboardInterrupt:
64│ return 1
13 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/console/commands/version.py:87 in handle
85│ else:
86│ self.line(
→ 87│ f"{self.poetry.package.name}"
88│ f" {self.poetry.package.pretty_version}"
89│ )
12 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/console/commands/command.py:23 in poetry
21│ def poetry(self) -> Poetry:
22│ if self._poetry is None:
→ 23│ return self.get_application().poetry
24│
25│ return self._poetry
11 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/console/application.py:129 in poetry
127│ project_path = self._io.input.option("directory")
128│
→ 129│ self._poetry = Factory().create_poetry(
130│ cwd=project_path,
131│ io=self._io,
10 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/factory.py:55 in create_poetry
53│ io = NullIO()
54│
→ 55│ base_poetry = super().create_poetry(cwd=cwd, with_groups=with_groups)
56│
57│ locker = Locker(
9 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/factory.py:65 in create_poetry
63│ assert isinstance(version, str)
64│ package = self.get_package(name, version)
→ 65│ package = self.configure_package(
66│ package, local_config, poetry_file.parent, with_groups=with_groups
67│ )
8 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/factory.py:159 in configure_package
157│
158│ if "dependencies" in config:
→ 159│ cls._add_package_group_dependencies(
160│ package=package, group=MAIN_GROUP, dependencies=config["dependencies"]
161│ )
7 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/factory.py:105 in _add_package_group_dependencies
103│
104│ group.add_dependency(
→ 105│ cls.create_dependency(
106│ name,
107│ _constraint,
6 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/factory.py:347 in create_dependency
345│ )
346│
→ 347│ marker = parse_marker(markers) if markers else AnyMarker()
348│
349│ if python_versions:
5 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/version/markers.py:791 in parse_marker
789│ parsed = _parser.parse(marker)
790│
→ 791│ markers = _compact_markers(parsed.children)
792│
793│ return markers
4 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/version/markers.py:809 in _compact_markers
807│ if token.data == "marker":
808│ groups[-1] = MultiMarker.of(
→ 809│ groups[-1], _compact_markers(token.children, tree_prefix=tree_prefix)
810│ )
811│ elif token.data == f"{tree_prefix}item":
3 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/version/markers.py:821 in _compact_markers
819│ value = value[1:-1]
820│ groups[-1] = MultiMarker.of(
→ 821│ groups[-1], SingleMarker(str(name), f"{op}{value}")
822│ )
823│ elif token.data == f"{tree_prefix}BOOL_OP" and token.children[0] == "or":
2 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/version/markers.py:232 in __init__
230│ self._constraint = self._parser(glue.join(versions))
231│ else:
→ 232│ self._constraint = self._parser(constraint_string)
233│ else:
234│ # if we have a in/not in operator we split the constraint
1 ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/constraints/version/parser.py:36 in parse_constraint
34│ constraint_objects.append(parse_single_constraint(constraint))
35│ else:
→ 36│ constraint_objects.append(parse_single_constraint(and_constraints[0]))
37│
38│ if len(constraint_objects) == 1:
ParseConstraintError
Could not parse version constraint: ==1.0.0-ourversion
at ~/.local/pipx/venvs/poetry/lib/python3.11/site-packages/poetry/core/constraints/version/parser.py:167 in parse_single_constraint
163│ if op == "!=":
164│ return VersionUnion(VersionRange(max=version), VersionRange(min=version))
165│ return version
166│
→ 167│ raise ParseConstraintError(f"Could not parse version constraint: {constraint}")
168│