Commits

Ali Gholami Rudi  committed 1f60a19

Inlining classmethods

  • Participants
  • Parent commits 6e9f718

Comments (0)

Files changed (4)

File docs/dev/workingon.txt

 Small Stories
 =============
 
-- object inference and ``self`` argument; it gets the value of
-  subclasses
-- What to do about methods with no parameters?
+- Inlining classmethods
+
+* Generate method and functions
+* Assignment to parameters in inline method
 
 * What to do with files that cannot be compiled when refactoring?
 * Extracting subexpressions; look at `extracttest` for more info
 * Recursive SOI; Go where the calls go with limited depth
 * Adding ``do when unsure`` config to all refactorings?
 * Removing ``pass`` when introducing factory for empty classes
-p* Importing star and removing self imports; stack overflow
+* Importing star and removing self imports; stack overflow
 
 UI and IDE:
 

File rope/refactor/functionutils.py

 
 class _FunctionParser(object):
 
-    def __init__(self, call, is_method):
+    def __init__(self, call, implicit_arg):
         self.call = call
-        self.is_method = is_method
+        self.implicit_arg = implicit_arg
         self.word_finder = rope.base.codeanalyze.WordRangeFinder(self.call)
         self.last_parens = self.call.rindex(')')
         self.first_parens = self.word_finder._find_parens_start(self.last_parens)
             return self.word_finder.get_primary_at(self.first_parens - 1)
 
     def is_called_as_a_method(self):
-        return self.is_method and '.' in self.call[:self.first_parens]
+        return self.implicit_arg and '.' in self.call[:self.first_parens]
 
 
 class DefinitionInfo(object):
         scope = pyfunction.get_scope()
         parent = scope.parent
         parameter_names = pyfunction.get_param_names()
-        is_method = len(parameter_names) > 0 and \
-                    (parent is not None and parent.pyobject == pyfunction.
-                     get_parameters()[parameter_names[0]].get_object().get_type()) and \
-                     (isinstance(parent.pyobject, rope.base.pyobjects.PyClass))
+        is_method = pyfunction.get_kind() == 'method'
         info = _FunctionParser(code, is_method)
         args, keywords = info.get_parameters()
         args_arg = None
 class CallInfo(object):
 
     def __init__(self, function_name, args, keywords, args_arg,
-                 keywords_arg, is_method_call, is_constructor):
+                 keywords_arg, implicit_arg, constructor):
         self.function_name = function_name
         self.args = args
         self.keywords = keywords
         self.args_arg = args_arg
         self.keywords_arg = keywords_arg
-        self.is_method_call = is_method_call
-        self.is_constructor = is_constructor
+        self.implicit_arg = implicit_arg
+        self.constructor = constructor
 
     def to_string(self):
         function = self.function_name
-        if self.is_method_call:
+        if self.implicit_arg:
             function = self.args[0] + '.' + self.function_name
         params = []
         start = 0
-        if self.is_method_call or self.is_constructor:
+        if self.implicit_arg or self.constructor:
             start = 1
         if self.args[start:]:
             params.extend(self.args[start:])
 
     @staticmethod
     def read(primary, pyname, definition_info, code):
-        is_method_call = False
-        is_constructor = False
-        if CallInfo._is_instance(primary) and _is_method(pyname):
-            is_method_call = True
-        if CallInfo._is_class(pyname):
-            is_constructor = True
-        info = _FunctionParser(code, is_method_call)
+        is_method_call = CallInfo._is_method_call(primary, pyname)
+        is_constructor = CallInfo._is_class(pyname)
+        is_classmethod = CallInfo._is_classmethod(pyname)
+        info = _FunctionParser(code, is_method_call or is_classmethod)
         args, keywords = info.get_parameters()
         args_arg = None
         keywords_arg = None
         if is_constructor:
             args.insert(0, definition_info.args_with_defaults[0][0])
         return CallInfo(info.get_function_name(), args, keywords, args_arg,
-                        keywords_arg, is_method_call, is_constructor)
+                        keywords_arg, is_method_call or is_classmethod,
+                        is_constructor)
 
     @staticmethod
-    def _is_instance(pyname):
-        return pyname is not None and \
-               isinstance(pyname.get_object().get_type(),
-                          rope.base.pyobjects.PyClass)
+    def _is_method_call(primary, pyname):
+        return primary is not None and \
+               isinstance(primary.get_object().get_type(),
+                          rope.base.pyobjects.PyClass) and \
+                          CallInfo._is_method(pyname)
 
     @staticmethod
     def _is_class(pyname):
                isinstance(pyname.get_object(),
                           rope.base.pyobjects.PyClass)
 
+    @staticmethod
+    def _is_method(pyname):
+        if pyname is not None and \
+           isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction):
+            return pyname.get_object().get_kind() == 'method'
+        return False
 
-def _is_method(pyname):
-    if pyname is not None and \
-       isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction):
-        return pyname.get_object().get_kind() == 'method'
-    return False
+    @staticmethod
+    def _is_classmethod(pyname):
+        if pyname is not None and \
+           isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction):
+            return pyname.get_object().get_kind() == 'classmethod'
+        return False
 
 
 class ArgumentMapping(object):
         keywords.extend(self.keyword_args)
         return CallInfo(self.call_info.function_name, args, keywords,
                         self.call_info.args_arg, self.call_info.keywords_arg,
-                        self.call_info.is_method_call, self.call_info.is_constructor)
+                        self.call_info.implicit_arg, self.call_info.constructor)

File rope/refactor/inline.py

            definition_info.keywords_arg is not None:
             raise rope.base.exceptions.RefactoringError(
                 'Cannot inline functions with list and keyword arguements.')
+        if self.pyfunction.get_kind() == 'classmethod':
+            paramdict[definition_info.args_with_defaults[0][0]] = self.pyfunction.parent.get_name()
         return paramdict
 
     def get_function_name(self):
         header = ''
         to_be_inlined = []
         for name, value in paramdict.items():
-            if name != value:
+            if name != value and value is not None:
                 header += name + ' = ' + value + '\n'
                 to_be_inlined.append(name)
         source = header + self.body

File ropetest/refactor/inlinetest.py

                   'print 2\n'
         self.assertEquals(expected, self.mod.read())
 
+    def test_inlining_classmethods(self):
+        a_class = 'class A(object):\n' \
+                  '    @classmethod\n' \
+                  '    def a_func(cls, param):\n' \
+                  '        print(param)\n' \
+                  'A.a_func(1)\n'
+        self.mod.write(a_class)
+        self._inline2(self.mod, self.mod.read().index('a_func') + 1)
+        expected = 'class A(object):\n' \
+                   '    pass\n' \
+                   'print(1)\n'
+        self.assertEquals(expected, self.mod.read())
+
+    def test_inlining_classmethods2(self):
+        a_class = 'class A(object):\n' \
+                  '    @classmethod\n' \
+                  '    def a_func(cls, param):\n' \
+                  '        return cls\n' \
+                  'print(A.a_func(1))\n'
+        self.mod.write(a_class)
+        self._inline2(self.mod, self.mod.read().index('a_func') + 1)
+        expected = 'class A(object):\n' \
+                   '    pass\n' \
+                   'print(A)\n'
+        self.assertEquals(expected, self.mod.read())
+
     def test_simple_return_values_and_inlining_functions(self):
         self.mod.write('def a_func():\n    return 1\na = a_func()\n')
         self._inline2(self.mod, self.mod.read().index('a_func') + 1)