parent
9b7144047c
commit
dea064ff13
6 changed files with 48 additions and 63 deletions
14
WORKSPACE
14
WORKSPACE
|
@ -40,7 +40,7 @@ bazel_skylib_workspace()
|
||||||
git_repository(
|
git_repository(
|
||||||
name = "rules_python",
|
name = "rules_python",
|
||||||
remote = "https://github.com/bazelbuild/rules_python.git",
|
remote = "https://github.com/bazelbuild/rules_python.git",
|
||||||
tag = "0.3.0",
|
tag = "0.4.0",
|
||||||
# commit = "...",
|
# commit = "...",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,14 +48,20 @@ register_toolchains("//tools/python:python3_toolchain")
|
||||||
|
|
||||||
# pip package pinnings need to be initialized.
|
# pip package pinnings need to be initialized.
|
||||||
# this generates a bunch of bzl rules so that each pip dep is a bzl target
|
# this generates a bunch of bzl rules so that each pip dep is a bzl target
|
||||||
load("@rules_python//python:pip.bzl", "pip_install")
|
load("@rules_python//python:pip.bzl", "pip_parse")
|
||||||
|
|
||||||
pip_install(
|
pip_parse(
|
||||||
name = "arrdem_source_pypi",
|
name = "arrdem_source_pypi",
|
||||||
requirements = "//tools/python:requirements.txt",
|
requirements_lock = "//tools/python:requirements.txt",
|
||||||
python_interpreter = "/usr/bin/python3.9",
|
python_interpreter = "/usr/bin/python3.9",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Load the starlark macro which will define your dependencies.
|
||||||
|
load("@arrdem_source_pypi//:requirements.bzl", "install_deps")
|
||||||
|
|
||||||
|
# Call it to define repos for your requirements.
|
||||||
|
install_deps()
|
||||||
|
|
||||||
# git_repository(
|
# git_repository(
|
||||||
# name = "rules_zapp",
|
# name = "rules_zapp",
|
||||||
# remote = "https://github.com/arrdem/rules_zapp.git",
|
# remote = "https://github.com/arrdem/rules_zapp.git",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from black import patched_main, nullcontext
|
from black import nullcontext, patched_main
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
load("@rules_python//python:defs.bzl",
|
load("@rules_python//python:defs.bzl",
|
||||||
"py_runtime_pair"
|
"py_runtime_pair",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
load("@arrdem_source_pypi//:requirements.bzl", "all_requirements")
|
||||||
|
|
||||||
package(default_visibility = ["//visibility:public"])
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
licenses(["notice"])
|
licenses(["notice"])
|
||||||
|
@ -40,8 +42,5 @@ py_pytest(
|
||||||
data = [
|
data = [
|
||||||
"requirements.txt",
|
"requirements.txt",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = all_requirements,
|
||||||
py_requirement("requests"),
|
|
||||||
py_requirement("requirements-parser"),
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,6 +14,9 @@ load("@rules_zapp//zapp:zapp.bzl",
|
||||||
"zapp_binary",
|
"zapp_binary",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
load("@bazel_skylib//lib:sets.bzl", "sets")
|
||||||
|
|
||||||
|
|
||||||
def py_requirement(*args, **kwargs):
|
def py_requirement(*args, **kwargs):
|
||||||
"""A re-export of requirement()"""
|
"""A re-export of requirement()"""
|
||||||
return _py_requirement(*args, **kwargs)
|
return _py_requirement(*args, **kwargs)
|
||||||
|
@ -39,11 +42,11 @@ def py_pytest(name, srcs, deps, main=None, python_version=None, args=None, **kwa
|
||||||
|
|
||||||
f = "//tools/python:bzl_pytest_shim.py"
|
f = "//tools/python:bzl_pytest_shim.py"
|
||||||
|
|
||||||
deps = [
|
deps = sets.to_list(sets.make([
|
||||||
py_requirement("pytest"),
|
py_requirement("pytest"),
|
||||||
py_requirement("jedi"),
|
py_requirement("jedi"),
|
||||||
py_requirement("pytest-pudb"),
|
py_requirement("pytest-pudb"),
|
||||||
] + deps
|
] + deps))
|
||||||
|
|
||||||
srcs = [f] + srcs
|
srcs = [f] + srcs
|
||||||
|
|
||||||
|
@ -60,12 +63,14 @@ def py_pytest(name, srcs, deps, main=None, python_version=None, args=None, **kwa
|
||||||
# FIXME (arrdem 2020-09-27):
|
# FIXME (arrdem 2020-09-27):
|
||||||
# This really needs to be a py_image_test.
|
# This really needs to be a py_image_test.
|
||||||
# Not clear how to achieve that.
|
# Not clear how to achieve that.
|
||||||
# py_image(
|
# zapp_binary(
|
||||||
# name = name + ".containerized",
|
# name = name + ".hermetic",
|
||||||
# main = f,
|
# main = f,
|
||||||
# args = args,
|
# args = args,
|
||||||
# srcs = srcs,
|
# srcs = srcs,
|
||||||
# deps = deps,
|
# deps = deps,
|
||||||
|
# test = True,
|
||||||
|
# zip_safe = False,
|
||||||
# **kwargs,
|
# **kwargs,
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ packaging==20.9
|
||||||
parso==0.8.2
|
parso==0.8.2
|
||||||
pathspec==0.9.0
|
pathspec==0.9.0
|
||||||
pep517==0.11.0
|
pep517==0.11.0
|
||||||
piexif==1.1.3
|
pip==20.3.3
|
||||||
pip-tools==6.2.0
|
pip-tools==6.2.0
|
||||||
platformdirs==2.3.0
|
platformdirs==2.3.0
|
||||||
pluggy==0.13.1
|
pluggy==0.13.1
|
||||||
|
@ -73,6 +73,7 @@ regex==2021.8.28
|
||||||
requests==2.25.1
|
requests==2.25.1
|
||||||
requests-toolbelt==0.9.1
|
requests-toolbelt==0.9.1
|
||||||
requirements-parser==0.2.0
|
requirements-parser==0.2.0
|
||||||
|
setuptools==51.0.0
|
||||||
six==1.15.0
|
six==1.15.0
|
||||||
snowballstemmer==2.1.0
|
snowballstemmer==2.1.0
|
||||||
sortedcontainers==2.3.0
|
sortedcontainers==2.3.0
|
||||||
|
@ -99,6 +100,7 @@ urwid==2.1.2
|
||||||
wcwidth==0.2.5
|
wcwidth==0.2.5
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
Werkzeug==2.0.1
|
Werkzeug==2.0.1
|
||||||
|
wheel==0.36.2
|
||||||
yamllint==1.26.1
|
yamllint==1.26.1
|
||||||
yarl==1.6.3
|
yarl==1.6.3
|
||||||
yaspin==1.5.0
|
yaspin==1.5.0
|
||||||
|
|
|
@ -4,10 +4,8 @@ Validate 3rdparty library licenses as approved.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from pkg_resources import DistInfoDistribution, working_set
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
|
||||||
import requirements
|
|
||||||
from requirements.requirement import Requirement
|
|
||||||
|
|
||||||
|
|
||||||
# Licenses approved as representing non-copyleft and not precluding commercial usage.
|
# Licenses approved as representing non-copyleft and not precluding commercial usage.
|
||||||
|
@ -63,14 +61,10 @@ APPROVED_PACKAGES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
with open("tools/python/requirements.txt") as fd:
|
|
||||||
PACKAGES = list(requirements.parse(fd))
|
|
||||||
|
|
||||||
|
|
||||||
def bash_license(ln):
|
def bash_license(ln):
|
||||||
while True:
|
while True:
|
||||||
lnn = re.sub(
|
lnn = re.sub(
|
||||||
r"[(),]|( version)|( license)|( ?v(?=\d))|([ -]clause)", "", ln.lower()
|
r"[(),]|( version)|( license)|( ?v(?=\d))|([ -]clause)|(or later)", "", ln.lower()
|
||||||
)
|
)
|
||||||
if ln != lnn:
|
if ln != lnn:
|
||||||
ln = lnn
|
ln = lnn
|
||||||
|
@ -98,59 +92,38 @@ def test_bash_license(a, b):
|
||||||
assert bash_license(a) == b
|
assert bash_license(a) == b
|
||||||
|
|
||||||
|
|
||||||
def licenses(package: Requirement):
|
def licenses(dist: DistInfoDistribution):
|
||||||
"""Get package metadata (the licenses list) from PyPi.
|
"""Get dist metadata (the licenses list) from PyPi.
|
||||||
|
|
||||||
pip and other tools use the local package metadata to introspect licenses which requires that
|
pip and other tools use the local dist metadata to introspect licenses which requires that
|
||||||
packages be installed. Going to PyPi isn't strictly reproducible both because the PyPi database
|
packages be installed. Going to PyPi isn't strictly reproducible both because the PyPi database
|
||||||
could be updated and we could see network failures but there really isn't a good way to solve
|
could be updated and we could see network failures but there really isn't a good way to solve
|
||||||
this problem.
|
this problem.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
lics = []
|
lics = []
|
||||||
version = next((v for op, v in package.specs if op == "=="), None)
|
name = dist.project_name
|
||||||
print(package.name, version)
|
version = dist.version
|
||||||
|
print(name, version, type(dist))
|
||||||
|
|
||||||
# If we don't have a version (eg. forked git dep) assume we've got the same license constraints
|
meta = dist.get_metadata(dist.PKG_INFO).split("\n")
|
||||||
# as the latest upstream release. After all we can't re-license stuff.
|
classifiers = [l.replace("Classifier: ", "", 1) for l in meta if l.startswith("Classifier: ")]
|
||||||
if not version:
|
license = bash_license(next((l for l in meta if l.startswith("License:")), "License: UNKNOWN").replace("License: ", "", 1))
|
||||||
blob = requests.get(
|
lics.extend(l for l in classifiers if l.startswith("License ::"))
|
||||||
f"https://pypi.org/pypi/{package.name}/json",
|
|
||||||
headers={"Accept": "application/json"},
|
|
||||||
).json()
|
|
||||||
if ln := bash_license(blob.get("license")):
|
|
||||||
lics.append(ln)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
version = list(blob.get("releases", {}).keys())[-1]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# If we have a version, try to pull that release's metadata since it may have more/better.
|
if not lics:
|
||||||
if version:
|
lics.append(license)
|
||||||
blob = requests.get(
|
|
||||||
f"https://pypi.org/pypi/{package.name}/{version}/json",
|
|
||||||
headers={"Accept": "application/json"},
|
|
||||||
).json()
|
|
||||||
lics.extend(
|
|
||||||
[
|
|
||||||
c
|
|
||||||
for c in blob.get("info", {}).get("classifiers", [])
|
|
||||||
if c.startswith("License")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
ln = blob.get("info", {}).get("license")
|
|
||||||
if ln and not lics:
|
|
||||||
lics.append(bash_license(ln))
|
|
||||||
|
|
||||||
return lics
|
return lics
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("package", PACKAGES)
|
@pytest.mark.parametrize("dist", (w for w in working_set if w.location.find("arrdem_source_pypi") != -1), ids=lambda dist: dist.project_name)
|
||||||
def test_approved_license(package):
|
def test_approved_license(dist: DistInfoDistribution):
|
||||||
"""Ensure that a given package is either allowed by name or uses an approved license."""
|
"""Ensure that a given package is either allowed by name or uses an approved license."""
|
||||||
|
|
||||||
_licenses = licenses(package)
|
_licenses = licenses(dist)
|
||||||
assert package.name in APPROVED_PACKAGES or any(
|
print(dist.location)
|
||||||
|
assert dist.project_name in APPROVED_PACKAGES or any(
|
||||||
lic in APPROVED_LICENSES for lic in _licenses
|
lic in APPROVED_LICENSES for lic in _licenses
|
||||||
), f"{package} was not approved and its license(s) were unknown {_licenses!r}"
|
), f"{dist.project_name} ({dist.location}) was not approved and its license(s) were unknown {_licenses!r}"
|
||||||
|
|
Loading…
Reference in a new issue