Skip to content

Commit

Permalink
Merge pull request #32 from larsrinn/strategy_from_pyproject_toml
Browse files Browse the repository at this point in the history
Read strategy from pyproject toml
  • Loading branch information
Olivier Chédru authored Feb 25, 2020
2 parents 2921c76 + 22f40e6 commit d11ef8d
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 41 deletions.
101 changes: 75 additions & 26 deletions liccheck/command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import textwrap
import sys
import semantic_version
import toml

import pkg_resources

Expand All @@ -26,12 +27,72 @@
except ImportError:
from pip.req import parse_requirements

try:
FileNotFoundError
except NameError:
FileNotFoundError = IOError


class NoValidConfigurationInPyprojectToml(BaseException):
pass


class Strategy:
def __init__(self):
self.AUTHORIZED_LICENSES = []
self.UNAUTHORIZED_LICENSES = []
self.AUTHORIZED_PACKAGES = []
def __init__(self, authorized_licenses, unauthorized_licenses, authorized_packages):
self.AUTHORIZED_LICENSES = authorized_licenses
self.UNAUTHORIZED_LICENSES = unauthorized_licenses
self.AUTHORIZED_PACKAGES = authorized_packages

@classmethod
def from_pyproject_toml(cls):
try:
pyproject_toml = toml.load("pyproject.toml")
except FileNotFoundError:
raise NoValidConfigurationInPyprojectToml

try:
liccheck_section = pyproject_toml["tool"]["liccheck"]
except KeyError:
raise NoValidConfigurationInPyprojectToml

def elements_to_lower_str(lst):
return [str(_).lower() for _ in lst]

strategy = cls(
authorized_licenses=elements_to_lower_str(liccheck_section.get("authorized_licenses", [])),
unauthorized_licenses=elements_to_lower_str(liccheck_section.get("unauthorized_licenses", [])),
authorized_packages=liccheck_section.get("authorized_packages", dict())
)
return strategy

@classmethod
def from_config(cls, strategy_file):
config = ConfigParser()
# keep case of options
config.optionxform = str
config.read(strategy_file)

def get_config_list(section, option):
try:
value = config.get(section, option)
except NoOptionError:
return []
return [item for item in value.lower().split('\n') if item]

authorized_packages = dict()
if config.has_section('Authorized Packages'):
for name, value in config.items('Authorized Packages'):
authorized_packages[name] = value

strategy = cls(
authorized_licenses=get_config_list('Licenses', 'authorized_licenses'),
unauthorized_licenses=get_config_list('Licenses', 'unauthorized_licenses'),
authorized_packages=authorized_packages,
)
if config.has_section('Authorized Packages'):
for name, value in config.items('Authorized Packages'):
strategy.AUTHORIZED_PACKAGES[name] = value
return strategy


class Level(enum.Enum):
Expand Down Expand Up @@ -211,27 +272,15 @@ def format(l):
return ret


def read_strategy(strategy_file):
config = ConfigParser()
# keep case of options
config.optionxform = str
config.read(strategy_file)
strategy = Strategy()

def get_config_list(section, option):
try:
value = config.get(section, option)
except NoOptionError:
return []
return [item for item in value.lower().split('\n') if item]

strategy.AUTHORIZED_LICENSES = get_config_list('Licenses', 'authorized_licenses')
strategy.UNAUTHORIZED_LICENSES = get_config_list('Licenses', 'unauthorized_licenses')
strategy.AUTHORIZED_PACKAGES = dict()
if config.has_section('Authorized Packages'):
for name, value in config.items('Authorized Packages'):
strategy.AUTHORIZED_PACKAGES[name] = value
return strategy
def read_strategy(strategy_file=None):
try:
return Strategy.from_pyproject_toml()
except NoValidConfigurationInPyprojectToml:
pass
if strategy_file is None:
print("Need to either configure pyproject.toml or provide a strategy file")
sys.exit(1)
return Strategy.from_config(strategy_file=strategy_file)


def parse_args(args):
Expand All @@ -240,7 +289,7 @@ def parse_args(args):
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument(
'-s', '--sfile', dest='strategy_ini_file', help='strategy ini file',
required=True)
required=False)
parser.add_argument(
'-l', '--level', choices=Level,
default=Level.STANDARD, type=Level.starting,
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pip>=9.0.1
enum34;python_version<"3.4"
semantic_version
toml
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@

python_requires='>=2.7',

install_requires=['semantic_version'],
install_requires=['semantic_version', 'toml'],

# List additional groups of dependencies here (e.g. development
# dependencies). You can install these using the following syntax,
Expand Down
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pytest>=3.6.3
python-openid;python_version<="2.7"
python3-openid;python_version>="3.0"
pytest-mock>=1.10
10 changes: 5 additions & 5 deletions tests/test_check_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

@pytest.fixture('session')
def strategy():
strategy = Strategy()
strategy.AUTHORIZED_LICENSES = ['authorized 1', 'authorized 2']
strategy.UNAUTHORIZED_LICENSES = ['unauthorized 1', 'unauthorized 2']
strategy.AUTHORIZED_PACKAGES = {'whitelisted': '1'}

strategy = Strategy(
authorized_licenses=['authorized 1', 'authorized 2'],
unauthorized_licenses=['unauthorized 1', 'unauthorized 2'],
authorized_packages={'whitelisted': '1'}
)
return strategy


Expand Down
98 changes: 89 additions & 9 deletions tests/test_read_strategy.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,95 @@
import os

import pytest

from liccheck.command_line import read_strategy
from liccheck.command_line import Strategy, NoValidConfigurationInPyprojectToml, read_strategy


def test_absent_sections(tmpfile):
tmpfh, tmppath = tmpfile
tmpfh.write("""
class TestReadFromConfig:
def test_absent_sections_in_config_file(self, tmpfile):
tmpfh, tmppath = tmpfile
tmpfh.write("""
[Licenses]
""")
tmpfh.close()
strategy = read_strategy(tmppath)
assert strategy.AUTHORIZED_LICENSES == []
assert strategy.UNAUTHORIZED_LICENSES == []
assert strategy.AUTHORIZED_PACKAGES == {}
tmpfh.close()
strategy = Strategy.from_config(strategy_file=tmppath)
assert strategy.AUTHORIZED_LICENSES == []
assert strategy.UNAUTHORIZED_LICENSES == []
assert strategy.AUTHORIZED_PACKAGES == {}


class TestReadFromPyprojectToml:
def test_raises_if_no_pyproject_toml(self):
with pytest.raises(NoValidConfigurationInPyprojectToml):
Strategy.from_pyproject_toml()

@pytest.mark.usefixtures("empty_pyproject_toml_in_cwd")
def test_falls_back_to_strategy_if_no_liccheck_section_in_pyproject_toml(self):
with pytest.raises(NoValidConfigurationInPyprojectToml):
Strategy.from_pyproject_toml()

@pytest.mark.usefixtures("pyproject_toml_with_liccheck_section_in_cwd")
def test_with_liccheck_section_in_pyproject_toml(self):
strategy = Strategy.from_pyproject_toml()
assert strategy.AUTHORIZED_LICENSES == [
"bsd",
"new bsd",
"bsd license"
]
assert strategy.UNAUTHORIZED_LICENSES == [
"gpl v3"
]
assert strategy.AUTHORIZED_PACKAGES == {
"uuid": "1.30"
}

@pytest.fixture
def empty_pyproject_toml_in_cwd(self, tmpdir):
cwd = os.getcwd()
os.chdir(str(tmpdir))
open("pyproject.toml", "w").close()
yield
os.chdir(cwd)

@pytest.fixture
def pyproject_toml_with_liccheck_section_in_cwd(self, empty_pyproject_toml_in_cwd):
with open("pyproject.toml", "w") as file:
file.write(
"""
[tool.liccheck]
authorized_licenses = [
"BSD",
"new bsd",
"bsd license"
]
unauthorized_licenses = [
"gpl v3"
]
[tool.liccheck.authorized_packages]
uuid = "1.30"
"""
)


class TestReadStrategy:
@pytest.mark.usefixtures("from_pyproject_toml_raising")
def test_falls_back_to_config_if_no_valid_pyproject_toml(self, mocker):
from_config_mock = mocker.patch("liccheck.command_line.Strategy.from_config")
read_strategy(strategy_file="strategy_file")
from_config_mock.assert_called_once_with(strategy_file="strategy_file")

@pytest.mark.usefixtures("from_pyproject_toml_raising")
def test_displays_error_if_no_valid_pyproject_toml_and_no_strategy_file(self, capsys):
with pytest.raises(SystemExit) as exc:
read_strategy(strategy_file=None)
assert exc.value.code == 1
capture_result = capsys.readouterr()
std, _ = capture_result
assert "Need to either configure pyproject.toml or provide a strategy file" in std.split("\n")

@pytest.fixture
def from_pyproject_toml_raising(self, mocker):
mocker.patch(
"liccheck.command_line.Strategy.from_pyproject_toml",
side_effect=NoValidConfigurationInPyprojectToml
)

0 comments on commit d11ef8d

Please sign in to comment.