FreeCAD

Форк
0
/
addonmanager_licenses.py 
187 строк · 8.0 Кб
1
# SPDX-License-Identifier: LGPL-2.1-or-later
2
# ***************************************************************************
3
# *                                                                         *
4
# *   Copyright (c) 2024 FreeCAD Project Association                        *
5
# *                                                                         *
6
# *   This file is part of FreeCAD.                                         *
7
# *                                                                         *
8
# *   FreeCAD is free software: you can redistribute it and/or modify it    *
9
# *   under the terms of the GNU Lesser General Public License as           *
10
# *   published by the Free Software Foundation, either version 2.1 of the  *
11
# *   License, or (at your option) any later version.                       *
12
# *                                                                         *
13
# *   FreeCAD is distributed in the hope that it will be useful, but        *
14
# *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
15
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
16
# *   Lesser General Public License for more details.                       *
17
# *                                                                         *
18
# *   You should have received a copy of the GNU Lesser General Public      *
19
# *   License along with FreeCAD. If not, see                               *
20
# *   <https://www.gnu.org/licenses/>.                                      *
21
# *                                                                         *
22
# ***************************************************************************
23

24
""" Utilities for working with licenses. Based on SPDX info downloaded from
25
https://github.com/spdx/license-list-data and stored as part of the FreeCAD repo, loaded into a Qt
26
resource. """
27

28
import json
29

30
# Get whatever version of PySide we can
31
try:
32
    import PySide  # Use the FreeCAD wrapper
33
except ImportError:
34
    try:
35
        import PySide6  # Outside FreeCAD, try Qt6 first
36

37
        PySide = PySide6
38
    except ImportError:
39
        import PySide2  # Fall back to Qt5 (if this fails, Python will kill this module's import)
40

41
        PySide = PySide2
42

43
from PySide import QtCore
44

45
import addonmanager_freecad_interface as fci
46

47

48
class SPDXLicenseManager:
49
    """A class that loads a list of licenses from an internal Qt resource and provides access to
50
    some information about those licenses."""
51

52
    def __init__(self):
53
        self.license_data = {}
54
        self._load_license_data()
55

56
    def _load_license_data(self):
57
        qf = QtCore.QFile(f":/licenses/spdx.json")
58
        if qf.exists():
59
            qf.open(QtCore.QIODevice.ReadOnly)
60
            byte_data = qf.readAll()
61
            qf.close()
62

63
            string_data = str(byte_data, encoding="utf-8")
64
            raw_license_data = json.loads(string_data)
65

66
            self._process_raw_spdx_json(raw_license_data)
67

68
    def _process_raw_spdx_json(self, raw_license_data: dict):
69
        """The raw JSON data is a list of licenses, with the ID as an element of the contained
70
        data members. More useful for our purposes is a dictionary with the SPDX IDs as the keys
71
        and the remaining data as the values."""
72
        for entry in raw_license_data["licenses"]:
73
            self.license_data[entry["licenseId"]] = entry
74

75
    def is_osi_approved(self, spdx_id: str) -> bool:
76
        """Check to see if the license is OSI-approved, according to the SPDX database. Returns
77
        False if the license is not in the database, or is not marked as "isOsiApproved"."""
78
        if spdx_id == "UNLICENSED" or spdx_id == "UNLICENCED" or spdx_id.startswith("SEE LIC"):
79
            return False
80
        if spdx_id not in self.license_data:
81
            fci.Console.PrintWarning(
82
                f"WARNING: License ID {spdx_id} is not in the SPDX license "
83
                f"list. The Addon author must correct their metadata.\n"
84
            )
85
            return False
86
        return (
87
            "isOsiApproved" in self.license_data[spdx_id]
88
            and self.license_data[spdx_id]["isOsiApproved"]
89
        )
90

91
    def is_fsf_libre(self, spdx_id: str) -> bool:
92
        """Check to see if the license is FSF Free/Libre, according to the SPDX database. Returns
93
        False if the license is not in the database, or is not marked as "isFsfLibre"."""
