Bug
Using platform_machine as a marker in [python.resolves_to_sources] (via Pants) causes lockfile generation to fail with:
AttributeError: 'dict' object has no attribute 'get_values'
Steps to reproduce
In a Pants project with a named PyPI index, configure [python.resolves_to_sources] using platform_machine:
# pants.toml
[python-repos]
indexes = [
"https://pypi.org/simple/",
"pytorch_cpu=https://download.pytorch.org/whl/cpu"
]
[python.resolves_to_sources]
inference = [
"pytorch_cpu=torch; sys_platform == 'linux' and platform_machine == 'x86_64'",
"pytorch_cpu=torch; sys_platform == 'linux' and platform_machine == 'aarch64'",
]
Then run:
pants generate-lockfiles --resolve=inference
Error
File "pex/resolve/package_repository.py", line 125, in in_scope
elif isinstance(target_env, MarkerEnv) and not target_env.evaluate(self.marker):
File "pex/resolve/target_system.py", line 748, in evaluate
return eval_marker(self)
File "pex/resolve/target_system.py", line 264, in __call__
return self.lhs(marker_env) and cast("EvalMarker", self.rhs)(marker_env)
File "pex/resolve/target_system.py", line 513, in __call__
return self._get_values(marker_env).apply(self._func)
File "pex/resolve/target_system.py", line 327, in <lambda>
return lambda marker_env: marker_env.extra_markers.get_values(marker_name)
AttributeError: 'dict' object has no attribute 'get_values'
Root cause
MarkerEnv.as_dict() uses attr.asdict(self), which recursively converts the nested ExtraMarkers instance to a plain dict. But MarkerEnv.from_dict(data) does return cls(**data), passing that raw dict back as the extra_markers attribute instead of reconstructing the ExtraMarkers object.
When platform_machine is later evaluated, it falls through to:
# target_system.py ~line 327
return lambda marker_env: marker_env.extra_markers.get_values(marker_name)
…but extra_markers is a dict, not an ExtraMarkers, so .get_values() fails.
Note: UniversalTarget.from_dict in the same file already handles this correctly by reconstructing nested objects before passing to cls(...).
Proposed fix
In MarkerEnv.from_dict, reconstruct ExtraMarkers before passing to cls:
@classmethod
def from_dict(cls, data):
extra_markers = ExtraMarkers.from_dict(data.pop("extra_markers", {}))
return cls(extra_markers=extra_markers, **data)
Environment
- pex version: v2.69.2 (and confirmed still present in v2.92.1 — no changelog entry addresses this)
- Pants version: 2.30.0a0
- Python: 3.11
- OS: macOS arm64 (generating lockfile for cross-platform use)
Workaround
Avoid platform_machine in resolves_to_sources markers — use only sys_platform. This limits the ability to route different Linux architectures to different package indexes.
Bug
Using
platform_machineas a marker in[python.resolves_to_sources](via Pants) causes lockfile generation to fail with:Steps to reproduce
In a Pants project with a named PyPI index, configure
[python.resolves_to_sources]usingplatform_machine:Then run:
Error
Root cause
MarkerEnv.as_dict()usesattr.asdict(self), which recursively converts the nestedExtraMarkersinstance to a plaindict. ButMarkerEnv.from_dict(data)doesreturn cls(**data), passing that rawdictback as theextra_markersattribute instead of reconstructing theExtraMarkersobject.When
platform_machineis later evaluated, it falls through to:…but
extra_markersis adict, not anExtraMarkers, so.get_values()fails.Note:
UniversalTarget.from_dictin the same file already handles this correctly by reconstructing nested objects before passing tocls(...).Proposed fix
In
MarkerEnv.from_dict, reconstructExtraMarkersbefore passing tocls:Environment
Workaround
Avoid
platform_machineinresolves_to_sourcesmarkers — use onlysys_platform. This limits the ability to route different Linux architectures to different package indexes.