1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4from __future__ import absolute_import, division, print_function
5
6import pytest
7
8from packaging.markers import Marker
9from packaging.requirements import InvalidRequirement, Requirement, URL
10from packaging.requirements import URL_AND_MARKER
11from packaging.specifiers import SpecifierSet
12
13
14class TestRequirements:
15    def test_string_specifier_marker(self):
16        requirement = 'name[bar]>=3; python_version == "2.7"'
17        req = Requirement(requirement)
18        assert str(req) == requirement
19
20    def test_string_url(self):
21        requirement = "name@ http://foo.com"
22        req = Requirement(requirement)
23        assert str(req) == requirement
24
25    def test_string_url_with_marker(self):
26        requirement = 'name@ http://foo.com ; extra == "feature"'
27        req = Requirement(requirement)
28        assert str(req) == requirement
29
30    def test_repr(self):
31        req = Requirement("name")
32        assert repr(req) == "<Requirement('name')>"
33
34    def _assert_requirement(
35        self, req, name, url=None, extras=[], specifier="", marker=None
36    ):
37        assert req.name == name
38        assert req.url == url
39        assert sorted(req.extras) == sorted(extras)
40        assert str(req.specifier) == specifier
41        if marker:
42            assert str(req.marker) == marker
43        else:
44            assert req.marker is None
45
46    def test_simple_names(self):
47        for name in ("A", "aa", "name"):
48            req = Requirement(name)
49            self._assert_requirement(req, name)
50
51    def test_name_with_other_characters(self):
52        name = "foo-bar.quux_baz"
53        req = Requirement(name)
54        self._assert_requirement(req, name)
55
56    def test_invalid_name(self):
57        with pytest.raises(InvalidRequirement):
58            Requirement("foo!")
59
60    def test_name_with_version(self):
61        req = Requirement("name>=3")
62        self._assert_requirement(req, "name", specifier=">=3")
63
64    def test_with_legacy_version(self):
65        req = Requirement("name==1.0.org1")
66        self._assert_requirement(req, "name", specifier="==1.0.org1")
67
68    def test_with_legacy_version_and_marker(self):
69        req = Requirement("name>=1.x.y;python_version=='2.6'")
70        self._assert_requirement(
71            req, "name", specifier=">=1.x.y", marker='python_version == "2.6"'
72        )
73
74    def test_version_with_parens_and_whitespace(self):
75        req = Requirement("name (==4)")
76        self._assert_requirement(req, "name", specifier="==4")
77
78    def test_name_with_multiple_versions(self):
79        req = Requirement("name>=3,<2")
80        self._assert_requirement(req, "name", specifier="<2,>=3")
81
82    def test_name_with_multiple_versions_and_whitespace(self):
83        req = Requirement("name >=2, <3")
84        self._assert_requirement(req, "name", specifier="<3,>=2")
85
86    def test_extras(self):
87        req = Requirement("foobar [quux,bar]")
88        self._assert_requirement(req, "foobar", extras=["bar", "quux"])
89
90    def test_empty_extras(self):
91        req = Requirement("foo[]")
92        self._assert_requirement(req, "foo")
93
94    def test_url(self):
95        url_section = "@ http://example.com"
96        parsed = URL.parseString(url_section)
97        assert parsed.url == "http://example.com"
98
99    def test_url_and_marker(self):
100        instring = "@ http://example.com ; os_name=='a'"
101        parsed = URL_AND_MARKER.parseString(instring)
102        assert parsed.url == "http://example.com"
103        assert str(parsed.marker) == 'os_name == "a"'
104
105    def test_invalid_url(self):
106        with pytest.raises(InvalidRequirement) as e:
107            Requirement("name @ gopher:/foo/com")
108        assert "Invalid URL: " in str(e.value)
109        assert "gopher:/foo/com" in str(e.value)
110
111    def test_file_url(self):
112        req = Requirement("name @ file:///absolute/path")
113        self._assert_requirement(req, "name", "file:///absolute/path")
114        req = Requirement("name @ file://.")
115        self._assert_requirement(req, "name", "file://.")
116
117    def test_invalid_file_urls(self):
118        with pytest.raises(InvalidRequirement):
119            Requirement("name @ file:.")
120        with pytest.raises(InvalidRequirement):
121            Requirement("name @ file:/.")
122
123    def test_extras_and_url_and_marker(self):
124        req = Requirement("name [fred,bar] @ http://foo.com ; python_version=='2.7'")
125        self._assert_requirement(
126            req,
127            "name",
128            extras=["bar", "fred"],
129            url="http://foo.com",
130            marker='python_version == "2.7"',
131        )
132
133    def test_complex_url_and_marker(self):
134        url = "https://example.com/name;v=1.1/?query=foo&bar=baz#blah"
135        req = Requirement("foo @ %s ; python_version=='3.4'" % url)
136        self._assert_requirement(req, "foo", url=url, marker='python_version == "3.4"')
137
138    def test_multiple_markers(self):
139        req = Requirement(
140            "name[quux, strange];python_version<'2.7' and " "platform_version=='2'"
141        )
142        marker = 'python_version < "2.7" and platform_version == "2"'
143        self._assert_requirement(req, "name", extras=["strange", "quux"], marker=marker)
144
145    def test_multiple_comparsion_markers(self):
146        req = Requirement("name; os_name=='a' and os_name=='b' or os_name=='c'")
147        marker = 'os_name == "a" and os_name == "b" or os_name == "c"'
148        self._assert_requirement(req, "name", marker=marker)
149
150    def test_invalid_marker(self):
151        with pytest.raises(InvalidRequirement):
152            Requirement("name; foobar=='x'")
153
154    def test_types(self):
155        req = Requirement("foobar[quux]<2,>=3; os_name=='a'")
156        assert isinstance(req.name, str)
157        assert isinstance(req.extras, set)
158        assert req.url is None
159        assert isinstance(req.specifier, SpecifierSet)
160        assert isinstance(req.marker, Marker)
161
162    def test_types_with_nothing(self):
163        req = Requirement("foobar")
164        assert isinstance(req.name, str)
165        assert isinstance(req.extras, set)
166        assert req.url is None
167        assert isinstance(req.specifier, SpecifierSet)
168        assert req.marker is None
169
170    def test_types_with_url(self):
171        req = Requirement("foobar @ http://foo.com")
172        assert isinstance(req.name, str)
173        assert isinstance(req.extras, set)
174        assert isinstance(req.url, str)
175        assert isinstance(req.specifier, SpecifierSet)
176        assert req.marker is None
177
178    def test_sys_platform_linux_equal(self):
179        req = Requirement('something>=1.2.3; sys_platform == "foo"')
180
181        assert req.name == "something"
182        assert req.marker is not None
183        assert req.marker.evaluate(dict(sys_platform="foo")) is True
184        assert req.marker.evaluate(dict(sys_platform="bar")) is False
185
186    def test_sys_platform_linux_in(self):
187        req = Requirement("aviato>=1.2.3; 'f' in sys_platform")
188
189        assert req.name == "aviato"
190        assert req.marker is not None
191        assert req.marker.evaluate(dict(sys_platform="foo")) is True
192        assert req.marker.evaluate(dict(sys_platform="bar")) is False
193
194    def test_parseexception_error_msg(self):
195        with pytest.raises(InvalidRequirement) as e:
196            Requirement("toto 42")
197        assert "Expected stringEnd" in str(e.value)
198