94
        if spdx_id == "UNLICENSED" or spdx_id == "UNLICENCED" or spdx_id.startswith("SEE LIC"):
95
            return False
96
        if spdx_id not in self.license_data:
97
            fci.Console.PrintWarning(
98
                f"WARNING: License ID {spdx_id} is not in the SPDX license "
99
                f"list. The Addon author must correct their metadata.\n"
100
            )
101
            return False
102
        return (
103
            "isFsfLibre" in self.license_data[spdx_id] and self.license_data[spdx_id]["isFsfLibre"]
104
        )
105

106
    def name(self, spdx_id: str) -> str:
107
        if spdx_id == "UNLICENSED":
108
            return "All rights reserved"
109
        if spdx_id.startswith("SEE LIC"):  # "SEE LICENSE IN" or "SEE LICENCE IN"
110
            return f"Custom license: {spdx_id}"
111
        if spdx_id not in self.license_data:
112
            return ""
113
        return self.license_data[spdx_id]["name"]
114

115
    def url(self, spdx_id: str) -> str:
116
        if spdx_id not in self.license_data:
117
            return ""
118
        return self.license_data[spdx_id]["reference"]
119

120
    def details_json_url(self, spdx_id: str):
121
        """The "detailsUrl" entry in the SPDX database, which is a link to a JSON file containing
122
        the details of the license. As of SPDX v3 the fields are:
123
          * isDeprecatedLicenseId
124
          * isFsfLibre
125
          * licenseText
126
          * standardLicenseHeaderTemplate
127
          * standardLicenseTemplate
128
          * name
129
          * licenseId
130
          * standardLicenseHeader
131
          * crossRef
132
          * seeAlso
133
          * isOsiApproved
134
          * licenseTextHtml
135
          * standardLicenseHeaderHtml"""
136
        if spdx_id not in self.license_data:
137
            return ""
138
        return self.license_data[spdx_id]["detailsUrl"]
139

140
    def normalize(self, license_string: str) -> str:
141
        """Given a potentially non-compliant license string, attempt to normalize it to match an
142
        SPDX record. Takes a conservative view and tries not to over-expand stated rights (e.g.
143
        it will select 'GPL-3.0-only' rather than 'GPL-3.0-or-later' when given just GPL3)."""
144
        if self.name(license_string):
145
            return license_string
146
        fci.Console.PrintLog(
147
            f"Attempting to normalize non-compliant license '" f"{license_string}'... "
148
        )
149
        normed = license_string.replace("lgpl", "LGPL").replace("gpl", "GPL")
150
        normed = (
151
            normed.replace(" ", "-")
152
            .replace("v", "-")
153
            .replace("GPL2", "GPL-2")
154
            .replace("GPL3", "GPL-3")
155
        )
156
        or_later = ""
157
        if normed.endswith("+"):
158
            normed = normed[:-1]
159
            or_later = "-or-later"
160
        if self.name(normed + or_later):
161
            fci.Console.PrintLog(f"found valid SPDX license ID {normed}\n")
162
            return normed + or_later
163
        # If it still doesn't match, try some other things
164
        while "--" in normed:
165
            normed = normed.replace("--", "-")
166

167
        if self.name(normed + or_later):
168
            fci.Console.PrintLog(f"found valid SPDX license ID {normed}\n")
169
            return normed + or_later
170
        normed += ".0"
171
        if self.name(normed + or_later):
172
            fci.Console.PrintLog(f"found valid SPDX license ID {normed}\n")
173
            return normed + or_later
174
        fci.Console.PrintLog(f"failed to normalize (typo in ID or invalid version number??)\n")
175
        return license_string  # We failed to normalize this one
176

177

178
_LICENSE_MANAGER = None  # Internal use only, see get_license_manager()
179

180

181
def get_license_manager() -> SPDXLicenseManager:
182
    """Get the license manager. Prevents multiple re-loads of the license list by keeping a
183
    single copy of the manager."""
184
    global _LICENSE_MANAGER
185
    if _LICENSE_MANAGER is None:
186
        _LICENSE_MANAGER = SPDXLicenseManager()
187
    return _LICENSE_MANAGER
188

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.