Skip to content

Commit

Permalink
fix: [SUP-1872] fixed pipfile parser (#222)
Browse files Browse the repository at this point in the history
  • Loading branch information
gemaxim committed Sep 14, 2023
1 parent 6099b74 commit 65eacd7
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 4 deletions.
43 changes: 39 additions & 4 deletions pysrc/pipfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,58 @@ def __init__(self, name):
self.markers = None
self.provenance = None # a tuple of (file name, line)

def __repr__(self):
return str(self.__dict__())

def __dict__(self):
return {
"name": self.name,
"editable": self.editable,
"vcs": self.vcs,
"vcs_uri": self.vcs_uri,
"version": self.version,
"markers": self.markers,
"provenance": self.provenance,
}

def __eq__(self, other):
if isinstance(other, PipfileRequirement):
return self.__dict__() == other.__dict__()
return False

@classmethod
def from_dict(cls, name, requirement_dict, pos_in_toml):
req = cls(name)

req.version = requirement_dict.get('version')
req.editable = requirement_dict.get('editable', False)
req.version = parse_req(requirement_dict.get('version'))
req.editable = parse_req(requirement_dict.get('editable', False))
for vcs in ['git', 'hg', 'svn', 'bzr']:
if vcs in requirement_dict:
req.vcs = vcs
req.vcs_uri = requirement_dict[vcs]
break
req.markers = requirement_dict.get('markers')
req.markers = parse_req(requirement_dict.get('markers'))
# proper file name to be injected into provenance by the calling code
req.provenance = ('Pipfile', pos_in_toml[0], pos_in_toml[0])

return req

'''
The toml parser returns each requirement as a tuple
of the value and ending position, for multiple requirements
e.g.
{
'version': ('*', (9, 23)),
'markers': ("sys_platform == 'linux' ; python_version != '3.4'", (8, 36))
} for entry waitress = {version = "*", markers="sys_platform == 'linux' ; python_version != '3.4'"}
This functions returns the value without the position for one such instance
e.g. parse_req(("sys_platform == 'linux' ; python_version != '3.4'", (8, 36))) returns "sys_platform == 'linux' ; python_version != '3.4'"
'''
def parse_req(pipfile_req):
if type(pipfile_req) is tuple:
return pipfile_req[0]
else:
return pipfile_req

def val_with_pos(kind, text, value, pos):
return (value, pos)

Expand Down
184 changes: 184 additions & 0 deletions pysrc/test_pipfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# run with:
# cd pysrc; python3 test_pipfile.py; cd ..

from pipfile import PipfileRequirement, parse
from collections import namedtuple

import unittest

try:
from mock import patch
except:
from unittest.mock import patch

class TestPipfileRequirement(unittest.TestCase):
def test_init(self):
req = PipfileRequirement("example")
self.assertEqual(req.name, "example")
self.assertFalse(req.editable)
self.assertIsNone(req.vcs)
self.assertIsNone(req.vcs_uri)
self.assertIsNone(req.version)
self.assertIsNone(req.markers)
self.assertIsNone(req.provenance)

def test_from_dict(self):
test_cases = [
{
"input": {
"name": "example",
"requirement_dict": {
"version": '*',
"editable": True,
"git": 'git_uri',
"markers": 'sys_platform == "linux" ; python_version != "3.4"'
},
"pos_in_toml": (1, 2)
},
"expected_output": {
"name": "example",
"editable": True,
"vcs": "git",
"vcs_uri": "git_uri",
"version": "*",
"markers": 'sys_platform == "linux" ; python_version != "3.4"',
"provenance": ('Pipfile', 1, 1)
}
},
{
"input": {
"name": "example2",
"requirement_dict": {
"version": ('*', (9, 23)),
"editable": False,
"markers": ('sys_platform == "linux" ; python_version != "3.4"', (8, 36))
},
"pos_in_toml": (1, 2)
},
"expected_output": {
"name": "example2",
"editable": False,
"vcs": None,
"vcs_uri": None,
"version": "*",
"markers": 'sys_platform == "linux" ; python_version != "3.4"',
"provenance": ('Pipfile', 1, 1)
}
}
]
for test_case in test_cases:
test_input = test_case["input"]
expected_output = test_case["expected_output"]
req = PipfileRequirement.from_dict(test_input["name"], test_input["requirement_dict"], test_input["pos_in_toml"])
self.assertEqual(str(req), str(expected_output))

def test_parse(self):
test_cases = [
{
"input": """
[packages]
requests = "*"
flask = { version = "1.0", markers = "python_version < '3.7'" }
[dev-packages]
pytest = "*"
""",
"expected_output": {
"packages": [
{
"name": "flask",
"editable": False,
"vcs": None,
"vcs_uri": None,
"version": "1.0",
"markers": "python_version < \'3.7\'",
"provenance": ('Pipfile', 4, 4)
},
{
"name": "requests",
"editable": False,
"vcs": None,
"vcs_uri": None,
"version": "*",
"markers": None,
"provenance": ('Pipfile', 3, 3)
}
],
"dev-packages": [
{
"name": "pytest",
"editable": False,
"vcs": None,
"vcs_uri": None,
"version": "*",
"markers": None,
"provenance": ('Pipfile', 7, 7)
}
]
}
},
{
"input": """
[packages]
requests = {version = "==2.28.1"}
[requires]
python_version = "3.7"
""",
"expected_output": {
"packages": [
{
"name": "requests",
"editable": False,
"vcs": None,
"vcs_uri": None,
"version": "==2.28.1",
"markers": None,
"provenance": ('Pipfile', 3, 3)
}
],
"dev-packages": None,
}
},
{
"input": """
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
"Jinja2" = "*"
[dev-packages]
[requires]
""",
"expected_output": {
"packages": [
{
"name": "Jinja2",
"editable": False,
"vcs": None,
"vcs_uri": None,
"version": "*",
"markers": None,
"provenance": ('Pipfile', 8, 8)
}
],
"dev-packages": [],
}
}
]

for test_case in test_cases:
pipfile_content = test_case["input"]
expected_output = test_case["expected_output"]

parsed_data = parse(pipfile_content)

self.assertEqual(str(parsed_data), str(expected_output))


if __name__ == '__main__':
unittest.main()

0 comments on commit 65eacd7

Please sign in to comment.