Skip to content

Commit

Permalink
merged with master
Browse files Browse the repository at this point in the history
  • Loading branch information
climbus committed Sep 18, 2021
2 parents 7e70f63 + 4912fec commit edbacda
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 61 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Release 0.20.0

Date: 2021-09-18

New feature

- #377 Added the ability to extract method to @staticmethod/@classmethod (@climbus)
- #374 Changed Organize import to keep variables listed in `__all__`
- Change default .ropeproject/config.py to ignore code in folders named
.venv and venv (@0x1e02)

Syntax support

- #372 Add extract method refactoring of code containing `exec` (@ceridwen)
- #389 Add extract method refactoring of code containing `async def`, `async for`, and `await`
- #365, #386 Support extract method of expressions containing inline assignment (walrus operator)

Bug fixes

- #380 Fix list of variables that are returned and/or turned into argument when extracting method in a loop
32 changes: 0 additions & 32 deletions docs/dev/issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,35 +122,3 @@ Examples
.. ...
Possible Module Renamings
=========================

*First level*:

These module names are somehow inconsistent.

* change -> changes
* method_object -> methodobject
* default_config -> defaultconfig

*Second level*

Many modules use long names. They can be shortened without loss of
readability.

* methodobject -> methobj or funcobj
* usefunction -> usefunc
* multiproject -> mulprj
* functionutils -> funcutils
* importutils -> imputils
* introduce_factory -> factory
* change_signature -> signature
* encapsulate_field -> encapsulate
* sourceutils -> srcutils
* resourceobserver -> observer


Getting Ready For Python 3.0
============================

This has been moved to a separate branch.
8 changes: 0 additions & 8 deletions docs/dev/todo.rst

This file was deleted.

2 changes: 1 addition & 1 deletion docs/release-process.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
1. Increment version number in ``rope/__init__.py``
2. Tag the release with the tag annotation containing the release information
2. Tag the release with the tag annotation containing the release information, e.g. ``git tag -s 0.21.0``
3. ``python3 setup.py sdist``
4. ``twine upload -s dist/rope-$VERSION.tar.gz*``
2 changes: 1 addition & 1 deletion rope/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""rope, a python refactoring library"""

INFO = __doc__
VERSION = '0.19.0'
VERSION = '0.20.0'
COPYRIGHT = """\
Copyright (C) 2019-2021 Matej Cepl
Copyright (C) 2015-2018 Nicholas Smith
Expand Down
56 changes: 44 additions & 12 deletions rope/refactor/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ def base_conditions(self, info):
if end_scope != info.scope and end_scope.get_end() != end_line:
raise RefactoringError('Bad region selected for extract method')
try:
extracted = info.source[info.region[0]:info.region[1]]
extracted = info.extracted
if info.one_line:
extracted = '(%s)' % extracted
if _UnmatchedBreakOrContinueFinder.has_errors(extracted):
Expand All @@ -432,17 +432,22 @@ def one_line_conditions(self, info):
'span multiple lines.')
if usefunction._named_expr_count(info._parsed_extracted) - usefunction._namedexpr_last(info._parsed_extracted):
raise RefactoringError('Extracted piece cannot '
'contain named expression (:=) statements.')
'contain named expression (:= operator).')

def multi_line_conditions(self, info):
node = _parse_text(info.source[info.region[0]:info.region[1]])
count = usefunction._return_count(node)
extracted = info.extracted
if count > 1:
raise RefactoringError('Extracted piece can have only one '
'return statement.')
if usefunction._yield_count(node):
raise RefactoringError('Extracted piece cannot '
'have yield statements.')
if not hasattr(ast, 'PyCF_ALLOW_TOP_LEVEL_AWAIT') and _AsyncStatementFinder.has_errors(extracted):
raise RefactoringError('Extracted piece can only have async/await '
'statements if Rope is running on Python '
'3.8 or higher')
if count == 1 and not usefunction._returns_last(node):
raise RefactoringError('Return should be the last statement.')
if info.region != info.lines_region:
Expand Down Expand Up @@ -782,6 +787,9 @@ def _FunctionDef(self, node):
def _Global(self, node):
self.globals_.add(*node.names)

