封装自由软件许可证的库
项目描述
license 是一个 Python 库,提供有关常见自由软件许可证的一些元数据,例如 GNU GPL、MIT 等。它与 Python 3.3+ 和旧版 Python 2.7 兼容。
基本用法
要获得许可证,您可以使用SPDX 许可证标识符:
import license
mit = license.find('MIT')
每个许可证都是一个静态类,提供一些属性:
id - SPDX 标识符
name - 一个人类可读的许可证名称
python - PyPI 分类器
url - 指向许可证描述或网站的链接
mit.python
'License :: OSI Approved :: MIT License'
许可证类还提供了一个静态方法render(),它将输出整个许可证文本。必须将一些变量传递给它,通常是name、email和可选的year (省略时使用当前年份)。
mit.render(name='Petr Foo', email='petr@foo.org')
'''The MIT License (MIT)
Copyright (c) 2015 Petr Foo <petr@foo.org>
Permission is hereby granted... (snip)'''
一些许可证(例如来自 GPL 家族的许可证)也有一个标题文本,应该添加到每个源文件中。header()用于渲染,但要小心,如果许可证不使用特殊标头,则会引发AttributeError。
mit.header(name='Petr Foo', email='petr@foo.org')
AttributeError: The MIT license uses no header
如果您想通过其他密钥搜索许可证,您可以:
bsd = license.find_by_key('rpm', 'BSD')
bsd
[license.licenses.BSD3ClauseLicense, license.licenses.BSD2ClauseLicense]
bsd现在是一个列表,因为与 SPDX 标识符不同,其他键可能并不总是唯一的。如果您只需要具有此类标识符的第一个许可证,则可以将multiple=False传递给 find_by_key():
bsd = license.find_by_key('rpm', 'BSD', multiple=False)
bsd
license.licenses.BSD3ClauseLicense
如果没有找到这样的许可证,你会得到KeyError,就像普通的find()一样。
如果您想通过某个键执行大量搜索,您可以构建和索引,这应该(理论上)使搜索更快(未执行任何测量)。
license.build_index('rpm')
如果您想删除索引,请使用license.delete_index(key)。即使索引不存在,调用它也是安全的。
也可以使用find_by_function()来查找与某个表达式匹配的许可证。该函数应该接受一个参数(许可证类),如果许可证应该在结果中,则返回 True:
osi = license.find_by_function(lambda l: l.python.startswith('License :: OSI Approved :: '))
同样,它返回一个列表并有多个参数来改变它。
如果一个简单的功能还不够,您可以使用 license.iter()遍历所有许可证:
for cls in license.iter():
# do something
添加许可证
当前的许可证列表并不多,因此您最喜欢的许可证可能不在其中。如果您想更改它,请将许可证添加到license/licenses.py并将模板添加到 license/templates ,然后在 GitHub 上发送拉取请求。查看当前许可证以了解如何操作。许可证类如下所示:
class AGPLv3LaterLicense(license.base.License):
'''
GNU Affero General Public License v3.0 or later
'''
id = 'AGPL-3.0+'
rpm = 'AGPLv3+'
python = 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)'
url = 'http://www.gnu.org/licenses/agpl-3.0.html'
一个许可证可以从其他许可证继承并省略相同的密钥。请注意,文档字符串很重要,它被用作名称属性。许可证模板命名为id,标头模板以__header后缀命名。
如果您希望在代码中添加自定义许可证,您也可以这样做。如果您不使用 render()或header(),事情很简单。只需在任何地方定义此类并在其上调用 license.register() 即可。
但是,如果您随后调用render()或header(),则找不到模板。在这种情况下,您必须使用jinja2模板加载器创建自定义基本许可证。
CustomBaseLicense = license.base.custom_license_base_class(loader=jinja2.FileSystemLoader('path/to/templates'))
class CustomLicense(CustomBaseLicense):
...
license.register(CustomLicense)
加载器可以是任何有效的jinja2 加载器。如果您希望一次注册多个类,您可以使用license.autoregister()来注册给定模块中存在的所有类。您不想注册您的 CustomBaseLicense,因此您将在忽略参数中传递它。
license.autoregister(sys.modules[__name__], ignore=[CustomBaseLicense])
请注意,如果您添加自定义许可证并使用license.build_index(),您希望在注册后构建索引。多次调用build_index()是安全的。
(可能)常见问题
为什么许可证表示为子类而不是许可证的实例?
这样,可以更轻松地在多个许可证之间继承数据。类的定义更易于维护和阅读。
许可证不是保留名称吗?
是的,它打印了 Python 的许可证。可能你只会在交互式 Python 控制台中使用的东西。通过导入此库,您将覆盖它。我们可以用一些很酷和独特的东西来命名这个库,比如licenraptor,但我们想让这个名字尽可能简单。如果您不喜欢这样,您可以随时将import license 作为 somethignelse。
不是已经有可以渲染许可证文本的 Python 工具了吗?
对,他们是。然而,它们都是命令行实用程序,不为 Python 程序员提供 API。