Skip to content

Commit

Permalink
Merge pull request #68 from AlpAribal/parse-pyproject
Browse files Browse the repository at this point in the history
Add pyproject.toml parser
  • Loading branch information
yeisonvargasf committed Oct 16, 2023
2 parents 30cce1a + e8a8e0e commit 0838117
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Supported Files
+------------------+------------+-----------+
| setup.cfg | no (# 4_) | no (# 4_) |
+------------------+------------+-----------+
| pyproject.toml | yes | no |
+------------------+------------+-----------+

.. _2: https://github.com/pyupio/dparse/issues/2
.. _3: https://github.com/pyupio/dparse/issues/3
Expand Down
4 changes: 4 additions & 0 deletions dparse/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ def __init__(self, content, path=None, sha=None, file_type=None,
self.parser = parser_class.SetupCfgParser
elif file_type == filetypes.poetry_lock:
self.parser = parser_class.PoetryLockParser
elif file_type == filetypes.pyproject_toml:
self.parser = parser_class.PyprojectTomlParser

elif path is not None:
if path.endswith((".txt", ".in")):
Expand All @@ -161,6 +163,8 @@ def __init__(self, content, path=None, sha=None, file_type=None,
self.parser = parser_class.SetupCfgParser
elif path.endswith(filetypes.poetry_lock):
self.parser = parser_class.PoetryLockParser
elif path.endswith(filetypes.pyproject_toml):
self.parser = parser_class.PyprojectTomlParser

if not hasattr(self, "parser"):
raise errors.UnknownDependencyFileError
Expand Down
1 change: 1 addition & 0 deletions dparse/filetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
pipfile = "Pipfile"
pipfile_lock = "Pipfile.lock"
poetry_lock = "poetry.lock"
pyproject_toml = "pyproject.toml"
29 changes: 29 additions & 0 deletions dparse/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,35 @@ def parse(self):
raise MalformedDependencyFileError(info=str(e))


class PyprojectTomlParser(Parser):
def parse(self) -> None:
"""Parse a pyproject.toml file.
Refer to https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
for configuration specification.
"""
try:
cfg = tomllib.loads(self.obj.content)
except (tomllib.TOMLDecodeError, IndexError) as e:
raise MalformedDependencyFileError(info=str(e))

if not cfg or "project" not in cfg:
return

sections = {
"dependencies": cfg["project"].get("dependencies", []),
**cfg["project"].get("optional-dependencies", {}),
}

for section, lines in sections.items():
for line in lines:
req = RequirementsTXTLineParser.parse(line)
if req:
req.dependency_type = self.obj.file_type
req.section = section
self.obj.dependencies.append(req)


def parse(content, file_type=None, path=None, sha=None, marker=((), ()),
parser=None, resolve=False):
"""
Expand Down
122 changes: 122 additions & 0 deletions tests/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,125 @@ def test_poetry_lock_with_invalid_toml():
throw = e

assert isinstance(throw, MalformedDependencyFileError)


def test_pyproject_toml() -> None:
content = """
[project]
name = "my_package"
authors = [
{name = "John Doe", email = "john.doe@email"},
]
description = "My package description"
dependencies = [
"requests",
'importlib-metadata; python_version<"3.8"',
"packaging~=23.1",
"setuptools # comment"
]
[project.optional-dependencies]
pdf = ["ReportLab>=1.2"]
rest = ["pack ==1.1, ==1.3"]
"""

dep_file = parse(content, file_type=filetypes.pyproject_toml)

assert len(dep_file.dependencies) == 6

assert dep_file.dependencies[0].name == "requests"
assert dep_file.dependencies[0].specs == SpecifierSet("")
assert dep_file.dependencies[0].dependency_type == "pyproject.toml"
assert dep_file.dependencies[0].section == "dependencies"

assert dep_file.dependencies[1].name == "importlib-metadata"
assert dep_file.dependencies[1].specs == SpecifierSet("")
assert dep_file.dependencies[1].dependency_type == "pyproject.toml"
assert dep_file.dependencies[1].section == "dependencies"

assert dep_file.dependencies[2].name == "packaging"
assert dep_file.dependencies[2].specs == SpecifierSet("~=23.1")
assert dep_file.dependencies[2].dependency_type == "pyproject.toml"
assert dep_file.dependencies[2].section == "dependencies"

assert dep_file.dependencies[3].name == "setuptools"
assert dep_file.dependencies[3].specs == SpecifierSet("")
assert dep_file.dependencies[3].dependency_type == "pyproject.toml"
assert dep_file.dependencies[3].section == "dependencies"

assert dep_file.dependencies[4].name == "ReportLab"
assert dep_file.dependencies[4].specs == SpecifierSet(">=1.2")
assert dep_file.dependencies[4].dependency_type == "pyproject.toml"
assert dep_file.dependencies[4].section == "pdf"

assert dep_file.dependencies[5].name == "pack"
assert dep_file.dependencies[5].specs == SpecifierSet("==1.1, ==1.3")
assert dep_file.dependencies[5].dependency_type == "pyproject.toml"
assert dep_file.dependencies[5].section == "rest"


def test_pyproject_toml_no_deps() -> None:
content = """
[project]
name = "my_package"
authors = [
{name = "John Doe", email = "john.doe@email"},
]
description = "My package description"
"""

dep_file = parse(content, file_type=filetypes.pyproject_toml)

assert len(dep_file.dependencies) == 0


def test_pyproject_toml_no_main_deps() -> None:
content = """
[project]
name = "my_package"
authors = [
{name = "John Doe", email = "john.doe@email"},
]
description = "My package description"
[project.optional-dependencies]
pdf = ["ReportLab>=1.2"]
rest = ["pack ==1.1, ==1.3"]
"""

dep_file = parse(content, file_type=filetypes.pyproject_toml)

assert len(dep_file.dependencies) == 2

assert dep_file.dependencies[0].name == "ReportLab"
assert dep_file.dependencies[0].specs == SpecifierSet(">=1.2")
assert dep_file.dependencies[0].dependency_type == "pyproject.toml"
assert dep_file.dependencies[0].section == "pdf"

assert dep_file.dependencies[1].name == "pack"
assert dep_file.dependencies[1].specs == SpecifierSet("==1.1, ==1.3")
assert dep_file.dependencies[1].dependency_type == "pyproject.toml"
assert dep_file.dependencies[1].section == "rest"


def test_pyproject_toml_no_extra() -> None:
content = """
[project]
name = "my_package"
authors = [
{name = "John Doe", email = "john.doe@email"},
]
description = "My package description"
dependencies = [
"requests",
]
"""

dep_file = parse(content, file_type=filetypes.pyproject_toml)

assert len(dep_file.dependencies) == 1

assert dep_file.dependencies[0].name == "requests"
assert dep_file.dependencies[0].specs == SpecifierSet("")
assert dep_file.dependencies[0].dependency_type == "pyproject.toml"
assert dep_file.dependencies[0].section == "dependencies"

0 comments on commit 0838117

Please sign in to comment.