[Python-checkins] distutils2: split the micro-language for PEP 345 in markers

tarek.ziade python-checkins at python.org
Sat Jan 29 15:19:05 CET 2011


tarek.ziade pushed 608317e1a8b4 to distutils2:

http://hg.python.org/distutils2/rev/608317e1a8b4
changeset:   917:608317e1a8b4
tag:         tip
user:        Tarek Ziade <tarek at ziade.org>
date:        Sat Jan 29 15:18:54 2011 +0100
summary:
  split the micro-language for PEP 345 in markers

files:
  distutils2/markers.py
  distutils2/metadata.py
  distutils2/tests/test_markers.py
  distutils2/tests/test_metadata.py

diff --git a/distutils2/markers.py b/distutils2/markers.py
new file mode 100644
--- /dev/null
+++ b/distutils2/markers.py
@@ -0,0 +1,194 @@
+""" Micro-language for PEP 345 environment markers
+"""
+import sys
+import platform
+import os
+from tokenize import tokenize, NAME, OP, STRING, ENDMARKER
+from StringIO import StringIO
+
+__all__ = ['interpret']
+
+
+# allowed operators
+_OPERATORS = {'==': lambda x, y: x == y,
+              '!=': lambda x, y: x != y,
+              '>': lambda x, y: x > y,
+              '>=': lambda x, y: x >= y,
+              '<': lambda x, y: x < y,
+              '<=': lambda x, y: x <= y,
+              'in': lambda x, y: x in y,
+              'not in': lambda x, y: x not in y}
+
+
+def _operate(operation, x, y):
+    return _OPERATORS[operation](x, y)
+
+
+# restricted set of variables
+_VARS = {'sys.platform': sys.platform,
+         'python_version': sys.version[:3],
+         'python_full_version': sys.version.split(' ', 1)[0],
+         'os.name': os.name,
+         'platform.version': platform.version(),
+         'platform.machine': platform.machine()}
+
+
+class _Operation(object):
+
+    def __init__(self, execution_context=None):
+        self.left = None
+        self.op = None
+        self.right = None
+        if execution_context is None:
+            execution_context = {}
+        self.execution_context = execution_context
+
+    def _get_var(self, name):
+        if name in self.execution_context:
+            return self.execution_context[name]
+        return _VARS[name]
+
+    def __repr__(self):
+        return '%s %s %s' % (self.left, self.op, self.right)
+
+    def _is_string(self, value):
+        if value is None or len(value) < 2:
+            return False
+        for delimiter in '"\'':
+            if value[0] == value[-1] == delimiter:
+                return True
+        return False
+
+    def _is_name(self, value):
+        return value in _VARS
+
+    def _convert(self, value):
+        if value in _VARS:
+            return self._get_var(value)
+        return value.strip('"\'')
+
+    def _check_name(self, value):
+        if value not in _VARS:
+            raise NameError(value)
+
+    def _nonsense_op(self):
+        msg = 'This operation is not supported : "%s"' % self
+        raise SyntaxError(msg)
+
+    def __call__(self):
+        # make sure we do something useful
+        if self._is_string(self.left):
+            if self._is_string(self.right):
+                self._nonsense_op()
+            self._check_name(self.right)
+        else:
+            if not self._is_string(self.right):
+                self._nonsense_op()
+            self._check_name(self.left)
+
+        if self.op not in _OPERATORS:
+            raise TypeError('Operator not supported "%s"' % self.op)
+
+        left = self._convert(self.left)
+        right = self._convert(self.right)
+        return _operate(self.op, left, right)
+
+
+class _OR(object):
+    def __init__(self, left, right=None):
+        self.left = left
+        self.right = right
+
+    def filled(self):
+        return self.right is not None
+
+    def __repr__(self):
+        return 'OR(%r, %r)' % (self.left, self.right)
+
+    def __call__(self):
+        return self.left() or self.right()
+
+
+class _AND(object):
+    def __init__(self, left, right=None):
+        self.left = left
+        self.right = right
+
+    def filled(self):
+        return self.right is not None
+
+    def __repr__(self):
+        return 'AND(%r, %r)' % (self.left, self.right)
+
+    def __call__(self):
+        return self.left() and self.right()
+
+
+class _CHAIN(object):
+
+    def __init__(self, execution_context=None):
+        self.ops = []
+        self.op_starting = True
+        self.execution_context = execution_context
+
+    def eat(self, toktype, tokval, rowcol, line, logical_line):
+        if toktype not in (NAME, OP, STRING, ENDMARKER):
+            raise SyntaxError('Type not supported "%s"' % tokval)
+
+        if self.op_starting:
+            op = _Operation(self.execution_context)
+            if len(self.ops) > 0:
+                last = self.ops[-1]
+                if isinstance(last, (_OR, _AND)) and not last.filled():
+                    last.right = op
+                else:
+                    self.ops.append(op)
+            else:
+                self.ops.append(op)
+            self.op_starting = False
+        else:
+            op = self.ops[-1]
+
+        if (toktype == ENDMARKER or
+            (toktype == NAME and tokval in ('and', 'or'))):
+            if toktype == NAME and tokval == 'and':
+                self.ops.append(_AND(self.ops.pop()))
+            elif toktype == NAME and tokval == 'or':
+                self.ops.append(_OR(self.ops.pop()))
+            self.op_starting = True
+            return
+
+        if isinstance(op, (_OR, _AND)) and op.right is not None:
+            op = op.right
+
+        if ((toktype in (NAME, STRING) and tokval not in ('in', 'not'))
+            or (toktype == OP and tokval == '.')):
+            if op.op is None:
+                if op.left is None:
+                    op.left = tokval
+                else:
+                    op.left += tokval
+            else:
+                if op.right is None:
+                    op.right = tokval
+                else:
+                    op.right += tokval
+        elif toktype == OP or tokval in ('in', 'not'):
+            if tokval == 'in' and op.op == 'not':
+                op.op = 'not in'
+            else:
+                op.op = tokval
+
+    def result(self):
+        for op in self.ops:
+            if not op():
+                return False
+        return True
+
+
+def interpret(marker, execution_context=None):
+    """Interpret a marker and return a result depending on environment."""
+    marker = marker.strip()
+    operations = _CHAIN(execution_context)
+    tokenize(StringIO(marker).readline, operations.eat)
+    return operations.result()
diff --git a/distutils2/metadata.py b/distutils2/metadata.py
--- a/distutils2/metadata.py
+++ b/distutils2/metadata.py
@@ -5,13 +5,12 @@
 
 import os
 import sys
-import platform
 import re
 from StringIO import StringIO
 from email import message_from_file
-from tokenize import tokenize, NAME, OP, STRING, ENDMARKER
 
 from distutils2 import logger
+from distutils2.markers import interpret
 from distutils2.version import (is_valid_predicate, is_valid_version,
                                 is_valid_versions)
 from distutils2.errors import (MetadataMissingError,
@@ -291,7 +290,7 @@
         if not self.platform_dependent or ';' not in value:
             return True, value
         value, marker = value.split(';')
-        return _interpret(marker, self.execution_context), value
+        return interpret(marker, self.execution_context), value
 
     def _remove_line_prefix(self, value):
         return _LINE_PREFIX.sub('\n', value)
@@ -518,191 +517,3 @@
     def items(self):
         """Dict like api"""
         return [(key, self[key]) for key in self.keys()]
-
-
-#
-# micro-language for PEP 345 environment markers
-#
-
-# allowed operators
-_OPERATORS = {'==': lambda x, y: x == y,
-              '!=': lambda x, y: x != y,
-              '>': lambda x, y: x > y,
-              '>=': lambda x, y: x >= y,
-              '<': lambda x, y: x < y,
-              '<=': lambda x, y: x <= y,
-              'in': lambda x, y: x in y,
-              'not in': lambda x, y: x not in y}
-
-
-def _operate(operation, x, y):
-    return _OPERATORS[operation](x, y)
-
-# restricted set of variables
-_VARS = {'sys.platform': sys.platform,
-         'python_version': sys.version[:3],
-         'python_full_version': sys.version.split(' ', 1)[0],
-         'os.name': os.name,
-         'platform.version': platform.version(),
-         'platform.machine': platform.machine()}
-
-
-class _Operation(object):
-
-    def __init__(self, execution_context=None):
-        self.left = None
-        self.op = None
-        self.right = None
-        if execution_context is None:
-            execution_context = {}
-        self.execution_context = execution_context
-
-    def _get_var(self, name):
-        if name in self.execution_context:
-            return self.execution_context[name]
-        return _VARS[name]
-
-    def __repr__(self):
-        return '%s %s %s' % (self.left, self.op, self.right)
-
-    def _is_string(self, value):
-        if value is None or len(value) < 2:
-            return False
-        for delimiter in '"\'':
-            if value[0] == value[-1] == delimiter:
-                return True
-        return False
-
-    def _is_name(self, value):
-        return value in _VARS
-
-    def _convert(self, value):
-        if value in _VARS:
-            return self._get_var(value)
-        return value.strip('"\'')
-
-    def _check_name(self, value):
-        if value not in _VARS:
-            raise NameError(value)
-
-    def _nonsense_op(self):
-        msg = 'This operation is not supported : "%s"' % self
-        raise SyntaxError(msg)
-
-    def __call__(self):
-        # make sure we do something useful
-        if self._is_string(self.left):
-            if self._is_string(self.right):
-                self._nonsense_op()
-            self._check_name(self.right)
-        else:
-            if not self._is_string(self.right):
-                self._nonsense_op()
-            self._check_name(self.left)
-
-        if self.op not in _OPERATORS:
-            raise TypeError('Operator not supported "%s"' % self.op)
-
-        left = self._convert(self.left)
-        right = self._convert(self.right)
-        return _operate(self.op, left, right)
-
-
-class _OR(object):
-    def __init__(self, left, right=None):
-        self.left = left
-        self.right = right
-
-    def filled(self):
-        return self.right is not None
-
-    def __repr__(self):
-        return 'OR(%r, %r)' % (self.left, self.right)
-
-    def __call__(self):
-        return self.left() or self.right()
-
-
-class _AND(object):
-    def __init__(self, left, right=None):
-        self.left = left
-        self.right = right
-
-    def filled(self):
-        return self.right is not None
-
-    def __repr__(self):
-        return 'AND(%r, %r)' % (self.left, self.right)
-
-    def __call__(self):
-        return self.left() and self.right()
-
-
-class _CHAIN(object):
-
-    def __init__(self, execution_context=None):
-        self.ops = []
-        self.op_starting = True
-        self.execution_context = execution_context
-
-    def eat(self, toktype, tokval, rowcol, line, logical_line):
-        if toktype not in (NAME, OP, STRING, ENDMARKER):
-            raise SyntaxError('Type not supported "%s"' % tokval)
-
-        if self.op_starting:
-            op = _Operation(self.execution_context)
-            if len(self.ops) > 0:
-                last = self.ops[-1]
-                if isinstance(last, (_OR, _AND)) and not last.filled():
-                    last.right = op
-                else:
-                    self.ops.append(op)
-            else:
-                self.ops.append(op)
-            self.op_starting = False
-        else:
-            op = self.ops[-1]
-
-        if (toktype == ENDMARKER or
-            (toktype == NAME and tokval in ('and', 'or'))):
-            if toktype == NAME and tokval == 'and':
-                self.ops.append(_AND(self.ops.pop()))
-            elif toktype == NAME and tokval == 'or':
-                self.ops.append(_OR(self.ops.pop()))
-            self.op_starting = True
-            return
-
-        if isinstance(op, (_OR, _AND)) and op.right is not None:
-            op = op.right
-
-        if ((toktype in (NAME, STRING) and tokval not in ('in', 'not'))
-            or (toktype == OP and tokval == '.')):
-            if op.op is None:
-                if op.left is None:
-                    op.left = tokval
-                else:
-                    op.left += tokval
-            else:
-                if op.right is None:
-                    op.right = tokval
-                else:
-                    op.right += tokval
-        elif toktype == OP or tokval in ('in', 'not'):
-            if tokval == 'in' and op.op == 'not':
-                op.op = 'not in'
-            else:
-                op.op = tokval
-
-    def result(self):
-        for op in self.ops:
-            if not op():
-                return False
-        return True
-
-
-def _interpret(marker, execution_context=None):
-    """Interpret a marker and return a result depending on environment."""
-    marker = marker.strip()
-    operations = _CHAIN(execution_context)
-    tokenize(StringIO(marker).readline, operations.eat)
-    return operations.result()
diff --git a/distutils2/tests/test_markers.py b/distutils2/tests/test_markers.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/test_markers.py
@@ -0,0 +1,69 @@
+"""Tests for distutils.metadata."""
+import os
+import sys
+import platform
+from StringIO import StringIO
+
+from distutils2.markers import interpret
+from distutils2.tests import run_unittest, unittest
+from distutils2.tests.support import LoggingCatcher, WarningsCatcher
+
+
+class MarkersTestCase(LoggingCatcher, WarningsCatcher,
+                      unittest.TestCase):
+
+    def test_interpret(self):
+        sys_platform = sys.platform
+        version = sys.version.split()[0]
+        os_name = os.name
+        platform_version = platform.version()
+        platform_machine = platform.machine()
+
+        self.assertTrue(interpret("sys.platform == '%s'" % sys_platform))
+        self.assertTrue(interpret(
+            "sys.platform == '%s' or python_version == '2.4'" % sys_platform))
+        self.assertTrue(interpret(
+            "sys.platform == '%s' and python_full_version == '%s'" %
+            (sys_platform, version)))
+        self.assertTrue(interpret("'%s' == sys.platform" % sys_platform))
+        self.assertTrue(interpret('os.name == "%s"' % os_name))
+        self.assertTrue(interpret(
+            'platform.version == "%s" and platform.machine == "%s"' %
+            (platform_version, platform_machine)))
+
+        # stuff that need to raise a syntax error
+        ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'",
+               'okpjonon', '', 'os.name ==', 'python_version == 2.4')
+        for op in ops:
+            self.assertRaises(SyntaxError, interpret, op)
+
+        # combined operations
+        OP = 'os.name == "%s"' % os_name
+        AND = ' and '
+        OR = ' or '
+        self.assertTrue(interpret(OP + AND + OP))
+        self.assertTrue(interpret(OP + AND + OP + AND + OP))
+        self.assertTrue(interpret(OP + OR + OP))
+        self.assertTrue(interpret(OP + OR + OP + OR + OP))
+
+        # other operators
+        self.assertTrue(interpret("os.name != 'buuuu'"))
+        self.assertTrue(interpret("python_version > '1.0'"))
+        self.assertTrue(interpret("python_version < '5.0'"))
+        self.assertTrue(interpret("python_version <= '5.0'"))
+        self.assertTrue(interpret("python_version >= '1.0'"))
+        self.assertTrue(interpret("'%s' in os.name" % os_name))
+        self.assertTrue(interpret("'buuuu' not in os.name"))
+        self.assertTrue(interpret(
+            "'buuuu' not in os.name and '%s' in os.name" % os_name))
+
+        # execution context
+        self.assertTrue(interpret('python_version == "0.1"',
+                                  {'python_version': '0.1'}))
+
+
+def test_suite():
+    return unittest.makeSuite(MarkersTestCase)
+
+if __name__ == '__main__':
+    run_unittest(test_suite())
diff --git a/distutils2/tests/test_metadata.py b/distutils2/tests/test_metadata.py
--- a/distutils2/tests/test_metadata.py
+++ b/distutils2/tests/test_metadata.py
@@ -1,10 +1,10 @@
-"""Tests for distutils.command.bdist."""
+"""Tests for distutils.metadata."""
 import os
 import sys
 import platform
 from StringIO import StringIO
 
-from distutils2.metadata import (DistributionMetadata, _interpret,
+from distutils2.metadata import (DistributionMetadata,
                                  PKG_INFO_PREFERRED_VERSION)
 from distutils2.tests import run_unittest, unittest
 from distutils2.tests.support import LoggingCatcher, WarningsCatcher
@@ -46,55 +46,6 @@
         self.assertRaises(TypeError, DistributionMetadata,
                           PKG_INFO, mapping=m, fileobj=fp)
 
-    def test_interpret(self):
-        sys_platform = sys.platform
-        version = sys.version.split()[0]
-        os_name = os.name
-        platform_version = platform.version()
-        platform_machine = platform.machine()
-
-        self.assertTrue(_interpret("sys.platform == '%s'" % sys_platform))
-        self.assertTrue(_interpret(
-            "sys.platform == '%s' or python_version == '2.4'" % sys_platform))
-        self.assertTrue(_interpret(
-            "sys.platform == '%s' and python_full_version == '%s'" %
-            (sys_platform, version)))
-        self.assertTrue(_interpret("'%s' == sys.platform" % sys_platform))
-        self.assertTrue(_interpret('os.name == "%s"' % os_name))
-        self.assertTrue(_interpret(
-            'platform.version == "%s" and platform.machine == "%s"' %
-            (platform_version, platform_machine)))
-
-        # stuff that need to raise a syntax error
-        ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'",
-               'okpjonon', '', 'os.name ==', 'python_version == 2.4')
-        for op in ops:
-            self.assertRaises(SyntaxError, _interpret, op)
-
-        # combined operations
-        OP = 'os.name == "%s"' % os_name
-        AND = ' and '
-        OR = ' or '
-        self.assertTrue(_interpret(OP + AND + OP))
-        self.assertTrue(_interpret(OP + AND + OP + AND + OP))
-        self.assertTrue(_interpret(OP + OR + OP))
-        self.assertTrue(_interpret(OP + OR + OP + OR + OP))
-
-        # other operators
-        self.assertTrue(_interpret("os.name != 'buuuu'"))
-        self.assertTrue(_interpret("python_version > '1.0'"))
-        self.assertTrue(_interpret("python_version < '5.0'"))
-        self.assertTrue(_interpret("python_version <= '5.0'"))
-        self.assertTrue(_interpret("python_version >= '1.0'"))
-        self.assertTrue(_interpret("'%s' in os.name" % os_name))
-        self.assertTrue(_interpret("'buuuu' not in os.name"))
-        self.assertTrue(_interpret(
-            "'buuuu' not in os.name and '%s' in os.name" % os_name))
-
-        # execution context
-        self.assertTrue(_interpret('python_version == "0.1"',
-                                   {'python_version': '0.1'}))
-
     def test_metadata_read_write(self):
         PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
         metadata = DistributionMetadata(PKG_INFO)

--
Repository URL: http://hg.python.org/distutils2


More information about the Python-checkins mailing list