| Viewing file:  markers.py (4.87 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
# -*- coding: utf-8 -*-#
 # Copyright (C) 2012-2017 Vinay Sajip.
 # Licensed to the Python Software Foundation under a contributor agreement.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
 """
 Parser for the environment markers micro-language defined in PEP 508.
 """
 
 # Note: In PEP 345, the micro-language was Python compatible, so the ast
 # module could be used to parse it. However, PEP 508 introduced operators such
 # as ~= and === which aren't in Python, necessitating a different approach.
 
 import os
 import re
 import sys
 import platform
 
 from .compat import string_types
 from .util import in_venv, parse_marker
 from .version import NormalizedVersion as NV
 
 __all__ = ['interpret']
 
 _VERSION_PATTERN = re.compile(r'((\d+(\.\d+)*\w*)|\'(\d+(\.\d+)*\w*)\'|\"(\d+(\.\d+)*\w*)\")')
 
 def _is_literal(o):
 if not isinstance(o, string_types) or not o:
 return False
 return o[0] in '\'"'
 
 def _get_versions(s):
 result = []
 for m in _VERSION_PATTERN.finditer(s):
 result.append(NV(m.groups()[0]))
 return set(result)
 
 class Evaluator(object):
 """
 This class is used to evaluate marker expessions.
 """
 
 operations = {
 '==': lambda x, y: x == y,
 '===': lambda x, y: x == y,
 '~=': lambda x, y: x == y or x > y,
 '!=': lambda x, y: x != y,
 '<':  lambda x, y: x < y,
 '<=':  lambda x, y: x == y or x < y,
 '>':  lambda x, y: x > y,
 '>=':  lambda x, y: x == y or x > y,
 'and': lambda x, y: x and y,
 'or': lambda x, y: x or y,
 'in': lambda x, y: x in y,
 'not in': lambda x, y: x not in y,
 }
 
 def evaluate(self, expr, context):
 """
 Evaluate a marker expression returned by the :func:`parse_requirement`
 function in the specified context.
 """
 if isinstance(expr, string_types):
 if expr[0] in '\'"':
 result = expr[1:-1]
 else:
 if expr not in context:
 raise SyntaxError('unknown variable: %s' % expr)
 result = context[expr]
 else:
 assert isinstance(expr, dict)
 op = expr['op']
 if op not in self.operations:
 raise NotImplementedError('op not implemented: %s' % op)
 elhs = expr['lhs']
 erhs = expr['rhs']
 if _is_literal(expr['lhs']) and _is_literal(expr['rhs']):
 raise SyntaxError('invalid comparison: %s %s %s' % (elhs, op, erhs))
 
 lhs = self.evaluate(elhs, context)
 rhs = self.evaluate(erhs, context)
 if ((elhs == 'python_version' or erhs == 'python_version') and
 op in ('<', '<=', '>', '>=', '===', '==', '!=', '~=')):
 lhs = NV(lhs)
 rhs = NV(rhs)
 elif elhs == 'python_version' and op in ('in', 'not in'):
 lhs = NV(lhs)
 rhs = _get_versions(rhs)
 result = self.operations[op](lhs, rhs)
 return result
 
 def default_context():
 def format_full_version(info):
 version = '%s.%s.%s' % (info.major, info.minor, info.micro)
 kind = info.releaselevel
 if kind != 'final':
 version += kind[0] + str(info.serial)
 return version
 
 if hasattr(sys, 'implementation'):
 implementation_version = format_full_version(sys.implementation.version)
 implementation_name = sys.implementation.name
 else:
 implementation_version = '0'
 implementation_name = ''
 
 result = {
 'implementation_name': implementation_name,
 'implementation_version': implementation_version,
 'os_name': os.name,
 'platform_machine': platform.machine(),
 'platform_python_implementation': platform.python_implementation(),
 'platform_release': platform.release(),
 'platform_system': platform.system(),
 'platform_version': platform.version(),
 'platform_in_venv': str(in_venv()),
 'python_full_version': platform.python_version(),
 'python_version': platform.python_version()[:3],
 'sys_platform': sys.platform,
 }
 return result
 
 DEFAULT_CONTEXT = default_context()
 del default_context
 
 evaluator = Evaluator()
 
 def interpret(marker, execution_context=None):
 """
 Interpret a marker and return a result depending on environment.
 
 :param marker: The marker to interpret.
 :type marker: str
 :param execution_context: The context used for name lookup.
 :type execution_context: mapping
 """
 try:
 expr, rest = parse_marker(marker)
 except Exception as e:
 raise SyntaxError('Unable to interpret marker syntax: %s: %s' % (marker, e))
 if rest and rest[0] != '#':
 raise SyntaxError('unexpected trailing data in marker: %s: %s' % (marker, rest))
 context = dict(DEFAULT_CONTEXT)
 if execution_context:
 context.update(execution_context)
 return evaluator.evaluate(expr, context)
 
 |