pip-audit: Does not work with prerelease-only dependencies that have no specific version
Is your feature request related to a problem? Please describe.
pip-audit does not seem to support pre-release packages, or packages that depend on a pre-release package, if the prerelease version isn’t explicitly defined.
Describe the solution you’d like
When a package depends on something a version like *, including pre-releases, pip-audit should be able to find that release. It might be just the case that only packages that ONLY have prereleases aren’t able to be found. Dependency resolution should work the same way as pip install in that it will grab the latest version, even if the only versions are pre-releases.
Describe alternatives you’ve considered
I haven’t figured out any viable alternatives yet, but I was thinking of excluding the problem dependency before running pip-audit. That seems less than ideal because that would end up ignoring vulnerabilities. The less-than-ideal workaround I’m using now is:
pipenv requirements | grep -v sqlmodel > requirements.txt
pip-audit --requirement requirements.txt
Additional context
I have a pipenv package that depends on sqlmodel. When I run pipenv requirements or pipfile2req Pipfile.lock I get these two lines (among others):
sqlalchemy2-stubs==0.0.2a31 ; python_version >= '3.6'
sqlmodel==0.0.8
Pipenv is fine with the explicit version of sqlalchemy, but I believe that’s actually coming from sqlmodel, which has this in their pyproject.toml (using poetry):
[tool.poetry.dependencies]
# ...
sqlalchemy2-stubs = {version = "*", allow-prereleases = true}
It seems like pip-audit can’t determine the version of a prerelease package (I’m assuming this is just the case for ones where there is no non-prerelease version yet):
❯ docker run -it python:3.10-alpine sh
/ # pip install --quiet pip-audit
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
/ # echo sqlmodel > requirements.txt
/ # pip-audit --requirement requirements.txt
Traceback (most recent call last):
File "/usr/local/bin/pip-audit", line 8, in <module>
sys.exit(audit())
File "/usr/local/lib/python3.10/site-packages/pip_audit/_cli.py", line 455, in audit
for (spec, vulns) in auditor.audit(source):
File "/usr/local/lib/python3.10/site-packages/pip_audit/_audit.py", line 67, in audit
for dep, vulns in self._service.query_all(specs):
File "/usr/local/lib/python3.10/site-packages/pip_audit/_service/interface.py", line 155, in query_all
for spec in specs:
File "/usr/local/lib/python3.10/site-packages/pip_audit/_dependency_source/requirement.py", line 116, in collect
for _, dep in self._collect_cached_deps(filename, reqs):
File "/usr/local/lib/python3.10/site-packages/pip_audit/_dependency_source/requirement.py", line 328, in _collect_cached_deps
for req, resolved_deps in self._resolver.resolve_all(iter(req_values)):
File "/usr/local/lib/python3.10/site-packages/pip_audit/_dependency_source/interface.py", line 88, in resolve_all
yield (req, self.resolve(req))
File "/usr/local/lib/python3.10/site-packages/pip_audit/_dependency_source/resolvelib/resolvelib.py", line 77, in resolve
result = self.resolver.resolve([req])
File "/usr/local/lib/python3.10/site-packages/resolvelib/resolvers.py", line 521, in resolve
state = resolution.resolve(requirements, max_rounds=max_rounds)
File "/usr/local/lib/python3.10/site-packages/resolvelib/resolvers.py", line 414, in resolve
raise ResolutionImpossible(self.state.backtrack_causes)
resolvelib.resolvers.ResolutionImpossible: [RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs')>, parent=<sqlmodel==0.0.8 wheel=True>), RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs')>, parent=<sqlmodel==0.0.7 wheel=True>), RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs')>, parent=<sqlmodel==0.0.6 wheel=True>), RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs')>, parent=<sqlmodel==0.0.5 wheel=True>), RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs')>, parent=<sqlmodel==0.0.4 wheel=True>), RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs')>, parent=<sqlmodel==0.0.3 wheel=True>), RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs<0.0.2,>=0.0.2-alpha.5')>, parent=<sqlmodel==0.0.2 wheel=True>), RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs<0.0.2,>=0.0.2-alpha.5')>, parent=<sqlmodel==0.0.1 wheel=True>)]
And an even more minified example:
/ # pip-audit --requirement requirements.txt
WARNING:cachecontrol.controller:Cache entry deserialization failed, entry ignored
Traceback (most recent call last):
File "/usr/local/lib/python3.10/site-packages/resolvelib/resolvers.py", line 372, in resolve
self._add_to_criteria(self.state.criteria, r, parent=None)
File "/usr/local/lib/python3.10/site-packages/resolvelib/resolvers.py", line 173, in _add_to_criteria
raise RequirementsConflicted(criterion)
resolvelib.resolvers.RequirementsConflicted: Requirements conflict: <Requirement('sqlalchemy2-stubs')>
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/bin/pip-audit", line 8, in <module>
sys.exit(audit())
File "/usr/local/lib/python3.10/site-packages/pip_audit/_cli.py", line 455, in audit
for (spec, vulns) in auditor.audit(source):
File "/usr/local/lib/python3.10/site-packages/pip_audit/_audit.py", line 67, in audit
for dep, vulns in self._service.query_all(specs):
File "/usr/local/lib/python3.10/site-packages/pip_audit/_service/interface.py", line 155, in query_all
for spec in specs:
File "/usr/local/lib/python3.10/site-packages/pip_audit/_dependency_source/requirement.py", line 116, in collect
for _, dep in self._collect_cached_deps(filename, reqs):
File "/usr/local/lib/python3.10/site-packages/pip_audit/_dependency_source/requirement.py", line 328, in _collect_cached_deps
for req, resolved_deps in self._resolver.resolve_all(iter(req_values)):
File "/usr/local/lib/python3.10/site-packages/pip_audit/_dependency_source/interface.py", line 88, in resolve_all
yield (req, self.resolve(req))
File "/usr/local/lib/python3.10/site-packages/pip_audit/_dependency_source/resolvelib/resolvelib.py", line 77, in resolve
result = self.resolver.resolve([req])
File "/usr/local/lib/python3.10/site-packages/resolvelib/resolvers.py", line 521, in resolve
state = resolution.resolve(requirements, max_rounds=max_rounds)
File "/usr/local/lib/python3.10/site-packages/resolvelib/resolvers.py", line 374, in resolve
raise ResolutionImpossible(e.criterion.information)
resolvelib.resolvers.ResolutionImpossible: [RequirementInformation(requirement=<Requirement('sqlalchemy2-stubs')>, parent=None)]
However, I’m able to install sqlalchemy2-stubs with pip without any problems, and without specifying explicitly that I want a pre-release:
/ # pip install sqlalchemy2-stubs
Collecting sqlalchemy2-stubs
Downloading sqlalchemy2_stubs-0.0.2a31-py3-none-any.whl (191 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 191.3/191.3 kB 1.9 MB/s eta 0:00:00
Collecting typing-extensions>=3.7.4
Downloading typing_extensions-4.4.0-py3-none-any.whl (26 kB)
Installing collected packages: typing-extensions, sqlalchemy2-stubs
Successfully installed sqlalchemy2-stubs-0.0.2a31 typing-extensions-4.4.0
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 16 (16 by maintainers)
#477 has the WIP fix.
OK, I think I tracked this down.
It looks like pip is using
ss.filter((x,))rather thanx in ss, and the behavior is a bit different. Thefilterfunction will return prereleases even if you don’t haveprereleases=True, but only if there are no non-prereleases. Thecontainsfunction will always respectprereleaseswith no fallback. They behave identically if you haveprereleases=Truewhich would be the case if you use pip’s--preoption.Note the documentation in
SpecifierSet.filter:There’s a test in
pipto check that prereleases work, but no test that I could find for this specific situation where there are only prereleases and--preisn’t set.Minimized further: