Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extra environment marker should evaluate correctly. #116

Closed
19 changes: 19 additions & 0 deletions packaging/markers.py
Expand Up @@ -14,6 +14,7 @@

from ._compat import string_types
from .specifiers import Specifier, InvalidSpecifier
from .utils import safe_extra


__all__ = [
Expand Down Expand Up @@ -126,6 +127,20 @@ def serialize(self):
MARKER_VAR = VARIABLE | MARKER_VALUE

MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR)

# Special parsing rules apply for markers that evaluate extras.
EXTRA_VARIABLE = L('extra')
EXTRA_VARIABLE.setParseAction(lambda s, l, t: Variable(t[0]))

EXTRA_OP = (L("==") | L("===")) | L("!=")
EXTRA_OP.setParseAction(lambda s, l, t: Op(t[0]))

EXTRA_VALUE = QuotedString("'") | QuotedString('"')
EXTRA_VALUE.setParseAction(lambda s, l, t: Value(safe_extra(t[0])))

EXTRA_ITEM = Group(EXTRA_VARIABLE + EXTRA_OP + EXTRA_VALUE)

MARKER_ITEM = EXTRA_ITEM | MARKER_ITEM
MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0]))

LPAREN = L("(").suppress()
Expand Down Expand Up @@ -297,5 +312,9 @@ def evaluate(self, environment=None):
current_environment = default_environment()
if environment is not None:
current_environment.update(environment)
if current_environment.get('extra'):
current_environment['extra'] = safe_extra(
current_environment['extra']
)

return _evaluate_markers(self._markers, current_environment)
9 changes: 9 additions & 0 deletions packaging/utils.py
Expand Up @@ -12,3 +12,12 @@
def canonicalize_name(name):
# This is taken from PEP 503.
return _canonicalize_regex.sub("-", name).lower()


def safe_extra(extra):
"""Convert an arbitrary string to a standard 'extra' name

Any runs of non-alphanumeric characters are replaced with a single '_',
and the result is always lowercased.
"""
return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower()
5 changes: 5 additions & 0 deletions tests/test_markers.py
Expand Up @@ -303,6 +303,11 @@ def test_extra_with_no_extra_in_environment(self):
{"extra": "security"},
True,
),
(
"extra == 'SECURITY'",
{"extra": "security"},
True,
),
],
)
def test_evaluates(self, marker_string, environment, expected):
Expand Down