Repository: package-url/packageurl-python

Issue:
Incorrect PURL inferred from download URL
With https://github.com/pypa/get-virtualenv/raw/20.0.31/public/virtualenv.pyz this purl is inferred:  
`pkg:github/pypa/get-virtualenv@raw#20.0.31/public/virtualenv.pyz` and this is not correct.  
It should be instead:  
`pkg:github/pypa/get-virtualenv@20.0.31#/public/virtualenv.pyz`

```Python
>>> from packageurl import url2purl
>>> url2purl.get_purl('https://github.com/pypa/get-virtualenv/raw/20.0.31/public/virtualenv.pyz')
PackageURL(type='github', namespace='pypa', name='get-virtualenv', version='raw', qualifiers=OrderedDict(), subpath='20.0.31/public/virtualenv.pyz')
```



Comments:

Patch:
diff --git a/src/packageurl/contrib/url2purl.py b/src/packageurl/contrib/url2purl.py
index 8d5bde2..d10f4ed 100644
--- a/src/packageurl/contrib/url2purl.py
+++ b/src/packageurl/contrib/url2purl.py
@@ -343,7 +343,7 @@ def build_github_api_purl(url):
 
 github_codeload_pattern = (
     r"https?://codeload.github.com/"
-    r"(?P<namespace>.+)/(?P<name>.+)/(zip|tar.gz|tar.bz2|.tgz)/v(?P<version>.+)$"
+    r"(?P<namespace>.+)/(?P<name>.+)/(zip|tar.gz|tar.bz2|.tgz)/v?(?P<version>.+)$"
 )
 
 
@@ -367,17 +367,27 @@ def build_github_purl(url):
     https://github.com/package-url/packageurl-js or
     https://github.com/nexB/scancode-toolkit/archive/v3.1.1.zip
     """
-    #https://github.com/nexB/scancode-toolkit/archive/v3.1.1.zip
-    gh_pattern = r"https?://github.com/(?P<namespace>.+)/(?P<name>.+)/archive/v(?P<version>.+).(zip|tar.gz|tar.bz2|.tgz)"
-    matches = re.search(gh_pattern, url)
+    # https://github.com/nexB/scancode-toolkit/archive/v3.1.1.zip
+    archive_pattern = (
+        r"https?://github.com/(?P<namespace>.+)/(?P<name>.+)"
+        r"/archive/v?(?P<version>.+).(zip|tar.gz|tar.bz2|.tgz)"
+    )
 
-    if matches:
-        return purl_from_pattern(type_='github', pattern=gh_pattern, url=url)
+    # https://github.com/pypa/get-virtualenv/raw/20.0.31/public/virtualenv.pyz
+    raw_pattern = (
+        r"https?://github.com/(?P<namespace>.+)/(?P<name>.+)"
+        r"/raw/v?(?P<version>[^/]+)/(?P<subpath>.*)$"
+    )
 
-    segments = get_path_segments(url)
+    for pattern in [archive_pattern, raw_pattern]:
+        matches = re.search(pattern, url)
+        if matches:
+            return purl_from_pattern(type_='github', pattern=pattern, url=url)
 
-    if segments==[]:
+    segments = get_path_segments(url)
+    if not segments:
         return
+
     namespace = segments[0]
     name = segments[1]
     version = None


Test patch:
diff --git a/tests/contrib/data/url2purl.json b/tests/contrib/data/url2purl.json
index 899170a..db52fff 100644
--- a/tests/contrib/data/url2purl.json
+++ b/tests/contrib/data/url2purl.json
@@ -1,5 +1,5 @@
 {
-  "http://central.maven.org/maven2/ant-contrib/ant-contrib/1.0b3/ant-contrib-1.0b3.jar": "pkg:maven/ant-contrib/ant-contrib@1.0b3", 
+  "http://central.maven.org/maven2/ant-contrib/ant-contrib/1.0b3/ant-contrib-1.0b3.jar": "pkg:maven/ant-contrib/ant-contrib@1.0b3",
   "http://repo1.maven.org/maven2/ant-contrib/ant-contrib/1.0b3/ant-contrib-1.0b3.jar": "pkg:maven/ant-contrib/ant-contrib@1.0b3",
   "maven-index://repo1.maven.org/ant-contrib/ant-contrib/1.0b3/ant-contrib-1.0b3.jar": "pkg:maven/ant-contrib/ant-contrib@1.0b3",
   "maven-index://repo1.maven.org/ant-contrib/ant-contrib/1.0b3/": "pkg:maven/ant-contrib/ant-contrib@1.0b3",
@@ -13,18 +13,18 @@
   "http://central.maven.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1-sources.jar": "pkg:maven/org.apache.commons/commons-math3@3.6.1?classifier=sources",
   "http://repo1.maven.org/maven2/jdbm/jdbm/0.20-dev/jdbm-0.20-dev.pom": "pkg:maven/jdbm/jdbm@0.20-dev?type=pom",
   "http://central.maven.org/maven2/ant/ant-optional/1.5.3-1/ant-optional-1.5.3-1.jar": "pkg:maven/ant/ant-optional@1.5.3-1",
-  "http://central.maven.org/maven2/ant/ant/1.5/ant-1.5.jar": "pkg:maven/ant/ant@1.5", 
-  "http://central.maven.org/maven2/antlr/antlr/2.7.7/antlr-2.7.7.jar": "pkg:maven/antlr/antlr@2.7.7", 
-  "http://central.maven.org/maven2/aopalliance/aopalliance/1.0/aopalliance-1.0.jar": "pkg:maven/aopalliance/aopalliance@1.0", 
-  "http://central.maven.org/maven2/fr/opensagres/xdocreport/fr.opensagres.xdocreport.converter.docx.xwpf/1.0.5/fr.opensagres.xdocreport.converter.docx.xwpf-1.0.5.jar": "pkg:maven/fr.opensagres.xdocreport/fr.opensagres.xdocreport.converter.docx.xwpf@1.0.5", 
-  "http://central.maven.org/maven2/org/eclipse/jetty/orbit/org.apache.jasper.glassfish/2.2.2.v201112011158/org.apache.jasper.glassfish-2.2.2.v201112011158-sources.jar": "pkg:maven/org.eclipse.jetty.orbit/org.apache.jasper.glassfish@2.2.2.v201112011158?classifier=sources", 
-  "http://central.maven.org/maven2/org/eclipse/jetty/orbit/org.apache.taglibs.standard.glassfish/1.2.0.v201112081803/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803-sources.jar": "pkg:maven/org.eclipse.jetty.orbit/org.apache.taglibs.standard.glassfish@1.2.0.v201112081803?classifier=sources", 
-  "http://central.maven.org/maven2/org/springframework/security/kerberos/spring-security-kerberos-core/1.0.1.RELEASE/spring-security-kerberos-core-1.0.1.RELEASE-sources.jar": "pkg:maven/org.springframework.security.kerberos/spring-security-kerberos-core@1.0.1.RELEASE?classifier=sources", 
-  "http://central.maven.org/maven2/org/springframework/security/kerberos/spring-security-kerberos-web/1.0.1.RELEASE/spring-security-kerberos-web-1.0.1.RELEASE-sources.jar": "pkg:maven/org.springframework.security.kerberos/spring-security-kerberos-web@1.0.1.RELEASE?classifier=sources", 
-  "http://central.maven.org/maven2/xmlunit/xmlunit/1.1/xmlunit-1.1.jar": "pkg:maven/xmlunit/xmlunit@1.1", 
-  "http://central.maven.org/maven2/xom/xom/1.0/xom-1.0.jar": "pkg:maven/xom/xom@1.0", 
-  "http://central.maven.org/maven2/xom/xom/1.1/xom-1.1-sources.jar": "pkg:maven/xom/xom@1.1?classifier=sources", 
-  "http://central.maven.org/maven2/xpp3/xpp3/1.1.3.4.O/xpp3-1.1.3.4.O.jar": "pkg:maven/xpp3/xpp3@1.1.3.4.O", 
+  "http://central.maven.org/maven2/ant/ant/1.5/ant-1.5.jar": "pkg:maven/ant/ant@1.5",
+  "http://central.maven.org/maven2/antlr/antlr/2.7.7/antlr-2.7.7.jar": "pkg:maven/antlr/antlr@2.7.7",
+  "http://central.maven.org/maven2/aopalliance/aopalliance/1.0/aopalliance-1.0.jar": "pkg:maven/aopalliance/aopalliance@1.0",
+  "http://central.maven.org/maven2/fr/opensagres/xdocreport/fr.opensagres.xdocreport.converter.docx.xwpf/1.0.5/fr.opensagres.xdocreport.converter.docx.xwpf-1.0.5.jar": "pkg:maven/fr.opensagres.xdocreport/fr.opensagres.xdocreport.converter.docx.xwpf@1.0.5",
+  "http://central.maven.org/maven2/org/eclipse/jetty/orbit/org.apache.jasper.glassfish/2.2.2.v201112011158/org.apache.jasper.glassfish-2.2.2.v201112011158-sources.jar": "pkg:maven/org.eclipse.jetty.orbit/org.apache.jasper.glassfish@2.2.2.v201112011158?classifier=sources",
+  "http://central.maven.org/maven2/org/eclipse/jetty/orbit/org.apache.taglibs.standard.glassfish/1.2.0.v201112081803/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803-sources.jar": "pkg:maven/org.eclipse.jetty.orbit/org.apache.taglibs.standard.glassfish@1.2.0.v201112081803?classifier=sources",
+  "http://central.maven.org/maven2/org/springframework/security/kerberos/spring-security-kerberos-core/1.0.1.RELEASE/spring-security-kerberos-core-1.0.1.RELEASE-sources.jar": "pkg:maven/org.springframework.security.kerberos/spring-security-kerberos-core@1.0.1.RELEASE?classifier=sources",
+  "http://central.maven.org/maven2/org/springframework/security/kerberos/spring-security-kerberos-web/1.0.1.RELEASE/spring-security-kerberos-web-1.0.1.RELEASE-sources.jar": "pkg:maven/org.springframework.security.kerberos/spring-security-kerberos-web@1.0.1.RELEASE?classifier=sources",
+  "http://central.maven.org/maven2/xmlunit/xmlunit/1.1/xmlunit-1.1.jar": "pkg:maven/xmlunit/xmlunit@1.1",
+  "http://central.maven.org/maven2/xom/xom/1.0/xom-1.0.jar": "pkg:maven/xom/xom@1.0",
+  "http://central.maven.org/maven2/xom/xom/1.1/xom-1.1-sources.jar": "pkg:maven/xom/xom@1.1?classifier=sources",
+  "http://central.maven.org/maven2/xpp3/xpp3/1.1.3.4.O/xpp3-1.1.3.4.O.jar": "pkg:maven/xpp3/xpp3@1.1.3.4.O",
   "http://central.maven.org/maven2/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar": "pkg:maven/xpp3/xpp3_min@1.1.4c",
   "http://central.maven.org/maven2/org/apache/zookeeper/zookeeper/3.4.6/": "pkg:maven/org.apache.zookeeper/zookeeper@3.4.6",
   "http://central.maven.org/maven2/org/apache/zookeeper/zookeeper/3.4.6": "pkg:maven/org.apache.zookeeper/zookeeper@3.4.6",
@@ -62,24 +62,24 @@
   "https://registry.yarnpkg.com/@invisionag%2feslint-config-ivx": "pkg:npm/%40invisionag/eslint-config-ivx",
   "https://registry.npmjs.org/automatta/-/automatta-0.0.1.tgz": "pkg:npm/automatta@0.0.1",
   "http://registry.npmjs.org/1to2/-/1to2-1.0.0.tgz": "pkg:npm/1to2@1.0.0",
-  "http://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz": "pkg:npm/abbrev@1.0.9", 
-  "http://registry.npmjs.org/accepts/-/accepts-1.2.2.tgz": "pkg:npm/accepts@1.2.2", 
+  "http://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz": "pkg:npm/abbrev@1.0.9",
+  "http://registry.npmjs.org/accepts/-/accepts-1.2.2.tgz": "pkg:npm/accepts@1.2.2",
   "http://registry.npmjs.org/acorn/-/acorn-0.11.0.tgz": "pkg:npm/acorn@0.11.0",
-  "http://registry.npmjs.org/co/-/co-4.6.0.tgz": "pkg:npm/co@4.6.0", 
-  "http://registry.npmjs.org/d/-/d-0.1.1.tgz": "pkg:npm/d@0.1.1", 
-  "http://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz": "pkg:npm/functional-red-black-tree@1.0.1", 
-  "http://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz": "pkg:npm/json-stable-stringify-without-jsonify@1.0.1", 
+  "http://registry.npmjs.org/co/-/co-4.6.0.tgz": "pkg:npm/co@4.6.0",
+  "http://registry.npmjs.org/d/-/d-0.1.1.tgz": "pkg:npm/d@0.1.1",
+  "http://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz": "pkg:npm/functional-red-black-tree@1.0.1",
+  "http://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz": "pkg:npm/json-stable-stringify-without-jsonify@1.0.1",
   "http://registry.npmjs.org/ms/-/ms-0.7.1.tgz": "pkg:npm/ms@0.7.1",
-  "http://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz": "pkg:npm/validate-npm-package-license@3.0.1", 
+  "http://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz": "pkg:npm/validate-npm-package-license@3.0.1",
   "https://registry.npmjs.org/@invisionag/eslint-config-ivx/-/eslint-config-ivx-0.0.2.tgz": "pkg:npm/%40invisionag/eslint-config-ivx@0.0.2",
-  "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz": "pkg:npm/fast-json-stable-stringify@2.0.0", 
-  "https://registry.npmjs.org/q/-/q-1.5.1.tgz": "pkg:npm/q@1.5.1", 
-  "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz": "pkg:npm/remove-trailing-separator@1.1.0", 
-  "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz": "pkg:npm/wide-align@1.1.2", 
-  "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz": "pkg:npm/widest-line@2.0.0", 
-  "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz": "pkg:npm/write-file-atomic@2.3.0", 
-  "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz": "pkg:npm/xdg-basedir@3.0.0", 
-  "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz": "pkg:npm/yallist@2.1.2", 
+  "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz": "pkg:npm/fast-json-stable-stringify@2.0.0",
+  "https://registry.npmjs.org/q/-/q-1.5.1.tgz": "pkg:npm/q@1.5.1",
+  "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz": "pkg:npm/remove-trailing-separator@1.1.0",
+  "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz": "pkg:npm/wide-align@1.1.2",
+  "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz": "pkg:npm/widest-line@2.0.0",
+  "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz": "pkg:npm/write-file-atomic@2.3.0",
+  "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz": "pkg:npm/xdg-basedir@3.0.0",
+  "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz": "pkg:npm/yallist@2.1.2",
 
   "http://rubygems.org/downloads/":  null,
   "http://rubygems.org/downloads/macaddr-1.6.1":  null,
@@ -87,13 +87,13 @@
   "http://rubygems.org/downloads/open4-1.3.0.gem": "pkg:rubygems/open4@1.3.0",
   "https://rubygems.org/downloads/actionmailer-4.0.3.gem": "pkg:rubygems/actionmailer@4.0.3",
   "https://rubygems.org/downloads/activerecord-deprecated_finders-1.0.3.gem": "pkg:rubygems/activerecord-deprecated_finders@1.0.3",
-  "https://rubygems.org/downloads/ejs-1.1.1.gem": "pkg:rubygems/ejs@1.1.1", 
-  "https://rubygems.org/downloads/eventmachine-0.12.11.cloudfoundry.3.gem": "pkg:rubygems/eventmachine@0.12.11.cloudfoundry.3", 
-  "https://rubygems.org/downloads/ffi-1.9.3.gem": "pkg:rubygems/ffi@1.9.3", 
-  "https://rubygems.org/downloads/jwt-0.1.8.gem": "pkg:rubygems/jwt@0.1.8", 
-  "https://rubygems.org/downloads/ref-1.0.5.gem": "pkg:rubygems/ref@1.0.5", 
-  "https://rubygems.org/downloads/talentbox-delayed_job_sequel-4.0.0.gem": "pkg:rubygems/talentbox-delayed_job_sequel@4.0.0", 
-  "https://rubygems.org/downloads/unf-0.1.3.gem": "pkg:rubygems/unf@0.1.3", 
+  "https://rubygems.org/downloads/ejs-1.1.1.gem": "pkg:rubygems/ejs@1.1.1",
+  "https://rubygems.org/downloads/eventmachine-0.12.11.cloudfoundry.3.gem": "pkg:rubygems/eventmachine@0.12.11.cloudfoundry.3",
+  "https://rubygems.org/downloads/ffi-1.9.3.gem": "pkg:rubygems/ffi@1.9.3",
+  "https://rubygems.org/downloads/jwt-0.1.8.gem": "pkg:rubygems/jwt@0.1.8",
+  "https://rubygems.org/downloads/ref-1.0.5.gem": "pkg:rubygems/ref@1.0.5",
+  "https://rubygems.org/downloads/talentbox-delayed_job_sequel-4.0.0.gem": "pkg:rubygems/talentbox-delayed_job_sequel@4.0.0",
+  "https://rubygems.org/downloads/unf-0.1.3.gem": "pkg:rubygems/unf@0.1.3",
   "https://rubygems.org/downloads/yajl-ruby-1.2.0.gem": "pkg:rubygems/yajl-ruby@1.2.0",
 
   "https://pypi.python.org/packages/source/z/zc.recipe.egg/zc.recipe.egg-2.0.0.tar.gz": "pkg:pypi/zc.recipe.egg@2.0.0",
@@ -155,10 +155,16 @@
   "https://raw.githubusercontent.com/volatilityfoundation/dwarf2json/master/LICENSE.txt": "pkg:github/volatilityfoundation/dwarf2json@master#LICENSE.txt",
   "https://api.github.com/repos/nexB/scancode-toolkit": "pkg:github/nexb/scancode-toolkit",
   "https://api.github.com/repos/nexB/scancode-toolkit/commits/40593af0df6c8378d2b180324b97cb439fa11d66": "pkg:github/nexb/scancode-toolkit@40593af0df6c8378d2b180324b97cb439fa11d66",
+  "https://codeload.github.com/nexB/scancode-toolkit/tar.gz/3.1.1": "pkg:github/nexb/scancode-toolkit@3.1.1",
   "https://codeload.github.com/nexB/scancode-toolkit/tar.gz/v3.1.1": "pkg:github/nexb/scancode-toolkit@3.1.1",
+  "https://codeload.github.com/nexB/scancode-toolkit/zip/3.1.1": "pkg:github/nexb/scancode-toolkit@3.1.1",
   "https://codeload.github.com/nexB/scancode-toolkit/zip/v3.1.1": "pkg:github/nexb/scancode-toolkit@3.1.1",
+  "https://codeload.github.com/nexB/scancode.io/tar.gz/1.0": "pkg:github/nexb/scancode.io@1.0",
   "https://codeload.github.com/nexB/scancode.io/tar.gz/v1.0": "pkg:github/nexb/scancode.io@1.0",
+  "https://github.com/nexB/scancode-toolkit/archive/3.1.1.zip": "pkg:github/nexb/scancode-toolkit@3.1.1",
   "https://github.com/nexB/scancode-toolkit/archive/v3.1.1.zip": "pkg:github/nexb/scancode-toolkit@3.1.1",
+  "https://github.com/pypa/get-virtualenv/raw/20.0.31/public/virtualenv.pyz": "pkg:github/pypa/get-virtualenv@20.0.31#public/virtualenv.pyz",
+  "https://github.com/pypa/get-virtualenv/raw/v20.0.31/public/virtualenv.pyz": "pkg:github/pypa/get-virtualenv@20.0.31#public/virtualenv.pyz",
 
   "https://bitbucket.org/TG1999/first_repo/src/qa/": "pkg:bitbucket/tg1999/first_repo@qa",
   "https://bitbucket.org/TG1999/first_repo/src/QA/": "pkg:bitbucket/tg1999/first_repo@QA",