Introduce an immutable version of packaging.requirements.Requirement
#12806
Labels
resolution: deferred till PR
Further discussion will happen when a PR is made
type: refactor
Refactoring code
What's the problem this feature will solve?
Unfortunately
packaging.Requirements.Requirement
is mutable.While this is "neat", it is somewhat surprising behaviour and can easily lead to hard to understand/debug logic.1 In addition, we are introducing more and more caches across the codebase which contain requirement objects. Currently, these caches are liable to break if a cached requirement object is mutated after the fact (
get_requirement("black")
could end returning aRequirement("six")
object!).Describe the solution you'd like
To avoid issues with caches and generally pay down our technical debt, introduce an immutable version of
packaging.requirements.Requirement
.In general, there are two approaches possible:
Requirement
class (dataclass?) that's fully immutable and switch the codebase wholesale to itRequirement
classThe latter has the benefit that is that plain ol'
packaging.requirements.Requirement
objects are still compatible with code that uses the immutable requirement class internally. Although, this could be considered a downside as then mutablepackaging.requirements.Requirement
objects could continue to linger in pip's logic.Essentially, from a type-checking perspective requirements would be always immutable, while code would have to opt into using
ImmutableRequirement
to be protected at runtime (e.g. the cachingget_requirement()
utility function).I included a rough draft of the second approach below.
The amount of work would be likely similar for either approaches, so the deciding factor would be whether
packaging.requirements.Requirement
objects are created/used outside of pip's direct control or not.I prefer the first approach as it's simpler and stricter, but I'm not fully aware of how packaging's
Requirement
object is used across the codebase. Let me know what you think!Alternative Solutions
Alternatively, a smaller patch would be only enforcing immutability only at runtime. This has the benefit of that if we're fine with lying to mypy and (ab)using
__instancecheck__
, technically we could just add the immutable class and updateget_requirement()
to return it, while leaving all other references topackaging.requirements.Requirement
alone.This would be likely a major hack (all while introducing more technical debt) so I don't like this idea, but I thought to propose it just in case.
Additional context
See also #12663 where some early discussion on this problem occured.
Code of Conduct
Footnotes
I really hope the codebase doesn't depend on mutating requirements to function... ↩
The text was updated successfully, but these errors were encountered: