madpah / requirements-parser

A Pip requirements file parser.
https://requirements-parser.readthedocs.io
Apache License 2.0
125 stars 41 forks source link

Failing tests on the master branch #26

Closed belak closed 7 years ago

belak commented 8 years ago

The tests on the master branch are currently failing.

% py.test                                                                                              x [master]
================================================================= test session starts =================================================================
platform linux -- Python 3.5.2, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /home/belak/requirements-parser, inifile: 
collected 23 items 

tests/test_parser.py .......F.........F.....

====================================================================== FAILURES =======================================================================
______________________________________________________________ test_requirement_files[7] ______________________________________________________________

s = <_io.TextIOWrapper name='/home/belak/requirements-parser/tests/reqfiles/illustrative_requirements.txt' mode='r' encoding='UTF-8'>
expected = [{'editable': True, 'extras': [], 'line': '-e git+https://github.com/davidfischer/requirements-parser.git#egg=requirem...'local_file': False, ...}, {'editable': False, 'extras': ['pdf'], 'line': 'DocParser [PDF]', 'local_file': False, ...}]

    @fancy
    def check(s, expected):
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
>           assert_equal(listify([dict(r) for r in parse(s)]), expected)

tests/test_parser.py:41: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib64/python3.5/unittest/case.py:820: in assertEqual
    assertion_func(first, second, msg=msg)
/usr/lib64/python3.5/unittest/case.py:1018: in assertListEqual
    self.assertSequenceEqual(list1, list2, msg, seq_type=list)
/usr/lib64/python3.5/unittest/case.py:1000: in assertSequenceEqual
    self.fail(msg)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <nose.tools.trivial.Dummy testMethod=nop>
msg = "Lists differ: [{'ex[21 chars]t', 'line': '-e git+https://github.com/davidfi[842 chars]one}] != [{'ex[21 chars]t', 'na...Django', 'path': None, 'specs': [[144 chars]True}\n\nDiff is 1150 characters long. Set self.maxDiff to None to see it."

    def fail(self, msg=None):
        """Fail immediately, with the given message."""
>       raise self.failureException(msg)
E       AssertionError: Lists differ: [{'ex[21 chars]t', 'line': '-e git+https://github.com/davidfi[842 chars]one}] != [{'ex[21 chars]t', 'name': 'requirements', 'path': None, 'spe[842 chars]rue}]
E       
E       First differing element 1:
E       {'ext[19 chars]ne, 'line': 'Django >=1.5, <1.6', 'specifier':[144 chars]None}
E       {'ext[19 chars]ne, 'name': 'Django', 'path': None, 'specs': [[144 chars]True}
E       
E       Diff is 1150 characters long. Set self.maxDiff to None to see it.

/usr/lib64/python3.5/unittest/case.py:665: AssertionError
_____________________________________________________________ test_requirement_files[17] ______________________________________________________________

s = <_io.TextIOWrapper name='/home/belak/requirements-parser/tests/reqfiles/specs_1.txt' mode='r' encoding='UTF-8'>
expected = [{'editable': False, 'extras': [], 'line': 'PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1', 'local_file': False, ...}]

    @fancy
    def check(s, expected):
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
>           assert_equal(listify([dict(r) for r in parse(s)]), expected)

tests/test_parser.py:41: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib64/python3.5/unittest/case.py:820: in assertEqual
    assertion_func(first, second, msg=msg)
/usr/lib64/python3.5/unittest/case.py:1018: in assertListEqual
    self.assertSequenceEqual(list1, list2, msg, seq_type=list)
/usr/lib64/python3.5/unittest/case.py:1000: in assertSequenceEqual
    self.fail(msg)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <nose.tools.trivial.Dummy testMethod=nop>
msg = "Lists differ: [{'ex[20 chars]ne, 'line': 'PickyThing<1.6,>1.9,!=1.9.6,<2.0a[222 chars]one}] != [{'ex[20 chars]ne, 'na...        ^     --\n\n+             ['==', '2.4c1']],\n?               ^^    ++++\n\n    'uri': None,\n    'vcs': None}]"

    def fail(self, msg=None):
        """Fail immediately, with the given message."""
>       raise self.failureException(msg)
E       AssertionError: Lists differ: [{'ex[20 chars]ne, 'line': 'PickyThing<1.6,>1.9,!=1.9.6,<2.0a[222 chars]one}] != [{'ex[20 chars]ne, 'name': 'PickyThing', 'path': None, 'specs[222 chars]rue}]
E       
E       First differing element 0:
E       {'ext[19 chars]ne, 'line': 'PickyThing<1.6,>1.9,!=1.9.6,<2.0a[221 chars]None}
E       {'ext[19 chars]ne, 'name': 'PickyThing', 'path': None, 'specs[221 chars]True}
E       
E         [{'editable': False,
E           'extras': [],
E           'line': 'PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1',
E           'local_file': False,
E           'name': 'PickyThing',
E           'path': None,
E           'revision': None,
E           'specifier': True,
E       -   'specs': [['==', '2.4c1'],
E       ?               ^^    ^ ^^^
E       
E       +   'specs': [['<', '1.6'],
E       ?               ^    ^ ^
E       
E       +             ['>', '1.9'],
E       +             ['!=', '1.9.6'],
E                     ['<', '2.0a0'],
E       -             ['<', '1.6'],
E       -             ['!=', '1.9.6'],
E       -             ['>', '1.9']],
E       ?               ^     --
E       
E       +             ['==', '2.4c1']],
E       ?               ^^    ++++
E       
E           'uri': None,
E           'vcs': None}]
belak commented 8 years ago

It looks like this might be related to the order of elements? But I'm not sure why it would only fail sometimes.

belak commented 8 years ago

Even with the fixes on master, I'm still running into problems with running the tests locally.

davidfischer commented 7 years ago

It looks like this is a breaking change in setuptools (pkg_resources.Requirement.parse). It is somewhat of an undocumented API.

davidfischer commented 7 years ago
from pkg_resources import Requirement
req = Requirement.parse('PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1')
req.specs
# [('!=', '1.9.6'), ('<', '2.0a0'), ('>', '1.9'), ('==', '2.4c1'), ('<', '1.6')]
# The output order is not deterministic (although it will be the same until you restart the interpreter)

Previously, pkg_resources would return them in the order they came.

davidfischer commented 7 years ago

Ok, it looks like setuptools started relying on a library called packaging. This library stores the specifiers into a set.

I see a couple interesting things here. Firstly, packaging is probably a great library to rely on instead of setuptools' undocumented API!

fruch commented 7 years ago

So fix the tests to use sets also ?

sbidoul commented 7 years ago

@davidfischer @belak proposed fix in #31