diff --git a/.gitignore b/.gitignore index e6b6df7..c64a9fa 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ pyvenv.cfg .Python /.tox/ /.pytest_cache/ +venv diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 19a2949..6eefaf9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +0.9.7 (2022-02-04) +------------------ + +- Create a generic PackageURL for URLs that do not fit existing routes in url2purl #68 + 0.9.6 (2021-10-05) ------------------ @@ -31,7 +36,7 @@ Changelog - Document usage in README - Adopt SPDX license identifier - Add support for GitHub "raw" URLs in url2purl #43 -- Improve GitHub support for "v" prefixed version in url2purl #43 +- Improve GitHub support for "v" prefixed version in url2purl #43 0.9.1 (2020-08-05) diff --git a/src/packageurl/contrib/url2purl.py b/src/packageurl/contrib/url2purl.py index eea07ea..a8a3c6e 100644 --- a/src/packageurl/contrib/url2purl.py +++ b/src/packageurl/contrib/url2purl.py @@ -53,7 +53,9 @@ def url2purl(url): try: return purl_router.process(url) except NoRouteAvailable: - return + # If `url` does not fit in one of the existing routes, + # we attempt to create a generic PackageURL for `url` + return build_generic_purl(url) get_purl = url2purl @@ -83,21 +85,37 @@ def endpoint(url): def get_path_segments(url): """ - Return a list of path segments from a `url` string. This list may be empty. + Return a list of path segments from a `url` string. """ path = unquote_plus(urlparse(url).path) segments = [seg for seg in path.split("/") if seg] + return segments - if len(segments) <= 1: - segments = [] - return segments +def build_generic_purl(uri): + """ + Return a PackageURL from `uri`, if `uri` is a parsable URL, or None + + `uri` is assumed to be a download URL, e.g. http://example.com/example.tar.gz + """ + parsed_uri = urlparse(uri) + if parsed_uri.scheme and parsed_uri.netloc and parsed_uri.path: + # Get file name from `uri` + uri_path_segments = get_path_segments(uri) + file_name = uri_path_segments[-1] + return PackageURL( + type='generic', + name=file_name, + qualifiers={ + 'download_url': uri + } + ) @purl_router.route('https?://registry.npmjs.*/.*', 'https?://registry.yarnpkg.com/.*', - 'https?://(www\\.)?npmjs.*/package/.*', - 'https?://(www\\.)?yarnpkg.com/package/.*') + 'https?://(www\\.)?npmjs.*/package.*', + 'https?://(www\\.)?yarnpkg.com/package.*') def build_npm_purl(uri): # npm URLs are difficult to disambiguate with regex if '/package/' in uri: @@ -226,14 +244,21 @@ def build_maven_purl(uri): return PackageURL('maven', namespace, name, version, qualifiers) -# https://rubygems.org/downloads/jwt-0.1.8.gem -rubygems_pattern = ( - r"^https?://rubygems.org/downloads/" - r"(?P.+)-(?P.+)" - r"(\.gem)$" -) +@purl_router.route('https?://rubygems.org/downloads/.*') +def build_rubygems_purl(uri): + # We use a more general route pattern instead of using `rubygems_pattern` + # below by itself because we want to capture all rubygems download URLs, + # even the ones that are not completly formed. This helps prevent url2purl + # from attempting to create a generic PackageURL from an invalid rubygems + # download URL. -register_pattern('rubygems', rubygems_pattern) + # https://rubygems.org/downloads/jwt-0.1.8.gem + rubygems_pattern = ( + r"^https?://rubygems.org/downloads/" + r"(?P.+)-(?P.+)" + r"(\.gem)$" + ) + return purl_from_pattern('rubygems', rubygems_pattern, uri) # https://pypi.python.org/packages/source/a/anyjson/anyjson-0.3.3.tar.gz @@ -295,17 +320,40 @@ def build_pypi_purl(uri): register_pattern('nuget', nuget_api_pattern) -# http://master.dl.sourceforge.net/project/libpng/zlib/1.2.3/zlib-1.2.3.tar.bz2 -sourceforge_pattern = ( - r"^https?://.*sourceforge.net/project/" - r"(?P([^/]+))/" # do not allow more "/" segments - r"(?P.+)/" - r"(?P[0-9\.]+)/" # version restricted to digits and dots - r"(?P=name)-(?P=version).*" # {name}-{version} repeated in the filename - r"[^/]$" # not ending with "/" -) +@purl_router.route('https?://.*sourceforge.net/project/.*') +def build_sourceforge_purl(uri): + # We use a more general route pattern instead of using `sourceforge_pattern` + # below by itself because we want to capture all sourceforge download URLs, + # even the ones that do not fit `sourceforge_pattern`. This helps prevent + # url2purl from attempting to create a generic PackageURL from a sourceforge + # URL that we can't handle. + + # http://master.dl.sourceforge.net/project/libpng/zlib/1.2.3/zlib-1.2.3.tar.bz2 + sourceforge_pattern = ( + r"^https?://.*sourceforge.net/project/" + r"(?P([^/]+))/" # do not allow more "/" segments + r"(?P.+)/" + r"(?P[0-9\.]+)/" # version restricted to digits and dots + r"(?P=name)-(?P=version).*" # {name}-{version} repeated in the filename + r"[^/]$" # not ending with "/" + ) + + sourceforge_purl = purl_from_pattern('sourceforge', sourceforge_pattern, uri) + + if not sourceforge_purl: + # We create a more generic PackageURL from `uri` if `uri` doesn't fit + # `sourceforge_pattern` + uri_path_segments = get_path_segments(uri) + file_name = uri_path_segments[-1] + sourceforge_purl = PackageURL( + type='sourceforge', + name=file_name, + qualifiers={ + 'download_url': uri + } + ) -register_pattern('sourceforge', sourceforge_pattern) + return sourceforge_purl # https://crates.io/api/v1/crates/rand/0.7.2/download @@ -435,7 +483,7 @@ def build_github_purl(url): ) segments = get_path_segments(url) - if not segments: + if not len(segments) >= 2: return namespace = segments[0] @@ -474,7 +522,7 @@ def build_bitbucket_purl(url): segments = get_path_segments(url) - if not segments: + if not len(segments) >= 2: return namespace = segments[0] name = segments[1] @@ -529,7 +577,7 @@ def build_gitlab_purl(url): """ segments = get_path_segments(url) - if not segments: + if not len(segments) >= 2: return namespace = segments[0] name = segments[1] diff --git a/tests/contrib/data/url2purl.json b/tests/contrib/data/url2purl.json index 03d2299..4fbc962 100644 --- a/tests/contrib/data/url2purl.json +++ b/tests/contrib/data/url2purl.json @@ -56,7 +56,6 @@ "http://central.maven.org/maven2/org/apache/axis2/mex/1.6.2/mex-1.6.2.mar": "pkg:maven/org.apache.axis2/mex@1.6.2?type=mar", "http://central.maven.org/maven2/servicemix/servicemix/1.0/servicemix-1.0-src.zip": "pkg:maven/servicemix/servicemix@1.0?classifier=src&type=zip", "http://central.maven.org/maven2/org/apache/yoko/yoko/1.0/yoko-1.0.pom": "pkg:maven/org.apache.yoko/yoko@1.0?type=pom", - "https://registry.yarnpkg.com/@invisionag/": null, "https://registry.yarnpkg.com/@invisionag": null, "https://registry.yarnpkg.com/@invisionag/eslint-config-ivx": "pkg:npm/%40invisionag/eslint-config-ivx", @@ -106,9 +105,8 @@ "https://www.npmjs.org/package/@angular/cli": "pkg:npm/%40angular/cli", "https://www.npmjs.org/package": null, "https://www.npmjs.org/package/": null, - - "http://rubygems.org/downloads/": null, - "http://rubygems.org/downloads/macaddr-1.6.1": null, + "http://rubygems.org/downloads/": null, + "http://rubygems.org/downloads/macaddr-1.6.1": null, "http://rubygems.org/downloads/macaddr-1.6.1.gem": "pkg:rubygems/macaddr@1.6.1", "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", @@ -121,7 +119,6 @@ "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", "https://pypi.python.org/packages/source/p/python-openid/python-openid-2.2.5.zip": "pkg:pypi/python-openid@2.2.5", "https://pypi.python.org/packages/38/e2/b23434f4030bbb1af3bcdbb2ecff6b11cf2e467622446ce66a08e99f2ea9/pluggy-0.4.0.zip#md5=447a92368175965d2fbacaef9f3df842": "pkg:pypi/pluggy@0.4.0", @@ -134,7 +131,6 @@ "https://pypi.python.org/packages/f6/ae/bbc6a204f33d9d57c798fb3857a072cd14b836792244eea4b446fdb674c6/pycryptodome-3.4.7-cp27-cp27m-win32.whl#md5=78b341de1cd686077745cd9e3a93d8d3": "pkg:pypi/pycryptodome@3.4.7", "https://pypi.python.org/packages/bd/e8/ea44ba5357a0b4fd16e5fb60c355fc8722eae31b93d7597eec50f7c35a52/pycryptodome-3.4.7-cp27-cp27m-win_amd64.whl#md5=f20bb847322baf7ae24700e5cbb15e07": "pkg:pypi/pycryptodome@3.4.7", "https://pypi.python.org/packages/1e/75/8005d086cac4cc41d3b320d338972c5e5c6a21f88472f21ac9d0e031d300/pyahocorasick-1.1.4.tar.bz2#md5=ad445b6648dc06e9040705ce1ccb4384": "pkg:pypi/pyahocorasick@1.1.4", - "http://nuget.org/packages/EntityFramework/4.2.0.0": "pkg:nuget/EntityFramework@4.2.0.0", "http://www.nuget.org/packages/SharpGIS.GZipWebClient/1.2.0": "pkg:nuget/SharpGIS.GZipWebClient@1.2.0", "https://www.nuget.org/api/v2/package/Newtonsoft.Json/11.0.1": "pkg:nuget/Newtonsoft.Json@11.0.1", @@ -142,7 +138,6 @@ "https://www.nuget.org/api/v2/package/MvvmLightLibs/4.1.23": "pkg:nuget/MvvmLightLibs@4.1.23", "https://www.nuget.org/api/v2/package/Twilio/3.4.1": "pkg:nuget/Twilio@3.4.1", "https://api.nuget.org/v3-flatcontainer/newtonsoft.json/10.0.1/newtonsoft.json.10.0.1.nupkg": "pkg:nuget/newtonsoft.json@10.0.1", - "http://master.dl.sourceforge.net/project/zznotes/zznotes/1.1.2/zznotes-1.1.2.tar.gz": "pkg:sourceforge/zznotes/zznotes@1.1.2", "http://master.dl.sourceforge.net/project/zapping/zvbi/0.2.35/zvbi-0.2.35.tar.bz2": "pkg:sourceforge/zapping/zvbi@0.2.35", "http://master.dl.sourceforge.net/project/libpng/zlib/1.2.3/zlib-1.2.3.tar.bz2": "pkg:sourceforge/libpng/zlib@1.2.3", @@ -150,23 +145,21 @@ "http://master.dl.sourceforge.net/project/wxmozilla/wxMozilla/0.5.5/wxMozilla-0.5.5.exe": "pkg:sourceforge/wxmozilla/wxMozilla@0.5.5", "http://iweb.dl.sourceforge.net/project/sblim/sblim-cim-client2/2.2.5/sblim-cim-client2-2.2.5-src.zip": "pkg:sourceforge/sblim/sblim-cim-client2@2.2.5", "http://master.dl.sourceforge.net/project/zinnia/zinnia-win32/0.06/zinnia-win32-0.06.zip": "pkg:sourceforge/zinnia/zinnia-win32@0.06", - "http://iweb.dl.sourceforge.net/project/findbugs/findbugs/1.3.4/findbugs-1.3.4.tar.gz/": null, - "http://master.dl.sourceforge.net/project/arestc/net/sf/arestc/arestc/0.1.4/arestc-0.1.4-javadoc.jar": null, - "http://master.dl.sourceforge.net/project/intraperson/OldFiles/intraperson/0.28/intraperson-0.28.tar.gz": null, - "http://master.dl.sourceforge.net/project/pwiki/pwiki/0.1.2/0.1.2.zip": null, - "http://master.dl.sourceforge.net/project/iswraid/iswraid/0.1.4.3/2.4.28-pre3-iswraid.patch.gz": null, - "http://master.dl.sourceforge.net/project/aloyscore/aloyscore/0.1a1%20stable/0.1a1_stable_AloysCore.zip": null, - "http://master.dl.sourceforge.net/project/myenterprise/OldFiles/1.0.0.2.MyEnterprise.Source.zip": null, - "http://master.dl.sourceforge.net/project/wxhaskell/wxhaskell/wxhaskell-0.9/wxhaskell-src-0.9.zip": null, - "http://master.dl.sourceforge.net/project/a2freedom/A2/1.2/a2freedom-1.2.zip": null, - "http://master.dl.sourceforge.net/project/tinyos/OldFiles/tinyos/1.1.0/tinyos-1.1.0.tar.gz": null, - "http://master.dl.sourceforge.net/project/urlchecker/lu/ng/urlchecker/urlchecker/1.7/urlchecker-1.7-javadoc.jar": null, - "http://master.dl.sourceforge.net/project/zclasspath/maven2/org/zclasspath/zclasspath/1.5/zclasspath-1.5.jar": null, - + "http://iweb.dl.sourceforge.net/project/findbugs/findbugs/1.3.4/findbugs-1.3.4.tar.gz/": "pkg:sourceforge/findbugs-1.3.4.tar.gz?download_url=http://iweb.dl.sourceforge.net/project/findbugs/findbugs/1.3.4/findbugs-1.3.4.tar.gz/", + "http://master.dl.sourceforge.net/project/arestc/net/sf/arestc/arestc/0.1.4/arestc-0.1.4-javadoc.jar": "pkg:sourceforge/arestc-0.1.4-javadoc.jar?download_url=http://master.dl.sourceforge.net/project/arestc/net/sf/arestc/arestc/0.1.4/arestc-0.1.4-javadoc.jar", + "http://master.dl.sourceforge.net/project/intraperson/OldFiles/intraperson/0.28/intraperson-0.28.tar.gz": "pkg:sourceforge/intraperson-0.28.tar.gz?download_url=http://master.dl.sourceforge.net/project/intraperson/OldFiles/intraperson/0.28/intraperson-0.28.tar.gz", + "http://master.dl.sourceforge.net/project/pwiki/pwiki/0.1.2/0.1.2.zip": "pkg:sourceforge/0.1.2.zip?download_url=http://master.dl.sourceforge.net/project/pwiki/pwiki/0.1.2/0.1.2.zip", + "http://master.dl.sourceforge.net/project/iswraid/iswraid/0.1.4.3/2.4.28-pre3-iswraid.patch.gz": "pkg:sourceforge/2.4.28-pre3-iswraid.patch.gz?download_url=http://master.dl.sourceforge.net/project/iswraid/iswraid/0.1.4.3/2.4.28-pre3-iswraid.patch.gz", + "http://master.dl.sourceforge.net/project/aloyscore/aloyscore/0.1a1%20stable/0.1a1_stable_AloysCore.zip": "pkg:sourceforge/0.1a1_stable_AloysCore.zip?download_url=http://master.dl.sourceforge.net/project/aloyscore/aloyscore/0.1a1%2520stable/0.1a1_stable_AloysCore.zip", + "http://master.dl.sourceforge.net/project/myenterprise/OldFiles/1.0.0.2.MyEnterprise.Source.zip": "pkg:sourceforge/1.0.0.2.MyEnterprise.Source.zip?download_url=http://master.dl.sourceforge.net/project/myenterprise/OldFiles/1.0.0.2.MyEnterprise.Source.zip", + "http://master.dl.sourceforge.net/project/wxhaskell/wxhaskell/wxhaskell-0.9/wxhaskell-src-0.9.zip": "pkg:sourceforge/wxhaskell-src-0.9.zip?download_url=http://master.dl.sourceforge.net/project/wxhaskell/wxhaskell/wxhaskell-0.9/wxhaskell-src-0.9.zip", + "http://master.dl.sourceforge.net/project/a2freedom/A2/1.2/a2freedom-1.2.zip": "pkg:sourceforge/a2freedom-1.2.zip?download_url=http://master.dl.sourceforge.net/project/a2freedom/A2/1.2/a2freedom-1.2.zip", + "http://master.dl.sourceforge.net/project/tinyos/OldFiles/tinyos/1.1.0/tinyos-1.1.0.tar.gz": "pkg:sourceforge/tinyos-1.1.0.tar.gz?download_url=http://master.dl.sourceforge.net/project/tinyos/OldFiles/tinyos/1.1.0/tinyos-1.1.0.tar.gz", + "http://master.dl.sourceforge.net/project/urlchecker/lu/ng/urlchecker/urlchecker/1.7/urlchecker-1.7-javadoc.jar": "pkg:sourceforge/urlchecker-1.7-javadoc.jar?download_url=http://master.dl.sourceforge.net/project/urlchecker/lu/ng/urlchecker/urlchecker/1.7/urlchecker-1.7-javadoc.jar", + "http://master.dl.sourceforge.net/project/zclasspath/maven2/org/zclasspath/zclasspath/1.5/zclasspath-1.5.jar": "pkg:sourceforge/zclasspath-1.5.jar?download_url=http://master.dl.sourceforge.net/project/zclasspath/maven2/org/zclasspath/zclasspath/1.5/zclasspath-1.5.jar", "https://crates.io/api/v1/crates/rand/0.7.2/download": "pkg:cargo/rand@0.7.2", "https://crates.io/api/v1/crates/clap/2.33.0/download": "pkg:cargo/clap@2.33.0", "https://crates.io/api/v1/crates/structopt/0.3.11/download": "pkg:cargo/structopt@0.3.11", - "https://github.com/TG1999/fetchcode/tree/documentation/fetchcode": "pkg:github/tg1999/fetchcode@documentation#fetchcode", "https://github.com/nexB/scancode-toolkit/tree/develop/plugins/scancode-ctags-macosx_10_9_intel": "pkg:github/nexb/scancode-toolkit@develop#plugins/scancode-ctags-macosx_10_9_intel", "https://github.com/package-url/packageurl-js/tree/master/test/data": "pkg:github/package-url/packageurl-js@master#test/data", @@ -216,15 +209,14 @@ "https://github.com/swagger-api/swagger-codegen/archive/refs/tags/v3.0.25.tar.gz": "pkg:github/swagger-api/swagger-codegen@3.0.25", "https://github.com/bareos/bareos/archive/Release/16.2.6.zip": "pkg:github/bareos/bareos@16.2.6", "https://github.com/hessu/bchunk/archive/release/1.2.2.tar.gz": "pkg:github/hessu/bchunk@1.2.2", - "https://github.com/downloads/mozilla/rhino/rhino1_7R4.zip" : "pkg:github/mozilla/rhino@1_7R4", - "https://github.com/pombredanne/schematics.git" : "pkg:github/pombredanne/schematics", + "https://github.com/downloads/mozilla/rhino/rhino1_7R4.zip": "pkg:github/mozilla/rhino@1_7R4", + "https://github.com/pombredanne/schematics.git": "pkg:github/pombredanne/schematics", "https://github.com/jgoerzen/configfile/archive/upstream/1.1.4.tar.gz": "pkg:github/jgoerzen/configfile@1.1.4", "https://github.com/JetBrains/intellij-community/archive/idea/173.4710.11.zip": "pkg:github/jetbrains/intellij-community@173.4710.11", "https://github.com/knime/knime-core/archive/analytics-platform/3.6.0.zip": "pkg:github/knime/knime-core@3.6.0", "https://github.com/renozao/NMF/archive/hotfix/0.20.1.zip": "pkg:github/renozao/nmf@0.20.1", "https://github.com/apache/cordova-osx/archive/rel/4.0.0.zip": "pkg:github/apache/cordova-osx@4.0.0", "https://github.com/sehmaschine/django-grappelli/archive/stable/2.13.x.zip": "pkg:github/sehmaschine/django-grappelli@2.13.x", - "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", "https://bitbucket.org/TG1999/first_repo/": "pkg:bitbucket/tg1999/first_repo", @@ -234,21 +226,20 @@ "https://bitbucket.org/TG1999/first_repo/src/master/new_folder/": "pkg:bitbucket/tg1999/first_repo@master#new_folder", "https://bitbucket.org/TG1999/first_repo/src/MASTER/NEW_FOLDER/": "pkg:bitbucket/tg1999/first_repo@MASTER#NEW_FOLDER", "https://bitbucket.org/TG1999/first_repo/new_folder/": "pkg:bitbucket/tg1999/first_repo@new_folder", - "https://bitbucket.org/multicoreware/x265/downloads/x265_2.6.tar.gz" : "pkg:bitbucket/multicoreware/x265?download_url=https://bitbucket.org/multicoreware/x265/downloads/x265_2.6.tar.gz", - "https://bitbucket.org/robeden/trove/downloads/trove-3.0.3.zip" : "pkg:bitbucket/robeden/trove?download_url=https://bitbucket.org/robeden/trove/downloads/trove-3.0.3.zip", - + "https://bitbucket.org/multicoreware/x265/downloads/x265_2.6.tar.gz": "pkg:bitbucket/multicoreware/x265?download_url=https://bitbucket.org/multicoreware/x265/downloads/x265_2.6.tar.gz", + "https://bitbucket.org/robeden/trove/downloads/trove-3.0.3.zip": "pkg:bitbucket/robeden/trove?download_url=https://bitbucket.org/robeden/trove/downloads/trove-3.0.3.zip", "https://gitlab.com/TG1999/firebase/-/tree/1a122122/views": "pkg:gitlab/tg1999/firebase@1a122122#views", "https://gitlab.com/tg1999/firebase": "pkg:gitlab/tg1999/firebase", "https://gitlab.com/TG1999/firebase/-/": "pkg:gitlab/tg1999/firebase", "https://gitlab.com/TG1999/firebase/-/tree": "pkg:gitlab/tg1999/firebase", - "https://gitlab.com/TG1999/firebase/-/master": "pkg:gitlab/tg1999/firebase@master", + "https://gitlab.com/TG1999/firebase/-/master": "pkg:gitlab/tg1999/firebase", "https://gitlab.com/TG1999/firebase/tree/": "pkg:gitlab/tg1999/firebase", "https://gitlab.com/TG1999/firebase/master": "pkg:gitlab/tg1999/firebase@master", "https://gitlab.com/TG1999/firebase/-/tree/master": "pkg:gitlab/tg1999/firebase@master", "https://gitlab.com/tg1999/Firebase/-/tree/master": "pkg:gitlab/tg1999/firebase@master", "https://gitlab.com/TG1999/FIREBASE": "pkg:gitlab/tg1999/firebase", - "https://hackage.haskell.org/package/a50-0.5/a50-0.5.tar.gz": "pkg:hackage/a50@0.5", "https://hackage.haskell.org/package/AC-HalfInteger-1.2.1/AC-HalfInteger-1.2.1.tar.gz": "pkg:hackage/AC-HalfInteger@1.2.1", - "https://hackage.haskell.org/package/3d-graphics-examples-0.0.0.2/3d-graphics-examples-0.0.0.2.tar.gz": "pkg:hackage/3d-graphics-examples@0.0.0.2" -} + "https://hackage.haskell.org/package/3d-graphics-examples-0.0.0.2/3d-graphics-examples-0.0.0.2.tar.gz": "pkg:hackage/3d-graphics-examples@0.0.0.2", + "https://salsa.debian.org/lxc-team/lxc/-/archive/master/lxc-master.tar.gz": "pkg:generic/lxc-master.tar.gz?download_url=https://salsa.debian.org/lxc-team/lxc/-/archive/master/lxc-master.tar.gz" +} \ No newline at end of file diff --git a/tests/contrib/test_get_path_segments.py b/tests/contrib/test_get_path_segments.py index 6f499b8..9df36c5 100644 --- a/tests/contrib/test_get_path_segments.py +++ b/tests/contrib/test_get_path_segments.py @@ -43,4 +43,4 @@ def test_parsing_empty_string(): def test_parsing_with_one_segment(): url = 'https://github.com/TG1999' segments = get_path_segments(url) - assert [] == segments + assert ["TG1999"] == segments diff --git a/tests/contrib/test_url2purl.py b/tests/contrib/test_url2purl.py index 5da09d0..2c5d2c8 100644 --- a/tests/contrib/test_url2purl.py +++ b/tests/contrib/test_url2purl.py @@ -83,9 +83,8 @@ def build_tests(clazz, test_file='url2purl.json', regen=False): if regen: tests_data = {test_url: get_purl(test_url) for test_url in tests_data.keys()} - dumpable = json.dumps(sorted(tests_data.items()), indent=2) - with io.open(test_file, 'wb') as regened: - regened.write(dumpable) + with io.open(test_file, 'w') as regened: + json.dump(tests_data, regened, indent=2) for test_url, expected_purl in sorted(tests_data.items()): test_name = 'test_url2purl_{test_url}'.format(test_url=test_url)