1import py
2import pytest
3from iniconfig import IniConfig, ParseError, __all__ as ALL
4from iniconfig import iscommentline
5from textwrap import dedent
6
7
8check_tokens = {
9    'section': (
10        '[section]',
11        [(0, 'section', None, None)]
12    ),
13    'value': (
14        'value = 1',
15        [(0, None, 'value', '1')]
16    ),
17    'value in section': (
18        '[section]\nvalue=1',
19        [(0, 'section', None, None), (1, 'section', 'value', '1')]
20    ),
21    'value with continuation': (
22        'names =\n Alice\n Bob',
23        [(0, None, 'names', 'Alice\nBob')]
24    ),
25    'value with aligned continuation': (
26        'names = Alice\n'
27        '        Bob',
28        [(0, None, 'names', 'Alice\nBob')]
29    ),
30    'blank line': (
31        '[section]\n\nvalue=1',
32        [(0, 'section', None, None), (2, 'section', 'value', '1')]
33    ),
34    'comment': (
35        '# comment',
36        []
37    ),
38    'comment on value': (
39        'value = 1',
40        [(0, None, 'value', '1')]
41    ),
42
43    'comment on section': (
44        '[section] #comment',
45        [(0, 'section', None, None)]
46    ),
47    'comment2': (
48        '; comment',
49        []
50    ),
51
52    'comment2 on section': (
53        '[section] ;comment',
54        [(0, 'section', None, None)]
55    ),
56    'pseudo section syntax in value': (
57        'name = value []',
58        [(0, None, 'name', 'value []')]
59    ),
60    'assignment in value': (
61        'value = x = 3',
62        [(0, None, 'value', 'x = 3')]
63    ),
64    'use of colon for name-values': (
65        'name: y',
66        [(0, None, 'name', 'y')]
67    ),
68    'use of colon without space': (
69        'value:y=5',
70        [(0, None, 'value', 'y=5')]
71    ),
72    'equality gets precedence': (
73        'value=xyz:5',
74        [(0, None, 'value', 'xyz:5')]
75    ),
76
77}
78
79
80@pytest.fixture(params=sorted(check_tokens))
81def input_expected(request):
82    return check_tokens[request.param]
83
84
85@pytest.fixture
86def input(input_expected):
87    return input_expected[0]
88
89
90@pytest.fixture
91def expected(input_expected):
92    return input_expected[1]
93
94
95def parse(input):
96    # only for testing purposes - _parse() does not use state except path
97    ini = object.__new__(IniConfig)
98    ini.path = "sample"
99    return ini._parse(input.splitlines(True))
100
101
102def parse_a_error(input):
103    return py.test.raises(ParseError, parse, input)
104
105
106def test_tokenize(input, expected):
107    parsed = parse(input)
108    assert parsed == expected
109
110
111def test_parse_empty():
112    parsed = parse("")
113    assert not parsed
114    ini = IniConfig("sample", "")
115    assert not ini.sections
116
117
118def test_ParseError():
119    e = ParseError("filename", 0, "hello")
120    assert str(e) == "filename:1: hello"
121
122
123def test_continuation_needs_perceeding_token():
124    excinfo = parse_a_error(' Foo')
125    assert excinfo.value.lineno == 0
126
127
128def test_continuation_cant_be_after_section():
129    excinfo = parse_a_error('[section]\n Foo')
130    assert excinfo.value.lineno == 1
131
132
133def test_section_cant_be_empty():
134    excinfo = parse_a_error('[]')
135    assert excinfo.value.lineno == 0
136
137
138@py.test.mark.parametrize('line', [
139    '!!',
140    ])
141def test_error_on_weird_lines(line):
142    parse_a_error(line)
143
144
145def test_iniconfig_from_file(tmpdir):
146    path = tmpdir/'test.txt'
147    path.write('[metadata]\nname=1')
148
149    config = IniConfig(path=path)
150    assert list(config.sections) == ['metadata']
151    config = IniConfig(path, "[diff]")
152    assert list(config.sections) == ['diff']
153    with pytest.raises(TypeError):
154        IniConfig(data=path.read())
155
156
157def test_iniconfig_section_first(tmpdir):
158    with pytest.raises(ParseError) as excinfo:
159        IniConfig("x", data='name=1')
160    assert excinfo.value.msg == "no section header defined"
161
162
163def test_iniconig_section_duplicate_fails():
164    with pytest.raises(ParseError) as excinfo:
165        IniConfig("x", data='[section]\n[section]')
166    assert 'duplicate section' in str(excinfo.value)
167
168
169def test_iniconfig_duplicate_key_fails():
170    with pytest.raises(ParseError) as excinfo:
171        IniConfig("x", data='[section]\nname = Alice\nname = bob')
172
173    assert 'duplicate name' in str(excinfo.value)
174
175
176def test_iniconfig_lineof():
177    config = IniConfig("x.ini", data=(
178        '[section]\n'
179        'value = 1\n'
180        '[section2]\n'
181        '# comment\n'
182        'value =2'
183    ))
184
185    assert config.lineof('missing') is None
186    assert config.lineof('section') == 1
187    assert config.lineof('section2') == 3
188    assert config.lineof('section', 'value') == 2
189    assert config.lineof('section2', 'value') == 5
190
191    assert config['section'].lineof('value') == 2
192    assert config['section2'].lineof('value') == 5
193
194
195def test_iniconfig_get_convert():
196    config = IniConfig("x", data='[section]\nint = 1\nfloat = 1.1')
197    assert config.get('section', 'int') == '1'
198    assert config.get('section', 'int', convert=int) == 1
199
200
201def test_iniconfig_get_missing():
202    config = IniConfig("x", data='[section]\nint = 1\nfloat = 1.1')
203    assert config.get('section', 'missing', default=1) == 1
204    assert config.get('section', 'missing') is None
205
206
207def test_section_get():
208    config = IniConfig("x", data='[section]\nvalue=1')
209    section = config['section']
210    assert section.get('value', convert=int) == 1
211    assert section.get('value', 1) == "1"
212    assert section.get('missing', 2) == 2
213
214
215def test_missing_section():
216    config = IniConfig("x", data='[section]\nvalue=1')
217    with pytest.raises(KeyError):
218            config["other"]
219
220
221def test_section_getitem():
222    config = IniConfig("x", data='[section]\nvalue=1')
223    assert config['section']['value'] == '1'
224    assert config['section']['value'] == '1'
225
226
227def test_section_iter():
228    config = IniConfig("x", data='[section]\nvalue=1')
229    names = list(config['section'])
230    assert names == ['value']
231    items = list(config['section'].items())
232    assert items == [('value', '1')]
233
234
235def test_config_iter():
236    config = IniConfig("x.ini", data=dedent('''
237          [section1]
238          value=1
239          [section2]
240          value=2
241    '''))
242    l = list(config)
243    assert len(l) == 2
244    assert l[0].name == 'section1'
245    assert l[0]['value'] == '1'
246    assert l[1].name == 'section2'
247    assert l[1]['value'] == '2'
248
249
250def test_config_contains():
251    config = IniConfig("x.ini", data=dedent('''
252          [section1]
253          value=1
254          [section2]
255          value=2
256    '''))
257    assert 'xyz' not in config
258    assert 'section1' in config
259    assert 'section2' in config
260
261
262def test_iter_file_order():
263    config = IniConfig("x.ini", data="""
264[section2] #cpython dict ordered before section
265value = 1
266value2 = 2 # dict ordered before value
267[section]
268a = 1
269b = 2
270""")
271    l = list(config)
272    secnames = [x.name for x in l]
273    assert secnames == ['section2', 'section']
274    assert list(config['section2']) == ['value', 'value2']
275    assert list(config['section']) == ['a', 'b']
276
277
278def test_example_pypirc():
279    config = IniConfig("pypirc", data=dedent('''
280        [distutils]
281        index-servers =
282            pypi
283            other
284
285        [pypi]
286        repository: <repository-url>
287        username: <username>
288        password: <password>
289
290        [other]
291        repository: http://example.com/pypi
292        username: <username>
293        password: <password>
294    '''))
295    distutils, pypi, other = list(config)
296    assert distutils["index-servers"] == "pypi\nother"
297    assert pypi['repository'] == '<repository-url>'
298    assert pypi['username'] == '<username>'
299    assert pypi['password'] == '<password>'
300    assert ['repository', 'username', 'password'] == list(other)
301
302
303def test_api_import():
304    assert ALL == ['IniConfig', 'ParseError']
305
306
307@pytest.mark.parametrize("line", [
308    "#qwe",
309    "  #qwe",
310    ";qwe",
311    " ;qwe",
312])
313def test_iscommentline_true(line):
314    assert iscommentline(line)
315