Skip to content

Commit 8c6d3dd

Browse files
authored
feat: no entrypoint registration required in tree (#4207)
Signed-off-by: John Andersen <[email protected]>
1 parent e2fb8c4 commit 8c6d3dd

File tree

7 files changed

+95
-91
lines changed

7 files changed

+95
-91
lines changed

cve_bin_tool/checkers/__init__.py

+21
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@
66

77
import collections
88
import re
9+
import sys
910

1011
from cve_bin_tool.error_handler import InvalidCheckerError
1112
from cve_bin_tool.util import regex_find
1213

14+
if sys.version_info >= (3, 10):
15+
from importlib import metadata as importlib_metadata
16+
else:
17+
import importlib_metadata
18+
if sys.version_info >= (3, 9):
19+
import importlib.resources as resources
20+
else:
21+
import importlib_resources as resources
22+
1323
__all__ = [
1424
"Checker",
1525
"VendorProductPair",
@@ -439,3 +449,14 @@ def get_version(self, lines, filename):
439449
)
440450

441451
return version_info
452+
453+
454+
BUILTIN_CHECKERS = {
455+
checker_path.stem: importlib_metadata.EntryPoint(
456+
checker_path.stem,
457+
f'cve_bin_tool.checkers.{checker_path.stem}:{"".join(checker_path.stem.replace("_", " ").title().split())}Checker',
458+
"cve_bin_tool.checkers",
459+
)
460+
for checker_path in resources.files("cve_bin_tool.checkers").iterdir()
461+
if (checker_path.suffix == ".py" and not checker_path.name.startswith("__"))
462+
}

cve_bin_tool/cli.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,6 @@
7575
from cve_bin_tool.version import VERSION
7676
from cve_bin_tool.version_scanner import VersionScanner
7777

78-
if sys.version_info >= (3, 10):
79-
from importlib import metadata as importlib_metadata
80-
else:
81-
import importlib_metadata
82-
8378
sys.excepthook = excepthook # Always install excepthook for entrypoint module.
8479

8580

@@ -911,12 +906,10 @@ def main(argv=None):
911906
runs = args["runs"]
912907
skips = list(
913908
map(
914-
lambda checker: checker.name,
909+
lambda checker_name: checker_name,
915910
filter(
916-
lambda checker: checker.name not in runs,
917-
importlib_metadata.entry_points().select(
918-
group="cve_bin_tool.checker"
919-
),
911+
lambda checker_name: checker_name not in runs,
912+
VersionScanner.available_checkers(),
920913
),
921914
)
922915
)

cve_bin_tool/parsers/parse.py

+46-3
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,67 @@
22
# SPDX-License-Identifier: GPL-3.0-or-later
33
from __future__ import annotations
44

5+
import os
6+
import re
57
import sys
68

79
if sys.version_info >= (3, 10):
810
from importlib import metadata as importlib_metadata
911
else:
1012
import importlib_metadata
13+
if sys.version_info >= (3, 9):
14+
import importlib.resources as resources
15+
else:
16+
import importlib_resources as resources
1117

1218
from cve_bin_tool.parsers import Parser
1319

1420
PARSERS_ENTRYPOINT = "cve_bin_tool.parsers"
1521

1622

23+
def enumerate_builtin_parsers():
24+
"""Reads the files in cve_bin_tool/parsers/ to auto determine list"""
25+
parsers = {}
26+
for path in resources.files("cve_bin_tool.parsers").iterdir():
27+
if path.suffix != ".py" or path.stem in ("__init__", "parser"):
28+
continue
29+
contents = path.read_text()
30+
for re_match in re.finditer(r"^class (\w+)", contents, re.MULTILINE):
31+
parser_cls_name = re_match[1]
32+
parsers[".".join([path.stem, parser_cls_name])] = ":".join(
33+
[
34+
str(path.relative_to(path.parents[2]).with_suffix("")).replace(
35+
os.path.sep, "."
36+
),
37+
parser_cls_name,
38+
],
39+
)
40+
return parsers
41+
42+
43+
BUILTIN_PARSERS = {
44+
parser_entry_point_name: importlib_metadata.EntryPoint(
45+
parser_entry_point_name,
46+
entry_point_path,
47+
"cve_bin_tool.parsers",
48+
)
49+
for (
50+
parser_entry_point_name,
51+
entry_point_path,
52+
) in enumerate_builtin_parsers().items()
53+
}
54+
55+
1756
def load_valid_files() -> dict[str, list[type[Parser]]]:
1857
"""Loads file parsers"""
1958
valid_files: dict[str, list[type[Parser]]] = {}
20-
for entrypoint in importlib_metadata.entry_points().select(
21-
group=PARSERS_ENTRYPOINT
22-
):
59+
60+
for entrypoint in [
61+
*BUILTIN_PARSERS.values(),
62+
*importlib_metadata.entry_points().select(
63+
group=PARSERS_ENTRYPOINT,
64+
),
65+
]:
2366
parser_cls = entrypoint.load()
2467
for match_filename in getattr(parser_cls, "PARSER_MATCH_FILENAMES", []):
2568
valid_files.setdefault(match_filename, [])

cve_bin_tool/version_scanner.py

+22-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from pathlib import Path
1010
from typing import Iterator
1111

12-
from cve_bin_tool.checkers import Checker
12+
from cve_bin_tool.checkers import BUILTIN_CHECKERS, Checker
1313
from cve_bin_tool.cvedb import CVEDB
1414
from cve_bin_tool.egg_updater import IS_DEVELOP, update_egg
1515
from cve_bin_tool.error_handler import ErrorMode
@@ -82,22 +82,32 @@ def __init__(
8282
@classmethod
8383
def load_checkers(cls) -> dict[str, type[Checker]]:
8484
"""Loads CVE checkers"""
85-
checkers = dict(
86-
map(
87-
lambda checker: (checker.name, checker.load()),
88-
importlib_metadata.entry_points().select(group=cls.CHECKER_ENTRYPOINT),
85+
entrypoint_checkers = {
86+
checker.name: checker.load()
87+
for checker in importlib_metadata.entry_points().select(
88+
group=cls.CHECKER_ENTRYPOINT
8989
)
90-
)
91-
return checkers
90+
}
91+
builtin_checkers = {
92+
checker.name: checker.load() for checker in BUILTIN_CHECKERS.values()
93+
}
94+
all_checkers = {
95+
**builtin_checkers,
96+
**entrypoint_checkers,
97+
}
98+
return all_checkers
9299

93100
@classmethod
94101
def available_checkers(cls) -> list[str]:
95102
"""Discover and list available checker by inspecting the entry points"""
96-
checkers = importlib_metadata.entry_points().select(
97-
group=cls.CHECKER_ENTRYPOINT
98-
)
99-
checker_list = [item.name for item in checkers]
100-
return checker_list
103+
entrypoint_checker_names = [
104+
checker.name
105+
for checker in importlib_metadata.entry_points().select(
106+
group=cls.CHECKER_ENTRYPOINT
107+
)
108+
]
109+
all_checker_names = list(BUILTIN_CHECKERS.keys()) + entrypoint_checker_names
110+
return all_checker_names
101111

102112
def remove_skiplist(self, skips: list[str]) -> None:
103113
"""Remove specific checkers If a checker is in the skip list, it will be removed from this dictionary. If it's not found or is not a valid checker name, it logs error messages."""

example/oot-checker/oot_checker/checker_name.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ class CurlChecker(Checker):
1818
CONTAINS_PATTERNS = [r"curl ([678]+\.[0-9]+\.[0-9]+)"]
1919
FILENAME_PATTERNS = [r"curl"]
2020
VERSION_PATTERNS = [r"curl ([678]+\.[0-9]+\.[0-9]+)"]
21-
VENDOR_PACKAGE = [("haxx", "curl")]
21+
VENDOR_PRODUCT = [("haxx", "curl")]

setup.py

-54
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import ast
55
import os
66
import pathlib
7-
import re
87

98
from setuptools import find_packages, setup
109

@@ -23,29 +22,6 @@
2322
break
2423

2524

26-
def enumerate_entry_points_parsers():
27-
"""Reads the files in cve_bin_tool/parsers/to auto determine list"""
28-
parsers = {}
29-
for path in PACKAGE_ROOT_PATH.joinpath(
30-
"cve_bin_tool",
31-
"parsers",
32-
).glob("*.py"):
33-
if "__init__" == path.stem:
34-
continue
35-
contents = path.read_text()
36-
for re_match in re.finditer(r"^class (\w+)", contents, re.MULTILINE):
37-
parser_cls_name = re_match[1]
38-
parsers[".".join([path.stem, parser_cls_name])] = ":".join(
39-
[
40-
str(path.relative_to(PACKAGE_ROOT_PATH).with_suffix("")).replace(
41-
os.path.sep, "."
42-
),
43-
parser_cls_name,
44-
],
45-
)
46-
return parsers
47-
48-
4925
setup_kwargs = dict(
5026
name="cve-bin-tool",
5127
version=VERSION,
@@ -97,36 +73,6 @@ def enumerate_entry_points_parsers():
9773
"cve-bin-tool = cve_bin_tool.cli:main",
9874
"csv2cve = cve_bin_tool.csv2cve:main",
9975
],
100-
"cve_bin_tool.checker": [
101-
"{} = cve_bin_tool.checkers.{}:{}".format(
102-
filename.replace(".py", ""),
103-
filename.replace(".py", ""),
104-
"".join(
105-
(filename.replace(".py", "") + " checker")
106-
.replace("_", " ")
107-
.title()
108-
.split()
109-
),
110-
)
111-
for filename in os.listdir(
112-
os.path.join(
113-
os.path.abspath(os.path.dirname(__file__)),
114-
"cve_bin_tool",
115-
"checkers",
116-
)
117-
)
118-
if filename.endswith(".py") and "__init__" not in filename
119-
],
120-
"cve_bin_tool.parsers": [
121-
"{} = {}".format(
122-
parser_entry_point_name,
123-
entry_point_path,
124-
)
125-
for (
126-
parser_entry_point_name,
127-
entry_point_path,
128-
) in enumerate_entry_points_parsers().items()
129-
],
13076
},
13177
)
13278

test/test_checkers.py

+2-11
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,13 @@
44
from __future__ import annotations
55

66
import re
7-
import sys
87

98
import pytest
109

11-
from cve_bin_tool.checkers import Checker, VendorProductPair
10+
from cve_bin_tool.checkers import BUILTIN_CHECKERS, Checker, VendorProductPair
1211
from cve_bin_tool.egg_updater import IS_DEVELOP, update_egg
1312
from cve_bin_tool.log import LOGGER
1413

15-
if sys.version_info >= (3, 10):
16-
from importlib import metadata as importlib_metadata
17-
else:
18-
import importlib_metadata
19-
2014
Pattern = type(re.compile("", 0))
2115

2216

@@ -136,10 +130,7 @@ def setup_class(cls):
136130
)
137131
def test_filename_is(self, checker_name, file_name, expected_results):
138132
"""Test a checker's filename detection"""
139-
checkers = importlib_metadata.entry_points().select(
140-
group="cve_bin_tool.checker"
141-
)
142-
for checker in checkers:
133+
for checker in BUILTIN_CHECKERS.values():
143134
if checker.name == checker_name:
144135
Checker = checker.load()
145136
checker = Checker()

0 commit comments

Comments
 (0)