Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 52 additions & 28 deletions src/packageurl.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,41 +65,29 @@ def quote(s):
return quoted.replace('%3A', ':')


def normalize(type, namespace, name, version, qualifiers, subpath, encode=True): # NOQA
def get_quoter(encode=True):
"""
Return normalized purl components.
Return quoting callable given an `encode` tri-boolean (True, False or None)
"""
if encode is True:
quoting = quote
return quote
elif encode is False:
quoting = percent_unquote
return percent_unquote
elif encode is None:
quoting = lambda x: x
return lambda x: x

if type:
type = type.strip().lower() # NOQA

if namespace:
namespace = namespace.strip().strip('/')
if type and type in ('bitbucket', 'github', 'pypi'):
namespace = namespace.lower()
segments = namespace.split('/')
segments = [seg for seg in segments if seg and seg.strip()]
segments = map(quoting, segments)
namespace = '/'.join(segments)

if name:
name = name.strip().strip('/')
if type in ('bitbucket', 'github', 'pypi',):
name = name.lower()
if type in ('pypi',):
name = name.replace('_', '-')
name = quoting(name)
def normalize_qualifiers(qualifiers, encode=True):
"""
Return normalized qualifiers.

name = name or None
If `qualifiers` is a dictionary of qualifiers and values and `encode` is true,
the dictionary is then converted to a string of qualifiers, formatted to the purl specifications.

if version:
version = quoting(version.strip())
If `qualifiers` is a string of qualfiers, formatted to the purl specifications, and `encode`
is false, the string is then converted to a dictionary of qualifiers and their values.
"""
quoting = get_quoter(encode)

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

return qualifiers or None


def normalize(type, namespace, name, version, qualifiers, subpath, encode=True): # NOQA
"""
Return normalized purl components.
"""
quoting = get_quoter(encode)

if type:
type = type.strip().lower() # NOQA

if namespace:
namespace = namespace.strip().strip('/')
if type in ('bitbucket', 'github', 'pypi'):
namespace = namespace.lower()
segments = namespace.split('/')
segments = [seg for seg in segments if seg and seg.strip()]
segments = map(quoting, segments)
namespace = '/'.join(segments)

if name:
name = name.strip().strip('/')
if type in ('bitbucket', 'github', 'pypi',):
name = name.lower()
if type in ('pypi',):
name = name.replace('_', '-')
name = quoting(name)

name = name or None

if version:
version = quoting(version.strip())

qualifiers = normalize_qualifiers(qualifiers, encode)

if subpath:
segments = subpath.split('/')
segments = [quoting(s) for s in segments if s and s.strip()
Expand Down Expand Up @@ -167,8 +191,8 @@ def __new__(self, type=None, namespace=None, name=None, # NOQA
raise ValueError('Invalid purl: {} argument must be a string: {}.'
.format(key, repr(value)))

if qualifiers and not isinstance(qualifiers, (dict, OrderedDict,)):
raise ValueError('Invalid purl: {} argument must be a dict: {}.'
if qualifiers and not isinstance(qualifiers, (basestring, dict, OrderedDict,)):
raise ValueError('Invalid purl: {} argument must be a dict or a string: {}.'
.format('qualifiers', repr(qualifiers)))

type, namespace, name, version, qualifiers, subpath = normalize(# NOQA
Expand Down
29 changes: 29 additions & 0 deletions test_purl.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import re
import unittest

from packageurl import normalize_qualifiers
from packageurl import PackageURL

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


build_tests()


class NormalizePurlQualifiersTest(unittest.TestCase):
canonical_purl = 'pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?classifier=sources&repository_url=repo.spring.io/release'
type = 'maven'
namespace = 'org.apache.xmlgraphics'
name = 'batik-anim'
version = '1.9.1'
qualifiers_as_dict = {
'classifier': 'sources',
'repository_url': 'repo.spring.io/release'
}
qualifiers_as_string = 'classifier=sources&repository_url=repo.spring.io/release'
subpath = None

def test_normalize_qualifiers_as_string(self):
assert self.qualifiers_as_string == normalize_qualifiers(self.qualifiers_as_dict, encode=True)

def test_normalize_qualifiers_as_dict(self):
assert self.qualifiers_as_dict == normalize_qualifiers(self.qualifiers_as_string, encode=False)

def test_create_PackageURL_from_qualifiers_string(self):
assert self.canonical_purl == PackageURL(self.type, self.namespace, self.name, self.version,
self.qualifiers_as_string, self.subpath).to_string()

def test_create_PackageURL_from_qualifiers_dict(self):
assert self.canonical_purl == PackageURL(self.type, self.namespace, self.name, self.version,
self.qualifiers_as_dict, self.subpath).to_string()