Skip to content

Commit

Permalink
Bugfix: downloaded files may not match all environments
Browse files Browse the repository at this point in the history
When Morgan mirrors packages, it chooses the most recent version
that satisfies the requirement strings, and the environments.
However, when matching environments, it would select files (and
therefore version) that matches at least one of the Python versions
of the environments, which could lead to Morgan choosing versions
that do not support all of the Python versions.

This commit fixes this bug by requiring the selected versions match
ALL of the Python versions defined in the environments. This could
potentially lead to Morgan not finding matches at all, but this
would be fixed in a subsequent release by treating each environment
independently of the others.
  • Loading branch information
ido50 committed Aug 30, 2023
1 parent 4e612eb commit 7cb0edd
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 22 deletions.
2 changes: 1 addition & 1 deletion morgan/__about__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.13.1"
__version__ = "0.13.2"
53 changes: 32 additions & 21 deletions morgan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
import re
import tarfile
import traceback
from typing import Iterable, Tuple, Dict
import urllib.parse
import urllib.request
import zipfile
from typing import Dict, Iterable, Tuple

import packaging.requirements
import packaging.version
import packaging.utils
import packaging.tags
import packaging.specifiers
import packaging.tags
import packaging.utils
import packaging.version

from morgan import server, configurator, metadata
from morgan import configurator, metadata, server
from morgan.__about__ import __version__

PYPI_ADDRESS = "https://pypi.org/simple/"
Expand Down Expand Up @@ -156,6 +156,9 @@ def _mirror(
# for any of our environments and don't return an error
return None

if len(files) == 0:
raise Exception(f"No files match requirement {requirement}")

# download all files
depdict = {}
for file in files:
Expand Down Expand Up @@ -221,35 +224,39 @@ def _filter_files(
files))

if len(files) == 0:
print("Skipping {}, no version matches requirement".format(
requirement.name))
print(f"Skipping {requirement}, no version matches requirement")
return None

latest_version = files[0]["version"]
# Now we only have files that satisfy the requirement, and we need to
# filter out files that do not match our environments.
files = list(filter(
lambda file: file["version"] == latest_version, files))
lambda file: self._matches_environments(file), files))

if len(files) == 0:
print(f"Skipping {requirement}, no file matches environments")

# now we only have files from the latest version that satisfies the
# requirement. we need to filter out files that do not match any of
# our environments
# Only keep files from the latest version that satisifies all
# specifiers and environments
latest_version = files[0]["version"]
files = list(filter(
lambda file: self._should_download(file), files))
lambda file: file["version"] == latest_version, files))

return files

def _should_download(self, fileinfo: dict) -> bool:
def _matches_environments(self, fileinfo: dict) -> bool:
if fileinfo.get("requires-python", None):
# The Python versions in all of our environments must be supported
# by this file in order to match
spec_set = packaging.specifiers.SpecifierSet(
fileinfo["requires-python"])
supported = False
for supported_python in self._supported_pyversions:
if spec_set.contains(supported_python):
supported = True
break
if not supported:
return False
if not spec_set.contains(supported_python):
# file does not support the Python version of one of our
# environments, reject it
return False

if fileinfo.get("tags", None):
# At least one of the tags must match ALL of our environments
for tag in fileinfo["tags"]:
(intrp_name, intrp_ver) = parse_interpreter(tag.interpreter)
if intrp_name not in ("py", "cp"):
Expand All @@ -264,7 +271,10 @@ def _should_download(self, fileinfo: dict) -> bool:
else:
for platformre in self._supported_platforms:
if platformre.fullmatch(tag.platform):
# tag matched, accept this file
return True

# none of the tags matched, reject this file
return False

return True
Expand Down Expand Up @@ -447,7 +457,7 @@ def main():
parser.add_argument(
"command",
choices=["generate_env", "generate_reqs", "mirror", "serve",
"copy_server", "version"],
"copy_server", "version"],
help="Command to execute")

args = parser.parse_args()
Expand All @@ -465,5 +475,6 @@ def main():
elif args.command == "version":
print("Morgan v{}".format(__version__))


if __name__ == "__main__":
main()

0 comments on commit 7cb0edd

Please sign in to comment.