Vendorise `packaging.versions.LegacyVersion`

Fix https://github.com/galaxyproject/galaxy/issues/15898 .
This commit is contained in:
Nicola Soranzo
2023-05-07 17:30:59 +01:00
parent 853d009b04
commit c11fb070a7
17 changed files with 361 additions and 99 deletions
+1 -1
View File
@@ -66,7 +66,7 @@ myst-parser==1.0.0 ; python_version >= "3.7" and python_version < "3.12"
numpy==1.21.6 ; python_version >= "3.7" and python_version < "3.8"
numpy==1.24.2 ; python_version >= "3.8" and python_version < "3.12"
outcome==1.2.0 ; python_version >= "3.7" and python_version < "3.12"
packaging==21.3 ; python_version >= "3.7" and python_version < "3.12"
packaging==23.1 ; python_version >= "3.7" and python_version < "3.12"
pathspec==0.11.1 ; python_version >= "3.7" and python_version < "3.12"
pillow==9.5.0 ; python_version >= "3.7" and python_version < "3.12"
pkce==1.0.3 ; python_version >= "3.7" and python_version < "3.12"
@@ -110,7 +110,7 @@ numpy==1.21.6 ; python_version >= "3.7" and python_version < "3.8"
numpy==1.24.2 ; python_version >= "3.8" and python_version < "3.12"
oauthlib==3.2.2 ; python_version >= "3.7" and python_version < "3.12"
oyaml==1.0 ; python_version >= "3.7" and python_version < "3.12"
packaging==21.3 ; python_version >= "3.7" and python_version < "3.12"
packaging==23.1 ; python_version >= "3.7" and python_version < "3.12"
paramiko==3.1.0 ; python_version >= "3.7" and python_version < "3.12"
parse==1.19.0 ; python_version >= "3.7" and python_version < "3.12"
parsley==1.3 ; python_version >= "3.7" and python_version < "3.12"
+3 -3
View File
@@ -22,8 +22,8 @@ from typing import (
TYPE_CHECKING,
)
import packaging.version
import yaml
from packaging.version import Version
from pulsar.client.staging import COMMAND_VERSION_FILENAME
from galaxy import (
@@ -1089,7 +1089,7 @@ class MinimalJobWrapper(HasResourceParameters):
@property
def outputs_directory(self):
"""Default location of ``outputs_to_working_directory``."""
return None if self.created_with_galaxy_version < packaging.version.parse("20.01") else "outputs"
return None if self.created_with_galaxy_version < Version("20.01") else "outputs"
@property
def outputs_to_working_directory(self):
@@ -1098,7 +1098,7 @@ class MinimalJobWrapper(HasResourceParameters):
@property
def created_with_galaxy_version(self):
galaxy_version = self.get_job().galaxy_version or "19.05"
return packaging.version.parse(galaxy_version)
return Version(galaxy_version)
@property
def dependency_shell_commands(self):
+7 -7
View File
@@ -14,9 +14,9 @@ from typing import (
Dict,
)
import packaging.version
import pulsar.core
import yaml
from packaging.version import Version
from pulsar.client import (
build_client_manager,
CLIENT_INPUT_PATH_TYPES,
@@ -64,9 +64,9 @@ __all__ = (
)
MINIMUM_PULSAR_VERSIONS = {
"_default_": packaging.version.parse("0.7.0.dev3"),
"remote_metadata": packaging.version.parse("0.8.0"),
"remote_container_handling": packaging.version.parse("0.9.1.dev0"), # probably 0.10 ultimately?
"_default_": Version("0.7.0.dev3"),
"remote_metadata": Version("0.8.0"),
"remote_container_handling": Version("0.9.1.dev0"), # probably 0.10 ultimately?
}
NO_REMOTE_GALAXY_FOR_METADATA_MESSAGE = "Pulsar misconfiguration - Pulsar client configured to set metadata remotely, but remote Pulsar isn't properly configured with a galaxy_home directory."
@@ -498,7 +498,7 @@ class PulsarJobRunner(AsynchronousJobRunner):
pulsar_version=pulsar_version,
)
rewrite_paths = not PulsarJobRunner.__rewrite_parameters(client)
if pulsar_version < packaging.version.parse("0.14.999") and rewrite_paths:
if pulsar_version < Version("0.14.999") and rewrite_paths:
job_wrapper.disable_commands_in_new_shell()
container = None
if remote_container is None:
@@ -810,7 +810,7 @@ class PulsarJobRunner(AsynchronousJobRunner):
@staticmethod
def pulsar_version(remote_job_config):
pulsar_version = packaging.version.parse(remote_job_config.get("pulsar_version", "0.6.0"))
pulsar_version = Version(remote_job_config.get("pulsar_version", "0.6.0"))
return pulsar_version
@staticmethod
@@ -818,7 +818,7 @@ class PulsarJobRunner(AsynchronousJobRunner):
check_features = check_features or {}
# 0.6.0 was newest Pulsar version that did not report it's version.
pulsar_version = PulsarJobRunner.pulsar_version(remote_job_config)
needed_version = packaging.version.parse("0.0.0")
needed_version = Version("0.0.0")
log.info(f"pulsar_version is {pulsar_version}")
for feature, needed in list(check_features.items()) + [("_default_", True)]:
if not needed:
+15 -14
View File
@@ -20,8 +20,9 @@ from typing import (
Union,
)
import packaging.version
from packaging.version import Version
from galaxy.tool_util.version import parse_version
from galaxy.util import (
commands,
download_to_file,
@@ -87,7 +88,7 @@ def find_conda_prefix() -> str:
class CondaContext(installable.InstallableContext):
installable_description = "Conda"
_conda_build_available: Optional[bool]
_conda_version: Optional[Union[packaging.version.Version, packaging.version.LegacyVersion]]
_conda_version: Optional[Version]
_experimental_solver_available: Optional[bool]
def __init__(
@@ -132,10 +133,10 @@ class CondaContext(installable.InstallableContext):
self._experimental_solver_available = None
@property
def conda_version(self) -> Union[packaging.version.Version, packaging.version.LegacyVersion]:
def conda_version(self) -> Version:
if self._conda_version is None:
self._guess_conda_properties()
assert isinstance(self._conda_version, (packaging.version.Version, packaging.version.LegacyVersion))
assert isinstance(self._conda_version, Version)
return self._conda_version
@property
@@ -147,12 +148,12 @@ class CondaContext(installable.InstallableContext):
def _guess_conda_properties(self) -> None:
info = self.conda_info()
self._conda_version = packaging.version.parse(info["conda_version"])
self._conda_version = Version(info["conda_version"])
self._conda_build_available = False
conda_build_version = info.get("conda_build_version")
if conda_build_version and conda_build_version != "not installed":
try:
packaging.version.parse(conda_build_version)
Version(conda_build_version)
self._conda_build_available = True
except Exception:
pass
@@ -169,9 +170,9 @@ class CondaContext(installable.InstallableContext):
@property
def _experimental_solver_args(self) -> List[str]:
if self._experimental_solver_available is None:
self._experimental_solver_available = self.conda_version >= packaging.version.parse(
"4.12.0"
) and self.is_package_installed("conda-libmamba-solver")
self._experimental_solver_available = self.conda_version >= Version("4.12.0") and self.is_package_installed(
"conda-libmamba-solver"
)
if self._experimental_solver_available:
return ["--experimental-solver", "libmamba"]
else:
@@ -282,7 +283,7 @@ class CondaContext(installable.InstallableContext):
for try_strict in [True, False]:
create_args = ["-y", "--quiet"]
if try_strict:
if self.conda_version >= packaging.version.parse("4.7.5"):
if self.conda_version >= Version("4.7.5"):
create_args.append("--strict-channel-priority")
else:
continue
@@ -313,7 +314,7 @@ class CondaContext(installable.InstallableContext):
for try_strict in [True, False]:
install_args = ["-y"]
if try_strict:
if self.conda_version >= packaging.version.parse("4.7.5"):
if self.conda_version >= Version("4.7.5"):
install_args.append("--strict-channel-priority")
else:
continue
@@ -567,7 +568,7 @@ def best_search_result(
# the latest update time.
hits = json.loads(res).get(conda_target.package, [])[::-1]
hits = sorted(hits, key=lambda hit: hit["build_number"], reverse=True)
hits = sorted(hits, key=lambda hit: packaging.version.parse(hit["version"]), reverse=True)
hits = sorted(hits, key=lambda hit: parse_version(hit["version"]), reverse=True)
except commands.CommandLineException as e:
log.error(f"Could not execute: '{e.command}'\n{e}")
hits = []
@@ -632,8 +633,8 @@ def build_isolated_environment(
# Adjust fix if they fix Conda - xref
# - https://github.com/galaxyproject/galaxy/issues/3635
# - https://github.com/conda/conda/issues/2035
offline_works = (conda_context.conda_version < packaging.version.parse("4.3")) or (
conda_context.conda_version >= packaging.version.parse("4.4")
offline_works = (conda_context.conda_version < Version("4.3")) or (
conda_context.conda_version >= Version("4.4")
)
if offline_works:
create_args.append("--offline")
+4 -3
View File
@@ -17,11 +17,12 @@ from typing import (
Union,
)
import packaging.version
import requests
from conda_package_streaming.package_streaming import stream_conda_info
from conda_package_streaming.url import stream_conda_info as stream_conda_info_from_url
from galaxy.tool_util.version import parse_version
if TYPE_CHECKING:
from galaxy.tool_util.deps.container_resolvers import ResolutionCache
@@ -189,8 +190,8 @@ def parse_tag(tag):
build_number = -1
return PARSED_TAG(
tag=tag,
version=packaging.version.parse(version),
build_string=packaging.version.parse(build_string),
version=parse_version(version),
build_string=parse_version(build_string),
build_number=build_number,
)
+6 -3
View File
@@ -1,7 +1,10 @@
"""This module contains linting functions for general aspects of the tool."""
import re
import packaging.version
from galaxy.tool_util.version import (
LegacyVersion,
parse_version,
)
ERROR_VERSION_MSG = "Tool version is missing or empty."
WARN_VERSION_MSG = "Tool version [%s] is not compliant with PEP 440."
@@ -34,10 +37,10 @@ def lint_general(tool_source, lint_ctx):
else:
tool_node = None
version = tool_source.parse_version() or ""
parsed_version = packaging.version.parse(version)
parsed_version = parse_version(version)
if not version:
lint_ctx.error(ERROR_VERSION_MSG, node=tool_node)
elif isinstance(parsed_version, packaging.version.LegacyVersion):
elif isinstance(parsed_version, LegacyVersion):
lint_ctx.warn(WARN_VERSION_MSG % version, node=tool_node)
elif version != version.strip():
lint_ctx.warn(WARN_WHITESPACE_PRESUFFIX % ("Tool version", version), node=tool_node)
+4 -4
View File
@@ -9,7 +9,7 @@ from typing import (
Optional,
)
import packaging.version
from packaging.version import Version
from galaxy.tool_util.deps import requirements
from galaxy.tool_util.parser.util import (
@@ -525,7 +525,7 @@ class XmlToolSource(ToolSource):
def parse_strict_shell(self):
command_el = self._command_el
if packaging.version.parse(self.parse_profile()) < packaging.version.parse("20.09"):
if Version(self.parse_profile()) < Version("20.09"):
default = "False"
else:
default = "True"
@@ -558,7 +558,7 @@ class XmlToolSource(ToolSource):
return rval
def parse_profile(self):
def parse_profile(self) -> str:
# Pre-16.04 or default XML defaults
# - Use standard error for error detection.
# - Don't run shells with -e
@@ -573,7 +573,7 @@ class XmlToolSource(ToolSource):
def parse_python_template_version(self):
python_template_version = self.root.get("python_template_version")
if python_template_version is not None:
python_template_version = packaging.version.Version(python_template_version)
python_template_version = Version(python_template_version)
return python_template_version
def parse_creator(self):
@@ -5,9 +5,9 @@ from typing import (
List,
)
import packaging.version
from sortedcontainers import SortedSet
from galaxy.tool_util.version import parse_version
from galaxy.util.tool_version import remove_version_from_guid
@@ -46,7 +46,7 @@ class ToolLineage:
def __init__(self, tool_id, **kwds):
self.tool_id = tool_id
self.tool_versions = SortedSet(key=packaging.version.parse)
self.tool_versions = SortedSet(key=parse_version)
@property
def tool_ids(self) -> List[str]:
+2 -5
View File
@@ -25,10 +25,7 @@ from typing import (
)
import requests
from packaging.version import (
parse as parse_version,
Version,
)
from packaging.version import Version
from requests import Response
from requests.cookies import RequestsCookieJar
from typing_extensions import (
@@ -200,7 +197,7 @@ class GalaxyInteractorApi:
@property
def target_galaxy_version(self):
if self._target_galaxy_version is None:
self._target_galaxy_version = parse_version(self._get("version").json()["version_major"])
self._target_galaxy_version = Version(self._get("version").json()["version_major"])
return self._target_galaxy_version
@property
+164
View File
@@ -0,0 +1,164 @@
# Copyright (c) Donald Stufft and individual contributors.
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# The following code derives from packaging 21.3 before the LegacyVersion class
# was removed: https://github.com/pypa/packaging/blob/21.3/packaging/version.py
import re
from typing import (
Iterator,
List,
Tuple,
Union,
)
from packaging.version import (
_BaseVersion,
InvalidVersion,
Version,
)
__all__ = ["parse_version", "LegacyVersion"]
LegacyCmpKey = Tuple[int, Tuple[str, ...]]
def parse_version(version: str) -> Union["LegacyVersion", "Version"]:
"""
Parse the given version string and return either a :class:`Version` object
or a :class:`LegacyVersion` object depending on if the given version is
a valid PEP 440 version or a legacy version.
"""
try:
return Version(version)
except InvalidVersion:
return LegacyVersion(version)
class LegacyVersion(_BaseVersion):
def __init__(self, version: str) -> None:
self._version = str(version)
self._key = _legacy_cmpkey(self._version)
def __str__(self) -> str:
return self._version
def __repr__(self) -> str:
return f"<LegacyVersion('{self}')>"
@property
def public(self) -> str:
return self._version
@property
def base_version(self) -> str:
return self._version
@property
def epoch(self) -> int:
return -1
@property
def release(self) -> None:
return None
@property
def pre(self) -> None:
return None
@property
def post(self) -> None:
return None
@property
def dev(self) -> None:
return None
@property
def local(self) -> None:
return None
@property
def is_prerelease(self) -> bool:
return False
@property
def is_postrelease(self) -> bool:
return False
@property
def is_devrelease(self) -> bool:
return False
_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE)
_legacy_version_replacement_map = {
"pre": "c",
"preview": "c",
"-": "final-",
"rc": "c",
"dev": "@",
}
def _parse_version_parts(s: str) -> Iterator[str]:
for part in _legacy_version_component_re.split(s):
part = _legacy_version_replacement_map.get(part, part)
if not part or part == ".":
continue
if part[:1] in "0123456789":
# pad for numeric comparison
yield part.zfill(8)
else:
yield "*" + part
# ensure that alpha/beta/candidate are before final
yield "*final"
def _legacy_cmpkey(version: str) -> LegacyCmpKey:
# We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
# greater than or equal to 0. This will effectively put the LegacyVersion,
# which uses the defacto standard originally implemented by setuptools,
# as before all PEP 440 versions.
epoch = -1
# This scheme is taken from pkg_resources.parse_version setuptools prior to
# it's adoption of the packaging library.
parts: List[str] = []
for part in _parse_version_parts(version.lower()):
if part.startswith("*"):
# remove "-" before a prerelease tag
if part < "*final":
while parts and parts[-1] == "*final-":
parts.pop()
# remove trailing zeros from each series of numeric parts
while parts and parts[-1] == "00000000":
parts.pop()
parts.append(part)
return epoch, tuple(parts)
+45 -45
View File
@@ -25,10 +25,10 @@ from typing import (
)
from urllib.parse import unquote_plus
import packaging.version
import webob.exc
from lxml import etree
from mako.template import Template
from packaging.version import Version
from galaxy import (
exceptions,
@@ -79,6 +79,10 @@ from galaxy.tool_util.toolbox import (
)
from galaxy.tool_util.toolbox.views.sources import StaticToolBoxViewSources
from galaxy.tool_util.verify.test_data import TestDataNotFoundError
from galaxy.tool_util.version import (
LegacyVersion,
parse_version,
)
from galaxy.tools import expressions
from galaxy.tools.actions import (
DefaultToolAction,
@@ -223,28 +227,28 @@ GALAXY_LIB_TOOLS_UNVERSIONED = [
# Tools that needed galaxy on the PATH in the past but no longer do along
# with the version at which they were fixed.
GALAXY_LIB_TOOLS_VERSIONED = {
"meme_fimo": packaging.version.parse("5.0.5"),
"Extract genomic DNA 1": packaging.version.parse("3.0.0"),
"fetchflank": packaging.version.parse("1.0.1"),
"gops_intersect_1": packaging.version.parse("1.0.0"),
"lastz_wrapper_2": packaging.version.parse("1.3"),
"PEsortedSAM2readprofile": packaging.version.parse("1.1.1"),
"sam_to_bam": packaging.version.parse("1.1.3"),
"sam_pileup": packaging.version.parse("1.1.3"),
"vcf_to_maf_customtrack1": packaging.version.parse("1.0.1"),
"secure_hash_message_digest": packaging.version.parse("0.0.2"),
"join1": packaging.version.parse("2.1.3"),
"wiggle2simple1": packaging.version.parse("1.0.1"),
"CONVERTER_wiggle_to_interval_0": packaging.version.parse("1.0.1"),
"aggregate_scores_in_intervals2": packaging.version.parse("1.1.4"),
"CONVERTER_fastq_to_fqtoc0": packaging.version.parse("1.0.1"),
"CONVERTER_tar_to_directory": packaging.version.parse("1.0.1"),
"tabular_to_dbnsfp": packaging.version.parse("1.0.1"),
"cufflinks": packaging.version.parse("2.2.1.3"),
"Convert characters1": packaging.version.parse("1.0.1"),
"substitutions1": packaging.version.parse("1.0.1"),
"winSplitter": packaging.version.parse("1.0.1"),
"Interval2Maf1": packaging.version.parse("1.0.1+galaxy0"),
"meme_fimo": parse_version("5.0.5"),
"Extract genomic DNA 1": parse_version("3.0.0"),
"fetchflank": parse_version("1.0.1"),
"gops_intersect_1": parse_version("1.0.0"),
"lastz_wrapper_2": parse_version("1.3"),
"PEsortedSAM2readprofile": parse_version("1.1.1"),
"sam_to_bam": parse_version("1.1.3"),
"sam_pileup": parse_version("1.1.3"),
"vcf_to_maf_customtrack1": parse_version("1.0.1"),
"secure_hash_message_digest": parse_version("0.0.2"),
"join1": parse_version("2.1.3"),
"wiggle2simple1": parse_version("1.0.1"),
"CONVERTER_wiggle_to_interval_0": parse_version("1.0.1"),
"aggregate_scores_in_intervals2": parse_version("1.1.4"),
"CONVERTER_fastq_to_fqtoc0": parse_version("1.0.1"),
"CONVERTER_tar_to_directory": parse_version("1.0.1"),
"tabular_to_dbnsfp": parse_version("1.0.1"),
"cufflinks": parse_version("2.2.1.3"),
"Convert characters1": parse_version("1.0.1"),
"substitutions1": parse_version("1.0.1"),
"winSplitter": parse_version("1.0.1"),
"Interval2Maf1": parse_version("1.0.1+galaxy0"),
}
REQUIRE_FULL_DIRECTORY = {
@@ -252,7 +256,7 @@ REQUIRE_FULL_DIRECTORY = {
}
IMPLICITLY_REQUIRED_TOOL_FILES: Dict[str, Dict] = {
"deseq2": {
"version": packaging.version.parse("2.11.40.6"),
"version": parse_version("2.11.40.6"),
"required": {"includes": [{"path": "*.R", "path_type": "glob"}]},
},
# minimum example:
@@ -277,21 +281,21 @@ IMPLICITLY_REQUIRED_TOOL_FILES: Dict[str, Dict] = {
class safe_update(NamedTuple):
min_version: Union[packaging.version.LegacyVersion, packaging.version.Version]
current_version: Union[packaging.version.LegacyVersion, packaging.version.Version]
min_version: Union[LegacyVersion, Version]
current_version: Union[LegacyVersion, Version]
# Tool updates that did not change parameters in a way that requires rebuilding workflows
WORKFLOW_SAFE_TOOL_VERSION_UPDATES = {
"Filter1": safe_update(packaging.version.parse("1.1.0"), packaging.version.parse("1.1.1")),
"__BUILD_LIST__": safe_update(packaging.version.parse("1.0.0"), packaging.version.parse("1.1.0")),
"__APPLY_RULES__": safe_update(packaging.version.parse("1.0.0"), packaging.version.parse("1.1.0")),
"__EXTRACT_DATASET__": safe_update(packaging.version.parse("1.0.0"), packaging.version.parse("1.0.1")),
"Grep1": safe_update(packaging.version.parse("1.0.1"), packaging.version.parse("1.0.4")),
"Show beginning1": safe_update(packaging.version.parse("1.0.0"), packaging.version.parse("1.0.2")),
"Show tail1": safe_update(packaging.version.parse("1.0.0"), packaging.version.parse("1.0.1")),
"sort1": safe_update(packaging.version.parse("1.1.0"), packaging.version.parse("1.2.0")),
"CONVERTER_interval_to_bgzip_0": safe_update(packaging.version.parse("1.0.1"), packaging.version.parse("1.0.2")),
"Filter1": safe_update(parse_version("1.1.0"), parse_version("1.1.1")),
"__BUILD_LIST__": safe_update(parse_version("1.0.0"), parse_version("1.1.0")),
"__APPLY_RULES__": safe_update(parse_version("1.0.0"), parse_version("1.1.0")),
"__EXTRACT_DATASET__": safe_update(parse_version("1.0.0"), parse_version("1.0.1")),
"Grep1": safe_update(parse_version("1.0.1"), parse_version("1.0.4")),
"Show beginning1": safe_update(parse_version("1.0.0"), parse_version("1.0.2")),
"Show tail1": safe_update(parse_version("1.0.0"), parse_version("1.0.1")),
"sort1": safe_update(parse_version("1.1.0"), parse_version("1.2.0")),
"CONVERTER_interval_to_bgzip_0": safe_update(parse_version("1.0.1"), parse_version("1.0.2")),
}
@@ -757,7 +761,7 @@ class Tool(Dictifiable):
# guid attribute since it is useful to have.
self.guid = guid
self.old_id: Optional[str] = None
self.python_template_version: Optional[packaging.version.Version] = None
self.python_template_version: Optional[Version] = None
self._lineage = None
self.dependencies: List = []
# populate toolshed repository info, if available
@@ -835,7 +839,7 @@ class Tool(Dictifiable):
@property
def version_object(self):
return packaging.version.parse(self.version)
return parse_version(self.version)
@property
def sa_session(self):
@@ -1005,12 +1009,8 @@ class Tool(Dictifiable):
if not dynamic and not self.id:
raise Exception(f"Missing tool 'id' for tool at '{tool_source}'")
profile = packaging.version.parse(str(self.profile))
if (
self.app.name == "galaxy"
and profile >= packaging.version.parse("16.04")
and packaging.version.parse(VERSION_MAJOR) < profile
):
profile = Version(str(self.profile))
if self.app.name == "galaxy" and profile >= Version("16.04") and Version(VERSION_MAJOR) < profile:
message = f"The tool [{self.id}] targets version {self.profile} of Galaxy, you should upgrade Galaxy to ensure proper functioning of this tool."
raise Exception(message)
@@ -1018,9 +1018,9 @@ class Tool(Dictifiable):
if self.python_template_version is None:
# If python_template_version not specified we assume tools with profile versions >= 19.05 are python 3 ready
if self.profile >= 19.05:
self.python_template_version = packaging.version.Version("3.5")
self.python_template_version = Version("3.5")
else:
self.python_template_version = packaging.version.Version("2.7")
self.python_template_version = Version("2.7")
# Get the (user visible) name of the tool
self.name = tool_source.parse_name()
+2 -2
View File
@@ -7,7 +7,7 @@ from typing import (
Sequence,
)
import packaging.version
from packaging.version import Version
log = logging.getLogger(__name__)
@@ -147,7 +147,7 @@ def _json_wrap_input(input, value_wrapper, profile, handle_files="skip"):
else:
json_value = _cast_if_not_none(value_wrapper, bool, empty_to_none=input.optional)
elif input_type == "select":
if packaging.version.parse(str(profile)) < packaging.version.parse("20.05"):
if Version(str(profile)) < Version("20.05"):
json_value = _cast_if_not_none(value_wrapper, str)
else:
if input.multiple:
+2 -2
View File
@@ -3,11 +3,11 @@
import traceback
from lib2to3.refactor import RefactoringTool
import packaging.version
from Cheetah.Compiler import Compiler
from Cheetah.NameMapper import NotFound
from Cheetah.Parser import ParseError
from Cheetah.Template import Template
from packaging.version import Version
from past.translation import myfixes
from . import unicodify
@@ -55,7 +55,7 @@ def fill_template(
if not context:
context = kwargs
if isinstance(python_template_version, str):
python_template_version = packaging.version.parse(python_template_version)
python_template_version = Version(python_template_version)
try:
klass = Template.compile(source=template_text, compilerClass=compiler_class)
except ParseError as e:
+2 -6
View File
@@ -17,7 +17,6 @@ from typing import (
Union,
)
import packaging.version
from cwl_utils.expression import do_eval
from typing_extensions import TypedDict
@@ -48,6 +47,7 @@ from galaxy.schema.invocation import (
)
from galaxy.tool_util.cwl.util import set_basename_and_derived_properties
from galaxy.tool_util.parser.output_objects import ToolExpressionOutput
from galaxy.tool_util.version import parse_version
from galaxy.tools import (
DatabaseOperationTool,
DefaultToolState,
@@ -1679,11 +1679,7 @@ class ToolModule(WorkflowModule):
if safe_version and self.tool.lineage:
# tool versions are sorted from old to new, so check newest version first
for lineage_version in reversed(self.tool.lineage.tool_versions):
if (
safe_version.current_version
>= packaging.version.parse(lineage_version)
>= safe_version.min_version
):
if safe_version.current_version >= parse_version(lineage_version) >= safe_version.min_version:
self.tool = trans.app.toolbox.get_tool(
tool_id, tool_version=lineage_version, exact=True, tool_uuid=tool_uuid
)
+1 -1
View File
@@ -72,7 +72,7 @@ Mercurial = "*"
mrcfile = "*"
nodeenv = "*"
numpy = "*"
packaging = "<22" # packaging 22.0 dropped LegacyVersion
packaging = "*"
paramiko = "!=2.9.0, !=2.9.1" # https://github.com/paramiko/paramiko/issues/1961
Parsley = "*"
Paste = "*"
+100
View File
@@ -0,0 +1,100 @@
import pytest
from packaging.version import Version
from galaxy.tool_util.version import (
LegacyVersion,
parse_version,
)
# This list must be in the correct sorting order
VERSIONS = [
# Implicit epoch of 0
"1.0.dev456",
"1.0a1",
"1.0a2.dev456",
"1.0a12.dev456",
"1.0a12",
"1.0b1.dev456",
"1.0b2",
"1.0b2.post345.dev456",
"1.0b2.post345",
"1.0b2-346",
"1.0c1.dev456",
"1.0c1",
"1.0rc2",
"1.0c3",
"1.0",
"1.0.post456.dev34",
"1.0.post456",
"1.1.dev1",
"1.2+123abc",
"1.2+123abc456",
"1.2+abc",
"1.2+abc123",
"1.2+abc123def",
"1.2+1234.abc",
"1.2+123456",
"1.2.r32+123456",
"1.2.rev33+123456",
# Explicit epoch of 1
"1!1.0.dev456",
"1!1.0a1",
"1!1.0a2.dev456",
"1!1.0a12.dev456",
"1!1.0a12",
"1!1.0b1.dev456",
"1!1.0b2",
"1!1.0b2.post345.dev456",
"1!1.0b2.post345",
"1!1.0b2-346",
"1!1.0c1.dev456",
"1!1.0c1",
"1!1.0rc2",
"1!1.0c3",
"1!1.0",
"1!1.0.post456.dev34",
"1!1.0.post456",
"1!1.1.dev1",
"1!1.2+123abc",
"1!1.2+123abc456",
"1!1.2+abc",
"1!1.2+abc123",
"1!1.2+abc123def",
"1!1.2+1234.abc",
"1!1.2+123456",
"1!1.2.r32+123456",
"1!1.2.rev33+123456",
]
LEGACY_VERSIONS = ["foobar", "a cat is fine too", "lolwut", "1.8.1+2+galaxy0"]
@pytest.mark.parametrize("version", VERSIONS)
def test_parse_pep440_versions(version: str) -> None:
assert isinstance(parse_version(version), Version)
@pytest.mark.parametrize("version", LEGACY_VERSIONS)
def test_legacy_version_fields(version: str) -> None:
parsed_version = parse_version(version)
assert isinstance(parsed_version, LegacyVersion)
assert parsed_version.public == version
assert parsed_version.base_version == version
assert parsed_version.epoch == -1
assert parsed_version.release is None
assert parsed_version.pre is None
assert parsed_version.post is None
assert parsed_version.dev is None
assert parsed_version.local is None
assert parsed_version.is_prerelease is False
assert parsed_version.is_postrelease is False
assert parsed_version.is_devrelease is False
@pytest.mark.parametrize(
"lower_ver,greater_ver",
[(VERSIONS[i], VERSIONS[i + 1]) for i in range(len(VERSIONS) - 1)]
+ [(lv, v) for v in VERSIONS for lv in LEGACY_VERSIONS],
)
def test_version_cmp(lower_ver: str, greater_ver: str) -> None:
assert parse_version(lower_ver) < parse_version(greater_ver)