Skip to content

Commit 6106da1

Browse files
authored
Merge pull request package-url#10 from JonoYang/normalize-qualifiers-function
Break out qualifiers normalization subroutine as its own function
2 parents d78908a + 2a4ceec commit 6106da1

File tree

2 files changed

+81
-28
lines changed

2 files changed

+81
-28
lines changed

src/packageurl.py

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -65,41 +65,29 @@ def quote(s):
6565
return quoted.replace('%3A', ':')
6666

6767

68-
def normalize(type, namespace, name, version, qualifiers, subpath, encode=True): # NOQA
68+
def get_quoter(encode=True):
6969
"""
70-
Return normalized purl components.
70+
Return quoting callable given an `encode` tri-boolean (True, False or None)
7171
"""
7272
if encode is True:
73-
quoting = quote
73+
return quote
7474
elif encode is False:
75-
quoting = percent_unquote
75+
return percent_unquote
7676
elif encode is None:
77-
quoting = lambda x: x
77+
return lambda x: x
7878

79-
if type:
80-
type = type.strip().lower() # NOQA
81-
82-
if namespace:
83-
namespace = namespace.strip().strip('/')
84-
if type and type in ('bitbucket', 'github', 'pypi'):
85-
namespace = namespace.lower()
86-
segments = namespace.split('/')
87-
segments = [seg for seg in segments if seg and seg.strip()]
88-
segments = map(quoting, segments)
89-
namespace = '/'.join(segments)
9079

91-
if name:
92-
name = name.strip().strip('/')
93-
if type in ('bitbucket', 'github', 'pypi',):
94-
name = name.lower()
95-
if type in ('pypi',):
96-
name = name.replace('_', '-')
97-
name = quoting(name)
80+
def normalize_qualifiers(qualifiers, encode=True):
81+
"""
82+
Return normalized qualifiers.
9883
99-
name = name or None
84+
If `qualifiers` is a dictionary of qualifiers and values and `encode` is true,
85+
the dictionary is then converted to a string of qualifiers, formatted to the purl specifications.
10086
101-
if version:
102-
version = quoting(version.strip())
87+
If `qualifiers` is a string of qualfiers, formatted to the purl specifications, and `encode`
88+
is false, the string is then converted to a dictionary of qualifiers and their values.
89+
"""
90+
quoting = get_quoter(encode)
10391

10492
if qualifiers:
10593
if isinstance(qualifiers, basestring):
@@ -130,6 +118,42 @@ def normalize(type, namespace, name, version, qualifiers, subpath, encode=True):
130118
qualifiers = ['{}={}'.format(k, v) for k, v in qualifiers]
131119
qualifiers = '&'.join(qualifiers)
132120

121+
return qualifiers or None
122+
123+
124+
def normalize(type, namespace, name, version, qualifiers, subpath, encode=True): # NOQA
125+
"""
126+
Return normalized purl components.
127+
"""
128+
quoting = get_quoter(encode)
129+
130+
if type:
131+
type = type.strip().lower() # NOQA
132+
133+
if namespace:
134+
namespace = namespace.strip().strip('/')
135+
if type in ('bitbucket', 'github', 'pypi'):
136+
namespace = namespace.lower()
137+
segments = namespace.split('/')
138+
segments = [seg for seg in segments if seg and seg.strip()]
139+
segments = map(quoting, segments)
140+
namespace = '/'.join(segments)
141+
142+
if name:
143+
name = name.strip().strip('/')
144+
if type in ('bitbucket', 'github', 'pypi',):
145+
name = name.lower()
146+
if type in ('pypi',):
147+
name = name.replace('_', '-')
148+
name = quoting(name)
149+
150+
name = name or None
151+
152+
if version:
153+
version = quoting(version.strip())
154+
155+
qualifiers = normalize_qualifiers(qualifiers, encode)
156+
133157
if subpath:
134158
segments = subpath.split('/')
135159
segments = [quoting(s) for s in segments if s and s.strip()
@@ -167,8 +191,8 @@ def __new__(self, type=None, namespace=None, name=None, # NOQA
167191
raise ValueError('Invalid purl: {} argument must be a string: {}.'
168192
.format(key, repr(value)))
169193

170-
if qualifiers and not isinstance(qualifiers, (dict, OrderedDict,)):
171-
raise ValueError('Invalid purl: {} argument must be a dict: {}.'
194+
if qualifiers and not isinstance(qualifiers, (basestring, dict, OrderedDict,)):
195+
raise ValueError('Invalid purl: {} argument must be a dict or a string: {}.'
172196
.format('qualifiers', repr(qualifiers)))
173197

174198
type, namespace, name, version, qualifiers, subpath = normalize(# NOQA

test_purl.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import re
3030
import unittest
3131

32+
from packageurl import normalize_qualifiers
3233
from packageurl import PackageURL
3334

3435
# Python 2 and 3 support
@@ -141,3 +142,31 @@ def build_tests(clazz=PurlTest, test_file='test-suite-data.json'):
141142

142143

143144
build_tests()
145+
146+
147+
class NormalizePurlQualifiersTest(unittest.TestCase):
148+
canonical_purl = 'pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?classifier=sources&repository_url=repo.spring.io/release'
149+
type = 'maven'
150+
namespace = 'org.apache.xmlgraphics'
151+
name = 'batik-anim'
152+
version = '1.9.1'
153+
qualifiers_as_dict = {
154+
'classifier': 'sources',
155+
'repository_url': 'repo.spring.io/release'
156+
}
157+
qualifiers_as_string = 'classifier=sources&repository_url=repo.spring.io/release'
158+
subpath = None
159+
160+
def test_normalize_qualifiers_as_string(self):
161+
assert self.qualifiers_as_string == normalize_qualifiers(self.qualifiers_as_dict, encode=True)
162+
163+
def test_normalize_qualifiers_as_dict(self):
164+
assert self.qualifiers_as_dict == normalize_qualifiers(self.qualifiers_as_string, encode=False)
165+
166+
def test_create_PackageURL_from_qualifiers_string(self):
167+
assert self.canonical_purl == PackageURL(self.type, self.namespace, self.name, self.version,
168+
self.qualifiers_as_string, self.subpath).to_string()
169+
170+
def test_create_PackageURL_from_qualifiers_dict(self):
171+
assert self.canonical_purl == PackageURL(self.type, self.namespace, self.name, self.version,
172+
self.qualifiers_as_dict, self.subpath).to_string()

0 commit comments

Comments
 (0)