[Checkins] SVN: zope.fixers/trunk/zope/fixers/ Added a fixer for implementsOnly. Currently the decorator is called implementer_only. I'm not happy with
Lennart Regebro
regebro at gmail.com
Tue Apr 7 12:31:03 EDT 2009
Log message for revision 98979:
Added a fixer for implementsOnly. Currently the decorator is called implementer_only. I'm not happy with
that name, but we can easily change it until we make a first release of a Python 3 compatible
zope.interface.
Changed:
A zope.fixers/trunk/zope/fixers/base.py
U zope.fixers/trunk/zope/fixers/fix_implements.py
A zope.fixers/trunk/zope/fixers/fix_implements_only.py
U zope.fixers/trunk/zope/fixers/tests.py
-=-
Added: zope.fixers/trunk/zope/fixers/base.py
===================================================================
--- zope.fixers/trunk/zope/fixers/base.py (rev 0)
+++ zope.fixers/trunk/zope/fixers/base.py 2009-04-07 16:31:03 UTC (rev 98979)
@@ -0,0 +1,163 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Fixer for class interface declarations to class decorators
+
+$Id$
+"""
+
+# Local imports
+from lib2to3.fixer_base import BaseFix
+from lib2to3.patcomp import PatternCompiler
+from lib2to3.fixer_util import syms, Name
+from lib2to3.fixer_util import Node, Leaf
+
+class Function2DecoratorBase(BaseFix):
+
+ IMPORT_PATTERN = """
+ import_from< 'from' dotted_name< 'zope' '.' 'interface' > 'import' import_as_names< any* (name='%(function_name)s') any* > >
+ |
+ import_from< 'from' dotted_name< 'zope' '.' 'interface' > 'import' name='%(function_name)s' any* >
+ |
+ import_from< 'from' dotted_name< 'zope' > 'import' name='interface' any* >
+ |
+ import_from< 'from' dotted_name< 'zope' '.' 'interface' > 'import' import_as_name< name='%(function_name)s' 'as' rename=(any) any*> >
+ |
+ import_from< 'from' dotted_name< 'zope' > 'import' import_as_name< name='interface' 'as' rename=(any) any*> >
+ |
+ import_from< 'from' 'zope' 'import' import_as_name< 'interface' 'as' interface_rename=(any) > >
+ """
+
+ CLASS_PATTERN = """
+ classdef< 'class' any* ':' suite< any* simple_stmt< power< statement=(%s) trailer < '(' interface=any ')' > any* > any* > any* > >
+ """
+
+ FUNCTION_PATTERN = """
+ simple_stmt< power< old_statement=(%s) trailer < '(' any* ')' > > any* >
+ """
+
+ def should_skip(self, node):
+ module = str(node)
+ return not ('zope' in module and 'interface' in module)
+
+ def compile_pattern(self):
+ # Compile the import pattern.
+ self.named_import_pattern = PatternCompiler().compile_pattern(
+ self.IMPORT_PATTERN % {'function_name': self.FUNCTION_NAME})
+
+ def start_tree(self, tree, filename):
+ # Compile the basic class/function matches. This is done per tree,
+ # as further matches (based on what imports there are) also are done
+ # per tree.
+ self.class_patterns = []
+ self.function_patterns = []
+ self.fixups = []
+
+ self._add_pattern("'%s'" % self.FUNCTION_NAME)
+ self._add_pattern("'interface' trailer< '.' '%s' >" % self.FUNCTION_NAME)
+ self._add_pattern("'zope' trailer< '.' 'interface' > trailer< '.' '%s' >" % self.FUNCTION_NAME)
+
+ def _add_pattern(self, match):
+ self.class_patterns.append(PatternCompiler().compile_pattern(
+ self.CLASS_PATTERN % match))
+ self.function_patterns.append(PatternCompiler().compile_pattern(
+ self.FUNCTION_PATTERN % match))
+
+ def match(self, node):
+ # Matches up the imports
+ results = {"node": node}
+ if self.named_import_pattern.match(node, results):
+ return results
+
+ # Now match classes on all import variants found:
+ for pattern in self.class_patterns:
+ if pattern.match(node, results):
+ return results
+
+ # And lastly on all actual calls to the function:
+ for pattern in self.function_patterns:
+ if pattern.match(node, results):
+ return results
+
+ def transform(self, node, results):
+ if 'name' in results:
+ # This matched an import statement. Fix that up:
+ name = results["name"]
+ name.replace(Name(self.DECORATOR_NAME, prefix=name.get_prefix()))
+ if 'rename' in results:
+ # The import statement use import as
+ self._add_pattern("'%s'" % results['rename'].value)
+ if 'interface_rename' in results:
+ self._add_pattern("'%s' trailer< '.' '%s' > " % (
+ results['interface_rename'].value, self.FUNCTION_NAME))
+ if 'statement' in results:
+ # This matched a class that has an <FUNCTION_NAME>(IFoo) statement.
+ # We must convert that statement to a class decorator
+ # and put it before the class definition.
+
+ statement = results['statement']
+ if not isinstance(statement, list):
+ statement = [statement]
+ # Make a copy for insertion before the class:
+ statement = [x.clone() for x in statement]
+ # Get rid of leading whitespace:
+ statement[0].prefix = ''
+ # Rename function to decorator:
+ if statement[-1].children:
+ func = statement[-1].children[-1]
+ else:
+ func = statement[-1]
+ if func.value == self.FUNCTION_NAME:
+ func.value = self.DECORATOR_NAME
+
+ interface = results['interface']
+ if not isinstance(interface, list):
+ interface = [interface]
+ interface = [x.clone() for x in interface]
+
+ # Create the decorator:
+ decorator = Node(syms.decorator, [Leaf(50, '@'),] + statement +
+ [Leaf(7, '(')] + interface + [Leaf(8, ')')])
+
+ # Take the current class constructor prefix, and stick it into
+ # the decorator, to set the decorators indentation.
+ prefix = node.get_prefix()
+ decorator.set_prefix(prefix)
+
+ # Then find the last line of the previous node and use that as
+ # indentation, and add that to the class constructors prefix.
+ prefix = str(node.get_prev_sibling())
+ if not prefix:
+ prefix = node.get_prefix()
+ elif '\n' in prefix:
+ prefix = prefix[prefix.rfind('\n')+1:] + node.get_prefix()
+ else:
+ prefix = prefix + node.get_prefix()
+ if not prefix or prefix[0] != '\n':
+ prefix = '\n' + prefix
+ node.set_prefix(prefix)
+ node.insert_child(0, decorator)
+
+ if 'old_statement' in results:
+ # This matched an function statement. We'll remove it.
+ self.fixups.append(node)
+
+ def finish_tree(self, tree, filename):
+ for node in self.fixups:
+ parent = node.parent
+ node.remove()
+ if not str(parent).strip():
+ # This is an empty class. Stick in a pass
+ parent.insert_child(2, Leaf(0, 'pass'))
+ parent.insert_child(3, Leaf(0, '\n'))
+
\ No newline at end of file
Modified: zope.fixers/trunk/zope/fixers/fix_implements.py
===================================================================
--- zope.fixers/trunk/zope/fixers/fix_implements.py 2009-04-07 15:11:12 UTC (rev 98978)
+++ zope.fixers/trunk/zope/fixers/fix_implements.py 2009-04-07 16:31:03 UTC (rev 98979)
@@ -16,149 +16,8 @@
$Id$
"""
-# Local imports
-from lib2to3.fixer_base import BaseFix
-from lib2to3.patcomp import PatternCompiler
-from lib2to3.fixer_util import syms, Name
-from lib2to3.fixer_util import Node, Leaf
+from .base import Function2DecoratorBase
-helper = dict([(b,a) for (a,b) in syms.__dict__.items()])
-
-class FixImplements(BaseFix):
-
- IMPORT_PATTERN = """
- import_from< 'from' dotted_name< 'zope' '.' 'interface' > 'import' import_as_names< any* (name='implements') any* > >
- |
- import_from< 'from' dotted_name< 'zope' '.' 'interface' > 'import' name='implements' any* >
- |
- import_from< 'from' dotted_name< 'zope' > 'import' name='interface' any* >
- |
- import_from< 'from' dotted_name< 'zope' '.' 'interface' > 'import' import_as_name< name='implements' 'as' rename=(any) any*> >
- |
- import_from< 'from' dotted_name< 'zope' > 'import' import_as_name< name='interface' 'as' rename=(any) any*> >
- |
- import_from< 'from' 'zope' 'import' import_as_name< 'interface' 'as' interface_rename=(any) > >
- """
-
- CLASS_PATTERN = """
- classdef< 'class' any* ':' suite< any* simple_stmt< power< statement=(%s) trailer < '(' interface=any ')' > any* > any* > any* > >
- """
-
- IMPLEMENTS_PATTERN = """
- simple_stmt< power< old_statement=(%s) trailer < '(' any* ')' > > any* >
- """
-
- fixups = []
-
- def should_skip(self, node):
- module = str(node)
- return not ('zope' in module and 'interface' in module)
-
- def compile_pattern(self):
- # Compile the import pattern.
- self.named_import_pattern = PatternCompiler().compile_pattern(self.IMPORT_PATTERN)
-
- def start_tree(self, tree, filename):
- # Compile the basic class/implements matches. This is done per tree,
- # as further matches (based on what imports there are) also are done
- # per tree.
- self.class_patterns = []
- self.implements_patterns = []
- self._add_pattern("'implements'")
- self._add_pattern("'interface' trailer< '.' 'implements' >")
- self._add_pattern("'zope' trailer< '.' 'interface' > trailer< '.' 'implements' >")
-
- def _add_pattern(self, match):
- self.class_patterns.append(PatternCompiler().compile_pattern(
- self.CLASS_PATTERN % match))
- self.implements_patterns.append(PatternCompiler().compile_pattern(
- self.IMPLEMENTS_PATTERN % match))
-
- def match(self, node):
- # Matches up the imports
- results = {"node": node}
- if self.named_import_pattern.match(node, results):
- return results
-
- # Now match classes on all import variants found:
- for pattern in self.class_patterns:
- if pattern.match(node, results):
- return results
-
- # And lastly on all actual calls to implements:
- for pattern in self.implements_patterns:
- if pattern.match(node, results):
- return results
-
- def transform(self, node, results):
- if 'name' in results:
- # This matched an import statement. Fix that up:
- name = results["name"]
- name.replace(Name("implementer", prefix=name.get_prefix()))
- if 'rename' in results:
- # The import statement use import as
- self._add_pattern("'%s'" % results['rename'].value)
- if 'interface_rename' in results:
- self._add_pattern("'%s' trailer< '.' 'implements' > " % results['interface_rename'].value)
- if 'statement' in results:
- # This matched a class that has an implements(IFoo) statement.
- # We must convert that statement to a class decorator
- # and put it before the class definition.
-
- statement = results['statement']
- if not isinstance(statement, list):
- statement = [statement]
- # Make a copy for insertion before the class:
- statement = [x.clone() for x in statement]
- # Get rid of leading whitespace:
- statement[0].prefix = ''
- # Rename implements to implementer:
- if statement[-1].children:
- implements = statement[-1].children[-1]
- else:
- implements = statement[-1]
- if implements.value == 'implements':
- implements.value = 'implementer'
-
- interface = results['interface']
- if not isinstance(interface, list):
- interface = [interface]
- interface = [x.clone() for x in interface]
-
- # Create the decorator:
- decorator = Node(syms.decorator, [Leaf(50, '@'),] + statement +
- [Leaf(7, '(')] + interface + [Leaf(8, ')')])
-
- # Take the current class constructor prefix, and stick it into
- # the decorator, to set the decorators indentation.
- #import pdb;pdb.set_trace()
- prefix = node.get_prefix()
- decorator.set_prefix(prefix)
-
- # Then find the last line of the previous node and use that as
- # indentation, and add that to the class constructors prefix.
- prefix = str(node.get_prev_sibling())
- if not prefix:
- prefix = node.get_prefix()
- elif '\n' in prefix:
- prefix = prefix[prefix.rfind('\n')+1:] + node.get_prefix()
- else:
- prefix = prefix + node.get_prefix()
- if not prefix or prefix[0] != '\n':
- prefix = '\n' + prefix
- node.set_prefix(prefix)
- node.insert_child(0, decorator)
-
- if 'old_statement' in results:
- # This matched an implements statement. We'll remove it.
- self.fixups.append(node)
-
- def finish_tree(self, tree, filename):
- for node in self.fixups:
- parent = node.parent
- node.remove()
- if not str(parent).strip():
- # This is an empty class. Stick in a pass
- parent.insert_child(2, Leaf(0, 'pass'))
- parent.insert_child(3, Leaf(0, '\n'))
-
\ No newline at end of file
+class FixImplements(Function2DecoratorBase):
+ FUNCTION_NAME = 'implements'
+ DECORATOR_NAME = 'implementer'
Added: zope.fixers/trunk/zope/fixers/fix_implements_only.py
===================================================================
--- zope.fixers/trunk/zope/fixers/fix_implements_only.py (rev 0)
+++ zope.fixers/trunk/zope/fixers/fix_implements_only.py 2009-04-07 16:31:03 UTC (rev 98979)
@@ -0,0 +1,23 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Fixer for implements(IX) -> @implementer(IX).
+
+$Id$
+"""
+
+from .base import Function2DecoratorBase
+
+class FixImplementsOnly(Function2DecoratorBase):
+ FUNCTION_NAME = 'implementsOnly'
+ DECORATOR_NAME = 'implementer_only'
Modified: zope.fixers/trunk/zope/fixers/tests.py
===================================================================
--- zope.fixers/trunk/zope/fixers/tests.py 2009-04-07 15:11:12 UTC (rev 98978)
+++ zope.fixers/trunk/zope/fixers/tests.py 2009-04-07 16:31:03 UTC (rev 98979)
@@ -307,6 +307,11 @@
msg = msg.replace('\t', '------->')
msg = ("Test failed at character %i" % i) + msg
self.fail(msg)
+
+class ImplementsFixerTest(FixerTest):
+
+ def setUp(self):
+ self.tool = RefactoringTool(['zope.fixers.fix_implements'])
def test_imports(self):
self._test(imports_source, imports_target)
@@ -337,3 +342,35 @@
def test_edge_cases(self):
self._test(edge_cases_source, edge_cases_target)
+
+
+implements_only_source = """
+from zope.interface import implementsOnly
+
+class IFoo(Interface):
+ pass
+
+class Foo:
+ "An IFoo class"
+
+ implementsOnly(IFoo)
+"""
+
+implements_only_target = """
+from zope.interface import implementer_only
+
+class IFoo(Interface):
+ pass
+
+ at implementer_only(IFoo)
+class Foo:
+ "An IFoo class"
+"""
+
+class ImplementsOnlyFixerTest(FixerTest):
+
+ def setUp(self):
+ self.tool = RefactoringTool(['zope.fixers.fix_implements_only'])
+
+ def test_implements_only(self):
+ self._test(implements_only_source, implements_only_target)
More information about the Checkins
mailing list