From 81e803e9de619eeb1df7689e3461faf3c65f999f Mon Sep 17 00:00:00 2001 From: Qing Tomlinson Date: Mon, 8 Jul 2024 15:31:24 -0700 Subject: [PATCH] Derive license from info.license over classifiers in pypi registry data Based on the Python Packaging User Guide (https://packaging.python.org/en/latest/specifications/core-metadata/#license), it appears that the license field holds more specific information. Change to parsing license field before the classifiers. When classifier has the version info and license field does not, use classifier first in that case. --- providers/fetch/pypiFetch.js | 18 +- providers/process/pypiExtract.js | 2 +- .../fixtures/pypi/registryData-info_bsd3.json | 43 ++++ .../pypi/registryData-info_chardet-5.1.0.json | 55 ++++++ .../pypi/registryData-info_gpl2+.json | 81 ++++++++ .../fixtures/pypi/registryData-info_hpnd.json | 78 ++++++++ .../pypi/registryData-info_numba-0.56.0.json | 53 +++++ .../registryData-info_oslo.context-3.4.0.json | 51 +++++ .../registryData-info_platformdirs-4.2.0.json | 67 +++++++ .../registryData-info_pygobject-3.42.0.json | 45 +++++ .../pypi/registryData-info_pywin32-303.json | 50 +++++ .../registryData-info_sacremoses-0.0.51.json | 40 ++++ test/unit/providers/fetch/pypiFetchTests.js | 186 ++++++++++++------ 13 files changed, 701 insertions(+), 68 deletions(-) create mode 100644 test/fixtures/pypi/registryData-info_bsd3.json create mode 100644 test/fixtures/pypi/registryData-info_chardet-5.1.0.json create mode 100644 test/fixtures/pypi/registryData-info_gpl2+.json create mode 100644 test/fixtures/pypi/registryData-info_hpnd.json create mode 100644 test/fixtures/pypi/registryData-info_numba-0.56.0.json create mode 100644 test/fixtures/pypi/registryData-info_oslo.context-3.4.0.json create mode 100644 test/fixtures/pypi/registryData-info_platformdirs-4.2.0.json create mode 100644 test/fixtures/pypi/registryData-info_pygobject-3.42.0.json create mode 100644 test/fixtures/pypi/registryData-info_pywin32-303.json create mode 100644 test/fixtures/pypi/registryData-info_sacremoses-0.0.51.json diff --git a/providers/fetch/pypiFetch.js b/providers/fetch/pypiFetch.js index fa7d8731..8e5f4e5f 100644 --- a/providers/fetch/pypiFetch.js +++ b/providers/fetch/pypiFetch.js @@ -79,18 +79,24 @@ class PyPiFetch extends AbstractFetch { for (const classifier in classifiers) { if (classifiers[classifier].includes('License :: OSI Approved ::')) { const lastColon = classifiers[classifier].lastIndexOf(':') - const rawLicense = classifiers[classifier].slice(lastColon + 1) - return spdxCorrect(rawLicense) + return classifiers[classifier].slice(lastColon + 1) } } return null } _extractDeclaredLicense(registryData) { - const licenseFromClassifiers = this._extractLicenseFromClassifiers(registryData) - if (licenseFromClassifiers) return licenseFromClassifiers - const license = get(registryData, 'info.license') - return license && spdxCorrect(license) + const licenseInMetadata = get(registryData, 'info.license') + const hasVersionInMeta = /\d+/.test(licenseInMetadata) + const licenseInClassifiers = this._extractLicenseFromClassifiers(registryData) + const hasVersionInClassifier = /\d+/.test(licenseInClassifiers) + + let licenses = [licenseInMetadata, licenseInClassifiers] + if (hasVersionInClassifier && !hasVersionInMeta) licenses = [licenseInClassifiers, licenseInMetadata] + for (const rawLicense of licenses) { + const parsed = rawLicense && spdxCorrect(rawLicense) + if (parsed) return parsed + } } async _getPackage(spec, registryData, destination) { diff --git a/providers/process/pypiExtract.js b/providers/process/pypiExtract.js index 278de711..a9299d67 100644 --- a/providers/process/pypiExtract.js +++ b/providers/process/pypiExtract.js @@ -13,7 +13,7 @@ class PyPiExtract extends AbstractClearlyDefinedProcessor { } get toolVersion() { - return '1.1.1' + return '1.2.1' } canHandle(request) { diff --git a/test/fixtures/pypi/registryData-info_bsd3.json b/test/fixtures/pypi/registryData-info_bsd3.json new file mode 100644 index 00000000..ef67e2c8 --- /dev/null +++ b/test/fixtures/pypi/registryData-info_bsd3.json @@ -0,0 +1,43 @@ +{ + "info": { + "author": "Joel Nothman", + "author_email": "joel.nothman@gmail.com", + "bugtrack_url": null, + "classifiers": [ + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.6", + "Topic :: Scientific/Engineering :: Visualization" + ], + "description": "UpSetPlot documentation\n============================\n\n|version| |licence| |py-versions|\n\n|issues| |build| |docs| |coverage|\n\nThis is another Python implementation of UpSet plots by Lex et al. [Lex2014]_.\nUpSet plots are used to visualise set overlaps; like Venn diagrams but\nmore readable. Documentation is at https://upsetplot.readthedocs.io.\n\nThis ``upsetplot`` library tries to provide a simple interface backed by an\nextensible, object-oriented design.\n\nThere are many ways to represent the categorisation of data, as covered in\nour `Data Format Guide `_.\n\nOur internal input format uses a `pandas.Series` containing counts\ncorresponding to subset sizes, where each subset is an intersection of named\ncategories. The index of the Series indicates which rows pertain to which\ncategories, by having multiple boolean indices, like ``example`` in the\nfollowing::\n\n >>> from upsetplot import generate_counts\n >>> example = generate_counts()\n >>> example\n cat0 cat1 cat2\n False False False 56\n True 283\n True False 1279\n True 5882\n True False False 24\n True 90\n True False 429\n True 1957\n Name: value, dtype: int64\n\nThen::\n\n >>> from upsetplot import plot\n >>> plot(example) # doctest: +SKIP\n >>> from matplotlib import pyplot\n >>> pyplot.show() # doctest: +SKIP\n\nmakes:\n\n.. image:: http://upsetplot.readthedocs.io/en/latest/_images/sphx_glr_plot_generated_001.png\n :target: ../auto_examples/plot_generated.html\n\nAnd you can save the image in various formats::\n\n >>> pyplot.savefig(\"/path/to/myplot.pdf\") # doctest: +SKIP\n >>> pyplot.savefig(\"/path/to/myplot.png\") # doctest: +SKIP\n\nThis plot shows the cardinality of every category combination seen in our data.\nThe leftmost column counts items absent from any category. The next three\ncolumns count items only in ``cat1``, ``cat2`` and ``cat3`` respectively, with\nfollowing columns showing cardinalities for items in each combination of\nexactly two named sets. The rightmost column counts items in all three sets.\n\nRotation\n........\n\nWe call the above plot style \"horizontal\" because the category intersections\nare presented from left to right. `Vertical plots\n`__\nare also supported!\n\n.. image:: http://upsetplot.readthedocs.io/en/latest/_images/sphx_glr_plot_vertical_001.png\n :target: http://upsetplot.readthedocs.io/en/latest/auto_examples/plot_vertical.html\n\nDistributions\n.............\n\nProviding a DataFrame rather than a Series as input allows us to expressively\n`plot the distribution of variables\n`__\nin each subset.\n\n.. image:: http://upsetplot.readthedocs.io/en/latest/_images/sphx_glr_plot_diabetes_001.png\n :target: http://upsetplot.readthedocs.io/en/latest/auto_examples/plot_diabetes.html\n\nLoading datasets\n................\n\nWhile the dataset above is randomly generated, you can prepare your own dataset\nfor input to upsetplot. A helpful tool is `from_memberships`, which allows\nus to reconstruct the example above by indicating each data point's category\nmembership::\n\n >>> from upsetplot import from_memberships\n >>> example = from_memberships(\n ... [[],\n ... ['cat2'],\n ... ['cat1'],\n ... ['cat1', 'cat2'],\n ... ['cat0'],\n ... ['cat0', 'cat2'],\n ... ['cat0', 'cat1'],\n ... ['cat0', 'cat1', 'cat2'],\n ... ],\n ... data=[56, 283, 1279, 5882, 24, 90, 429, 1957]\n ... )\n >>> example\n cat0 cat1 cat2\n False False False 56\n True 283\n True False 1279\n True 5882\n True False False 24\n True 90\n True False 429\n True 1957\n dtype: int64\n\nSee also `from_contents`, another way to describe categorised data, and\n`from_indicators` which allows each category to be indicated by a column in\nthe data frame (or a function of the column's data such as whether it is a\nmissing value).\n\nInstallation\n------------\n\nTo install the library, you can use `pip`::\n\n $ pip install upsetplot\n\nInstallation requires:\n\n* pandas\n* matplotlib >= 2.0\n* seaborn to use `UpSet.add_catplot`\n\nIt should then be possible to::\n\n >>> import upsetplot\n\nin Python.\n\nWhy an alternative to py-upset?\n-------------------------------\n\nProbably for petty reasons. It appeared `py-upset\n`_ was not being maintained. Its\ninput format was undocumented, inefficient and, IMO, inappropriate. It did not\nfacilitate showing plots of each subset's distribution as in Lex et al's work\nintroducing UpSet plots. Nor did it include the horizontal bar plots\nillustrated there. It did not support Python 2. I decided it would be easier to\nconstruct a cleaner version than to fix it.\n\nReferences\n----------\n\n.. [Lex2014] Alexander Lex, Nils Gehlenborg, Hendrik Strobelt, Romain Vuillemot, Hanspeter Pfister,\n *UpSet: Visualization of Intersecting Sets*,\n IEEE Transactions on Visualization and Computer Graphics (InfoVis '14), vol. 20, no. 12, pp. 1983–1992, 2014.\n doi: `doi.org/10.1109/TVCG.2014.2346248 `_\n\n\n.. |py-versions| image:: https://img.shields.io/pypi/pyversions/upsetplot.svg\n :alt: Python versions supported\n\n.. |version| image:: https://badge.fury.io/py/UpSetPlot.svg\n :alt: Latest version on PyPi\n :target: https://badge.fury.io/py/UpSetPlot\n\n.. |build| image:: https://github.com/jnothman/upsetplot/actions/workflows/test.yml/badge.svg\n :alt: Github Workflows CI build status\n :scale: 100%\n :target: https://github.com/jnothman/UpSetPlot/actions/workflows/test.yml\n\n.. |issues| image:: https://img.shields.io/github/issues/jnothman/UpSetPlot.svg\n :alt: Issue tracker\n :target: https://github.com/jnothman/UpSetPlot\n\n.. |coverage| image:: https://coveralls.io/repos/github/jnothman/UpSetPlot/badge.svg\n :alt: Test coverage\n :target: https://coveralls.io/github/jnothman/UpSetPlot\n\n.. |docs| image:: https://readthedocs.org/projects/upsetplot/badge/?version=latest\n :alt: Documentation Status\n :scale: 100%\n :target: https://upsetplot.readthedocs.io/en/latest/?badge=latest\n\n.. |licence| image:: https://img.shields.io/badge/Licence-BSD-blue.svg\n :target: https://opensource.org/licenses/BSD-3-Clause\n", + "description_content_type": "", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "https://upsetplot.readthedocs.io", + "keywords": "", + "license": "BSD-3-Clause", + "maintainer": "", + "maintainer_email": "", + "name": "UpSetPlot", + "package_url": "https://pypi.org/project/UpSetPlot/", + "platform": null, + "project_url": "https://pypi.org/project/UpSetPlot/", + "project_urls": { + "Homepage": "https://upsetplot.readthedocs.io" + }, + "release_url": "https://pypi.org/project/UpSetPlot/0.9.0/", + "requires_dist": null, + "requires_python": "", + "summary": "Draw Lex et al.'s UpSet plots with Pandas and Matplotlib", + "version": "0.9.0", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_chardet-5.1.0.json b/test/fixtures/pypi/registryData-info_chardet-5.1.0.json new file mode 100644 index 00000000..e401d57c --- /dev/null +++ b/test/fixtures/pypi/registryData-info_chardet-5.1.0.json @@ -0,0 +1,55 @@ +{ + "info": { + "author": "Mark Pilgrim", + "author_email": "mark@diveintomark.org", + "bugtrack_url": null, + "classifiers": [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Text Processing :: Linguistic" + ], + "description": "Chardet: The Universal Character Encoding Detector\n--------------------------------------------------\n\n.. image:: https://img.shields.io/travis/chardet/chardet/stable.svg\n :alt: Build status\n :target: https://travis-ci.org/chardet/chardet\n\n.. image:: https://img.shields.io/coveralls/chardet/chardet/stable.svg\n :target: https://coveralls.io/r/chardet/chardet\n\n.. image:: https://img.shields.io/pypi/v/chardet.svg\n :target: https://warehouse.python.org/project/chardet/\n :alt: Latest version on PyPI\n\n.. image:: https://img.shields.io/pypi/l/chardet.svg\n :alt: License\n\n\nDetects\n - ASCII, UTF-8, UTF-16 (2 variants), UTF-32 (4 variants)\n - Big5, GB2312, EUC-TW, HZ-GB-2312, ISO-2022-CN (Traditional and Simplified Chinese)\n - EUC-JP, SHIFT_JIS, CP932, ISO-2022-JP (Japanese)\n - EUC-KR, ISO-2022-KR, Johab (Korean)\n - KOI8-R, MacCyrillic, IBM855, IBM866, ISO-8859-5, windows-1251 (Cyrillic)\n - ISO-8859-5, windows-1251 (Bulgarian)\n - ISO-8859-1, windows-1252, MacRoman (Western European languages)\n - ISO-8859-7, windows-1253 (Greek)\n - ISO-8859-8, windows-1255 (Visual and Logical Hebrew)\n - TIS-620 (Thai)\n\n.. note::\n Our ISO-8859-2 and windows-1250 (Hungarian) probers have been temporarily\n disabled until we can retrain the models.\n\nRequires Python 3.7+.\n\nInstallation\n------------\n\nInstall from `PyPI `_::\n\n pip install chardet\n\nDocumentation\n-------------\n\nFor users, docs are now available at https://chardet.readthedocs.io/.\n\nCommand-line Tool\n-----------------\n\nchardet comes with a command-line script which reports on the encodings of one\nor more files::\n\n % chardetect somefile someotherfile\n somefile: windows-1252 with confidence 0.5\n someotherfile: ascii with confidence 1.0\n\nAbout\n-----\n\nThis is a continuation of Mark Pilgrim's excellent original chardet port from C, and `Ian Cordasco `_'s\n`charade `_ Python 3-compatible fork.\n\n:maintainer: Dan Blanchard\n", + "description_content_type": "", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "https://github.com/chardet/chardet", + "keywords": "encoding,i18n,xml", + "license": "LGPL", + "maintainer": "Daniel Blanchard", + "maintainer_email": "dan.blanchard@gmail.com", + "name": "chardet", + "package_url": "https://pypi.org/project/chardet/", + "platform": null, + "project_url": "https://pypi.org/project/chardet/", + "project_urls": { + "Documentation": "https://chardet.readthedocs.io/", + "GitHub Project": "https://github.com/chardet/chardet", + "Homepage": "https://github.com/chardet/chardet", + "Issue Tracker": "https://github.com/chardet/chardet/issues" + }, + "release_url": "https://pypi.org/project/chardet/5.1.0/", + "requires_dist": null, + "requires_python": ">=3.7", + "summary": "Universal encoding detector for Python 3", + "version": "5.1.0", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_gpl2+.json b/test/fixtures/pypi/registryData-info_gpl2+.json new file mode 100644 index 00000000..78e55e58 --- /dev/null +++ b/test/fixtures/pypi/registryData-info_gpl2+.json @@ -0,0 +1,81 @@ +{ + "info": { + "author": null, + "author_email": "Python Code Quality Authority ", + "bugtrack_url": null, + "classifiers": [ + "Development Status :: 6 - Mature", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Software Development :: Debuggers", + "Topic :: Software Development :: Quality Assurance", + "Topic :: Software Development :: Testing", + "Typing :: Typed" + ], + "description": "`Pylint`_\n=========\n\n.. _`Pylint`: https://pylint.readthedocs.io/\n\n.. This is used inside the doc to recover the start of the introduction\n\n.. image:: https://github.com/pylint-dev/pylint/actions/workflows/tests.yaml/badge.svg?branch=main\n :target: https://github.com/pylint-dev/pylint/actions\n\n.. image:: https://codecov.io/gh/pylint-dev/pylint/branch/main/graph/badge.svg?token=ZETEzayrfk\n :target: https://codecov.io/gh/pylint-dev/pylint\n\n.. image:: https://img.shields.io/pypi/v/pylint.svg\n :alt: Pypi Package version\n :target: https://pypi.python.org/pypi/pylint\n\n.. image:: https://readthedocs.org/projects/pylint/badge/?version=latest\n :target: https://pylint.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n\n.. image:: https://img.shields.io/badge/linting-pylint-yellowgreen\n :target: https://github.com/pylint-dev/pylint\n\n.. image:: https://results.pre-commit.ci/badge/github/pylint-dev/pylint/main.svg\n :target: https://results.pre-commit.ci/latest/github/pylint-dev/pylint/main\n :alt: pre-commit.ci status\n\n.. image:: https://bestpractices.coreinfrastructure.org/projects/6328/badge\n :target: https://bestpractices.coreinfrastructure.org/projects/6328\n :alt: CII Best Practices\n\n.. image:: https://img.shields.io/ossf-scorecard/github.com/PyCQA/pylint?label=openssf%20scorecard&style=flat\n :target: https://api.securityscorecards.dev/projects/github.com/PyCQA/pylint\n :alt: OpenSSF Scorecard\n\n.. image:: https://img.shields.io/discord/825463413634891776.svg\n :target: https://discord.gg/qYxpadCgkx\n :alt: Discord\n\nWhat is Pylint?\n---------------\n\nPylint is a `static code analyser`_ for Python 2 or 3. The latest version supports Python\n3.8.0 and above.\n\n.. _`static code analyser`: https://en.wikipedia.org/wiki/Static_code_analysis\n\nPylint analyses your code without actually running it. It checks for errors, enforces a\ncoding standard, looks for `code smells`_, and can make suggestions about how the code\ncould be refactored.\n\n.. _`code smells`: https://martinfowler.com/bliki/CodeSmell.html\n\nInstall\n-------\n\n.. This is used inside the doc to recover the start of the short text for installation\n\nFor command line use, pylint is installed with::\n\n pip install pylint\n\nOr if you want to also check spelling with ``enchant`` (you might need to\n`install the enchant C library `_):\n\n.. code-block:: sh\n\n pip install pylint[spelling]\n\nIt can also be integrated in most editors or IDEs. More information can be found\n`in the documentation`_.\n\n.. _in the documentation: https://pylint.readthedocs.io/en/latest/user_guide/installation/index.html\n\n.. This is used inside the doc to recover the end of the short text for installation\n\nWhat differentiates Pylint?\n---------------------------\n\nPylint is not trusting your typing and is inferring the actual value of nodes (for a\nstart because there was no typing when pylint started off) using its internal code\nrepresentation (astroid). If your code is ``import logging as argparse``, Pylint\ncan check and know that ``argparse.error(...)`` is in fact a logging call and not an\nargparse call. This makes pylint slower, but it also lets pylint find more issues if\nyour code is not fully typed.\n\n [inference] is the killer feature that keeps us using [pylint] in our project despite how painfully slow it is.\n - `Realist pylint user`_, 2022\n\n.. _`Realist pylint user`: https://github.com/charliermarsh/ruff/issues/970#issuecomment-1381067064\n\npylint, not afraid of being a little slower than it already is, is also a lot more thorough than other linters.\nThere are more checks, including some opinionated ones that are deactivated by default\nbut can be enabled using configuration.\n\nHow to use pylint\n-----------------\n\nPylint isn't smarter than you: it may warn you about things that you have\nconscientiously done or check for some things that you don't care about.\nDuring adoption, especially in a legacy project where pylint was never enforced,\nit's best to start with the ``--errors-only`` flag, then disable\nconvention and refactor messages with ``--disable=C,R`` and progressively\nre-evaluate and re-enable messages as your priorities evolve.\n\nPylint is highly configurable and permits to write plugins in order to add your\nown checks (for example, for internal libraries or an internal rule). Pylint also has an\necosystem of existing plugins for popular frameworks and third-party libraries.\n\n.. note::\n\n Pylint supports the Python standard library out of the box. Third-party\n libraries are not always supported, so a plugin might be needed. A good place\n to start is ``PyPI`` which often returns a plugin by searching for\n ``pylint ``. `pylint-pydantic`_, `pylint-django`_ and\n `pylint-sonarjson`_ are examples of such plugins. More information about plugins\n and how to load them can be found at `plugins`_.\n\n.. _`plugins`: https://pylint.readthedocs.io/en/latest/development_guide/how_tos/plugins.html#plugins\n.. _`pylint-pydantic`: https://pypi.org/project/pylint-pydantic\n.. _`pylint-django`: https://github.com/pylint-dev/pylint-django\n.. _`pylint-sonarjson`: https://github.com/omegacen/pylint-sonarjson\n\nAdvised linters alongside pylint\n--------------------------------\n\nProjects that you might want to use alongside pylint include ruff_ (**really** fast,\nwith builtin auto-fix and a large number of checks taken from popular linters, but\nimplemented in ``rust``) or flake8_ (a framework to implement your own checks in python using ``ast`` directly),\nmypy_, pyright_ / pylance or pyre_ (typing checks), bandit_ (security oriented checks), black_ and\nisort_ (auto-formatting), autoflake_ (automated removal of unused imports or variables), pyupgrade_\n(automated upgrade to newer python syntax) and pydocstringformatter_ (automated pep257).\n\n.. _ruff: https://github.com/charliermarsh/ruff\n.. _flake8: https://github.com/PyCQA/flake8\n.. _bandit: https://github.com/PyCQA/bandit\n.. _mypy: https://github.com/python/mypy\n.. _pyright: https://github.com/microsoft/pyright\n.. _pyre: https://github.com/facebook/pyre-check\n.. _black: https://github.com/psf/black\n.. _autoflake: https://github.com/myint/autoflake\n.. _pyupgrade: https://github.com/asottile/pyupgrade\n.. _pydocstringformatter: https://github.com/DanielNoord/pydocstringformatter\n.. _isort: https://pycqa.github.io/isort/\n\nAdditional tools included in pylint\n-----------------------------------\n\nPylint ships with two additional tools:\n\n- pyreverse_ (standalone tool that generates package and class diagrams.)\n- symilar_ (duplicate code finder that is also integrated in pylint)\n\n.. _pyreverse: https://pylint.readthedocs.io/en/latest/pyreverse.html\n.. _symilar: https://pylint.readthedocs.io/en/latest/symilar.html\n\n\n.. This is used inside the doc to recover the end of the introduction\n\nContributing\n------------\n\n.. This is used inside the doc to recover the start of the short text for contribution\n\nWe welcome all forms of contributions such as updates for documentation, new code, checking issues for duplicates or telling us\nthat we can close them, confirming that issues still exist, `creating issues because\nyou found a bug or want a feature`_, etc. Everything is much appreciated!\n\nPlease follow the `code of conduct`_ and check `the Contributor Guides`_ if you want to\nmake a code contribution.\n\n.. _creating issues because you found a bug or want a feature: https://pylint.readthedocs.io/en/latest/contact.html#bug-reports-feedback\n.. _code of conduct: https://github.com/pylint-dev/pylint/blob/main/CODE_OF_CONDUCT.md\n.. _the Contributor Guides: https://pylint.readthedocs.io/en/latest/development_guide/contribute.html\n\n.. This is used inside the doc to recover the end of the short text for contribution\n\nShow your usage\n-----------------\n\nYou can place this badge in your README to let others know your project uses pylint.\n\n .. image:: https://img.shields.io/badge/linting-pylint-yellowgreen\n :target: https://github.com/pylint-dev/pylint\n\nLearn how to add a badge to your documentation in `the badge documentation`_.\n\n.. _the badge documentation: https://pylint.readthedocs.io/en/latest/user_guide/installation/badge.html\n\nLicense\n-------\n\npylint is, with a few exceptions listed below, `GPLv2 `_.\n\nThe icon files are licensed under the `CC BY-SA 4.0 `_ license:\n\n- `doc/logo.png `_\n- `doc/logo.svg `_\n\nSupport\n-------\n\nPlease check `the contact information`_.\n\n.. _`the contact information`: https://pylint.readthedocs.io/en/latest/contact.html\n\n.. |tideliftlogo| image:: https://raw.githubusercontent.com/pylint-dev/pylint/main/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png\n :width: 200\n :alt: Tidelift\n\n.. list-table::\n :widths: 10 100\n\n * - |tideliftlogo|\n - Professional support for pylint is available as part of the `Tidelift\n Subscription`_. Tidelift gives software development teams a single source for\n purchasing and maintaining their software, with professional grade assurances\n from the experts who know it best, while seamlessly integrating with existing\n tools.\n\n.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-pylint?utm_source=pypi-pylint&utm_medium=referral&utm_campaign=readme\n", + "description_content_type": "text/x-rst", + "docs_url": null, + "download_url": null, + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "dynamic": null, + "home_page": null, + "keywords": "static code analysis, linter, python, lint", + "license": "GPL-2.0-or-later", + "maintainer": null, + "maintainer_email": null, + "name": "pylint", + "package_url": "https://pypi.org/project/pylint/", + "platform": null, + "project_url": "https://pypi.org/project/pylint/", + "project_urls": { + "Bug Tracker": "https://github.com/pylint-dev/pylint/issues", + "Discord Server": "https://discord.com/invite/Egy6P8AMB5", + "Docs: Contributor Guide": "https://pylint.readthedocs.io/en/latest/development_guide/contributor_guide/index.html", + "Docs: User Guide": "https://pylint.readthedocs.io/en/latest/", + "Source Code": "https://github.com/pylint-dev/pylint", + "What's New": "https://pylint.readthedocs.io/en/latest/whatsnew/3/", + "homepage": "https://github.com/pylint-dev/pylint" + }, + "provides_extra": [ + "spelling", + "testutils" + ], + "release_url": "https://pypi.org/project/pylint/3.2.3/", + "requires_dist": [ + "platformdirs>=2.2.0", + "astroid<=3.3.0-dev0,>=3.2.2", + "isort!=5.13.0,<6,>=4.2.5", + "mccabe<0.8,>=0.6", + "tomlkit>=0.10.1", + "typing-extensions>=3.10.0; python_version < \"3.10\"", + "dill>=0.2; python_version < \"3.11\"", + "tomli>=1.1.0; python_version < \"3.11\"", + "dill>=0.3.6; python_version >= \"3.11\"", + "dill>=0.3.7; python_version >= \"3.12\"", + "colorama>=0.4.5; sys_platform == \"win32\"", + "pyenchant~=3.2; extra == \"spelling\"", + "gitpython>3; extra == \"testutils\"" + ], + "requires_python": ">=3.8.0", + "summary": "python code static checker", + "version": "3.2.3", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_hpnd.json b/test/fixtures/pypi/registryData-info_hpnd.json new file mode 100644 index 00000000..7e15c1cb --- /dev/null +++ b/test/fixtures/pypi/registryData-info_hpnd.json @@ -0,0 +1,78 @@ +{ + "info": { + "author": "Jeffrey A. Clark (Alex)", + "author_email": "aclark@aclark.net", + "bugtrack_url": null, + "classifiers": [ + "Development Status :: 6 - Mature", + "License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Multimedia :: Graphics", + "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", + "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", + "Topic :: Multimedia :: Graphics :: Graphics Conversion", + "Topic :: Multimedia :: Graphics :: Viewers" + ], + "description": "

\n \"Pillow\n

\n\n# Pillow\n\n## Python Imaging Library (Fork)\n\nPillow is the friendly PIL fork by [Jeffrey A. Clark (Alex) and\ncontributors](https://github.com/python-pillow/Pillow/graphs/contributors).\nPIL is the Python Imaging Library by Fredrik Lundh and Contributors.\nAs of 2019, Pillow development is\n[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise).\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
docs\n \n
tests\n \n \n \n \n \n \n \n \n \n \n \n
package\n \n \n \n \n \n
social\n \n \n \n
\n\n## Overview\n\nThe Python Imaging Library adds image processing capabilities to your Python interpreter.\n\nThis library provides extensive file format support, an efficient internal representation, and fairly powerful image processing capabilities.\n\nThe core image library is designed for fast access to data stored in a few basic pixel formats. It should provide a solid foundation for a general image processing tool.\n\n## More Information\n\n- [Documentation](https://pillow.readthedocs.io/)\n - [Installation](https://pillow.readthedocs.io/en/latest/installation.html)\n - [Handbook](https://pillow.readthedocs.io/en/latest/handbook/index.html)\n- [Contribute](https://github.com/python-pillow/Pillow/blob/main/.github/CONTRIBUTING.md)\n - [Issues](https://github.com/python-pillow/Pillow/issues)\n - [Pull requests](https://github.com/python-pillow/Pillow/pulls)\n- [Release notes](https://pillow.readthedocs.io/en/stable/releasenotes/index.html)\n- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)\n - [Pre-fork](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst#pre-fork)\n\n## Report a Vulnerability\n\nTo report a security vulnerability, please follow the procedure described in the [Tidelift security policy](https://tidelift.com/docs/security).\n", + "description_content_type": "text/markdown", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "https://python-pillow.org", + "keywords": "Imaging", + "license": "HPND", + "maintainer": "", + "maintainer_email": "", + "name": "Pillow", + "package_url": "https://pypi.org/project/Pillow/", + "platform": null, + "project_url": "https://pypi.org/project/Pillow/", + "project_urls": { + "Changelog": "https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst", + "Documentation": "https://pillow.readthedocs.io", + "Funding": "https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=pypi", + "Homepage": "https://python-pillow.org", + "Mastodon": "https://fosstodon.org/@pillow", + "Release notes": "https://pillow.readthedocs.io/en/stable/releasenotes/index.html", + "Source": "https://github.com/python-pillow/Pillow", + "Twitter": "https://twitter.com/PythonPillow" + }, + "release_url": "https://pypi.org/project/Pillow/10.1.0/", + "requires_dist": [ + "furo ; extra == 'docs'", + "olefile ; extra == 'docs'", + "sphinx >=2.4 ; extra == 'docs'", + "sphinx-copybutton ; extra == 'docs'", + "sphinx-inline-tabs ; extra == 'docs'", + "sphinx-removed-in ; extra == 'docs'", + "sphinxext-opengraph ; extra == 'docs'", + "check-manifest ; extra == 'tests'", + "coverage ; extra == 'tests'", + "defusedxml ; extra == 'tests'", + "markdown2 ; extra == 'tests'", + "olefile ; extra == 'tests'", + "packaging ; extra == 'tests'", + "pyroma ; extra == 'tests'", + "pytest ; extra == 'tests'", + "pytest-cov ; extra == 'tests'", + "pytest-timeout ; extra == 'tests'" + ], + "requires_python": ">=3.8", + "summary": "Python Imaging Library (Fork)", + "version": "10.1.0", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_numba-0.56.0.json b/test/fixtures/pypi/registryData-info_numba-0.56.0.json new file mode 100644 index 00000000..a5471811 --- /dev/null +++ b/test/fixtures/pypi/registryData-info_numba-0.56.0.json @@ -0,0 +1,53 @@ +{ + "info": { + "author": "", + "author_email": "", + "bugtrack_url": null, + "classifiers": [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Software Development :: Compilers" + ], + "description": "*****\nNumba\n*****\n\n.. image:: https://badges.gitter.im/numba/numba.svg\n :target: https://gitter.im/numba/numba?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge\n :alt: Gitter\n\n.. image:: https://img.shields.io/badge/discuss-on%20discourse-blue\n :target: https://numba.discourse.group/\n :alt: Discourse\n\n.. image:: https://zenodo.org/badge/3659275.svg\n :target: https://zenodo.org/badge/latestdoi/3659275\n :alt: Zenodo DOI\n\n.. image:: https://img.shields.io/pypi/v/numba.svg\n :target: https://pypi.python.org/pypi/numba/\n :alt: PyPI\n\n.. image:: https://dev.azure.com/numba/numba/_apis/build/status/numba.numba?branchName=main\n :target: https://dev.azure.com/numba/numba/_build/latest?definitionId=1?branchName=main\n :alt: Azure Pipelines\n\nA Just-In-Time Compiler for Numerical Functions in Python\n#########################################################\n\nNumba is an open source, NumPy-aware optimizing compiler for Python sponsored\nby Anaconda, Inc. It uses the LLVM compiler project to generate machine code\nfrom Python syntax.\n\nNumba can compile a large subset of numerically-focused Python, including many\nNumPy functions. Additionally, Numba has support for automatic\nparallelization of loops, generation of GPU-accelerated code, and creation of\nufuncs and C callbacks.\n\nFor more information about Numba, see the Numba homepage:\nhttps://numba.pydata.org and the online documentation:\nhttps://numba.readthedocs.io/en/stable/index.html\n\nInstallation\n============\n\nPlease follow the instructions:\n\nhttps://numba.readthedocs.io/en/stable/user/installing.html\n\nDemo\n====\n\nPlease have a look and the demo notebooks via the mybinder service:\n\nhttps://mybinder.org/v2/gh/numba/numba-examples/master?filepath=notebooks\n\nContact\n=======\n\nNumba has a discourse forum for discussions:\n\n* https://numba.discourse.group\n\n\n\n", + "description_content_type": "", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "https://numba.pydata.org", + "keywords": "", + "license": "BSD", + "maintainer": "", + "maintainer_email": "", + "name": "numba", + "package_url": "https://pypi.org/project/numba/", + "platform": null, + "project_url": "https://pypi.org/project/numba/", + "project_urls": { + "Homepage": "https://numba.pydata.org" + }, + "release_url": "https://pypi.org/project/numba/0.56.0/", + "requires_dist": [ + "llvmlite (<0.40,>=0.39.0dev0)", + "numpy (<1.23,>=1.18)", + "setuptools", + "importlib-metadata ; python_version < \"3.9\"" + ], + "requires_python": ">=3.7", + "summary": "compiling Python code using LLVM", + "version": "0.56.0", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_oslo.context-3.4.0.json b/test/fixtures/pypi/registryData-info_oslo.context-3.4.0.json new file mode 100644 index 00000000..4a8dffed --- /dev/null +++ b/test/fixtures/pypi/registryData-info_oslo.context-3.4.0.json @@ -0,0 +1,51 @@ +{ + "info": { + "author": "OpenStack", + "author_email": "openstack-discuss@lists.openstack.org", + "bugtrack_url": null, + "classifiers": [ + "Environment :: OpenStack", + "Intended Audience :: Information Technology", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython" + ], + "description": "====================\nOslo Context Library\n====================\n\nThe Oslo context library has helpers to maintain useful information\nabout a request context. The request context is usually populated in\nthe WSGI pipeline and used by various modules such as logging.\n\n* License: Apache License, Version 2.0\n* Documentation: https://docs.openstack.org/oslo.context/latest/\n* Source: https://opendev.org/openstack/oslo.context\n* Bugs: https://bugs.launchpad.net/oslo.context\n* Release notes: https://docs.openstack.org/releasenotes/oslo.context/\n\nTeam and repository tags\n========================\n\n.. image:: https://governance.openstack.org/tc/badges/oslo.context.svg\n :target: https://governance.openstack.org/tc/reference/tags/index.html\n\n.. Change things from this point on\n\n.. image:: https://img.shields.io/pypi/v/oslo.context.svg\n :target: https://pypi.org/project/oslo.context/\n :alt: Latest Version\n\n.. image:: https://img.shields.io/pypi/dm/oslo.context.svg\n :target: https://pypi.org/project/oslo.context/\n :alt: Downloads\n\n\n\n", + "description_content_type": "", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "https://docs.openstack.org/oslo.context/latest/", + "keywords": "", + "license": "", + "maintainer": "", + "maintainer_email": "", + "name": "oslo.context", + "package_url": "https://pypi.org/project/oslo.context/", + "platform": null, + "project_url": "https://pypi.org/project/oslo.context/", + "project_urls": { + "Homepage": "https://docs.openstack.org/oslo.context/latest/" + }, + "release_url": "https://pypi.org/project/oslo.context/5.1.1/", + "requires_dist": [ + "debtcollector (>=1.2.0)", + "pbr (!=2.1.0,>=2.0.0)" + ], + "requires_python": ">=3.8", + "summary": "Oslo Context library", + "version": "5.1.1", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_platformdirs-4.2.0.json b/test/fixtures/pypi/registryData-info_platformdirs-4.2.0.json new file mode 100644 index 00000000..94c2f518 --- /dev/null +++ b/test/fixtures/pypi/registryData-info_platformdirs-4.2.0.json @@ -0,0 +1,67 @@ +{ + "info": { + "author": null, + "author_email": null, + "bugtrack_url": null, + "classifiers": [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Software Development :: Libraries :: Python Modules" + ], + "description": "The problem\n===========\n\n.. image:: https://github.com/platformdirs/platformdirs/actions/workflows/check.yml/badge.svg\n :target: https://github.com/platformdirs/platformdirs/actions\n\nWhen writing desktop application, finding the right location to store user data\nand configuration varies per platform. Even for single-platform apps, there\nmay by plenty of nuances in figuring out the right location.\n\nFor example, if running on macOS, you should use::\n\n ~/Library/Application Support/\n\nIf on Windows (at least English Win) that should be::\n\n C:\\Documents and Settings\\\\Application Data\\Local Settings\\\\\n\nor possibly::\n\n C:\\Documents and Settings\\\\Application Data\\\\\n\nfor `roaming profiles `_ but that is another story.\n\nOn Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be::\n\n ~/.local/share/\n\n.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html\n\n``platformdirs`` to the rescue\n==============================\n\nThis kind of thing is what the ``platformdirs`` package is for.\n``platformdirs`` will help you choose an appropriate:\n\n- user data dir (``user_data_dir``)\n- user config dir (``user_config_dir``)\n- user cache dir (``user_cache_dir``)\n- site data dir (``site_data_dir``)\n- site config dir (``site_config_dir``)\n- user log dir (``user_log_dir``)\n- user documents dir (``user_documents_dir``)\n- user downloads dir (``user_downloads_dir``)\n- user pictures dir (``user_pictures_dir``)\n- user videos dir (``user_videos_dir``)\n- user music dir (``user_music_dir``)\n- user desktop dir (``user_desktop_dir``)\n- user runtime dir (``user_runtime_dir``)\n\nAnd also:\n\n- Is slightly opinionated on the directory names used. Look for \"OPINION\" in\n documentation and code for when an opinion is being applied.\n\nExample output\n==============\n\nOn macOS:\n\n.. code-block:: pycon\n\n >>> from platformdirs import *\n >>> appname = \"SuperApp\"\n >>> appauthor = \"Acme\"\n >>> user_data_dir(appname, appauthor)\n '/Users/trentm/Library/Application Support/SuperApp'\n >>> site_data_dir(appname, appauthor)\n '/Library/Application Support/SuperApp'\n >>> user_cache_dir(appname, appauthor)\n '/Users/trentm/Library/Caches/SuperApp'\n >>> user_log_dir(appname, appauthor)\n '/Users/trentm/Library/Logs/SuperApp'\n >>> user_documents_dir()\n '/Users/trentm/Documents'\n >>> user_downloads_dir()\n '/Users/trentm/Downloads'\n >>> user_pictures_dir()\n '/Users/trentm/Pictures'\n >>> user_videos_dir()\n '/Users/trentm/Movies'\n >>> user_music_dir()\n '/Users/trentm/Music'\n >>> user_desktop_dir()\n '/Users/trentm/Desktop'\n >>> user_runtime_dir(appname, appauthor)\n '/Users/trentm/Library/Caches/TemporaryItems/SuperApp'\n\nOn Windows:\n\n.. code-block:: pycon\n\n >>> from platformdirs import *\n >>> appname = \"SuperApp\"\n >>> appauthor = \"Acme\"\n >>> user_data_dir(appname, appauthor)\n 'C:\\\\Users\\\\trentm\\\\AppData\\\\Local\\\\Acme\\\\SuperApp'\n >>> user_data_dir(appname, appauthor, roaming=True)\n 'C:\\\\Users\\\\trentm\\\\AppData\\\\Roaming\\\\Acme\\\\SuperApp'\n >>> user_cache_dir(appname, appauthor)\n 'C:\\\\Users\\\\trentm\\\\AppData\\\\Local\\\\Acme\\\\SuperApp\\\\Cache'\n >>> user_log_dir(appname, appauthor)\n 'C:\\\\Users\\\\trentm\\\\AppData\\\\Local\\\\Acme\\\\SuperApp\\\\Logs'\n >>> user_documents_dir()\n 'C:\\\\Users\\\\trentm\\\\Documents'\n >>> user_downloads_dir()\n 'C:\\\\Users\\\\trentm\\\\Downloads'\n >>> user_pictures_dir()\n 'C:\\\\Users\\\\trentm\\\\Pictures'\n >>> user_videos_dir()\n 'C:\\\\Users\\\\trentm\\\\Videos'\n >>> user_music_dir()\n 'C:\\\\Users\\\\trentm\\\\Music'\n >>> user_desktop_dir()\n 'C:\\\\Users\\\\trentm\\\\Desktop'\n >>> user_runtime_dir(appname, appauthor)\n 'C:\\\\Users\\\\trentm\\\\AppData\\\\Local\\\\Temp\\\\Acme\\\\SuperApp'\n\nOn Linux:\n\n.. code-block:: pycon\n\n >>> from platformdirs import *\n >>> appname = \"SuperApp\"\n >>> appauthor = \"Acme\"\n >>> user_data_dir(appname, appauthor)\n '/home/trentm/.local/share/SuperApp'\n >>> site_data_dir(appname, appauthor)\n '/usr/local/share/SuperApp'\n >>> site_data_dir(appname, appauthor, multipath=True)\n '/usr/local/share/SuperApp:/usr/share/SuperApp'\n >>> user_cache_dir(appname, appauthor)\n '/home/trentm/.cache/SuperApp'\n >>> user_log_dir(appname, appauthor)\n '/home/trentm/.local/state/SuperApp/log'\n >>> user_config_dir(appname)\n '/home/trentm/.config/SuperApp'\n >>> user_documents_dir()\n '/home/trentm/Documents'\n >>> user_downloads_dir()\n '/home/trentm/Downloads'\n >>> user_pictures_dir()\n '/home/trentm/Pictures'\n >>> user_videos_dir()\n '/home/trentm/Videos'\n >>> user_music_dir()\n '/home/trentm/Music'\n >>> user_desktop_dir()\n '/home/trentm/Desktop'\n >>> user_runtime_dir(appname, appauthor)\n '/run/user/{os.getuid()}/SuperApp'\n >>> site_config_dir(appname)\n '/etc/xdg/SuperApp'\n >>> os.environ[\"XDG_CONFIG_DIRS\"] = \"/etc:/usr/local/etc\"\n >>> site_config_dir(appname, multipath=True)\n '/etc/SuperApp:/usr/local/etc/SuperApp'\n\nOn Android::\n\n >>> from platformdirs import *\n >>> appname = \"SuperApp\"\n >>> appauthor = \"Acme\"\n >>> user_data_dir(appname, appauthor)\n '/data/data/com.myApp/files/SuperApp'\n >>> user_cache_dir(appname, appauthor)\n '/data/data/com.myApp/cache/SuperApp'\n >>> user_log_dir(appname, appauthor)\n '/data/data/com.myApp/cache/SuperApp/log'\n >>> user_config_dir(appname)\n '/data/data/com.myApp/shared_prefs/SuperApp'\n >>> user_documents_dir()\n '/storage/emulated/0/Documents'\n >>> user_downloads_dir()\n '/storage/emulated/0/Downloads'\n >>> user_pictures_dir()\n '/storage/emulated/0/Pictures'\n >>> user_videos_dir()\n '/storage/emulated/0/DCIM/Camera'\n >>> user_music_dir()\n '/storage/emulated/0/Music'\n >>> user_desktop_dir()\n '/storage/emulated/0/Desktop'\n >>> user_runtime_dir(appname, appauthor)\n '/data/data/com.myApp/cache/SuperApp/tmp'\n\nNote: Some android apps like Termux and Pydroid are used as shells. These\napps are used by the end user to emulate Linux environment. Presence of\n``SHELL`` environment variable is used by Platformdirs to differentiate\nbetween general android apps and android apps used as shells. Shell android\napps also support ``XDG_*`` environment variables.\n\n\n``PlatformDirs`` for convenience\n================================\n\n.. code-block:: pycon\n\n >>> from platformdirs import PlatformDirs\n >>> dirs = PlatformDirs(\"SuperApp\", \"Acme\")\n >>> dirs.user_data_dir\n '/Users/trentm/Library/Application Support/SuperApp'\n >>> dirs.site_data_dir\n '/Library/Application Support/SuperApp'\n >>> dirs.user_cache_dir\n '/Users/trentm/Library/Caches/SuperApp'\n >>> dirs.user_log_dir\n '/Users/trentm/Library/Logs/SuperApp'\n >>> dirs.user_documents_dir\n '/Users/trentm/Documents'\n >>> dirs.user_downloads_dir\n '/Users/trentm/Downloads'\n >>> dirs.user_pictures_dir\n '/Users/trentm/Pictures'\n >>> dirs.user_videos_dir\n '/Users/trentm/Movies'\n >>> dirs.user_music_dir\n '/Users/trentm/Music'\n >>> dirs.user_desktop_dir\n '/Users/trentm/Desktop'\n >>> dirs.user_runtime_dir\n '/Users/trentm/Library/Caches/TemporaryItems/SuperApp'\n\nPer-version isolation\n=====================\n\nIf you have multiple versions of your app in use that you want to be\nable to run side-by-side, then you may want version-isolation for these\ndirs::\n\n >>> from platformdirs import PlatformDirs\n >>> dirs = PlatformDirs(\"SuperApp\", \"Acme\", version=\"1.0\")\n >>> dirs.user_data_dir\n '/Users/trentm/Library/Application Support/SuperApp/1.0'\n >>> dirs.site_data_dir\n '/Library/Application Support/SuperApp/1.0'\n >>> dirs.user_cache_dir\n '/Users/trentm/Library/Caches/SuperApp/1.0'\n >>> dirs.user_log_dir\n '/Users/trentm/Library/Logs/SuperApp/1.0'\n >>> dirs.user_documents_dir\n '/Users/trentm/Documents'\n >>> dirs.user_downloads_dir\n '/Users/trentm/Downloads'\n >>> dirs.user_pictures_dir\n '/Users/trentm/Pictures'\n >>> dirs.user_videos_dir\n '/Users/trentm/Movies'\n >>> dirs.user_music_dir\n '/Users/trentm/Music'\n >>> dirs.user_desktop_dir\n '/Users/trentm/Desktop'\n >>> dirs.user_runtime_dir\n '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0'\n\nBe wary of using this for configuration files though; you'll need to handle\nmigrating configuration files manually.\n\nWhy this Fork?\n==============\n\nThis repository is a friendly fork of the wonderful work started by\n`ActiveState `_ who created\n``appdirs``, this package's ancestor.\n\nMaintaining an open source project is no easy task, particularly\nfrom within an organization, and the Python community is indebted\nto ``appdirs`` (and to Trent Mick and Jeff Rouse in particular) for\ncreating an incredibly useful simple module, as evidenced by the wide\nnumber of users it has attracted over the years.\n\nNonetheless, given the number of long-standing open issues\nand pull requests, and no clear path towards `ensuring\nthat maintenance of the package would continue or grow\n`_, this fork was\ncreated.\n\nContributions are most welcome.\n", + "description_content_type": "text/x-rst", + "docs_url": null, + "download_url": null, + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "dynamic": null, + "home_page": null, + "keywords": "appdirs, application, cache, directory, log, user", + "license": null, + "maintainer": null, + "maintainer_email": "Bernát Gábor , Julian Berman , Ofek Lev , Ronny Pfannschmidt ", + "name": "platformdirs", + "package_url": "https://pypi.org/project/platformdirs/", + "platform": null, + "project_url": "https://pypi.org/project/platformdirs/", + "project_urls": { + "Documentation": "https://platformdirs.readthedocs.io", + "Homepage": "https://github.com/platformdirs/platformdirs", + "Source": "https://github.com/platformdirs/platformdirs", + "Tracker": "https://github.com/platformdirs/platformdirs/issues" + }, + "provides_extra": null, + "release_url": "https://pypi.org/project/platformdirs/4.2.2/", + "requires_dist": [ + "furo>=2023.9.10; extra == \"docs\"", + "proselint>=0.13; extra == \"docs\"", + "sphinx-autodoc-typehints>=1.25.2; extra == \"docs\"", + "sphinx>=7.2.6; extra == \"docs\"", + "appdirs==1.4.4; extra == \"test\"", + "covdefaults>=2.3; extra == \"test\"", + "pytest-cov>=4.1; extra == \"test\"", + "pytest-mock>=3.12; extra == \"test\"", + "pytest>=7.4.3; extra == \"test\"", + "mypy>=1.8; extra == \"type\"" + ], + "requires_python": ">=3.8", + "summary": "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`.", + "version": "4.2.2", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_pygobject-3.42.0.json b/test/fixtures/pypi/registryData-info_pygobject-3.42.0.json new file mode 100644 index 00000000..0a0225b1 --- /dev/null +++ b/test/fixtures/pypi/registryData-info_pygobject-3.42.0.json @@ -0,0 +1,45 @@ +{ + "info": { + "author": "James Henstridge", + "author_email": "james@daa.com.au", + "bugtrack_url": null, + "classifiers": [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Programming Language :: C", + "Programming Language :: Python", + "Topic :: Software Development :: Libraries :: Python Modules" + ], + "description": ".. image:: https://pygobject.readthedocs.io/en/latest/_images/pygobject.svg\n :align: center\n :width: 400px\n :height: 98px\n\n|\n\n**PyGObject** is a Python package which provides bindings for `GObject\n`__ based libraries such as `GTK\n`__, `GStreamer `__,\n`WebKitGTK `__, `GLib\n`__, `GIO\n`__ and many more.\n\nIt supports Linux, Windows and macOS and works with **Python 3.7+** and\n**PyPy3**. PyGObject, including this documentation, is licensed under the\n**LGPLv2.1+**.\n\n\n----\n\nFor more information visit https://pygobject.readthedocs.io\n", + "description_content_type": "", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "https://pygobject.readthedocs.io", + "keywords": "", + "license": "GNU LGPL", + "maintainer": "Simon Feltman", + "maintainer_email": "sfeltman@src.gnome.org", + "name": "PyGObject", + "package_url": "https://pypi.org/project/PyGObject/", + "platform": "POSIX, Windows", + "project_url": "https://pypi.org/project/PyGObject/", + "project_urls": { + "Homepage": "https://pygobject.readthedocs.io" + }, + "release_url": "https://pypi.org/project/PyGObject/3.44.1/", + "requires_dist": null, + "requires_python": ">=3.7, <4", + "summary": "Python bindings for GObject Introspection", + "version": "3.44.1", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_pywin32-303.json b/test/fixtures/pypi/registryData-info_pywin32-303.json new file mode 100644 index 00000000..f3acab6c --- /dev/null +++ b/test/fixtures/pypi/registryData-info_pywin32-303.json @@ -0,0 +1,50 @@ +{ + "info": { + "author": "Mark Hammond (et al)", + "author_email": "mhammond@skippinet.com.au", + "bugtrack_url": null, + "classifiers": [ + "Environment :: Win32 (MS Windows)", + "Intended Audience :: Developers", + "License :: OSI Approved :: Python Software Foundation License", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython" + ], + "description": "# pywin32\r\n\r\n[![CI](https://github.com/mhammond/pywin32/workflows/CI/badge.svg)](https://github.com/mhammond/pywin32/actions?query=workflow%3ACI)\r\n[![PyPI - Version](https://img.shields.io/pypi/v/pywin32.svg)](https://pypi.org/project/pywin32)\r\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pywin32.svg)](https://pypi.org/project/pywin32)\r\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/pywin32.svg)](https://pypi.org/project/pywin32)\r\n[![License - PSF-2.0](https://img.shields.io/badge/license-PSF--2.0-9400d3.svg)](https://spdx.org/licenses/PSF-2.0.html)\r\n\r\n-----\r\n\r\nThis is the readme for the Python for Win32 (pywin32) extensions, which provides access to many of the Windows APIs from Python.\r\n\r\nSee [CHANGES.txt](https://github.com/mhammond/pywin32/blob/master/CHANGES.txt) for recent notable changes.\r\n\r\nOnly Python 3 is supported. If you want Python 2 support, you want build `228`.\r\n\r\n## Docs\r\n\r\nThe docs are a long and sad story, but [there's now an online version](https://mhammond.github.io/pywin32/)\r\nof the helpfile that ships with the installers (thanks [@ofek](https://github.com/mhammond/pywin32/pull/1774)!).\r\nLots of that is very old, but some is auto-generated and current. Would love help untangling the docs!\r\n\r\n## Support\r\n\r\nFeel free to [open issues](https://github.com/mhammond/pywin32/issues) for\r\nall bugs (or suspected bugs) in pywin32. [pull-requests](https://github.com/mhammond/pywin32/pulls)\r\nfor all bugs or features are also welcome.\r\n\r\nHowever, please **do not open github issues for general support requests**, or\r\nfor problems or questions using the modules in this package - they will be\r\nclosed. For such issues, please email the\r\n[python-win32 mailing list](http://mail.python.org/mailman/listinfo/python-win32) -\r\nnote that you must be subscribed to the list before posting.\r\n\r\n## Binaries\r\n[Binary releases are deprecated.](https://mhammond.github.io/pywin32_installers.html)\r\nWhile they are still provided, [find them here](https://github.com/mhammond/pywin32/releases)\r\n\r\n## Installing via PIP\r\n\r\nYou should install pywin32 via pip - eg,\r\n> python -m pip install --upgrade pywin32\r\n\r\nIf you encounter any problems when upgrading (eg, \"module not found\" errors or similar), you\r\nshould execute:\r\n\r\n> python Scripts/pywin32_postinstall.py -install\r\n\r\nThis will make some small attempts to cleanup older conflicting installs.\r\n\r\nNote that if you want to use pywin32 for \"system wide\" features, such as\r\nregistering COM objects or implementing Windows Services, then you must run\r\nthat command from an elevated (ie, \"Run as Administrator) command prompt.\r\n\r\nFor unreleased changes, you can download builds made by [github actions](https://github.com/mhammond/pywin32/actions/) -\r\nchoose any \"workflow\" from the `main` branch and download its \"artifacts\")\r\n\r\n### `The specified procedure could not be found` / `Entry-point not found` Errors?\r\nA very common report is that people install pywin32, but many imports fail with errors\r\nsimilar to the above.\r\n\r\nIn almost all cases, this tends to mean there are other pywin32 DLLs installed in your system,\r\nbut in a different location than the new ones. This sometimes happens in environments that\r\ncome with pywin32 pre-shipped (eg, anaconda?).\r\n\r\nThe possible solutions are:\r\n\r\n* Run the \"post_install\" script documented above.\r\n\r\n* Otherwise, find and remove all other copies of `pywintypesXX.dll` and `pythoncomXX.dll`\r\n (where `XX` is the Python version - eg, \"39\")\r\n\r\n### Running as a Windows Service\r\n\r\nModern Python installers do not, by default, install Python in a way that is suitable for\r\nrunning as a service, particularly for other users.\r\n\r\n* Ensure Python is installed in a location where the user running the service has\r\n access to the installation and is able to load `pywintypesXX.dll` and `pythonXX.dll`.\r\n\r\n* Manually copy `pythonservice.exe` from the `site-packages/win32` directory to\r\n the same place as these DLLs.\r\n\r\n## Building from source\r\n\r\nInstall Visual Studio 2019 (later probably works, but options might be different),\r\nselect \"Desktop Development with C++\", then the following options:\r\n* Windows 10 SDK (latest offered I guess? At time of writing, 10.0.18362)\r\n* \"C++ for MFC for ...\"\r\n* ARM build tools if necessary.\r\n\r\n(the free compilers probably work too, but haven't been tested - let me know your experiences!)\r\n\r\n`setup.py` is a standard distutils build script, so you probably want:\r\n\r\n> python setup.py install\r\n\r\nor\r\n\r\n> python setup.py --help\r\n\r\nSome modules need obscure SDKs to build - `setup.py` should succeed, gracefully\r\ntelling you why it failed to build them - if the build actually fails with your\r\nconfiguration, please [open an issue](https://github.com/mhammond/pywin32/issues).\r\n\r\n## Release process\r\n\r\nThe following steps are performed when making a new release - this is mainly\r\nto form a checklist so mhammond doesn't forget what to do :)\r\n\r\n* Ensure CHANGES.txt has everything worth noting, commit it.\r\n\r\n* Update setup.py with the new build number.\r\n\r\n* Execute build.bat, wait forever, test the artifacts.\r\n\r\n* Upload .whl artifacts to pypi - we do this before pushing the tag because they might be\r\n rejected for an invalid `README.md`. Done via `py -3.? -m twine upload dist/*XXX*.whl`.\r\n\r\n* Commit setup.py (so the new build number is in the repo), create a new git tag\r\n\r\n* Upload the .exe installers to github.\r\n\r\n* Update setup.py with the new build number + \".1\" (eg, 123.1), to ensure\r\n future test builds aren't mistaken for the real release.\r\n\r\n* Make sure everything is pushed to github, including the tag (ie,\r\n `git push --tags`)\r\n\r\n* Send mail to python-win32\r\n", + "description_content_type": "text/markdown", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "dynamic": null, + "home_page": "https://github.com/mhammond/pywin32", + "keywords": "", + "license": "PSF", + "maintainer": "", + "maintainer_email": "", + "name": "pywin32", + "package_url": "https://pypi.org/project/pywin32/", + "platform": null, + "project_url": "https://pypi.org/project/pywin32/", + "project_urls": { + "Homepage": "https://github.com/mhammond/pywin32" + }, + "provides_extra": null, + "release_url": "https://pypi.org/project/pywin32/306/", + "requires_dist": null, + "requires_python": "", + "summary": "Python for Window Extensions", + "version": "306", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/fixtures/pypi/registryData-info_sacremoses-0.0.51.json b/test/fixtures/pypi/registryData-info_sacremoses-0.0.51.json new file mode 100644 index 00000000..6810a877 --- /dev/null +++ b/test/fixtures/pypi/registryData-info_sacremoses-0.0.51.json @@ -0,0 +1,40 @@ +{ + "info": { + "author": "", + "author_email": "", + "bugtrack_url": null, + "classifiers": [ + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3" + ], + "description": "MosesTokenizer in Python\n\n", + "description_content_type": "", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "https://github.com/alvations/sacremoses", + "keywords": "", + "license": "", + "maintainer": "", + "maintainer_email": "", + "name": "sacremoses", + "package_url": "https://pypi.org/project/sacremoses/", + "platform": null, + "project_url": "https://pypi.org/project/sacremoses/", + "project_urls": { + "Homepage": "https://github.com/alvations/sacremoses" + }, + "release_url": "https://pypi.org/project/sacremoses/0.0.53/", + "requires_dist": null, + "requires_python": "", + "summary": "SacreMoses", + "version": "0.0.53", + "yanked": false, + "yanked_reason": null + } +} \ No newline at end of file diff --git a/test/unit/providers/fetch/pypiFetchTests.js b/test/unit/providers/fetch/pypiFetchTests.js index 2d6e2da5..8e8dc8f7 100644 --- a/test/unit/providers/fetch/pypiFetchTests.js +++ b/test/unit/providers/fetch/pypiFetchTests.js @@ -67,74 +67,138 @@ describe('pypiFetch handle function', () => { expect(result.outcome).to.be.equal('Missing ') }) - it('parses the correct license information from classifiers in registry data', () => { - const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData_lgpl2.json')) - const declared = fetch._extractDeclaredLicense(registryData) - expect(declared).to.be.equal('LGPL-2.0-only') - }) + describe('extractDeclaredLicense', () => { + it('parses LGPL-2.1-only: info.license over classifiers (crawler/issues/523)', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData_lgpl2.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('LGPL-2.1-only') + }) - it('parses the correct license information from info.license in registry data', () => { - const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData_dnspython.json')) - const declared = fetch._extractDeclaredLicense(registryData) - expect(declared).to.be.equal('ISC') - }) + it('parses the correct license information from info.license in registry data', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData_dnspython.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('ISC') + }) + + it('parses BSD-3-Clause for UpSetPlot/0.9.0: info.license over classifiers (crawler/issues/523)', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_bsd3.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('BSD-3-Clause') + }) + + it('parses GPL-2.0-or-later for pylint/3.2.3: info.license over classifiers (curated-data/pull/27902#issuecomment-2191907126)', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_gpl2+.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('GPL-2.0-or-later') + }) + + it('parses HPND for pillow/8.3.0: info.license over classifiers (crawler/issues/519, crawler/issues/429)', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_hpnd.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('HPND') + }) + + it('parses the correct license information from classifier for platformdirs, license: null', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_platformdirs-4.2.0.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('MIT') + }) + + it('parses the correct license information from classifier for sacremoses: license: ""', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_sacremoses-0.0.51.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('MIT') + }) + + it('parses the correct license information from classifier for chardet, license: LGPL, classifier: LGPLv2+', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_chardet-5.1.0.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('LGPL-2.0-or-later') + }) + + it('parses the correct license information from classifier for pygobject, license: GNU LGPL, classifier: LGPLv2+', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_pygobject-3.42.0.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('LGPL-2.0-or-later') + }) + + it('parses the correct license information from classifier for oslo.context: license: "", classifier: Apache Software License', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_oslo.context-3.4.0.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('Apache-2.0') + }) - it('parses correct LGPL license information', () => { - // See https://github.com/jslicense/spdx-correct.js/blob/main/test.js - const conversions = { - 'GNU LESSER GENERAL PUBLIC LICENSE': 'LGPL-2.1-only', - 'LESSER GENERAL PUBLIC LICENSE': 'LGPL-2.1-only', - 'GNU Lesser General Public License v2.1': 'LGPL-2.1-only', - 'GNU Lesser General Public License v3': 'LGPL-3.0-or-later', - 'GNU Lesser General Public License v2': 'LGPL-2.0-only', - 'GNU Lesser General Public License v2.0': 'LGPL-2.0-only', - 'GNU Lesser General Public License v3.0': 'LGPL-3.0-or-later', - 'GNU LGPL v3.0': 'LGPL-3.0-or-later', - '(LGPL)': 'LGPL-3.0-or-later', - LGLP3: 'LGPL-3.0-or-later', - 'LGPL 2.1': 'LGPL-2.1-only', - 'LGPL 3': 'LGPL-3.0-or-later', - 'LGPL 3.0': 'LGPL-3.0-or-later', - 'LGPL Version 3.0': 'LGPL-3.0-or-later', - 'LGPL v2': 'LGPL-2.0-only', - 'LGPL v2+': 'LGPL-2.0-or-later', - 'LGPL v3': 'LGPL-3.0-or-later', - LGPL: 'LGPL-3.0-or-later', - 'LGPL-2': 'LGPL-2.0-only', - 'LGPL-3': 'LGPL-3.0-or-later', - 'LGPL.v3': 'LGPL-3.0-or-later', - LGPL2: 'LGPL-2.0-only', - 'LGPL2.1': 'LGPL-2.1-only', - 'LGPL2.1+': 'LGPL-2.1-or-later', - LGPL3: 'LGPL-3.0-or-later', - 'LGPL3+': 'LGPL-3.0-or-later', - 'LGPL3.0': 'LGPL-3.0-or-later', - 'LGPL:': 'LGPL-3.0-or-later', - 'LGPLv2.1': 'LGPL-2.1-only', - LGPLv3: 'LGPL-3.0-or-later', - 'LGPLv3+': 'LGPL-3.0-or-later', - 'LGPL-2.0+': 'LGPL-2.0-or-later', - 'LGPL-2.1+': 'LGPL-2.1-or-later', - 'LGPL-3.0+': 'LGPL-3.0-or-later' - } - for (const [key, value] of Object.entries(conversions)) { - expect(spdxCorrect(key)).to.be.equal(value) - } + it('parses the correct license information for pywin32: PSF', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_pywin32-303.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('PSF-2.0') + }) + + it('parses the correct license information for numba: BSD in license and classifier', () => { + const registryData = JSON.parse(fs.readFileSync('test/fixtures/pypi/registryData-info_numba-0.56.0.json')) + const declared = fetch._extractDeclaredLicense(registryData) + expect(declared).to.be.equal('BSD-2-Clause') + }) }) - it('parses the correct license information with patching', () => { - let declared = spdxCorrect('GNU Lesser General Public License v2 (LGPLv2)') - expect(declared).to.be.equal('LGPL-2.0-only') + describe('test spdxCorrect', () => { + it('parses correct LGPL license information', () => { + // See https://github.com/jslicense/spdx-correct.js/blob/main/test.js + const conversions = { + 'GNU LESSER GENERAL PUBLIC LICENSE': 'LGPL-2.1-only', + 'LESSER GENERAL PUBLIC LICENSE': 'LGPL-2.1-only', + 'GNU Lesser General Public License v2.1': 'LGPL-2.1-only', + 'GNU Lesser General Public License v3': 'LGPL-3.0-or-later', + 'GNU Lesser General Public License v2': 'LGPL-2.0-only', + 'GNU Lesser General Public License v2.0': 'LGPL-2.0-only', + 'GNU Lesser General Public License v3.0': 'LGPL-3.0-or-later', + 'GNU LGPL v3.0': 'LGPL-3.0-or-later', + '(LGPL)': 'LGPL-3.0-or-later', + LGLP3: 'LGPL-3.0-or-later', + 'LGPL 2.1': 'LGPL-2.1-only', + 'LGPL 3': 'LGPL-3.0-or-later', + 'LGPL 3.0': 'LGPL-3.0-or-later', + 'LGPL Version 3.0': 'LGPL-3.0-or-later', + 'LGPL v2': 'LGPL-2.0-only', + 'LGPL v2+': 'LGPL-2.0-or-later', + 'LGPL v3': 'LGPL-3.0-or-later', + LGPL: 'LGPL-3.0-or-later', + 'LGPL-2': 'LGPL-2.0-only', + 'LGPL-3': 'LGPL-3.0-or-later', + 'LGPL.v3': 'LGPL-3.0-or-later', + LGPL2: 'LGPL-2.0-only', + 'LGPL2.1': 'LGPL-2.1-only', + 'LGPL2.1+': 'LGPL-2.1-or-later', + LGPL3: 'LGPL-3.0-or-later', + 'LGPL3+': 'LGPL-3.0-or-later', + 'LGPL3.0': 'LGPL-3.0-or-later', + 'LGPL:': 'LGPL-3.0-or-later', + 'LGPLv2.1': 'LGPL-2.1-only', + LGPLv3: 'LGPL-3.0-or-later', + 'LGPLv3+': 'LGPL-3.0-or-later', + 'LGPL-2.0+': 'LGPL-2.0-or-later', + 'LGPL-2.1+': 'LGPL-2.1-or-later', + 'LGPL-3.0+': 'LGPL-3.0-or-later' + } + for (const [key, value] of Object.entries(conversions)) { + expect(spdxCorrect(key)).to.be.equal(value) + } + }) + + it('parses the correct license information with patching', () => { + let declared = spdxCorrect('GNU Lesser General Public License v2 (LGPLv2)') + expect(declared).to.be.equal('LGPL-2.0-only') - declared = spdxCorrect('GNU Lesser General Public License v2 or later (LGPLv2+)') - expect(declared).to.be.equal('LGPL-2.0-or-later') + declared = spdxCorrect('GNU Lesser General Public License v2 or later (LGPLv2+)') + expect(declared).to.be.equal('LGPL-2.0-or-later') - //Previously patched cases - declared = spdxCorrect('GNU Lesser General Public License v3 (LGPLv3)') - expect(declared).to.be.equal('LGPL-3.0-or-later') + //Previously patched cases + declared = spdxCorrect('GNU Lesser General Public License v3 (LGPLv3)') + expect(declared).to.be.equal('LGPL-3.0-or-later') - declared = spdxCorrect('GNU LGPL') - expect(declared).to.be.equal('LGPL-3.0-or-later') + declared = spdxCorrect('GNU LGPL') + expect(declared).to.be.equal('LGPL-3.0-or-later') + }) }) })