Add package manager parsers (npm, pip, go, cargo)
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-02-04 14:56:30 +00:00
parent 9690e255ce
commit 6c94340428

124
src/depcheck/parsers/pip.py Normal file
View File

@@ -0,0 +1,124 @@
"""Pip requirements.txt and pyproject.toml parser."""
import re
from pathlib import Path
import toml
from depcheck.models import Dependency, PackageManager
from depcheck.parsers import Parser
from depcheck.utils import parse_version_string
class PipParser(Parser):
"""Parser for pip requirements.txt and pyproject.toml files."""
package_manager = PackageManager.PIP
def supports_file(self, file_path: Path) -> bool:
return file_path.name in ("requirements.txt", "pyproject.toml")
def get_file_patterns(self) -> list[str]:
return ["requirements.txt", "pyproject.toml"]
def parse(self, file_path: Path) -> list[Dependency]:
if file_path.name == "requirements.txt":
return self._parse_requirements_txt(file_path)
elif file_path.name == "pyproject.toml":
return self._parse_pyproject_toml(file_path)
return []
def _parse_requirements_txt(self, file_path: Path) -> list[Dependency]:
dependencies: list[Dependency] = []
try:
content = file_path.read_text()
except OSError:
return dependencies
for line in content.splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
match = self._parse_requirement_line(line)
if match:
name, version = match
dependencies.append(
Dependency(
name=name,
current_version=version,
package_manager=self.package_manager,
category="dependencies",
source_file=str(file_path),
)
)
return dependencies
def _parse_requirement_line(self, line: str) -> tuple[str, str] | None:
line = line.split("#")[0].strip()
line = line.split(";")[0].strip()
patterns = [
r"^([a-zA-Z0-9_-]+)==([a-zA-Z0-9._-]+)$",
r"^([a-zA-Z0-9_-]+)>=([a-zA-Z0-9._-]+)$",
r"^([a-zA-Z0-9_-]+)<=([a-zA-Z0-9._-]+)$",
r"^([a-zA-Z0-9_-]+)~=([a-zA-Z0-9._-]+)$",
r"^([a-zA-Z0-9_-]+)!=([a-zA-Z0-9._-]+)$",
r"^([a-zA-Z0-9_-]+)$",
]
for pattern in patterns:
match = re.match(pattern, line)
if match:
name = match.group(1)
lastindex = match.lastindex
version = match.group(2) if lastindex is not None and lastindex >= 2 else "latest"
version = parse_version_string(version)
return (name, version) if version else (name, "unknown")
return None
def _parse_pyproject_toml(self, file_path: Path) -> list[Dependency]:
dependencies: list[Dependency] = []
try:
content = file_path.read_text()
data = toml.loads(content)
except (toml.TomlDecodeError, OSError):
return dependencies
project_section = data.get("project", {})
for key in ["dependencies", "optional-dependencies"]:
section = project_section.get(key, [])
if isinstance(section, list):
for item in section:
if isinstance(item, str):
match = self._parse_requirement_line(item)
if match:
name, version = match
dependencies.append(
Dependency(
name=name,
current_version=version,
package_manager=self.package_manager,
category="dependencies",
source_file=str(file_path),
)
)
elif isinstance(item, dict):
for name, version in item.items():
if isinstance(version, str):
parsed_version = parse_version_string(version)
dependencies.append(
Dependency(
name=name,
current_version=parsed_version or "unknown",
package_manager=self.package_manager,
category="dependencies",
source_file=str(file_path),
)
)
return dependencies