Codebase list dh-python / f263e81
Break out environment marker parsing into its own function and regex This allows us to handle complex markers using both types of quotation marks. Stefano Rivera 2 years ago
4 changed file(s) with 85 addition(s) and 34 deletion(s). Raw diff Collapse all Expand all
0 dh-python (5.20220101) UNRELEASED; urgency=medium
1
2 * Handle complex environment markers with both quote types.
3
4 -- Stefano Rivera <stefanor@debian.org> Sat, 01 Jan 2022 12:06:23 -0400
5
06 dh-python (5.20211231) unstable; urgency=medium
17
28 * Handle parenthetical environment markers.
0 # Copyright © 2022 Stefano Rivera <stefanor@debian.org>
1 #
2 # Permission is hereby granted, free of charge, to any person obtaining a copy
3 # of this software and associated documentation files (the "Software"), to deal
4 # in the Software without restriction, including without limitation the rights
5 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
6 # copies of the Software, and to permit persons to whom the Software is
7 # furnished to do so, subject to the following conditions:
8 #
9 # The above copyright notice and this permission notice shall be included in
10 # all copies or substantial portions of the Software.
11 #
12 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
18 # THE SOFTWARE.
19
20 """
21 Handle Environment Markers
22 https://www.python.org/dev/peps/pep-0508/#environment-markers
23
24 TODO: Ideally replace with the packaging library, but the API is currently
25 private: https://github.com/pypa/packaging/issues/496
26 """
27
28 import re
29
30
31 SIMPLE_ENV_MARKER_RE = re.compile(r'''
32 (?P<marker>[a-z_]+)
33 \s*
34 (?P<op><=?|>=?|[=!~]=|===)
35 \s*
36 (?P<quote>['"])
37 (?P<value>.*) # Could contain additional markers
38 (?P=quote)
39 ''', re.VERBOSE)
40 COMPLEX_ENV_MARKER_RE = re.compile(r'''
41 (?:\s|\))
42 (?:and|or)
43 (?:\s|\()
44 ''', re.VERBOSE)
45
46
47 class ComplexEnvironmentMarker(Exception):
48 pass
49
50
51 def parse_environment_marker(marker):
52 """Parse a simple marker of <= 1 environment restriction"""
53 marker = marker.strip()
54 if marker.startswith('(') and marker.endswith(')'):
55 marker = marker[1:-1].strip()
56
57 m = COMPLEX_ENV_MARKER_RE.search(marker)
58 if m:
59 raise ComplexEnvironmentMarker()
60
61 m = SIMPLE_ENV_MARKER_RE.match(marker)
62 if not m:
63 raise ComplexEnvironmentMarker()
64
65 return (
66 m.group('marker'),
67 m.group('op'),
68 m.group('value'),
69 )
3333
3434 from dhpython import PKG_PREFIX_MAP, PUBLIC_DIR_RE,\
3535 PYDIST_DIRS, PYDIST_OVERRIDES_FNAMES, PYDIST_DPKG_SEARCH_TPLS
36 from dhpython.markers import ComplexEnvironmentMarker, parse_environment_marker
37 from dhpython.tools import memoize
3638 from dhpython.version import get_requested_versions, Version
37 from dhpython.tools import memoize
3839
3940 log = logging.getLogger('dhpython')
4041
7374 \)? # optional closing parenthesis
7475 \s*
7576 (?:; # optional environment markers
76 \s*
77 \(? # optional parenthesis
78 \s*
79 (?P<environment_marker>[a-z_]+)
80 \s*
81 (?P<environment_marker_op><=?|>=?|[=!~]=|===)
82 \s*
83 (?P<environment_marker_quote>['"])
84 (?P<environment_marker_value>.*)
85 (?P=environment_marker_quote)
86 \)? # optional parenthesis
87 \s*
77 (?P<environment_marker>.+)
8878 )?
8979 ''', re.VERBOSE)
9080 EXTRA_RE = re.compile(r'''
10494 (?P<section>[a-zA-Z0-9-_.]+)?
10595 \s*
10696 (?::
107 \s*
108 \(*
109 \s*
110 (?P<environment_marker>[a-z_]+)
111 \s*
112 (?P<environment_marker_op><=?|>=?|[=!~]=|===)
113 \s*
114 (?P<environment_marker_quote>['"])
115 (?P<environment_marker_value>.*)
116 (?P=environment_marker_quote)
117 \s*
118 \)*
119 \s*
97 (?P<environment_marker>.+)
12098 )?
12199 \]
122100 \s*
221199 action = check_environment_marker_restrictions(
222200 req,
223201 req_d['environment_marker'],
224 req_d['environment_marker_op'],
225 req_d['environment_marker_value'],
226202 impl)
227203 if action is False:
228204 return
321297 # return pname
322298
323299
324 def check_environment_marker_restrictions(req, marker, op, value, impl):
300 def check_environment_marker_restrictions(req, marker_str, impl):
325301 """Check wither we should include or skip a dependency based on its
326302 environment markers.
327303
333309 log.info('Ignoring environment markers for non-Python 3.x: %s', req)
334310 return False
335311
336 # TODO: Replace with an AST that can handle complex logic
337 if ' or ' in value or ' and ' in value:
312 try:
313 marker, op, value = parse_environment_marker(marker_str)
314 except ComplexEnvironmentMarker:
338315 log.info('Ignoring complex environment marker: %s', req)
339316 return False
340317
498475 env_action = check_environment_marker_restrictions(
499476 line,
500477 m.group('environment_marker'),
501 m.group('environment_marker_op'),
502 m.group('environment_marker_value'),
503478 impl)
504479 processed.append(line)
505480 continue
348348 "Requires-Dist: extra_test; extra == 'test'",
349349 "Requires-Dist: complex_marker; os_name != 'windows' "
350350 "and implementation_name == 'cpython'",
351 "Requires-Dist: complex_marker_2; (os_name != 'windows') "
351 "Requires-Dist: complex_marker_2; (python_version > \"3.4\") "
352352 "and extra == 'test'",
353353 "Requires-Dist: no_markers_2",
354354 ),