Skip to content

Commit

Permalink
Support conda version specifiers (conda-incubator#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
analog-cbarber committed Jul 6, 2021
1 parent 936fce0 commit 8916294
Show file tree
Hide file tree
Showing 4 changed files with 752 additions and 14 deletions.
3 changes: 2 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Copyright (c) <2017-2018>, Valassis Digital
Copyright (c) <2016-2017>, MaxPoint Interactive
Copyright (c) <2015-2018>, Eric Dill
Copyright (c) <2012>, Anaconda, Inc

All rights reserved. Redistribution and use in source and binary forms, with
or without modification, are permitted provided that the following conditions
Expand All @@ -27,4 +28,4 @@ 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.
POSSIBILITY OF SUCH DAMAGE.
52 changes: 40 additions & 12 deletions conda_mirror/conda_mirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import multiprocessing
import os
import pdb
import re
import shutil
import sys
import tarfile
import tempfile
import time
import random
from pprint import pformat
from typing import Any, Callable, Dict, Union

import requests
import yaml
Expand Down Expand Up @@ -70,41 +72,67 @@ def _maybe_split_channel(channel):
return download_template, channel


def _match(all_packages, key_glob_dict):
def _match(all_packages, key_pattern_dict: Dict[str, str]):
"""
Parameters
----------
all_packages : iterable
Iterable of package metadata dicts from repodata.json
key_glob_dict : iterable of kv pairs
Iterable of (key, glob_value) dicts
key_pattern_dict : iterable of kv pairs
Iterable of (key, pattern) dicts. The pattern may either
be a glob expression or if the key is 'version' may also
be a conda version specifier.
Returns
-------
matched : dict
Iterable of package metadata dicts which match the `target_packages`
(key, glob_value) tuples
(key, pattern) tuples
"""
matched = dict()
key_glob_dict = {key.lower(): glob.lower() for key, glob in key_glob_dict.items()}
key_matcher_dict: Dict[str, Callable[[Any], bool]] = {}
for key, pattern in key_pattern_dict.items():
key = key.lower()
pattern = pattern.lower()
if key == "version" and re.search(r"[<>=^$!]", pattern):
# If matching the version and the pattern contains one of the characters
# in '<>=^$!', then use conda's version matcher, otherwise assume a glob.
matcher = _version_matcher(pattern)
else:
matcher = _glob_matcher(pattern)
key_matcher_dict[key] = matcher
for pkg_name, pkg_info in all_packages.items():
matched_all = []
# normalize the strings so that comparisons are easier
for key, pattern in key_glob_dict.items():
name = str(pkg_info.get(key, "")).lower()
if fnmatch.fnmatch(name, pattern):
matched_all.append(True)
else:
matched_all.append(False)
for key, matcher in key_matcher_dict.items():
value = str(pkg_info.get(key, "")).lower()
matched_all.append(matcher(value))
if all(matched_all):
matched.update({pkg_name: pkg_info})

return matched


def _str_or_false(x):
def _glob_matcher(pattern: str) -> Callable[[Any], bool]:
"""Returns a function that will match against given glob expression."""
def _globmatch(v, p=pattern):
return fnmatch.fnmatch(v, p)
return _globmatch


def _version_matcher(pattern: str) -> Callable[[Any], bool]:
"""Returns a function that will match against given conda version specifier."""
try:
from conda.models.version import VersionSpec
except ImportError:
from .versionspec import VersionSpec

return VersionSpec(pattern).match


def _str_or_false(x: str) -> Union[str, bool]:
"""
Returns a boolean False if x is the string "False" or similar.
Returns the original string otherwise.
Expand Down
Loading

0 comments on commit 8916294

Please sign in to comment.