Commit 3bd5118e authored by Steve Kowalik's avatar Steve Kowalik

Destroy MarkerEvaluation for being pointless, fix two silly mistakes in tests...

Destroy MarkerEvaluation for being pointless, fix two silly mistakes in tests and update packaging changes.
parent 7d63ebff
......@@ -7,4 +7,6 @@ update-vendored:
pip install -r pkg_resources/_vendor/vendored.txt -t pkg_resources/_vendor/
sed -i -e 's/ \(pyparsing\)/ pkg_resources._vendor.\1/' \
pkg_resources/_vendor/packaging/*.py
sed -i -e 's/ \(six\)/ pkg_resources._vendor.\1/' \
pkg_resources/_vendor/packaging/*.py
rm -rf pkg_resources/_vendor/*.{egg,dist}-info
......@@ -1407,170 +1407,31 @@ def to_filename(name):
return name.replace('-','_')
class MarkerEvaluation(object):
values = {
'os_name': lambda: os.name,
'sys_platform': lambda: sys.platform,
'python_full_version': platform.python_version,
'python_version': lambda: platform.python_version()[:3],
'platform_version': platform.version,
'platform_machine': platform.machine,
'platform_python_implementation': platform.python_implementation,
'python_implementation': platform.python_implementation,
}
@classmethod
def is_invalid_marker(cls, text):
"""
Validate text as a PEP 508 environment marker; return an exception
if invalid or False otherwise.
"""
try:
cls.evaluate_marker(text)
except packaging.markers.InvalidMarker as e:
e.filename = None
e.lineno = None
return e
return False
@staticmethod
def normalize_exception(exc):
"""
Given a SyntaxError from a marker evaluation, normalize the error
message:
- Remove indications of filename and line number.
- Replace platform-specific error messages with standard error
messages.
"""
subs = {
'unexpected EOF while parsing': 'invalid syntax',
'parenthesis is never closed': 'invalid syntax',
}
exc.filename = None
exc.lineno = None
exc.msg = subs.get(exc.msg, exc.msg)
return exc
@classmethod
def and_test(cls, nodelist):
# MUST NOT short-circuit evaluation, or invalid syntax can be skipped!
items = [
cls.interpret(nodelist[i])
for i in range(1, len(nodelist), 2)
]
return functools.reduce(operator.and_, items)
@classmethod
def test(cls, nodelist):
# MUST NOT short-circuit evaluation, or invalid syntax can be skipped!
items = [
cls.interpret(nodelist[i])
for i in range(1, len(nodelist), 2)
]
return functools.reduce(operator.or_, items)
@classmethod
def atom(cls, nodelist):
t = nodelist[1][0]
if t == token.LPAR:
if nodelist[2][0] == token.RPAR:
raise SyntaxError("Empty parentheses")
return cls.interpret(nodelist[2])
msg = "Language feature not supported in environment markers"
raise SyntaxError(msg)
@classmethod
def comparison(cls, nodelist):
if len(nodelist) > 4:
msg = "Chained comparison not allowed in environment markers"
raise SyntaxError(msg)
comp = nodelist[2][1]
cop = comp[1]
if comp[0] == token.NAME:
if len(nodelist[2]) == 3:
if cop == 'not':
cop = 'not in'
else:
cop = 'is not'
try:
cop = cls.get_op(cop)
except KeyError:
msg = repr(cop) + " operator not allowed in environment markers"
raise SyntaxError(msg)
return cop(cls.evaluate(nodelist[1]), cls.evaluate(nodelist[3]))
@classmethod
def get_op(cls, op):
ops = {
symbol.test: cls.test,
symbol.and_test: cls.and_test,
symbol.atom: cls.atom,
symbol.comparison: cls.comparison,
'not in': lambda x, y: x not in y,
'in': lambda x, y: x in y,
'==': operator.eq,
'!=': operator.ne,
'<': operator.lt,
'>': operator.gt,
'<=': operator.le,
'>=': operator.ge,
}
if hasattr(symbol, 'or_test'):
ops[symbol.or_test] = cls.test
return ops[op]
@classmethod
def evaluate_marker(cls, text, extra=None):
"""
Evaluate a PEP 508 environment marker on CPython 2.4+.
Return a boolean indicating the marker result in this environment.
Raise InvalidMarker if marker is invalid.
This implementation uses the 'pyparsing' module.
"""
marker = packaging.markers.Marker(text)
return marker.evaluate()
def invalid_marker(text):
"""
Validate text as a PEP 508 environment marker; return an exception
if invalid or False otherwise.
"""
try:
evaluate_marker(text)
except packaging.markers.InvalidMarker as e:
e.filename = None
e.lineno = None
return e
return False
@classmethod
def interpret(cls, nodelist):
while len(nodelist)==2: nodelist = nodelist[1]
try:
op = cls.get_op(nodelist[0])
except KeyError:
raise SyntaxError("Comparison or logical expression expected")
return op(nodelist)
@classmethod
def evaluate(cls, nodelist):
while len(nodelist)==2: nodelist = nodelist[1]
kind = nodelist[0]
name = nodelist[1]
if kind==token.NAME:
try:
op = cls.values[name]
except KeyError:
raise SyntaxError("Unknown name %r" % name)
return op()
if kind==token.STRING:
s = nodelist[1]
if not cls._safe_string(s):
raise SyntaxError(
"Only plain strings allowed in environment markers")
return s[1:-1]
msg = "Language feature not supported in environment markers"
raise SyntaxError(msg)
def evaluate_marker(text, extra=None):
"""
Evaluate a PEP 508 environment marker.
Return a boolean indicating the marker result in this environment.
Raise InvalidMarker if marker is invalid.
@staticmethod
def _safe_string(cand):
return (
cand[:1] in "'\"" and
not cand.startswith('"""') and
not cand.startswith("'''") and
'\\' not in cand
)
This implementation uses the 'pyparsing' module.
"""
marker = packaging.markers.Marker(text)
return marker.evaluate()
invalid_marker = MarkerEvaluation.is_invalid_marker
evaluate_marker = MarkerEvaluation.evaluate_marker
class NullProvider:
"""Try to implement resources and metadata for arbitrary PEP 302 loaders"""
......
......@@ -55,7 +55,7 @@ class Value(Node):
VARIABLE = (
L("implementation_version") |
L("python_implementation") |
L("platform_python_implementation") |
L("implementation_name") |
L("python_full_version") |
L("platform_release") |
......@@ -209,7 +209,7 @@ def default_environment():
"platform_system": platform.system(),
"platform_version": platform.version(),
"python_full_version": platform.python_version(),
"python_implementation": platform.python_implementation(),
"platform_python_implementation": platform.python_implementation(),
"python_version": platform.python_version()[:3],
"sys_platform": sys.platform,
}
......
......@@ -9,7 +9,7 @@ import re
from pkg_resources._vendor.pyparsing import stringStart, stringEnd, originalTextFor, ParseException
from pkg_resources._vendor.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine
from pkg_resources._vendor.pyparsing import Literal as L # noqa
from six.moves.urllib import parse as urlparse
from pkg_resources._vendor.six.moves.urllib import parse as urlparse
from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
......@@ -73,6 +73,12 @@ REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
class Requirement(object):
"""Parse a requirement.
Parse a given requirement string into its parts, such as name, specifier,
URL, and extras. Raises InvalidRequirement on a badly-formed requirement
string.
"""
# TODO: Can we test whether something is contained within a requirement?
# If so how do we do that? Do we need to test against the _name_ of
......@@ -82,9 +88,10 @@ class Requirement(object):
def __init__(self, requirement_string):
try:
req = REQUIREMENT.parseString(requirement_string)
except ParseException:
except ParseException as e:
raise InvalidRequirement(
"Invalid requirement: {0!r}".format(requirement_string))
"Invalid requirement, parse error at \"{0!r}\"".format(
requirement_string[e.loc:e.loc + 8]))
self.name = req.name
if req.url:
......
......@@ -225,7 +225,8 @@ class LegacySpecifier(_IndividualSpecifier):
"""
)
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.X | re.I)
_regex = re.compile(
r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_operators = {
"==": "equal",
......@@ -366,7 +367,8 @@ class Specifier(_IndividualSpecifier):
"""
)
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.X | re.I)
_regex = re.compile(
r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_operators = {
"~=": "compatible",
......
packaging==15.3
pyparsing==2.0.6
six==1.10.0
......@@ -382,7 +382,7 @@ Environment Markers
>>> print(im("os.open=='y'"))
Invalid marker: "os.open=='y'"
>>> em("'linux' in sys_platform")
>>> em("sys_platform=='win32'") == (sys.platform=='win32')
True
>>> em("python_version >= '2.6'")
......
try:
import unittest.mock as mock
import unitest.mock as mock
except ImportError:
import mock
import mock
from pkg_resources import evaluate_marker
@mock.patch.dict('pkg_resources.MarkerEvaluation.values',
python_full_version=mock.Mock(return_value='2.7.10'))
def test_ordering():
assert evaluate_marker("python_full_version > '2.7.3'") is True
@mock.patch('platform.python_version', return_value='2.7.10')
def test_ordering(python_version_mock):
assert evaluate_marker("python_full_version > '2.7.3'") is True
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment