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(
|
||||
name = "rules_python",
|
||||
remote = "https://github.com/bazelbuild/rules_python.git",
|
||||
tag = "0.3.0",
|
||||
tag = "0.4.0",
|
||||
# commit = "...",
|
||||
)
|
||||
|
||||
|
@ -48,14 +48,20 @@ register_toolchains("//tools/python:python3_toolchain")
|
|||
|
||||
# pip package pinnings need to be initialized.
|
||||
# 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",
|
||||
requirements = "//tools/python:requirements.txt",
|
||||
requirements_lock = "//tools/python:requirements.txt",
|
||||
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(
|
||||
# name = "rules_zapp",
|
||||
# remote = "https://github.com/arrdem/rules_zapp.git",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import argparse
|
||||
import sys
|
||||
|
||||
from black import patched_main, nullcontext
|
||||
from black import nullcontext, patched_main
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
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"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
@ -40,8 +42,5 @@ py_pytest(
|
|||
data = [
|
||||
"requirements.txt",
|
||||
],
|
||||
deps = [
|
||||
py_requirement("requests"),
|
||||
py_requirement("requirements-parser"),
|
||||
]
|
||||
deps = all_requirements,
|
||||
)
|
||||
|
|
|
@ -14,6 +14,9 @@ load("@rules_zapp//zapp:zapp.bzl",
|
|||
"zapp_binary",
|
||||
)
|
||||
|
||||
load("@bazel_skylib//lib:sets.bzl", "sets")
|
||||
|
||||
|
||||
def py_requirement(*args, **kwargs):
|
||||
"""A re-export of requirement()"""
|
||||
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"
|
||||
|
||||
deps = [
|
||||
deps = sets.to_list(sets.make([
|
||||
py_requirement("pytest"),
|
||||
py_requirement("jedi"),
|
||||
py_requirement("pytest-pudb"),
|
||||
] + deps
|
||||
] + deps))
|
||||
|
||||
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):
|
||||
# This really needs to be a py_image_test.
|
||||
# Not clear how to achieve that.
|
||||
# py_image(
|
||||
# name = name + ".containerized",
|
||||
# zapp_binary(
|
||||
# name = name + ".hermetic",
|
||||
# main = f,
|
||||
# args = args,
|
||||
# srcs = srcs,
|
||||
# deps = deps,
|
||||
# test = True,
|
||||
# zip_safe = False,
|
||||
# **kwargs,
|
||||
# )
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ packaging==20.9
|
|||
parso==0.8.2
|
||||
pathspec==0.9.0
|
||||
pep517==0.11.0
|
||||
piexif==1.1.3
|
||||
pip==20.3.3
|
||||
pip-tools==6.2.0
|
||||
platformdirs==2.3.0
|
||||
pluggy==0.13.1
|
||||
|
@ -73,6 +73,7 @@ regex==2021.8.28
|
|||
requests==2.25.1
|
||||
requests-toolbelt==0.9.1
|
||||
requirements-parser==0.2.0
|
||||
setuptools==51.0.0
|
||||
six==1.15.0
|
||||
snowballstemmer==2.1.0
|
||||
sortedcontainers==2.3.0
|
||||
|
@ -99,6 +100,7 @@ urwid==2.1.2
|
|||
wcwidth==0.2.5
|
||||
webencodings==0.5.1
|
||||
Werkzeug==2.0.1
|
||||
wheel==0.36.2
|
||||
yamllint==1.26.1
|
||||
yarl==1.6.3
|
||||
yaspin==1.5.0
|
||||
|
|
|
@ -4,10 +4,8 @@ Validate 3rdparty library licenses as approved.
|
|||
|
||||
import re
|
||||
|
||||
from pkg_resources import DistInfoDistribution, working_set
|
||||
import pytest
|
||||
import requests
|
||||
import requirements
|
||||
from requirements.requirement import Requirement
|
||||
|
||||
|
||||
# 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):
|
||||
while True:
|
||||
lnn = re.sub(
|
||||
r"[(),]|( version)|( license)|( ?v(?=\d))|([ -]clause)", "", ln.lower()
|
||||
r"[(),]|( version)|( license)|( ?v(?=\d))|([ -]clause)|(or later)", "", ln.lower()
|
||||
)
|
||||
if ln != lnn:
|
||||
ln = lnn
|
||||
|
@ -98,59 +92,38 @@ def test_bash_license(a, b):
|
|||
assert bash_license(a) == b
|
||||
|
||||
|
||||
def licenses(package: Requirement):
|
||||
"""Get package metadata (the licenses list) from PyPi.
|
||||
def licenses(dist: DistInfoDistribution):
|
||||
"""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
|
||||
could be updated and we could see network failures but there really isn't a good way to solve
|
||||
this problem.
|
||||
|
||||
"""
|
||||
|
||||
lics = []
|
||||
version = next((v for op, v in package.specs if op == "=="), None)
|
||||
print(package.name, version)
|
||||
name = dist.project_name
|
||||
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
|
||||
# as the latest upstream release. After all we can't re-license stuff.
|
||||
if not version:
|
||||
blob = requests.get(
|
||||
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
|
||||
meta = dist.get_metadata(dist.PKG_INFO).split("\n")
|
||||
classifiers = [l.replace("Classifier: ", "", 1) for l in meta if l.startswith("Classifier: ")]
|
||||
license = bash_license(next((l for l in meta if l.startswith("License:")), "License: UNKNOWN").replace("License: ", "", 1))
|
||||
lics.extend(l for l in classifiers if l.startswith("License ::"))
|
||||
|
||||
# If we have a version, try to pull that release's metadata since it may have more/better.
|
||||
if version:
|
||||
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))
|
||||
if not lics:
|
||||
lics.append(license)
|
||||
|
||||
return lics
|
||||
|
||||
|
||||
@pytest.mark.parametrize("package", PACKAGES)
|
||||
def test_approved_license(package):
|
||||
@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(dist: DistInfoDistribution):
|
||||
"""Ensure that a given package is either allowed by name or uses an approved license."""
|
||||
|
||||
_licenses = licenses(package)
|
||||
assert package.name in APPROVED_PACKAGES or any(
|
||||
_licenses = licenses(dist)
|
||||
print(dist.location)
|
||||
assert dist.project_name in APPROVED_PACKAGES or any(
|
||||
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