def _AsyncFunctionDef(self, node):
self._FunctionDef(node)

def _Name(self, node):
if isinstance(node.ctx, (ast.Store, ast.AugStore)):
self._written_variable(node.id, node.lineno)
Expand Down Expand Up @@ -894,7 +902,18 @@ def find_reads_for_one_liners(code):
return visitor.read


class _UnmatchedBreakOrContinueFinder(object):
class _BaseErrorFinder(object):
@classmethod
def has_errors(cls, code):
if code.strip() == '':
return False
node = _parse_text(code)
visitor = cls()
ast.walk(node, visitor)
return visitor.error


class _UnmatchedBreakOrContinueFinder(_BaseErrorFinder):

def __init__(self):
self.error = False
Expand Down Expand Up @@ -934,14 +953,23 @@ def _FunctionDef(self, node):
def _ClassDef(self, node):
pass

@staticmethod
def has_errors(code):
if code.strip() == '':
return False
node = _parse_text(code)
visitor = _UnmatchedBreakOrContinueFinder()
ast.walk(node, visitor)
return visitor.error

class _AsyncStatementFinder(_BaseErrorFinder):

def __init__(self):
self.error = False

def _AsyncFor(self, node):
self.error = True

def _AsyncWith(self, node):
self.error = True

def _FunctionDef(self, node):
pass

def _ClassDef(self, node):
pass


class _GlobalFinder(object):
Expand All @@ -962,7 +990,11 @@ def _parse_text(body):
node = ast.parse(body)
except SyntaxError:
# needed to parse expression containing := operator
node = ast.parse('(' + body + ')')
try:
node = ast.parse('(' + body + ')')
except SyntaxError:
node = ast.parse('async def __rope_placeholder__():\n' + sourceutils.fix_indentation(body, 4))
node.body = node.body[0].body
return node


Expand Down
34 changes: 30 additions & 4 deletions rope/refactor/patchedast.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,14 +468,24 @@ def _ExtSlice(self, node):
children.append(dim)
self._handle(node, children)

def _For(self, node):
children = ['for', node.target, 'in', node.iter, ':']
def _handle_for_loop_node(self, node, is_async):
if is_async:
children = ['async', 'for']
else:
children = ['for']
children.extend([node.target, 'in', node.iter, ':'])
children.extend(node.body)
if node.orelse:
children.extend(['else', ':'])
children.extend(node.orelse)
self._handle(node, children)

def _For(self, node):
self._handle_for_loop_node(node, is_async=False)

def _AsyncFor(self, node):
self._handle_for_loop_node(node, is_async=True)

def _ImportFrom(self, node):
children = ['from']
if node.level:
Expand All @@ -492,7 +502,7 @@ def _alias(self, node):
children.extend(['as', node.asname])
self._handle(node, children)

def _FunctionDef(self, node):
def _handle_function_def_node(self, node, is_async):
children = []
try:
decorators = getattr(node, 'decorator_list')
Expand All @@ -502,11 +512,21 @@ def _FunctionDef(self, node):
for decorator in decorators:
children.append('@')
children.append(decorator)
children.extend(['def', node.name, '(', node.args])
if is_async:
children.extend(['async', 'def'])
else:
children.extend(['def'])
children.extend([node.name, '(', node.args])
children.extend([')', ':'])
children.extend(node.body)
self._handle(node, children)

def _FunctionDef(self, node):
self._handle_function_def_node(node, is_async=False)

def _AsyncFunctionDef(self, node):
self._handle_function_def_node(node, is_async=True)

def _arguments(self, node):
children = []
args = list(node.args)
Expand Down Expand Up @@ -793,6 +813,12 @@ def _UnaryOp(self, node):
children.append(node.operand)
self._handle(node, children)

def _Await(self, node):
children = ['await']
if node.value:
children.append(node.value)
self._handle(node, children)

def _Yield(self, node):
children = ['yield']
if node.value:
Expand Down
Loading

0 comments on commit edbacda

Please sign in to comment.