Repository: conda/conda

Issue:
Add license and license_family to MatchSpec for `conda search`



Discussion:

Resolving patch:
diff --git a/conda/models/match_spec.py b/conda/models/match_spec.py
--- a/conda/models/match_spec.py
+++ b/conda/models/match_spec.py
@@ -172,6 +172,8 @@ class MatchSpec(object):
         'features',
         'url',
         'md5',
+        'license',
+        'license_family',
     )
 
     def __init__(self, optional=False, target=None, **kwargs):
@@ -769,7 +771,8 @@ def __init__(self, value):
         if value.startswith('^') and value.endswith('$'):
             self._re_match = re.compile(value).match
         elif '*' in value:
-            self._re_match = re.compile(r'^(?:%s)$' % value.replace('*', r'.*')).match
+            value = re.escape(value).replace('\\*', r'.*')
+            self._re_match = re.compile(r'^(?:%s)$' % value).match
 
     def match(self, other):
         try:
@@ -860,6 +863,21 @@ def __init__(self, value):
         super(LowerStrMatch, self).__init__(value.lower())
 
 
+class CaseInsensitiveStrMatch(LowerStrMatch):
+
+    def match(self, other):
+        try:
+            _other_val = other._raw_value
+        except AttributeError:
+            _other_val = text_type(other)
+
+        _other_val = _other_val.lower()
+        if self._re_match:
+            return self._re_match(_other_val)
+        else:
+            return self._raw_value == _other_val
+
+
 _implementors = {
     'name': LowerStrMatch,
     'track_features': FeatureMatch,
@@ -867,4 +885,6 @@ def __init__(self, value):
     'version': VersionSpec,
     'build_number': BuildNumberMatch,
     'channel': ChannelMatch,
+    'license': CaseInsensitiveStrMatch,
+    'license_family': CaseInsensitiveStrMatch,
 }
diff --git a/conda/models/records.py b/conda/models/records.py
--- a/conda/models/records.py
+++ b/conda/models/records.py
@@ -262,8 +262,9 @@ class IndexJsonRecord(BasePackageRef):
                          default_in_dump=False)  # TODO: rename to package_type
     preferred_env = StringField(required=False, nullable=True, default=None, default_in_dump=False)
 
-    license = StringField(required=False)
-    license_family = StringField(required=False)
+    license = StringField(required=False, nullable=True, default=None, default_in_dump=False)
+    license_family = StringField(required=False, nullable=True, default=None,
+                                 default_in_dump=False)
 
     timestamp = TimestampField(required=False)
 


Test patch:
diff --git a/tests/models/test_match_spec.py b/tests/models/test_match_spec.py
--- a/tests/models/test_match_spec.py
+++ b/tests/models/test_match_spec.py
@@ -426,6 +426,25 @@ def test_bracket_matches(self):
         assert not MatchSpec("numpy[build_number='>7']").match(record)
         assert MatchSpec("numpy[build_number='>=7']").match(record)
 
+    def test_license_match(self):
+        record = {
+            'name': 'numpy',
+            'version': '1.11.0',
+            'build': 'py34_7',
+            'build_number': 7,
+            'license': 'LGPLv3+',
+            'license_family': 'LGPL',
+        }
+        assert MatchSpec("*[license_family='LGPL']").match(record)
+        assert MatchSpec("*[license_family='lgpl']").match(record)
+        assert MatchSpec("*[license_family='*GP*']").match(record)
+        assert MatchSpec("*[license_family='*gp*']").match(record)
+        assert MatchSpec("*[license_family='*GPL*']").match(record)
+        assert MatchSpec("*[license_family='*gpl*']").match(record)
+
+        assert MatchSpec("*[license='*gpl*']").match(record)
+        assert MatchSpec("*[license='*v3+']").match(record)
+
 
 class TestArg2Spec(TestCase):