mirror of
https://github.com/wassname/pip-package-list.git
synced 2026-06-27 16:10:20 +08:00
Move main func into dedicated function
This commit is contained in:
+28
-16
@@ -1,26 +1,38 @@
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from .requirements import RequirementsEditableEntry, RequirementsRecursiveEntry
|
||||
from .requirements_txt_parser import parse_requirements_txt
|
||||
from .setup_py_parser import parse_setup_py
|
||||
from .list import list as list_packages
|
||||
|
||||
|
||||
def main() -> int:
|
||||
input_file = sys.argv[1]
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--recurse-recursive",
|
||||
default=False,
|
||||
help="recurse into -r entries",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--recurse-editable",
|
||||
default=False,
|
||||
help="recurse into -e entries",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"file_paths",
|
||||
nargs="+",
|
||||
help="list of requirements.txt or setup.py files",
|
||||
)
|
||||
|
||||
aggregated_entries = []
|
||||
for requirement in parse_requirements_txt(input_file):
|
||||
if isinstance(requirement, RequirementsRecursiveEntry):
|
||||
aggregated_entries.extend(
|
||||
list(parse_requirements_txt(requirement.path))
|
||||
)
|
||||
elif isinstance(requirement, RequirementsEditableEntry):
|
||||
aggregated_entries.extend(list(parse_setup_py(requirement.path)))
|
||||
else:
|
||||
aggregated_entries.append(requirement)
|
||||
args = parser.parse_args()
|
||||
|
||||
for requirement in list_packages(
|
||||
args.file_paths,
|
||||
recurse_recursive=args.recurse_recursive,
|
||||
recurse_editable=args.recurse_editable,
|
||||
):
|
||||
print(requirement)
|
||||
|
||||
for req in aggregated_entries:
|
||||
print(req.source.line, req.source.path)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import enum
|
||||
|
||||
|
||||
class PackageListFileType(enum.Enum):
|
||||
REQUIREMENTS_TXT = "requirements.txt"
|
||||
SETUP_PY = "setup.py"
|
||||
|
||||
|
||||
def identify_package_list_file_type(file_path: str) -> PackageListFileType:
|
||||
if file_path.endswith("setup.py"):
|
||||
return PackageListFileType.SETUP_PY
|
||||
|
||||
return PackageListFileType.REQUIREMENTS_TXT
|
||||
@@ -0,0 +1,49 @@
|
||||
from typing import Generator, List
|
||||
|
||||
from .identify_package_list_file_type import (
|
||||
PackageListFileType,
|
||||
identify_package_list_file_type,
|
||||
)
|
||||
from .requirements import (
|
||||
RequirementsEditableEntry,
|
||||
RequirementsEntry,
|
||||
RequirementsRecursiveEntry,
|
||||
)
|
||||
from .requirements_txt_parser import parse_requirements_txt
|
||||
from .setup_py_parser import parse_setup_py
|
||||
|
||||
|
||||
def list(
|
||||
file_paths: List[str],
|
||||
recurse_recursive: bool = True,
|
||||
recurse_editable: bool = True,
|
||||
) -> Generator[RequirementsEntry, None, None]:
|
||||
generators = []
|
||||
|
||||
for file_path in file_paths:
|
||||
package_list_file_type = identify_package_list_file_type(file_path)
|
||||
if package_list_file_type == PackageListFileType.REQUIREMENTS_TXT:
|
||||
generators.append(parse_requirements_txt(file_path))
|
||||
elif package_list_file_type == PackageListFileType.SETUP_PY:
|
||||
generators.append(parse_setup_py(file_path))
|
||||
|
||||
while len(generators) > 0:
|
||||
for requirement in generators[0]:
|
||||
if isinstance(requirement, RequirementsRecursiveEntry):
|
||||
if recurse_recursive:
|
||||
generators.append(
|
||||
parse_requirements_txt(requirement.absolute_path)
|
||||
)
|
||||
else:
|
||||
yield requirement
|
||||
elif isinstance(requirement, RequirementsEditableEntry):
|
||||
if recurse_editable:
|
||||
generators.append(
|
||||
parse_setup_py(requirement.resolved_absolute_path)
|
||||
)
|
||||
else:
|
||||
yield requirement
|
||||
else:
|
||||
yield requirement
|
||||
|
||||
generators = generators[1:]
|
||||
@@ -19,12 +19,23 @@ class RequirementsEntry:
|
||||
|
||||
@dataclass
|
||||
class RequirementsRecursiveEntry(RequirementsEntry):
|
||||
path: str
|
||||
original_path: str
|
||||
absolute_path: str
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"-r {self.absolute_path}"
|
||||
|
||||
|
||||
@dataclass
|
||||
class RequirementsEditableEntry(RequirementsEntry):
|
||||
path: str
|
||||
original_path: str
|
||||
absolute_path: str
|
||||
|
||||
resolved_path: str
|
||||
resolved_absolute_path: str
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"-e {self.absolute_path}"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -33,6 +44,13 @@ class RequirementsVCSPackageEntry(RequirementsEntry):
|
||||
uri: str
|
||||
tag: Optional[str]
|
||||
|
||||
def __str__(self) -> str:
|
||||
result = f"{self.vcs}+{self.uri}"
|
||||
if self.tag:
|
||||
result += f"#{self.tag}"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@dataclass
|
||||
class RequirementsPackageEntry(RequirementsEntry):
|
||||
@@ -40,6 +58,9 @@ class RequirementsPackageEntry(RequirementsEntry):
|
||||
operator: str
|
||||
version: str
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name}{self.operator}{self.version}"
|
||||
|
||||
|
||||
def parse_requirements(
|
||||
source: Optional[RequirementsEntrySource], lines: List[str]
|
||||
@@ -63,7 +84,6 @@ def parse_requirements(
|
||||
elif stripped_line.startswith("-e"):
|
||||
yield parse_editable_requirements_entry(line_source, stripped_line)
|
||||
|
||||
# TODO: add support for other VCS's
|
||||
elif re.match(r"^(.+)\+", stripped_line):
|
||||
yield parse_vcs_requirements_entry(line_source, stripped_line)
|
||||
else:
|
||||
@@ -73,20 +93,38 @@ def parse_requirements(
|
||||
def parse_recursive_requirements_entry(
|
||||
source: RequirementsEntrySource, line: str
|
||||
) -> RequirementsRecursiveEntry:
|
||||
path = _clean_line(line.replace("-r", ""))
|
||||
original_path = _clean_line(line.replace("-r", ""))
|
||||
absolute_path = os.path.realpath(
|
||||
os.path.join(os.path.dirname(source.path), original_path)
|
||||
)
|
||||
|
||||
return RequirementsRecursiveEntry(
|
||||
source=source,
|
||||
path=os.path.realpath(os.path.join(os.path.dirname(source.path), path)),
|
||||
source=source, original_path=original_path, absolute_path=absolute_path,
|
||||
)
|
||||
|
||||
|
||||
def parse_editable_requirements_entry(
|
||||
source: RequirementsEntrySource, line: str
|
||||
) -> RequirementsEditableEntry:
|
||||
path = _clean_line(line.replace("-e", ""))
|
||||
original_path = _clean_line(line.replace("-e", ""))
|
||||
resolved_path = original_path
|
||||
|
||||
if not original_path.endswith(".py"):
|
||||
resolved_path = os.path.join(original_path, "setup.py")
|
||||
|
||||
absolute_path = os.path.realpath(
|
||||
os.path.join(os.path.dirname(source.path), original_path)
|
||||
)
|
||||
resolved_absolute_path = os.path.realpath(
|
||||
os.path.join(os.path.dirname(source.path), resolved_path)
|
||||
)
|
||||
|
||||
return RequirementsEditableEntry(
|
||||
source=source,
|
||||
path=os.path.realpath(os.path.join(os.path.dirname(source.path), path)),
|
||||
original_path=original_path,
|
||||
absolute_path=absolute_path,
|
||||
resolved_path=resolved_path,
|
||||
resolved_absolute_path=resolved_absolute_path,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
from typing import Generator
|
||||
|
||||
from .requirements import (
|
||||
@@ -11,7 +13,7 @@ def parse_requirements_txt(
|
||||
file_path: str,
|
||||
) -> Generator[RequirementsEntry, None, None]:
|
||||
source = RequirementsEntrySource(
|
||||
path=file_path, line=None, line_number=None
|
||||
path=os.path.realpath(file_path), line=None, line_number=None
|
||||
)
|
||||
|
||||
with open(file_path, "r") as fp:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
from typing import Generator
|
||||
|
||||
import setuptools
|
||||
@@ -21,7 +23,7 @@ def parse_setup_py(file_path: str) -> Generator[RequirementsEntry, None, None]:
|
||||
exec(fp.read())
|
||||
|
||||
source = RequirementsEntrySource(
|
||||
path=file_path, line=None, line_number=None
|
||||
path=os.path.realpath(file_path), line=None, line_number=None
|
||||
)
|
||||
|
||||
requirements = setup_kwargs.get("install_requires") or []
|
||||
|
||||
@@ -27,7 +27,7 @@ def test_parse_requirements_recursive_entry(path):
|
||||
assert requirements[0].source.path == source.path
|
||||
assert requirements[0].source.line == line
|
||||
assert requirements[0].source.line_number == 1
|
||||
assert requirements[0].path == os.path.realpath(
|
||||
assert requirements[0].absolute_path == os.path.realpath(
|
||||
os.path.join(os.getcwd(), path)
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ def test_parse_requirements_editable_entry(path):
|
||||
assert requirements[0].source.path == source.path
|
||||
assert requirements[0].source.line == line
|
||||
assert requirements[0].source.line_number == 1
|
||||
assert requirements[0].path == os.path.realpath(
|
||||
assert requirements[0].absolute_path == os.path.realpath(
|
||||
os.path.join(os.getcwd(), path)
|
||||
)
|
||||
|
||||
@@ -126,13 +126,15 @@ def test_parse_requirements_ignores_leading_and_trailing_whitespace():
|
||||
assert requirements[1].source.path == source.path
|
||||
assert requirements[1].source.line == "-r ./otherfile.txt"
|
||||
assert requirements[1].source.line_number == 2
|
||||
assert requirements[1].path == os.path.join(os.getcwd(), "otherfile.txt")
|
||||
assert requirements[1].absolute_path == os.path.join(
|
||||
os.getcwd(), "otherfile.txt"
|
||||
)
|
||||
|
||||
assert isinstance(requirements[2], RequirementsEditableEntry)
|
||||
assert requirements[2].source.path == source.path
|
||||
assert requirements[2].source.line == "-e ../"
|
||||
assert requirements[2].source.line_number == 3
|
||||
assert requirements[2].path == os.path.realpath(
|
||||
assert requirements[2].absolute_path == os.path.realpath(
|
||||
os.path.join(os.getcwd(), "..")
|
||||
)
|
||||
|
||||
|
||||
@@ -22,5 +22,7 @@ def test_parse_requirements_txt():
|
||||
assert isinstance(requirements[3], RequirementsEditableEntry)
|
||||
|
||||
for index, requirement in enumerate(requirements):
|
||||
assert requirement.source.path == requirements_txt_path
|
||||
assert requirement.source.path == os.path.realpath(
|
||||
requirements_txt_path
|
||||
)
|
||||
assert requirement.source.line_number == index + 1
|
||||
|
||||
@@ -23,7 +23,7 @@ def test_parse_setup_py():
|
||||
assert isinstance(requirements[3], RequirementsEditableEntry)
|
||||
|
||||
for index, requirement in enumerate(requirements):
|
||||
assert requirement.source.path == setup_py_path
|
||||
assert requirement.source.path == os.path.realpath(setup_py_path)
|
||||
assert requirement.source.line_number == index + 1
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user