diff --git a/doc/pyproject_toml.rst b/doc/pyproject_toml.rst index 9620ce75..f6755a87 100644 --- a/doc/pyproject_toml.rst +++ b/doc/pyproject_toml.rst @@ -96,7 +96,8 @@ requires-python A version specifier for the versions of Python this requires, e.g. ``~=3.3`` or ``>=3.3,<4``, which are equivalents. license - A table with either a ``file`` key (a relative path to a license file) or a + A valid SPDX `license expression `_ + or a table with either a ``file`` key (a relative path to a license file) or a ``text`` key (the license text). license-files A list of glob patterns for license files to include. diff --git a/flit_core/flit_core/_spdx_data.py b/flit_core/flit_core/_spdx_data.py new file mode 100644 index 00000000..5af71dbb --- /dev/null +++ b/flit_core/flit_core/_spdx_data.py @@ -0,0 +1,651 @@ +# This file is generated from SPDX license data; don't edit it manually. + +licenses = \ +{'0bsd': {'id': '0BSD'}, + '3d-slicer-1.0': {'id': '3D-Slicer-1.0'}, + 'aal': {'id': 'AAL'}, + 'abstyles': {'id': 'Abstyles'}, + 'adacore-doc': {'id': 'AdaCore-doc'}, + 'adobe-2006': {'id': 'Adobe-2006'}, + 'adobe-display-postscript': {'id': 'Adobe-Display-PostScript'}, + 'adobe-glyph': {'id': 'Adobe-Glyph'}, + 'adobe-utopia': {'id': 'Adobe-Utopia'}, + 'adsl': {'id': 'ADSL'}, + 'afl-1.1': {'id': 'AFL-1.1'}, + 'afl-1.2': {'id': 'AFL-1.2'}, + 'afl-2.0': {'id': 'AFL-2.0'}, + 'afl-2.1': {'id': 'AFL-2.1'}, + 'afl-3.0': {'id': 'AFL-3.0'}, + 'afmparse': {'id': 'Afmparse'}, + 'agpl-1.0-only': {'id': 'AGPL-1.0-only'}, + 'agpl-1.0-or-later': {'id': 'AGPL-1.0-or-later'}, + 'agpl-3.0-only': {'id': 'AGPL-3.0-only'}, + 'agpl-3.0-or-later': {'id': 'AGPL-3.0-or-later'}, + 'aladdin': {'id': 'Aladdin'}, + 'amd-newlib': {'id': 'AMD-newlib'}, + 'amdplpa': {'id': 'AMDPLPA'}, + 'aml': {'id': 'AML'}, + 'aml-glslang': {'id': 'AML-glslang'}, + 'ampas': {'id': 'AMPAS'}, + 'antlr-pd': {'id': 'ANTLR-PD'}, + 'antlr-pd-fallback': {'id': 'ANTLR-PD-fallback'}, + 'any-osi': {'id': 'any-OSI'}, + 'any-osi-perl-modules': {'id': 'any-OSI-perl-modules'}, + 'apache-1.0': {'id': 'Apache-1.0'}, + 'apache-1.1': {'id': 'Apache-1.1'}, + 'apache-2.0': {'id': 'Apache-2.0'}, + 'apafml': {'id': 'APAFML'}, + 'apl-1.0': {'id': 'APL-1.0'}, + 'app-s2p': {'id': 'App-s2p'}, + 'apsl-1.0': {'id': 'APSL-1.0'}, + 'apsl-1.1': {'id': 'APSL-1.1'}, + 'apsl-1.2': {'id': 'APSL-1.2'}, + 'apsl-2.0': {'id': 'APSL-2.0'}, + 'arphic-1999': {'id': 'Arphic-1999'}, + 'artistic-1.0': {'id': 'Artistic-1.0'}, + 'artistic-1.0-cl8': {'id': 'Artistic-1.0-cl8'}, + 'artistic-1.0-perl': {'id': 'Artistic-1.0-Perl'}, + 'artistic-2.0': {'id': 'Artistic-2.0'}, + 'aswf-digital-assets-1.0': {'id': 'ASWF-Digital-Assets-1.0'}, + 'aswf-digital-assets-1.1': {'id': 'ASWF-Digital-Assets-1.1'}, + 'baekmuk': {'id': 'Baekmuk'}, + 'bahyph': {'id': 'Bahyph'}, + 'barr': {'id': 'Barr'}, + 'bcrypt-solar-designer': {'id': 'bcrypt-Solar-Designer'}, + 'beerware': {'id': 'Beerware'}, + 'bitstream-charter': {'id': 'Bitstream-Charter'}, + 'bitstream-vera': {'id': 'Bitstream-Vera'}, + 'bittorrent-1.0': {'id': 'BitTorrent-1.0'}, + 'bittorrent-1.1': {'id': 'BitTorrent-1.1'}, + 'blessing': {'id': 'blessing'}, + 'blueoak-1.0.0': {'id': 'BlueOak-1.0.0'}, + 'boehm-gc': {'id': 'Boehm-GC'}, + 'boehm-gc-without-fee': {'id': 'Boehm-GC-without-fee'}, + 'borceux': {'id': 'Borceux'}, + 'brian-gladman-2-clause': {'id': 'Brian-Gladman-2-Clause'}, + 'brian-gladman-3-clause': {'id': 'Brian-Gladman-3-Clause'}, + 'bsd-1-clause': {'id': 'BSD-1-Clause'}, + 'bsd-2-clause': {'id': 'BSD-2-Clause'}, + 'bsd-2-clause-darwin': {'id': 'BSD-2-Clause-Darwin'}, + 'bsd-2-clause-first-lines': {'id': 'BSD-2-Clause-first-lines'}, + 'bsd-2-clause-patent': {'id': 'BSD-2-Clause-Patent'}, + 'bsd-2-clause-views': {'id': 'BSD-2-Clause-Views'}, + 'bsd-3-clause': {'id': 'BSD-3-Clause'}, + 'bsd-3-clause-acpica': {'id': 'BSD-3-Clause-acpica'}, + 'bsd-3-clause-attribution': {'id': 'BSD-3-Clause-Attribution'}, + 'bsd-3-clause-clear': {'id': 'BSD-3-Clause-Clear'}, + 'bsd-3-clause-flex': {'id': 'BSD-3-Clause-flex'}, + 'bsd-3-clause-hp': {'id': 'BSD-3-Clause-HP'}, + 'bsd-3-clause-lbnl': {'id': 'BSD-3-Clause-LBNL'}, + 'bsd-3-clause-modification': {'id': 'BSD-3-Clause-Modification'}, + 'bsd-3-clause-no-military-license': {'id': 'BSD-3-Clause-No-Military-License'}, + 'bsd-3-clause-no-nuclear-license': {'id': 'BSD-3-Clause-No-Nuclear-License'}, + 'bsd-3-clause-no-nuclear-license-2014': {'id': 'BSD-3-Clause-No-Nuclear-License-2014'}, + 'bsd-3-clause-no-nuclear-warranty': {'id': 'BSD-3-Clause-No-Nuclear-Warranty'}, + 'bsd-3-clause-open-mpi': {'id': 'BSD-3-Clause-Open-MPI'}, + 'bsd-3-clause-sun': {'id': 'BSD-3-Clause-Sun'}, + 'bsd-4-clause': {'id': 'BSD-4-Clause'}, + 'bsd-4-clause-shortened': {'id': 'BSD-4-Clause-Shortened'}, + 'bsd-4-clause-uc': {'id': 'BSD-4-Clause-UC'}, + 'bsd-4.3reno': {'id': 'BSD-4.3RENO'}, + 'bsd-4.3tahoe': {'id': 'BSD-4.3TAHOE'}, + 'bsd-advertising-acknowledgement': {'id': 'BSD-Advertising-Acknowledgement'}, + 'bsd-attribution-hpnd-disclaimer': {'id': 'BSD-Attribution-HPND-disclaimer'}, + 'bsd-inferno-nettverk': {'id': 'BSD-Inferno-Nettverk'}, + 'bsd-protection': {'id': 'BSD-Protection'}, + 'bsd-source-beginning-file': {'id': 'BSD-Source-beginning-file'}, + 'bsd-source-code': {'id': 'BSD-Source-Code'}, + 'bsd-systemics': {'id': 'BSD-Systemics'}, + 'bsd-systemics-w3works': {'id': 'BSD-Systemics-W3Works'}, + 'bsl-1.0': {'id': 'BSL-1.0'}, + 'busl-1.1': {'id': 'BUSL-1.1'}, + 'bzip2-1.0.6': {'id': 'bzip2-1.0.6'}, + 'c-uda-1.0': {'id': 'C-UDA-1.0'}, + 'cal-1.0': {'id': 'CAL-1.0'}, + 'cal-1.0-combined-work-exception': {'id': 'CAL-1.0-Combined-Work-Exception'}, + 'caldera': {'id': 'Caldera'}, + 'caldera-no-preamble': {'id': 'Caldera-no-preamble'}, + 'catharon': {'id': 'Catharon'}, + 'catosl-1.1': {'id': 'CATOSL-1.1'}, + 'cc-by-1.0': {'id': 'CC-BY-1.0'}, + 'cc-by-2.0': {'id': 'CC-BY-2.0'}, + 'cc-by-2.5': {'id': 'CC-BY-2.5'}, + 'cc-by-2.5-au': {'id': 'CC-BY-2.5-AU'}, + 'cc-by-3.0': {'id': 'CC-BY-3.0'}, + 'cc-by-3.0-at': {'id': 'CC-BY-3.0-AT'}, + 'cc-by-3.0-au': {'id': 'CC-BY-3.0-AU'}, + 'cc-by-3.0-de': {'id': 'CC-BY-3.0-DE'}, + 'cc-by-3.0-igo': {'id': 'CC-BY-3.0-IGO'}, + 'cc-by-3.0-nl': {'id': 'CC-BY-3.0-NL'}, + 'cc-by-3.0-us': {'id': 'CC-BY-3.0-US'}, + 'cc-by-4.0': {'id': 'CC-BY-4.0'}, + 'cc-by-nc-1.0': {'id': 'CC-BY-NC-1.0'}, + 'cc-by-nc-2.0': {'id': 'CC-BY-NC-2.0'}, + 'cc-by-nc-2.5': {'id': 'CC-BY-NC-2.5'}, + 'cc-by-nc-3.0': {'id': 'CC-BY-NC-3.0'}, + 'cc-by-nc-3.0-de': {'id': 'CC-BY-NC-3.0-DE'}, + 'cc-by-nc-4.0': {'id': 'CC-BY-NC-4.0'}, + 'cc-by-nc-nd-1.0': {'id': 'CC-BY-NC-ND-1.0'}, + 'cc-by-nc-nd-2.0': {'id': 'CC-BY-NC-ND-2.0'}, + 'cc-by-nc-nd-2.5': {'id': 'CC-BY-NC-ND-2.5'}, + 'cc-by-nc-nd-3.0': {'id': 'CC-BY-NC-ND-3.0'}, + 'cc-by-nc-nd-3.0-de': {'id': 'CC-BY-NC-ND-3.0-DE'}, + 'cc-by-nc-nd-3.0-igo': {'id': 'CC-BY-NC-ND-3.0-IGO'}, + 'cc-by-nc-nd-4.0': {'id': 'CC-BY-NC-ND-4.0'}, + 'cc-by-nc-sa-1.0': {'id': 'CC-BY-NC-SA-1.0'}, + 'cc-by-nc-sa-2.0': {'id': 'CC-BY-NC-SA-2.0'}, + 'cc-by-nc-sa-2.0-de': {'id': 'CC-BY-NC-SA-2.0-DE'}, + 'cc-by-nc-sa-2.0-fr': {'id': 'CC-BY-NC-SA-2.0-FR'}, + 'cc-by-nc-sa-2.0-uk': {'id': 'CC-BY-NC-SA-2.0-UK'}, + 'cc-by-nc-sa-2.5': {'id': 'CC-BY-NC-SA-2.5'}, + 'cc-by-nc-sa-3.0': {'id': 'CC-BY-NC-SA-3.0'}, + 'cc-by-nc-sa-3.0-de': {'id': 'CC-BY-NC-SA-3.0-DE'}, + 'cc-by-nc-sa-3.0-igo': {'id': 'CC-BY-NC-SA-3.0-IGO'}, + 'cc-by-nc-sa-4.0': {'id': 'CC-BY-NC-SA-4.0'}, + 'cc-by-nd-1.0': {'id': 'CC-BY-ND-1.0'}, + 'cc-by-nd-2.0': {'id': 'CC-BY-ND-2.0'}, + 'cc-by-nd-2.5': {'id': 'CC-BY-ND-2.5'}, + 'cc-by-nd-3.0': {'id': 'CC-BY-ND-3.0'}, + 'cc-by-nd-3.0-de': {'id': 'CC-BY-ND-3.0-DE'}, + 'cc-by-nd-4.0': {'id': 'CC-BY-ND-4.0'}, + 'cc-by-sa-1.0': {'id': 'CC-BY-SA-1.0'}, + 'cc-by-sa-2.0': {'id': 'CC-BY-SA-2.0'}, + 'cc-by-sa-2.0-uk': {'id': 'CC-BY-SA-2.0-UK'}, + 'cc-by-sa-2.1-jp': {'id': 'CC-BY-SA-2.1-JP'}, + 'cc-by-sa-2.5': {'id': 'CC-BY-SA-2.5'}, + 'cc-by-sa-3.0': {'id': 'CC-BY-SA-3.0'}, + 'cc-by-sa-3.0-at': {'id': 'CC-BY-SA-3.0-AT'}, + 'cc-by-sa-3.0-de': {'id': 'CC-BY-SA-3.0-DE'}, + 'cc-by-sa-3.0-igo': {'id': 'CC-BY-SA-3.0-IGO'}, + 'cc-by-sa-4.0': {'id': 'CC-BY-SA-4.0'}, + 'cc-pddc': {'id': 'CC-PDDC'}, + 'cc-pdm-1.0': {'id': 'CC-PDM-1.0'}, + 'cc-sa-1.0': {'id': 'CC-SA-1.0'}, + 'cc0-1.0': {'id': 'CC0-1.0'}, + 'cddl-1.0': {'id': 'CDDL-1.0'}, + 'cddl-1.1': {'id': 'CDDL-1.1'}, + 'cdl-1.0': {'id': 'CDL-1.0'}, + 'cdla-permissive-1.0': {'id': 'CDLA-Permissive-1.0'}, + 'cdla-permissive-2.0': {'id': 'CDLA-Permissive-2.0'}, + 'cdla-sharing-1.0': {'id': 'CDLA-Sharing-1.0'}, + 'cecill-1.0': {'id': 'CECILL-1.0'}, + 'cecill-1.1': {'id': 'CECILL-1.1'}, + 'cecill-2.0': {'id': 'CECILL-2.0'}, + 'cecill-2.1': {'id': 'CECILL-2.1'}, + 'cecill-b': {'id': 'CECILL-B'}, + 'cecill-c': {'id': 'CECILL-C'}, + 'cern-ohl-1.1': {'id': 'CERN-OHL-1.1'}, + 'cern-ohl-1.2': {'id': 'CERN-OHL-1.2'}, + 'cern-ohl-p-2.0': {'id': 'CERN-OHL-P-2.0'}, + 'cern-ohl-s-2.0': {'id': 'CERN-OHL-S-2.0'}, + 'cern-ohl-w-2.0': {'id': 'CERN-OHL-W-2.0'}, + 'cfitsio': {'id': 'CFITSIO'}, + 'check-cvs': {'id': 'check-cvs'}, + 'checkmk': {'id': 'checkmk'}, + 'clartistic': {'id': 'ClArtistic'}, + 'clips': {'id': 'Clips'}, + 'cmu-mach': {'id': 'CMU-Mach'}, + 'cmu-mach-nodoc': {'id': 'CMU-Mach-nodoc'}, + 'cnri-jython': {'id': 'CNRI-Jython'}, + 'cnri-python': {'id': 'CNRI-Python'}, + 'cnri-python-gpl-compatible': {'id': 'CNRI-Python-GPL-Compatible'}, + 'coil-1.0': {'id': 'COIL-1.0'}, + 'community-spec-1.0': {'id': 'Community-Spec-1.0'}, + 'condor-1.1': {'id': 'Condor-1.1'}, + 'copyleft-next-0.3.0': {'id': 'copyleft-next-0.3.0'}, + 'copyleft-next-0.3.1': {'id': 'copyleft-next-0.3.1'}, + 'cornell-lossless-jpeg': {'id': 'Cornell-Lossless-JPEG'}, + 'cpal-1.0': {'id': 'CPAL-1.0'}, + 'cpl-1.0': {'id': 'CPL-1.0'}, + 'cpol-1.02': {'id': 'CPOL-1.02'}, + 'cronyx': {'id': 'Cronyx'}, + 'crossword': {'id': 'Crossword'}, + 'crystalstacker': {'id': 'CrystalStacker'}, + 'cua-opl-1.0': {'id': 'CUA-OPL-1.0'}, + 'cube': {'id': 'Cube'}, + 'curl': {'id': 'curl'}, + 'cve-tou': {'id': 'cve-tou'}, + 'd-fsl-1.0': {'id': 'D-FSL-1.0'}, + 'dec-3-clause': {'id': 'DEC-3-Clause'}, + 'diffmark': {'id': 'diffmark'}, + 'dl-de-by-2.0': {'id': 'DL-DE-BY-2.0'}, + 'dl-de-zero-2.0': {'id': 'DL-DE-ZERO-2.0'}, + 'doc': {'id': 'DOC'}, + 'docbook-schema': {'id': 'DocBook-Schema'}, + 'docbook-stylesheet': {'id': 'DocBook-Stylesheet'}, + 'docbook-xml': {'id': 'DocBook-XML'}, + 'dotseqn': {'id': 'Dotseqn'}, + 'drl-1.0': {'id': 'DRL-1.0'}, + 'drl-1.1': {'id': 'DRL-1.1'}, + 'dsdp': {'id': 'DSDP'}, + 'dtoa': {'id': 'dtoa'}, + 'dvipdfm': {'id': 'dvipdfm'}, + 'ecl-1.0': {'id': 'ECL-1.0'}, + 'ecl-2.0': {'id': 'ECL-2.0'}, + 'efl-1.0': {'id': 'EFL-1.0'}, + 'efl-2.0': {'id': 'EFL-2.0'}, + 'egenix': {'id': 'eGenix'}, + 'elastic-2.0': {'id': 'Elastic-2.0'}, + 'entessa': {'id': 'Entessa'}, + 'epics': {'id': 'EPICS'}, + 'epl-1.0': {'id': 'EPL-1.0'}, + 'epl-2.0': {'id': 'EPL-2.0'}, + 'erlpl-1.1': {'id': 'ErlPL-1.1'}, + 'etalab-2.0': {'id': 'etalab-2.0'}, + 'eudatagrid': {'id': 'EUDatagrid'}, + 'eupl-1.0': {'id': 'EUPL-1.0'}, + 'eupl-1.1': {'id': 'EUPL-1.1'}, + 'eupl-1.2': {'id': 'EUPL-1.2'}, + 'eurosym': {'id': 'Eurosym'}, + 'fair': {'id': 'Fair'}, + 'fbm': {'id': 'FBM'}, + 'fdk-aac': {'id': 'FDK-AAC'}, + 'ferguson-twofish': {'id': 'Ferguson-Twofish'}, + 'frameworx-1.0': {'id': 'Frameworx-1.0'}, + 'freebsd-doc': {'id': 'FreeBSD-DOC'}, + 'freeimage': {'id': 'FreeImage'}, + 'fsfap': {'id': 'FSFAP'}, + 'fsfap-no-warranty-disclaimer': {'id': 'FSFAP-no-warranty-disclaimer'}, + 'fsful': {'id': 'FSFUL'}, + 'fsfullr': {'id': 'FSFULLR'}, + 'fsfullrwd': {'id': 'FSFULLRWD'}, + 'ftl': {'id': 'FTL'}, + 'furuseth': {'id': 'Furuseth'}, + 'fwlw': {'id': 'fwlw'}, + 'gcr-docs': {'id': 'GCR-docs'}, + 'gd': {'id': 'GD'}, + 'generic-xts': {'id': 'generic-xts'}, + 'gfdl-1.1-invariants-only': {'id': 'GFDL-1.1-invariants-only'}, + 'gfdl-1.1-invariants-or-later': {'id': 'GFDL-1.1-invariants-or-later'}, + 'gfdl-1.1-no-invariants-only': {'id': 'GFDL-1.1-no-invariants-only'}, + 'gfdl-1.1-no-invariants-or-later': {'id': 'GFDL-1.1-no-invariants-or-later'}, + 'gfdl-1.1-only': {'id': 'GFDL-1.1-only'}, + 'gfdl-1.1-or-later': {'id': 'GFDL-1.1-or-later'}, + 'gfdl-1.2-invariants-only': {'id': 'GFDL-1.2-invariants-only'}, + 'gfdl-1.2-invariants-or-later': {'id': 'GFDL-1.2-invariants-or-later'}, + 'gfdl-1.2-no-invariants-only': {'id': 'GFDL-1.2-no-invariants-only'}, + 'gfdl-1.2-no-invariants-or-later': {'id': 'GFDL-1.2-no-invariants-or-later'}, + 'gfdl-1.2-only': {'id': 'GFDL-1.2-only'}, + 'gfdl-1.2-or-later': {'id': 'GFDL-1.2-or-later'}, + 'gfdl-1.3-invariants-only': {'id': 'GFDL-1.3-invariants-only'}, + 'gfdl-1.3-invariants-or-later': {'id': 'GFDL-1.3-invariants-or-later'}, + 'gfdl-1.3-no-invariants-only': {'id': 'GFDL-1.3-no-invariants-only'}, + 'gfdl-1.3-no-invariants-or-later': {'id': 'GFDL-1.3-no-invariants-or-later'}, + 'gfdl-1.3-only': {'id': 'GFDL-1.3-only'}, + 'gfdl-1.3-or-later': {'id': 'GFDL-1.3-or-later'}, + 'giftware': {'id': 'Giftware'}, + 'gl2ps': {'id': 'GL2PS'}, + 'glide': {'id': 'Glide'}, + 'glulxe': {'id': 'Glulxe'}, + 'glwtpl': {'id': 'GLWTPL'}, + 'gnuplot': {'id': 'gnuplot'}, + 'gpl-1.0-only': {'id': 'GPL-1.0-only'}, + 'gpl-1.0-or-later': {'id': 'GPL-1.0-or-later'}, + 'gpl-2.0-only': {'id': 'GPL-2.0-only'}, + 'gpl-2.0-or-later': {'id': 'GPL-2.0-or-later'}, + 'gpl-3.0-only': {'id': 'GPL-3.0-only'}, + 'gpl-3.0-or-later': {'id': 'GPL-3.0-or-later'}, + 'graphics-gems': {'id': 'Graphics-Gems'}, + 'gsoap-1.3b': {'id': 'gSOAP-1.3b'}, + 'gtkbook': {'id': 'gtkbook'}, + 'gutmann': {'id': 'Gutmann'}, + 'haskellreport': {'id': 'HaskellReport'}, + 'hdparm': {'id': 'hdparm'}, + 'hidapi': {'id': 'HIDAPI'}, + 'hippocratic-2.1': {'id': 'Hippocratic-2.1'}, + 'hp-1986': {'id': 'HP-1986'}, + 'hp-1989': {'id': 'HP-1989'}, + 'hpnd': {'id': 'HPND'}, + 'hpnd-dec': {'id': 'HPND-DEC'}, + 'hpnd-doc': {'id': 'HPND-doc'}, + 'hpnd-doc-sell': {'id': 'HPND-doc-sell'}, + 'hpnd-export-us': {'id': 'HPND-export-US'}, + 'hpnd-export-us-acknowledgement': {'id': 'HPND-export-US-acknowledgement'}, + 'hpnd-export-us-modify': {'id': 'HPND-export-US-modify'}, + 'hpnd-export2-us': {'id': 'HPND-export2-US'}, + 'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston'}, + 'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG'}, + 'hpnd-intel': {'id': 'HPND-Intel'}, + 'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney'}, + 'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn'}, + 'hpnd-merchantability-variant': {'id': 'HPND-merchantability-variant'}, + 'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer'}, + 'hpnd-netrek': {'id': 'HPND-Netrek'}, + 'hpnd-pbmplus': {'id': 'HPND-Pbmplus'}, + 'hpnd-sell-mit-disclaimer-xserver': {'id': 'HPND-sell-MIT-disclaimer-xserver'}, + 'hpnd-sell-regexpr': {'id': 'HPND-sell-regexpr'}, + 'hpnd-sell-variant': {'id': 'HPND-sell-variant'}, + 'hpnd-sell-variant-mit-disclaimer': {'id': 'HPND-sell-variant-MIT-disclaimer'}, + 'hpnd-sell-variant-mit-disclaimer-rev': {'id': 'HPND-sell-variant-MIT-disclaimer-rev'}, + 'hpnd-uc': {'id': 'HPND-UC'}, + 'hpnd-uc-export-us': {'id': 'HPND-UC-export-US'}, + 'htmltidy': {'id': 'HTMLTIDY'}, + 'ibm-pibs': {'id': 'IBM-pibs'}, + 'icu': {'id': 'ICU'}, + 'iec-code-components-eula': {'id': 'IEC-Code-Components-EULA'}, + 'ijg': {'id': 'IJG'}, + 'ijg-short': {'id': 'IJG-short'}, + 'imagemagick': {'id': 'ImageMagick'}, + 'imatix': {'id': 'iMatix'}, + 'imlib2': {'id': 'Imlib2'}, + 'info-zip': {'id': 'Info-ZIP'}, + 'inner-net-2.0': {'id': 'Inner-Net-2.0'}, + 'innosetup': {'id': 'InnoSetup'}, + 'intel': {'id': 'Intel'}, + 'intel-acpi': {'id': 'Intel-ACPI'}, + 'interbase-1.0': {'id': 'Interbase-1.0'}, + 'ipa': {'id': 'IPA'}, + 'ipl-1.0': {'id': 'IPL-1.0'}, + 'isc': {'id': 'ISC'}, + 'isc-veillard': {'id': 'ISC-Veillard'}, + 'jam': {'id': 'Jam'}, + 'jasper-2.0': {'id': 'JasPer-2.0'}, + 'jpl-image': {'id': 'JPL-image'}, + 'jpnic': {'id': 'JPNIC'}, + 'json': {'id': 'JSON'}, + 'kastrup': {'id': 'Kastrup'}, + 'kazlib': {'id': 'Kazlib'}, + 'knuth-ctan': {'id': 'Knuth-CTAN'}, + 'lal-1.2': {'id': 'LAL-1.2'}, + 'lal-1.3': {'id': 'LAL-1.3'}, + 'latex2e': {'id': 'Latex2e'}, + 'latex2e-translated-notice': {'id': 'Latex2e-translated-notice'}, + 'leptonica': {'id': 'Leptonica'}, + 'lgpl-2.0-only': {'id': 'LGPL-2.0-only'}, + 'lgpl-2.0-or-later': {'id': 'LGPL-2.0-or-later'}, + 'lgpl-2.1-only': {'id': 'LGPL-2.1-only'}, + 'lgpl-2.1-or-later': {'id': 'LGPL-2.1-or-later'}, + 'lgpl-3.0-only': {'id': 'LGPL-3.0-only'}, + 'lgpl-3.0-or-later': {'id': 'LGPL-3.0-or-later'}, + 'lgpllr': {'id': 'LGPLLR'}, + 'libpng': {'id': 'Libpng'}, + 'libpng-2.0': {'id': 'libpng-2.0'}, + 'libselinux-1.0': {'id': 'libselinux-1.0'}, + 'libtiff': {'id': 'libtiff'}, + 'libutil-david-nugent': {'id': 'libutil-David-Nugent'}, + 'liliq-p-1.1': {'id': 'LiLiQ-P-1.1'}, + 'liliq-r-1.1': {'id': 'LiLiQ-R-1.1'}, + 'liliq-rplus-1.1': {'id': 'LiLiQ-Rplus-1.1'}, + 'linux-man-pages-1-para': {'id': 'Linux-man-pages-1-para'}, + 'linux-man-pages-copyleft': {'id': 'Linux-man-pages-copyleft'}, + 'linux-man-pages-copyleft-2-para': {'id': 'Linux-man-pages-copyleft-2-para'}, + 'linux-man-pages-copyleft-var': {'id': 'Linux-man-pages-copyleft-var'}, + 'linux-openib': {'id': 'Linux-OpenIB'}, + 'loop': {'id': 'LOOP'}, + 'lpd-document': {'id': 'LPD-document'}, + 'lpl-1.0': {'id': 'LPL-1.0'}, + 'lpl-1.02': {'id': 'LPL-1.02'}, + 'lppl-1.0': {'id': 'LPPL-1.0'}, + 'lppl-1.1': {'id': 'LPPL-1.1'}, + 'lppl-1.2': {'id': 'LPPL-1.2'}, + 'lppl-1.3a': {'id': 'LPPL-1.3a'}, + 'lppl-1.3c': {'id': 'LPPL-1.3c'}, + 'lsof': {'id': 'lsof'}, + 'lucida-bitmap-fonts': {'id': 'Lucida-Bitmap-Fonts'}, + 'lzma-sdk-9.11-to-9.20': {'id': 'LZMA-SDK-9.11-to-9.20'}, + 'lzma-sdk-9.22': {'id': 'LZMA-SDK-9.22'}, + 'mackerras-3-clause': {'id': 'Mackerras-3-Clause'}, + 'mackerras-3-clause-acknowledgment': {'id': 'Mackerras-3-Clause-acknowledgment'}, + 'magaz': {'id': 'magaz'}, + 'mailprio': {'id': 'mailprio'}, + 'makeindex': {'id': 'MakeIndex'}, + 'martin-birgmeier': {'id': 'Martin-Birgmeier'}, + 'mcphee-slideshow': {'id': 'McPhee-slideshow'}, + 'metamail': {'id': 'metamail'}, + 'minpack': {'id': 'Minpack'}, + 'mips': {'id': 'MIPS'}, + 'miros': {'id': 'MirOS'}, + 'mit': {'id': 'MIT'}, + 'mit-0': {'id': 'MIT-0'}, + 'mit-advertising': {'id': 'MIT-advertising'}, + 'mit-click': {'id': 'MIT-Click'}, + 'mit-cmu': {'id': 'MIT-CMU'}, + 'mit-enna': {'id': 'MIT-enna'}, + 'mit-feh': {'id': 'MIT-feh'}, + 'mit-festival': {'id': 'MIT-Festival'}, + 'mit-khronos-old': {'id': 'MIT-Khronos-old'}, + 'mit-modern-variant': {'id': 'MIT-Modern-Variant'}, + 'mit-open-group': {'id': 'MIT-open-group'}, + 'mit-testregex': {'id': 'MIT-testregex'}, + 'mit-wu': {'id': 'MIT-Wu'}, + 'mitnfa': {'id': 'MITNFA'}, + 'mmixware': {'id': 'MMIXware'}, + 'motosoto': {'id': 'Motosoto'}, + 'mpeg-ssg': {'id': 'MPEG-SSG'}, + 'mpi-permissive': {'id': 'mpi-permissive'}, + 'mpich2': {'id': 'mpich2'}, + 'mpl-1.0': {'id': 'MPL-1.0'}, + 'mpl-1.1': {'id': 'MPL-1.1'}, + 'mpl-2.0': {'id': 'MPL-2.0'}, + 'mpl-2.0-no-copyleft-exception': {'id': 'MPL-2.0-no-copyleft-exception'}, + 'mplus': {'id': 'mplus'}, + 'ms-lpl': {'id': 'MS-LPL'}, + 'ms-pl': {'id': 'MS-PL'}, + 'ms-rl': {'id': 'MS-RL'}, + 'mtll': {'id': 'MTLL'}, + 'mulanpsl-1.0': {'id': 'MulanPSL-1.0'}, + 'mulanpsl-2.0': {'id': 'MulanPSL-2.0'}, + 'multics': {'id': 'Multics'}, + 'mup': {'id': 'Mup'}, + 'naist-2003': {'id': 'NAIST-2003'}, + 'nasa-1.3': {'id': 'NASA-1.3'}, + 'naumen': {'id': 'Naumen'}, + 'nbpl-1.0': {'id': 'NBPL-1.0'}, + 'ncbi-pd': {'id': 'NCBI-PD'}, + 'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0'}, + 'ncl': {'id': 'NCL'}, + 'ncsa': {'id': 'NCSA'}, + 'netcdf': {'id': 'NetCDF'}, + 'newsletr': {'id': 'Newsletr'}, + 'ngpl': {'id': 'NGPL'}, + 'nicta-1.0': {'id': 'NICTA-1.0'}, + 'nist-pd': {'id': 'NIST-PD'}, + 'nist-pd-fallback': {'id': 'NIST-PD-fallback'}, + 'nist-software': {'id': 'NIST-Software'}, + 'nlod-1.0': {'id': 'NLOD-1.0'}, + 'nlod-2.0': {'id': 'NLOD-2.0'}, + 'nlpl': {'id': 'NLPL'}, + 'nokia': {'id': 'Nokia'}, + 'nosl': {'id': 'NOSL'}, + 'noweb': {'id': 'Noweb'}, + 'npl-1.0': {'id': 'NPL-1.0'}, + 'npl-1.1': {'id': 'NPL-1.1'}, + 'nposl-3.0': {'id': 'NPOSL-3.0'}, + 'nrl': {'id': 'NRL'}, + 'ntp': {'id': 'NTP'}, + 'ntp-0': {'id': 'NTP-0'}, + 'o-uda-1.0': {'id': 'O-UDA-1.0'}, + 'oar': {'id': 'OAR'}, + 'occt-pl': {'id': 'OCCT-PL'}, + 'oclc-2.0': {'id': 'OCLC-2.0'}, + 'odbl-1.0': {'id': 'ODbL-1.0'}, + 'odc-by-1.0': {'id': 'ODC-By-1.0'}, + 'offis': {'id': 'OFFIS'}, + 'ofl-1.0': {'id': 'OFL-1.0'}, + 'ofl-1.0-no-rfn': {'id': 'OFL-1.0-no-RFN'}, + 'ofl-1.0-rfn': {'id': 'OFL-1.0-RFN'}, + 'ofl-1.1': {'id': 'OFL-1.1'}, + 'ofl-1.1-no-rfn': {'id': 'OFL-1.1-no-RFN'}, + 'ofl-1.1-rfn': {'id': 'OFL-1.1-RFN'}, + 'ogc-1.0': {'id': 'OGC-1.0'}, + 'ogdl-taiwan-1.0': {'id': 'OGDL-Taiwan-1.0'}, + 'ogl-canada-2.0': {'id': 'OGL-Canada-2.0'}, + 'ogl-uk-1.0': {'id': 'OGL-UK-1.0'}, + 'ogl-uk-2.0': {'id': 'OGL-UK-2.0'}, + 'ogl-uk-3.0': {'id': 'OGL-UK-3.0'}, + 'ogtsl': {'id': 'OGTSL'}, + 'oldap-1.1': {'id': 'OLDAP-1.1'}, + 'oldap-1.2': {'id': 'OLDAP-1.2'}, + 'oldap-1.3': {'id': 'OLDAP-1.3'}, + 'oldap-1.4': {'id': 'OLDAP-1.4'}, + 'oldap-2.0': {'id': 'OLDAP-2.0'}, + 'oldap-2.0.1': {'id': 'OLDAP-2.0.1'}, + 'oldap-2.1': {'id': 'OLDAP-2.1'}, + 'oldap-2.2': {'id': 'OLDAP-2.2'}, + 'oldap-2.2.1': {'id': 'OLDAP-2.2.1'}, + 'oldap-2.2.2': {'id': 'OLDAP-2.2.2'}, + 'oldap-2.3': {'id': 'OLDAP-2.3'}, + 'oldap-2.4': {'id': 'OLDAP-2.4'}, + 'oldap-2.5': {'id': 'OLDAP-2.5'}, + 'oldap-2.6': {'id': 'OLDAP-2.6'}, + 'oldap-2.7': {'id': 'OLDAP-2.7'}, + 'oldap-2.8': {'id': 'OLDAP-2.8'}, + 'olfl-1.3': {'id': 'OLFL-1.3'}, + 'oml': {'id': 'OML'}, + 'openpbs-2.3': {'id': 'OpenPBS-2.3'}, + 'openssl': {'id': 'OpenSSL'}, + 'openssl-standalone': {'id': 'OpenSSL-standalone'}, + 'openvision': {'id': 'OpenVision'}, + 'opl-1.0': {'id': 'OPL-1.0'}, + 'opl-uk-3.0': {'id': 'OPL-UK-3.0'}, + 'opubl-1.0': {'id': 'OPUBL-1.0'}, + 'oset-pl-2.1': {'id': 'OSET-PL-2.1'}, + 'osl-1.0': {'id': 'OSL-1.0'}, + 'osl-1.1': {'id': 'OSL-1.1'}, + 'osl-2.0': {'id': 'OSL-2.0'}, + 'osl-2.1': {'id': 'OSL-2.1'}, + 'osl-3.0': {'id': 'OSL-3.0'}, + 'padl': {'id': 'PADL'}, + 'parity-6.0.0': {'id': 'Parity-6.0.0'}, + 'parity-7.0.0': {'id': 'Parity-7.0.0'}, + 'pddl-1.0': {'id': 'PDDL-1.0'}, + 'php-3.0': {'id': 'PHP-3.0'}, + 'php-3.01': {'id': 'PHP-3.01'}, + 'pixar': {'id': 'Pixar'}, + 'pkgconf': {'id': 'pkgconf'}, + 'plexus': {'id': 'Plexus'}, + 'pnmstitch': {'id': 'pnmstitch'}, + 'polyform-noncommercial-1.0.0': {'id': 'PolyForm-Noncommercial-1.0.0'}, + 'polyform-small-business-1.0.0': {'id': 'PolyForm-Small-Business-1.0.0'}, + 'postgresql': {'id': 'PostgreSQL'}, + 'ppl': {'id': 'PPL'}, + 'psf-2.0': {'id': 'PSF-2.0'}, + 'psfrag': {'id': 'psfrag'}, + 'psutils': {'id': 'psutils'}, + 'python-2.0': {'id': 'Python-2.0'}, + 'python-2.0.1': {'id': 'Python-2.0.1'}, + 'python-ldap': {'id': 'python-ldap'}, + 'qhull': {'id': 'Qhull'}, + 'qpl-1.0': {'id': 'QPL-1.0'}, + 'qpl-1.0-inria-2004': {'id': 'QPL-1.0-INRIA-2004'}, + 'radvd': {'id': 'radvd'}, + 'rdisc': {'id': 'Rdisc'}, + 'rhecos-1.1': {'id': 'RHeCos-1.1'}, + 'rpl-1.1': {'id': 'RPL-1.1'}, + 'rpl-1.5': {'id': 'RPL-1.5'}, + 'rpsl-1.0': {'id': 'RPSL-1.0'}, + 'rsa-md': {'id': 'RSA-MD'}, + 'rscpl': {'id': 'RSCPL'}, + 'ruby': {'id': 'Ruby'}, + 'ruby-pty': {'id': 'Ruby-pty'}, + 'sax-pd': {'id': 'SAX-PD'}, + 'sax-pd-2.0': {'id': 'SAX-PD-2.0'}, + 'saxpath': {'id': 'Saxpath'}, + 'scea': {'id': 'SCEA'}, + 'schemereport': {'id': 'SchemeReport'}, + 'sendmail': {'id': 'Sendmail'}, + 'sendmail-8.23': {'id': 'Sendmail-8.23'}, + 'sendmail-open-source-1.1': {'id': 'Sendmail-Open-Source-1.1'}, + 'sgi-b-1.0': {'id': 'SGI-B-1.0'}, + 'sgi-b-1.1': {'id': 'SGI-B-1.1'}, + 'sgi-b-2.0': {'id': 'SGI-B-2.0'}, + 'sgi-opengl': {'id': 'SGI-OpenGL'}, + 'sgp4': {'id': 'SGP4'}, + 'shl-0.5': {'id': 'SHL-0.5'}, + 'shl-0.51': {'id': 'SHL-0.51'}, + 'simpl-2.0': {'id': 'SimPL-2.0'}, + 'sissl': {'id': 'SISSL'}, + 'sissl-1.2': {'id': 'SISSL-1.2'}, + 'sl': {'id': 'SL'}, + 'sleepycat': {'id': 'Sleepycat'}, + 'smail-gpl': {'id': 'SMAIL-GPL'}, + 'smlnj': {'id': 'SMLNJ'}, + 'smppl': {'id': 'SMPPL'}, + 'snia': {'id': 'SNIA'}, + 'snprintf': {'id': 'snprintf'}, + 'softsurfer': {'id': 'softSurfer'}, + 'soundex': {'id': 'Soundex'}, + 'spencer-86': {'id': 'Spencer-86'}, + 'spencer-94': {'id': 'Spencer-94'}, + 'spencer-99': {'id': 'Spencer-99'}, + 'spl-1.0': {'id': 'SPL-1.0'}, + 'ssh-keyscan': {'id': 'ssh-keyscan'}, + 'ssh-openssh': {'id': 'SSH-OpenSSH'}, + 'ssh-short': {'id': 'SSH-short'}, + 'ssleay-standalone': {'id': 'SSLeay-standalone'}, + 'sspl-1.0': {'id': 'SSPL-1.0'}, + 'sugarcrm-1.1.3': {'id': 'SugarCRM-1.1.3'}, + 'sun-ppp': {'id': 'Sun-PPP'}, + 'sun-ppp-2000': {'id': 'Sun-PPP-2000'}, + 'sunpro': {'id': 'SunPro'}, + 'swl': {'id': 'SWL'}, + 'swrule': {'id': 'swrule'}, + 'symlinks': {'id': 'Symlinks'}, + 'tapr-ohl-1.0': {'id': 'TAPR-OHL-1.0'}, + 'tcl': {'id': 'TCL'}, + 'tcp-wrappers': {'id': 'TCP-wrappers'}, + 'termreadkey': {'id': 'TermReadKey'}, + 'tgppl-1.0': {'id': 'TGPPL-1.0'}, + 'thirdeye': {'id': 'ThirdEye'}, + 'threeparttable': {'id': 'threeparttable'}, + 'tmate': {'id': 'TMate'}, + 'torque-1.1': {'id': 'TORQUE-1.1'}, + 'tosl': {'id': 'TOSL'}, + 'tpdl': {'id': 'TPDL'}, + 'tpl-1.0': {'id': 'TPL-1.0'}, + 'trustedqsl': {'id': 'TrustedQSL'}, + 'ttwl': {'id': 'TTWL'}, + 'ttyp0': {'id': 'TTYP0'}, + 'tu-berlin-1.0': {'id': 'TU-Berlin-1.0'}, + 'tu-berlin-2.0': {'id': 'TU-Berlin-2.0'}, + 'ubuntu-font-1.0': {'id': 'Ubuntu-font-1.0'}, + 'ucar': {'id': 'UCAR'}, + 'ucl-1.0': {'id': 'UCL-1.0'}, + 'ulem': {'id': 'ulem'}, + 'umich-merit': {'id': 'UMich-Merit'}, + 'unicode-3.0': {'id': 'Unicode-3.0'}, + 'unicode-dfs-2015': {'id': 'Unicode-DFS-2015'}, + 'unicode-dfs-2016': {'id': 'Unicode-DFS-2016'}, + 'unicode-tou': {'id': 'Unicode-TOU'}, + 'unixcrypt': {'id': 'UnixCrypt'}, + 'unlicense': {'id': 'Unlicense'}, + 'upl-1.0': {'id': 'UPL-1.0'}, + 'urt-rle': {'id': 'URT-RLE'}, + 'vim': {'id': 'Vim'}, + 'vostrom': {'id': 'VOSTROM'}, + 'vsl-1.0': {'id': 'VSL-1.0'}, + 'w3c': {'id': 'W3C'}, + 'w3c-19980720': {'id': 'W3C-19980720'}, + 'w3c-20150513': {'id': 'W3C-20150513'}, + 'w3m': {'id': 'w3m'}, + 'watcom-1.0': {'id': 'Watcom-1.0'}, + 'widget-workshop': {'id': 'Widget-Workshop'}, + 'wsuipa': {'id': 'Wsuipa'}, + 'wtfpl': {'id': 'WTFPL'}, + 'wwl': {'id': 'wwl'}, + 'x11': {'id': 'X11'}, + 'x11-distribute-modifications-variant': {'id': 'X11-distribute-modifications-variant'}, + 'x11-swapped': {'id': 'X11-swapped'}, + 'xdebug-1.03': {'id': 'Xdebug-1.03'}, + 'xerox': {'id': 'Xerox'}, + 'xfig': {'id': 'Xfig'}, + 'xfree86-1.1': {'id': 'XFree86-1.1'}, + 'xinetd': {'id': 'xinetd'}, + 'xkeyboard-config-zinoviev': {'id': 'xkeyboard-config-Zinoviev'}, + 'xlock': {'id': 'xlock'}, + 'xnet': {'id': 'Xnet'}, + 'xpp': {'id': 'xpp'}, + 'xskat': {'id': 'XSkat'}, + 'xzoom': {'id': 'xzoom'}, + 'ypl-1.0': {'id': 'YPL-1.0'}, + 'ypl-1.1': {'id': 'YPL-1.1'}, + 'zed': {'id': 'Zed'}, + 'zeeff': {'id': 'Zeeff'}, + 'zend-2.0': {'id': 'Zend-2.0'}, + 'zimbra-1.3': {'id': 'Zimbra-1.3'}, + 'zimbra-1.4': {'id': 'Zimbra-1.4'}, + 'zlib': {'id': 'Zlib'}, + 'zlib-acknowledgement': {'id': 'zlib-acknowledgement'}, + 'zpl-1.1': {'id': 'ZPL-1.1'}, + 'zpl-2.0': {'id': 'ZPL-2.0'}, + 'zpl-2.1': {'id': 'ZPL-2.1'}} diff --git a/flit_core/flit_core/common.py b/flit_core/flit_core/common.py index 719e1072..22c188c8 100644 --- a/flit_core/flit_core/common.py +++ b/flit_core/flit_core/common.py @@ -336,6 +336,7 @@ class Metadata(object): maintainer = None maintainer_email = None license = None + license_expression = None description = None keywords = None download_url = None @@ -399,7 +400,6 @@ def write_metadata_file(self, fp): optional_fields = [ 'Summary', 'Home-page', - 'License', 'Keywords', 'Author', 'Author-email', @@ -423,6 +423,17 @@ def write_metadata_file(self, fp): value = '\n '.join(value.splitlines()) fp.write(u"{}: {}\n".format(field, value)) + + license_expr = getattr(self, self._normalise_field_name("License-Expression")) + license = getattr(self, self._normalise_field_name("License")) + if license_expr: + # TODO: License-Expression requires Metadata-Version '2.4' + # Backfill it to the 'License' field for now + # fp.write(u'License-Expression: {}\n'.format(license_expr)) + fp.write(u'License: {}\n'.format(license_expr)) + elif license: + fp.write(u'License: {}\n'.format(license)) + for clsfr in self.classifiers: fp.write(u'Classifier: {}\n'.format(clsfr)) diff --git a/flit_core/flit_core/config.py b/flit_core/flit_core/config.py index 2b2af868..3786001d 100644 --- a/flit_core/flit_core/config.py +++ b/flit_core/flit_core/config.py @@ -495,6 +495,14 @@ def _check_type(d, field_name, cls): "{} field should be {}, not {}".format(field_name, cls, type(d[field_name])) ) +def _check_types(d, field_name, cls_list) -> None: + if not isinstance(d[field_name], cls_list): + raise ConfigError( + "{} field should be {}, not {}".format( + field_name, ' or '.join(map(str, cls_list)), type(d[field_name]) + ) + ) + def _check_list_of_str(d, field_name): if not isinstance(d[field_name], list) or not all( isinstance(e, str) for e in d[field_name] @@ -577,30 +585,38 @@ def read_pep621_metadata(proj, path) -> LoadedConfig: license_files = set() if 'license' in proj: - _check_type(proj, 'license', dict) - license_tbl = proj['license'] - unrec_keys = set(license_tbl.keys()) - {'text', 'file'} - if unrec_keys: - raise ConfigError( - "Unrecognised keys in [project.license]: {}".format(unrec_keys) - ) + _check_types(proj, 'license', (str, dict)) + if isinstance(proj['license'], str): + md_dict['license_expression'] = normalize_license_expr(proj['license']) + else: + license_tbl = proj['license'] + unrec_keys = set(license_tbl.keys()) - {'text', 'file'} + if unrec_keys: + raise ConfigError( + "Unrecognised keys in [project.license]: {}".format(unrec_keys) + ) - # TODO: Do something with license info. - # The 'License' field in packaging metadata is a brief description of - # a license, not the full text or a file path. PEP 639 will improve on - # how licenses are recorded. - if 'file' in license_tbl: - if 'text' in license_tbl: + # The 'License' field in packaging metadata is a brief description of + # a license, not the full text or a file path. + if 'file' in license_tbl: + if 'text' in license_tbl: + raise ConfigError( + "[project.license] should specify file or text, not both" + ) + license_f = license_tbl['file'] + if isabs_ish(license_f): + raise ConfigError( + f"License file path ({license_f}) cannot be an absolute path" + ) + if not (path.parent / license_f).is_file(): + raise ConfigError(f"License file {license_f} does not exist") + license_files.add(license_tbl['file']) + elif 'text' in license_tbl: + pass + else: raise ConfigError( - "[project.license] should specify file or text, not both" + "file or text field required in [project.license] table" ) - license_files.add(license_tbl['file']) - elif 'text' in license_tbl: - pass - else: - raise ConfigError( - "file or text field required in [project.license] table" - ) if 'license-files' in proj: _check_type(proj, 'license-files', list) @@ -635,6 +651,16 @@ def read_pep621_metadata(proj, path) -> LoadedConfig: if 'classifiers' in proj: _check_list_of_str(proj, 'classifiers') + classifiers = proj['classifiers'] + license_expr = md_dict.get('license_expression', None) + if license_expr: + for cl in classifiers: + if not cl.startswith('License :: '): + continue + raise ConfigError( + "License classifier are deprecated in favor of the license expression. " + "Remove the '{}' classifier".format(cl) + ) md_dict['classifiers'] = proj['classifiers'] if 'urls' in proj: @@ -788,3 +814,36 @@ def isabs_ish(path): absolute paths, we also want to reject these odd halfway paths. """ return os.path.isabs(path) or path.startswith(('/', '\\')) + + +def normalize_license_expr(s: str): + """Validate & normalise an SPDX license expression + + For now this only handles simple expressions (referring to 1 license) + """ + from ._spdx_data import licenses + ls = s.lower() + if ls.startswith('licenseref-'): + ref = s.partition('-')[2] + if re.match(r'([a-zA-Z0-9\-.])+$', ref): + # Normalise case of LicenseRef, leave the rest alone + return "LicenseRef-" + ref + raise ConfigError( + "LicenseRef- license expression can only contain ASCII letters " + "& digits, - and ." + ) + + or_later = s.endswith('+') + if or_later: + ls = ls[:-1] + + try: + info = licenses[ls] + except KeyError: + if os.environ.get('FLIT_ALLOW_INVALID'): + log.warning("Invalid license ID {!r} allowed by FLIT_ALLOW_INVALID" + .format(s)) + return s + raise ConfigError(f"{s!r} is not a recognised SPDX license ID") + + return info['id'] + ('+' if or_later else '') diff --git a/flit_core/pyproject.toml b/flit_core/pyproject.toml index 53c47343..11702af8 100644 --- a/flit_core/pyproject.toml +++ b/flit_core/pyproject.toml @@ -12,9 +12,9 @@ description = "Distribution-building parts of Flit. See flit package for more in dependencies = [] requires-python = '>=3.6' readme = "README.rst" +license = "BSD-3-Clause" license-files = ["LICENSE*", "flit_core/vendor/**/LICENSE*"] classifiers = [ - "License :: OSI Approved :: BSD License", "Topic :: Software Development :: Libraries :: Python Modules", ] dynamic = ["version"] diff --git a/flit_core/tests_core/test_common.py b/flit_core/tests_core/test_common.py index 824fa5de..3823e27d 100644 --- a/flit_core/tests_core/test_common.py +++ b/flit_core/tests_core/test_common.py @@ -205,3 +205,27 @@ def test_metadata_2_3_provides_extra(provides_extra, expected_result): msg = email.parser.Parser(policy=email.policy.compat32).parse(sio) assert msg['Provides-Extra'] == expected_result assert not msg.defects + +@pytest.mark.parametrize( + ('value', 'expected_license', 'expected_license_expression'), + [ + ({'license': 'MIT'}, 'MIT', None), + ({'license_expression': 'MIT'}, 'MIT', None), # TODO Metadata 2.4 + ({'license_expression': 'Apache-2.0'}, 'Apache-2.0', None) # TODO Metadata 2.4 + ], +) +def test_metadata_license(value, expected_license, expected_license_expression): + d = { + 'name': 'foo', + 'version': '1.0', + **value, + } + md = Metadata(d) + sio = StringIO() + md.write_metadata_file(sio) + sio.seek(0) + + msg = email.parser.Parser(policy=email.policy.compat32).parse(sio) + assert msg.get('License') == expected_license + assert msg.get('License-Expression') == expected_license_expression + assert not msg.defects diff --git a/flit_core/tests_core/test_config.py b/flit_core/tests_core/test_config.py index 2558a3b1..b25bcc04 100644 --- a/flit_core/tests_core/test_config.py +++ b/flit_core/tests_core/test_config.py @@ -140,6 +140,12 @@ def test_bad_include_paths(path, err_match): ({'license': {'fromage': 2}}, '[Uu]nrecognised'), ({'license': {'file': 'LICENSE', 'text': 'xyz'}}, 'both'), ({'license': {}}, 'required'), + ({'license': 1}, "license field should be or , not "), + # ({'license': "MIT License"}, "Invalid license expression: 'MIT License'"), # TODO + ( + {'license': 'MIT', 'classifiers': ['License :: OSI Approved :: MIT License']}, + "License classifier are deprecated in favor of the license expression", + ), ({'license-files': 1}, r"\blist\b"), ({'license-files': ["/LICENSE"]}, r"'/LICENSE'.+must not start with '/'"), ({'license-files': ["../LICENSE"]}, r"'../LICENSE'.+must not contain '..'"), @@ -202,6 +208,37 @@ def test_bad_pep621_readme(readme, err_match): config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') +@pytest.mark.parametrize(('value', 'license_expression'), [ + # Accept and normalize valid SPDX expressions for 'license = ...' + ("mit", "MIT"), + ("apache-2.0", "Apache-2.0"), + ("APACHE-2.0+", "Apache-2.0+"), + # TODO: compound expressions + #("mit and (apache-2.0 or bsd-2-clause)", "MIT AND (Apache-2.0 OR BSD-2-Clause)"), + # LicenseRef expressions: only the LicenseRef is normalised + ("LiceNseref-Public-DoMain", "LicenseRef-Public-DoMain"), +]) +def test_license_expr(value, license_expression): + proj = { + 'name': 'module1', 'version': '1.0', 'description': 'x', 'license': value + } + info = config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') + assert 'license' not in info.metadata + assert info.metadata['license_expression'] == license_expression + +def test_license_expr_error(): + proj = { + 'name': 'module1', 'version': '1.0', 'description': 'x', + 'license': 'LicenseRef-foo_bar', # Underscore not allowed + } + with pytest.raises(config.ConfigError, match="can only contain"): + config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') + + proj['license'] = "BSD-33-Clause" # Not a real license + with pytest.raises(config.ConfigError, match="recognised"): + config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') + + def test_license_file_defaults_with_old_metadata(): metadata = {'module': 'mymod', 'author': ''} info = config._prep_metadata(metadata, samples_dir / 'pep621_license_files' / 'pyproject.toml') diff --git a/prepare_license_list.py b/prepare_license_list.py new file mode 100644 index 00000000..76e62d4c --- /dev/null +++ b/prepare_license_list.py @@ -0,0 +1,24 @@ +# Call with path to SPDX license-list-data repo, cloned from: +# https://github.com/spdx/license-list-data + +import json +import pprint +import sys +from pathlib import Path + +list_data_repo = Path(sys.argv[1]) +with (list_data_repo / 'json' / 'licenses.json').open('rb') as f: + licenses_json = json.load(f) + +condensed = { + l['licenseId'].lower() : {'id': l['licenseId']} + for l in licenses_json['licenses'] + if not l['isDeprecatedLicenseId'] +} + +with Path('flit_core', 'flit_core', '_spdx_data.py').open('w') as f: + f.write("# This file is generated from SPDX license data; don't edit it manually.\n\n") + + f.write("licenses = \\\n") + pprint.pprint(condensed, f) +