Skip to content

AttributeError: 'dict' object has no attribute 'get_values' when using platform_machine in resolves_to_sources #3147

@alxhslm

Description

@alxhslm

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.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions