Skip to content

Commit 145cdb4

Browse files
committed
updates to resolvers. pytests failing
1 parent ce61579 commit 145cdb4

File tree

8 files changed

+328
-135
lines changed

8 files changed

+328
-135
lines changed

licensecheck/cli.py

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
from dataclasses import fields
99
from pathlib import Path
1010
from sys import exit as sysexit
11-
from sys import stdout
11+
from sys import stdout, stdin
12+
from typing import TextIO
1213

1314
from fhconfparser import FHConfParser, SimpleConf
1415

@@ -30,17 +31,24 @@ def cli() -> None: # pragma: no cover
3031
"-f",
3132
help=f"Output format. one of: {', '.join(list(formatter.formatMap))}. default=simple",
3233
)
34+
parser.add_argument(
35+
"--deps-file",
36+
"-i",
37+
"-d",
38+
help="Filename to read from (omit for stdin)",
39+
nargs="+",
40+
)
41+
parser.add_argument(
42+
"--groups",
43+
"-g",
44+
help="Select groups from supported files",
45+
nargs="+",
46+
)
3347
parser.add_argument(
3448
"--file",
3549
"-o",
3650
help="Filename to write to (omit for stdout)",
3751
)
38-
parser.add_argument(
39-
"--using",
40-
"-u",
41-
help="Environment to use e.g. requirements.txt. one of: "
42-
f"{', '.join(get_deps.USINGS)}. default=poetry",
43-
)
4452
parser.add_argument(
4553
"--ignore-packages",
4654
help="a list of packages to ignore (compat=True)",
@@ -110,31 +118,34 @@ def main(args: dict) -> int:
110118
simpleConf = SimpleConf(configparser, "licensecheck", args)
111119

112120
# File
113-
textIO = (
121+
input_file = (
122+
simpleConf.get("deps_file") or stdin
123+
)
124+
output_file = (
114125
stdout
115126
if simpleConf.get("file") is None
116127
else Path(simpleConf.get("file")).open("w", encoding="utf-8")
117128
)
118129

119130
# Get my license
120-
myLiceTxt = args["license"] if args.get("license") else packageinfo.getMyPackageLicense()
121-
myLice = license_matrix.licenseType(myLiceTxt)[0]
122-
123-
# Get list of licenses
124-
depsWithLicenses = get_deps.getDepsWithLicenses(
125-
simpleConf.get("using", "poetry"),
126-
myLice,
127-
list(map(types.ucstr, simpleConf.get("ignore_packages", []))),
128-
list(map(types.ucstr, simpleConf.get("fail_packages", []))),
129-
list(map(types.ucstr, simpleConf.get("ignore_licenses", []))),
130-
list(map(types.ucstr, simpleConf.get("fail_licenses", []))),
131-
list(map(types.ucstr, simpleConf.get("only_licenses", []))),
132-
list(map(types.ucstr, simpleConf.get("skip_dependencies", []))),
131+
this_license_text = args["license"] if args.get("license") else packageinfo.getMyPackageLicense()
132+
this_license = license_matrix.licenseType(this_license_text)[0]
133+
134+
def getFromConfig(key: str) -> list[types.ucstr]:
135+
return list(map(types.ucstr, simpleConf.get(key, [])))
136+
137+
incompatible, depsWithLicenses = get_deps.check(
138+
input_file=input_file,
139+
groups=simpleConf.get("groups", []),
140+
this_license=this_license,
141+
ignore_packages=getFromConfig("ignore_packages"),
142+
fail_packages=getFromConfig("fail_packages"),
143+
ignore_licenses=getFromConfig("ignore_licenses"),
144+
fail_licenses=getFromConfig("fail_licenses"),
145+
only_licenses=getFromConfig("only_licenses"),
146+
skip_dependencies=getFromConfig("skip_dependencies"),
133147
)
134148

135-
# Are any licenses incompatible?
136-
incompatible = any(not lice.licenseCompat for lice in depsWithLicenses)
137-
138149
# Format the results
139150
hide_output_parameters = [types.ucstr(x) for x in simpleConf.get("hide_output_parameters", [])]
140151
available_params = [param.name.upper() for param in fields(types.PackageInfo)]
@@ -147,11 +158,11 @@ def main(args: dict) -> int:
147158
if simpleConf.get("format", "simple") in formatter.formatMap:
148159
print(
149160
formatter.formatMap[simpleConf.get("format", "simple")](
150-
myLice,
161+
this_license,
151162
sorted(depsWithLicenses),
152163
hide_output_parameters,
153164
),
154-
file=textIO,
165+
file=output_file,
155166
)
156167
else:
157168
exitCode = 2
@@ -162,5 +173,5 @@ def main(args: dict) -> int:
162173

163174
# Cleanup + exit
164175
if simpleConf.get("file") is not None:
165-
textIO.close()
176+
output_file.close()
166177
return exitCode

licensecheck/get_deps.py

Lines changed: 47 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -11,126 +11,89 @@
1111
from licensecheck.resolvers import uv as res_uv
1212
from licensecheck.types import JOINS, License, PackageInfo, ucstr
1313

14-
USINGS = ["requirements", "poetry", "PEP631"]
1514

1615

17-
def getReqs(using: str, skipDependencies: list[ucstr]) -> set[ucstr]:
18-
"""Get requirements for the end user project/ lib.
16+
def resolve_requirements(
17+
requirements_paths: list[str],
18+
groups: list[str],
19+
skip_dependencies: list[ucstr],
20+
) -> set[ucstr]:
1921

20-
>>> getReqs("poetry")
21-
>>> getReqs("poetry:dev")
22-
>>> getReqs("requirements")
23-
>>> getReqs("requirements:requirements.txt;requirements-dev.txt")
24-
>>> getReqs("PEP631")
25-
>>> getReqs("PEP631:tests")
26-
27-
Args:
28-
----
29-
using (str): use requirements, poetry or PEP631.
30-
skipDependencies (list[str]): list of dependencies to skip.
31-
32-
Returns:
33-
-------
34-
set[str]: set of requirement packages
35-
36-
"""
37-
38-
_ = using.split(":", 1)
39-
using = _[0]
40-
extras = _[1].split(";") if len(_) > 1 else []
41-
if using not in USINGS:
42-
using = "poetry"
43-
44-
# if using poetry or pep621
45-
requirementsPaths = ["pyproject.toml"]
46-
47-
# Requirements
48-
if using == "requirements":
49-
requirementsPaths = ["requirements.txt"] if len(extras) > 0 else extras
50-
extras = []
5122

5223
try:
5324
return res_uv.get_reqs(
54-
using=using,
55-
skipDependencies=skipDependencies,
56-
extras=extras,
57-
requirementsPaths=requirementsPaths,
25+
skipDependencies=skip_dependencies,
26+
extras=groups,
27+
requirementsPaths=requirements_paths,
5828
)
5929

6030
except RuntimeError:
6131
pyproject = {}
62-
if "pyproject.toml" in requirementsPaths:
32+
if "pyproject.toml" in requirements_paths:
6333
pyproject = tomli.loads(Path("pyproject.toml").read_text("utf-8"))
6434

6535
# Fallback to the old resolver (hopefully we can deprecate this asap!)
6636
return res_native.get_reqs(
67-
using=using,
68-
skipDependencies=skipDependencies,
69-
extras=extras,
37+
skipDependencies=skip_dependencies,
38+
extras=groups,
7039
pyproject=pyproject,
71-
requirementsPaths=[Path(x) for x in requirementsPaths],
40+
requirementsPaths=[Path(x) for x in requirements_paths],
7241
)
7342

7443

75-
def getDepsWithLicenses(
76-
using: str,
77-
myLice: License,
78-
ignorePackages: list[ucstr],
79-
failPackages: list[ucstr],
80-
ignoreLicenses: list[ucstr],
81-
failLicenses: list[ucstr],
82-
onlyLicenses: list[ucstr],
83-
skipDependencies: list[ucstr],
84-
) -> set[PackageInfo]:
85-
"""Get a set of dependencies with licenses and determine license compatibility.
86-
87-
Args:
88-
----
89-
using (str): use requirements or poetry
90-
myLice (License): user license
91-
ignorePackages (list[ucstr]): a list of packages to ignore (compat=True)
92-
failPackages (list[ucstr]): a list of packages to fail (compat=False)
93-
ignoreLicenses (list[ucstr]): a list of licenses to ignore (skipped, compat may still be
94-
False)
95-
failLicenses (list[ucstr]): a list of licenses to fail (compat=False)
96-
onlyLicenses (list[ucstr]): a list of allowed licenses (any other license will fail)
97-
skipDependencies (list[ucstr]): a list of dependencies to skip (compat=False)
98-
99-
Returns:
100-
-------
101-
tuple[License, set[PackageInfo]]: tuple of
102-
my package license
103-
set of updated dependencies with licenseCompat set
104-
105-
"""
106-
reqs = getReqs(using, skipDependencies)
44+
def check(
45+
requirements_paths: list[str],
46+
groups: list[str],
47+
this_license: License,
48+
ignore_packages: list[ucstr] | None = None,
49+
fail_packages: list[ucstr] | None = None,
50+
ignore_licenses: list[ucstr] | None = None,
51+
fail_licenses: list[ucstr] | None = None,
52+
only_licenses: list[ucstr] | None = None,
53+
skip_dependencies: list[ucstr] | None = None,
54+
) -> tuple[bool, set[PackageInfo]]:
55+
56+
# Def values
57+
ignore_packages = ignore_packages or []
58+
fail_packages = fail_packages or []
59+
ignore_licenses = ignore_licenses or []
60+
fail_licenses = fail_licenses or []
61+
only_licenses = only_licenses or []
62+
skip_dependencies = skip_dependencies or []
63+
64+
65+
requirements = resolve_requirements(requirements_paths, groups, skip_dependencies)
10766

10867
ignoreLicensesType = license_matrix.licenseType(
109-
ucstr(JOINS.join(ignoreLicenses)), ignoreLicenses
68+
ucstr(JOINS.join(ignore_licenses)), ignore_licenses
11069
)
111-
failLicensesType = license_matrix.licenseType(ucstr(JOINS.join(failLicenses)), ignoreLicenses)
112-
onlyLicensesType = license_matrix.licenseType(ucstr(JOINS.join(onlyLicenses)), ignoreLicenses)
70+
failLicensesType = license_matrix.licenseType(ucstr(JOINS.join(fail_licenses)), ignore_licenses)
71+
onlyLicensesType = license_matrix.licenseType(ucstr(JOINS.join(only_licenses)), ignore_licenses)
11372
# licenseType will always return NO_LICENSE when onlyLicenses is empty # noqa: ERA001
11473
if License.NO_LICENSE in onlyLicensesType:
11574
onlyLicensesType.remove(License.NO_LICENSE)
11675

11776
# Check it is compatible with packages and add a note
118-
packages = packageinfo.getPackages(reqs)
77+
packages = packageinfo.getPackages(requirements)
11978
for package in packages:
12079
# Deal with --ignore-packages and --fail-packages
12180
package.licenseCompat = False
12281
packageName = package.name.upper()
123-
if packageName in ignorePackages:
82+
if packageName in ignore_packages:
12483
package.licenseCompat = True
125-
elif packageName in failPackages:
84+
elif packageName in fail_packages:
12685
pass # package.licenseCompat = False
12786
# Else get compat with myLice
12887
else:
12988
package.licenseCompat = license_matrix.depCompatWMyLice(
130-
myLice,
131-
license_matrix.licenseType(package.license, ignoreLicenses),
89+
this_license,
90+
license_matrix.licenseType(package.license, ignore_licenses),
13291
ignoreLicensesType,
13392
failLicensesType,
13493
onlyLicensesType,
13594
)
136-
return packages
95+
96+
# Are any licenses incompatible?
97+
incompatible = any(not package.licenseCompat for package in packages)
98+
99+
return incompatible, packages

licensecheck/resolvers/native.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,39 @@
88

99
from packaging.requirements import Requirement
1010
from packaging.utils import canonicalize_name
11+
import tomli
1112

1213
from licensecheck.session import session
1314
from licensecheck.types import ucstr
1415

1516

1617
def get_reqs(
18+
skipDependencies: list[ucstr],
19+
extras: list[str],
20+
requirementsPaths: list[Path],
21+
pyproject: dict[str, Any],
22+
) -> set[ucstr]:
23+
24+
using = "[unknown]"
25+
26+
# determine using based on file type
27+
for requirementsPath in requirementsPaths:
28+
try:
29+
tomli.loads(requirementsPath.read_text("utf-8"))
30+
if pyproject.get("project", {}).get("dependencies") is not None:
31+
using = "PEP631"
32+
if pyproject.get("tool", {}).get("poetry", {}).get("dependencies") is not None:
33+
using = "poetry"
34+
35+
except tomli.TOMLDecodeError:
36+
using = "requirements"
37+
38+
39+
return do_get_reqs(using=using, skipDependencies=skipDependencies, extras=extras, pyproject=pyproject, requirementsPaths=requirementsPaths)
40+
41+
42+
43+
def do_get_reqs(
1744
using: str,
1845
skipDependencies: list[ucstr],
1946
extras: list[str],

licensecheck/resolvers/uv.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,10 @@
1313

1414

1515
def get_reqs(
16-
using: str,
1716
skipDependencies: list[ucstr],
1817
extras: list[str],
1918
requirementsPaths: list[str],
2019
) -> set[ucstr]:
21-
if using == "requirements" and len(extras) > 0:
22-
msg = "You may not use extras with requirements.txt"
23-
raise RuntimeError(msg)
2420

2521
for idx, requirement in enumerate(requirementsPaths):
2622
if not Path(requirement).exists():
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[project]
2+
# Added to example.
3+
name = "pep631_socks"
4+
license = "gpl-3.0-or-later"
5+
6+
# Example copied from https://peps.python.org/pep-0631/#example
7+
dependencies = [
8+
'cached-property >= 1.2.0, < 2',
9+
'distro >= 1.5.0, < 2',
10+
'docker[ssh] >= 4.2.2, < 5',
11+
'dockerpty >= 0.4.1, < 1',
12+
'docopt >= 0.6.1, < 1',
13+
'jsonschema >= 2.5.1, < 4',
14+
'PyYAML >= 3.10, < 6',
15+
'python-dotenv >= 0.13.0, < 1',
16+
'requests >= 2.20.0, < 3',
17+
'texttable >= 0.9.0, < 2',
18+
'websocket-client >= 0.32.0, < 1',
19+
# 'toskip >= 0.0.1',
20+
21+
# Conditional
22+
'backports.shutil_get_terminal_size == 1.0.0; python_version < "3.3"',
23+
'backports.ssl_match_hostname >= 3.5, < 4; python_version < "3.5"',
24+
'colorama >= 0.4, < 1; sys_platform == "win32"',
25+
'enum34 >= 1.0.4, < 2; python_version < "3.4"',
26+
'ipaddress >= 1.0.16, < 2; python_version < "3.3"',
27+
'subprocess32 >= 3.5.4, < 4; python_version < "3.2"',
28+
]
29+
30+
[project.optional-dependencies]
31+
socks = [ 'PySocks >= 1.5.6, != 1.5.7, < 2' ]
32+
tests = [
33+
'ddt >= 1.2.2, < 2',
34+
'pytest < 6',
35+
'mock >= 1.0.1, < 4; python_version < "3.4"',
36+
]
37+
38+
# Added to example.
39+
[tool.licensecheck]
40+
using = "PEP631:socks"

0 commit comments

Comments
 (0)