Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-rope for openSUSE:Factory checked in at 2022-02-21 17:46:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-rope (Old) and /work/SRC/openSUSE:Factory/.python-rope.new.1958 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-rope" Mon Feb 21 17:46:32 2022 rev:28 rq:956183 version:0.22.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-rope/python-rope.changes 2021-10-26 20:15:01.822052170 +0200 +++ /work/SRC/openSUSE:Factory/.python-rope.new.1958/python-rope.changes 2022-02-21 17:47:36.779612116 +0100 @@ -1,0 +2,9 @@ +Sun Feb 20 21:01:54 UTC 2022 - Dirk M??ller <[email protected]> + +- update to 0.22.0: + * #443 Implement `yield from` syntax support to patchedast.py + * #445, #446 Improve empty tuple and handling of parentheses + around tuple + * #270, #432 Fix rename import statement with dots and as keyword + +------------------------------------------------------------------- Old: ---- rope-0.21.0.tar.gz New: ---- rope-0.22.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-rope.spec ++++++ --- /var/tmp/diff_new_pack.GEtg9P/_old 2022-02-21 17:47:37.351612287 +0100 +++ /var/tmp/diff_new_pack.GEtg9P/_new 2022-02-21 17:47:37.355612288 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-rope # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %define upname rope %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-rope -Version: 0.21.0 +Version: 0.22.0 Release: 0 Summary: A python refactoring library License: LGPL-3.0-or-later ++++++ rope-0.21.0.tar.gz -> rope-0.22.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/CHANGELOG.md new/rope-0.22.0/CHANGELOG.md --- old/rope-0.21.0/CHANGELOG.md 2021-10-18 12:06:02.000000000 +0200 +++ new/rope-0.22.0/CHANGELOG.md 2021-11-22 16:05:08.000000000 +0100 @@ -1,9 +1,26 @@ # **Upcoming release** -TODO +## Syntax support + +- #443 Implement `yield from` syntax support to patchedast.py + +## Bug fixes + +- #445, #446 Improve empty tuple and handling of parentheses around tuple +- #270, #432 Fix rename import statement with dots and as keyword (@climbus) + +# Release 0.21.1 + +Date: 2021-11-11 + +## Bug fixes + +- #441. Start publishing wheel packages to allow offline installs # Release 0.21.0 +Date: 2021-10-18 + ## Syntax support - #392, #316 Handle `global` keyword when extracting method (@climbus) @@ -28,14 +45,14 @@ ## New feature -- #434 Move read() to FileSystemCommands +- #434 Move read() to FileSystemCommands (@lieryan) ## Misc - #410 Setup all-contributors bot (@lieryan) - #404 Blacken source code, rope now follows black code style (@climbus) -- #399 Add Github Actions to enforce black code style -- #403 Remove plain 'unittest' only runner +- #399 Add Github Actions to enforce black code style (@lieryan) +- #403 Remove plain 'unittest' only runner (@lieryan) # Release 0.20.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/PKG-INFO new/rope-0.22.0/PKG-INFO --- old/rope-0.21.0/PKG-INFO 2021-10-18 12:10:41.121533400 +0200 +++ new/rope-0.22.0/PKG-INFO 2021-11-22 16:08:12.375390500 +0100 @@ -1,34 +1,11 @@ Metadata-Version: 2.1 Name: rope -Version: 0.21.0 +Version: 0.22.0 Summary: a python refactoring library... Home-page: https://github.com/python-rope/rope Author: Ali Gholami Rudi Author-email: [email protected] License: LGPL-3.0-or-later -Description: - - .. _GitHub python-rope / rope: https://github.com/python-rope/rope - - - ========================================================================= - rope -- the world's most advanced open source Python refactoring library - ========================================================================= - - - Overview - ======== - - `Rope`_ is the world's most advanced open source Python refactoring library - (yes, I totally stole that tagline from Postgres). - - .. _`rope`: https://github.com/python-rope/rope - - - Most Python syntax from Python 2.7 up to Python 3.9 is supported. Please file bugs and contribute - patches if you encounter gaps. - - Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent @@ -51,3 +28,29 @@ Classifier: Topic :: Software Development Description-Content-Type: text/x-rst Provides-Extra: dev +License-File: COPYING + + + +.. _GitHub python-rope / rope: https://github.com/python-rope/rope + + +========================================================================= + rope -- the world's most advanced open source Python refactoring library +========================================================================= + + +Overview +======== + +`Rope`_ is the world's most advanced open source Python refactoring library +(yes, I totally stole that tagline from Postgres). + +.. _`rope`: https://github.com/python-rope/rope + + +Most Python syntax from Python 2.7 up to Python 3.9 is supported. Please file bugs and contribute +patches if you encounter gaps. + + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/docs/release-process.rst new/rope-0.22.0/docs/release-process.rst --- old/rope-0.21.0/docs/release-process.rst 2021-10-18 12:10:18.000000000 +0200 +++ new/rope-0.22.0/docs/release-process.rst 2021-11-11 14:22:16.000000000 +0100 @@ -2,6 +2,6 @@ 2. Update CHANGELOG.md 3. Increment version number in ``rope/__init__.py`` 4. Tag the release with the tag annotation containing the release information, e.g. ``git tag -s 0.21.0; git push origin 0.21.0`` -5. ``python3 setup.py sdist`` -6. ``twine upload -s dist/rope-$VERSION.tar.gz*`` +5. ``python3 -m build`` +6. ``twine upload -s dist/rope-$VERSION.{tar.gz,whl}`` 7. Publish to Discussions Announcement diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/rope/.ropeproject/config.py new/rope-0.22.0/rope/.ropeproject/config.py --- old/rope-0.21.0/rope/.ropeproject/config.py 1970-01-01 01:00:00.000000000 +0100 +++ new/rope-0.22.0/rope/.ropeproject/config.py 2021-11-11 14:51:05.000000000 +0100 @@ -0,0 +1,125 @@ +# The default ``config.py`` +# flake8: noqa + + +def set_prefs(prefs): + """This function is called before opening the project""" + + # Specify which files and folders to ignore in the project. + # Changes to ignored resources are not added to the history and + # VCSs. Also they are not returned in `Project.get_files()`. + # Note that ``?`` and ``*`` match all characters but slashes. + # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' + # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' + # '.svn': matches 'pkg/.svn' and all of its children + # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' + # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' + prefs["ignored_resources"] = [ + "*.pyc", + "*~", + ".ropeproject", + ".hg", + ".svn", + "_svn", + ".git", + ".tox", + ".venv", + "venv", + ] + + # Specifies which files should be considered python files. It is + # useful when you have scripts inside your project. Only files + # ending with ``.py`` are considered to be python files by + # default. + # prefs['python_files'] = ['*.py'] + + # Custom source folders: By default rope searches the project + # for finding source folders (folders that should be searched + # for finding modules). You can add paths to that list. Note + # that rope guesses project source folders correctly most of the + # time; use this if you have any problems. + # The folders should be relative to project root and use '/' for + # separating folders regardless of the platform rope is running on. + # 'src/my_source_folder' for instance. + # prefs.add('source_folders', 'src') + + # You can extend python path for looking up modules + # prefs.add('python_path', '~/python/') + + # Should rope save object information or not. + prefs["save_objectdb"] = True + prefs["compress_objectdb"] = False + + # If `True`, rope analyzes each module when it is being saved. + prefs["automatic_soa"] = True + # The depth of calls to follow in static object analysis + prefs["soa_followed_calls"] = 0 + + # If `False` when running modules or unit tests "dynamic object + # analysis" is turned off. This makes them much faster. + prefs["perform_doa"] = True + + # Rope can check the validity of its object DB when running. + prefs["validate_objectdb"] = True + + # How many undos to hold? + prefs["max_history_items"] = 32 + + # Shows whether to save history across sessions. + prefs["save_history"] = True + prefs["compress_history"] = False + + # Set the number spaces used for indenting. According to + # :PEP:`8`, it is best to use 4 spaces. Since most of rope's + # unit-tests use 4 spaces it is more reliable, too. + prefs["indent_size"] = 4 + + # Builtin and c-extension modules that are allowed to be imported + # and inspected by rope. + prefs["extension_modules"] = [] + + # Add all standard c-extensions to extension_modules list. + prefs["import_dynload_stdmods"] = True + + # If `True` modules with syntax errors are considered to be empty. + # The default value is `False`; When `False` syntax errors raise + # `rope.base.exceptions.ModuleSyntaxError` exception. + prefs["ignore_syntax_errors"] = False + + # If `True`, rope ignores unresolvable imports. Otherwise, they + # appear in the importing namespace. + prefs["ignore_bad_imports"] = False + + # If `True`, rope will insert new module imports as + # `from <package> import <module>` by default. + prefs["prefer_module_from_imports"] = False + + # If `True`, rope will transform a comma list of imports into + # multiple separate import statements when organizing + # imports. + prefs["split_imports"] = False + + # If `True`, rope will remove all top-level import statements and + # reinsert them at the top of the module when making changes. + prefs["pull_imports_to_top"] = True + + # If `True`, rope will sort imports alphabetically by module name instead + # of alphabetically by import statement, with from imports after normal + # imports. + prefs["sort_imports_alphabetically"] = False + + # Location of implementation of + # rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general + # case, you don't have to change this value, unless you're an rope expert. + # Change this value to inject you own implementations of interfaces + # listed in module rope.base.oi.type_hinting.providers.interfaces + # For example, you can add you own providers for Django Models, or disable + # the search type-hinting in a class hierarchy, etc. + prefs[ + "type_hinting_factory" + ] = "rope.base.oi.type_hinting.factory.default_type_hinting_factory" + + +def project_opened(project): + """This function is called after opening the project""" + # Do whatever you like here! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/rope/__init__.py new/rope-0.22.0/rope/__init__.py --- old/rope-0.21.0/rope/__init__.py 2021-10-18 12:07:35.000000000 +0200 +++ new/rope-0.22.0/rope/__init__.py 2021-11-22 16:05:38.000000000 +0100 @@ -1,7 +1,7 @@ """rope, a python refactoring library""" INFO = __doc__ -VERSION = "0.21.0" +VERSION = "0.22.0" COPYRIGHT = """\ Copyright (C) 2021-2021 Lie Ryan Copyright (C) 2019-2021 Matej Cepl diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/rope/base/worder.py new/rope-0.22.0/rope/base/worder.py --- old/rope-0.21.0/rope/base/worder.py 2021-09-29 00:07:33.000000000 +0200 +++ new/rope-0.22.0/rope/base/worder.py 2021-11-21 14:25:04.000000000 +0100 @@ -3,6 +3,8 @@ import rope.base.simplify +MINIMAL_LEN_FOR_AS = 5 + def get_name_at(resource, offset): source_code = resource.read() @@ -15,6 +17,12 @@ Note that in these methods, offset should be the index of the character not the index of the character after it. + + Some of the methods here doesn't exactly do what their name might lead you + to think they do, these probably should be fixed. Refer to + ropetest/codeanalyzetest.py for what these methods returns. Note that + codeanalyzetest.py documents the current behavior, rather than what they + should've been. """ def __init__(self, code, handle_ignores=False): @@ -370,13 +378,24 @@ ): return False try: - end = self._find_word_end(offset) + end = self._find_import_main_part_end(offset) + if not self._has_enough_len_for_as(end): + return False as_end = min(self._find_word_end(end + 1), len(self.code)) as_start = self._find_word_start(as_end) return self.code[as_start : as_end + 1] == "as" except ValueError: return False + def _has_enough_len_for_as(self, end): + return len(self.code) > end + MINIMAL_LEN_FOR_AS + + def _find_import_main_part_end(self, offset): + end = self._find_word_end(offset) + while len(self.code) > end + 2 and self.code[end + 1] == ".": + end = self._find_word_end(end + 2) + return end + def is_a_name_after_from_import(self, offset): try: if len(self.code) > offset and self.code[offset] == "\n": diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/rope/refactor/move.py new/rope-0.22.0/rope/refactor/move.py --- old/rope-0.21.0/rope/refactor/move.py 2021-10-09 15:13:14.000000000 +0200 +++ new/rope-0.22.0/rope/refactor/move.py 2021-11-21 14:25:04.000000000 +0100 @@ -234,7 +234,7 @@ self.old_pyname = evaluate.eval_location(this_pymodule, offset) if self.old_pyname is None: raise exceptions.RefactoringError( - "Move refactoring should be performed on a " "class/function/variable." + "Move refactoring should be performed on a class/function/variable." ) if self._is_variable(self.old_pyname): self.old_name = worder.get_name_at(resource, offset) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/rope/refactor/patchedast.py new/rope-0.22.0/rope/refactor/patchedast.py --- old/rope-0.21.0/rope/refactor/patchedast.py 2021-10-18 11:33:57.000000000 +0200 +++ new/rope-0.22.0/rope/refactor/patchedast.py 2021-11-22 04:56:26.000000000 +0100 @@ -82,6 +82,7 @@ exec_close_paren_or_space = object() exec_in_or_comma = object() with_or_comma_context_manager = object() + empty_tuple = object() def __call__(self, node): method = getattr(self, "_" + node.__class__.__name__, None) @@ -128,6 +129,8 @@ ) elif child is self.Number: region = self.source.consume_number() + elif child == self.empty_tuple: + region = self.source.consume_empty_tuple() elif child == "!=": # INFO: This has been added to handle deprecated ``<>`` region = self.source.consume_not_equal() @@ -844,7 +847,7 @@ if node.elts: self._handle(node, self._child_nodes(node.elts, ","), eat_parens=True) else: - self._handle(node, ["(", ")"]) + self._handle(node, [self.empty_tuple]) def _UnaryOp(self, node): children = self._get_op(node.op) @@ -863,6 +866,10 @@ children.append(node.value) self._handle(node, children) + def _YieldFrom(self, node): + children = ["yield", "from", node.value] + self._handle(node, children) + def _While(self, node): children = ["while", node.test, ":"] children.extend(node.body) @@ -946,6 +953,9 @@ repattern = _Source._number_pattern return self._consume_pattern(repattern) + def consume_empty_tuple(self): + return self._consume_pattern(re.compile(r"\(\s*\)")) + def consume_not_equal(self): if _Source._not_equals_pattern is None: _Source._not_equals_pattern = re.compile(r"<>|!=") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/rope.egg-info/PKG-INFO new/rope-0.22.0/rope.egg-info/PKG-INFO --- old/rope-0.21.0/rope.egg-info/PKG-INFO 2021-10-18 12:10:40.000000000 +0200 +++ new/rope-0.22.0/rope.egg-info/PKG-INFO 2021-11-22 16:08:12.000000000 +0100 @@ -1,34 +1,11 @@ Metadata-Version: 2.1 Name: rope -Version: 0.21.0 +Version: 0.22.0 Summary: a python refactoring library... Home-page: https://github.com/python-rope/rope Author: Ali Gholami Rudi Author-email: [email protected] License: LGPL-3.0-or-later -Description: - - .. _GitHub python-rope / rope: https://github.com/python-rope/rope - - - ========================================================================= - rope -- the world's most advanced open source Python refactoring library - ========================================================================= - - - Overview - ======== - - `Rope`_ is the world's most advanced open source Python refactoring library - (yes, I totally stole that tagline from Postgres). - - .. _`rope`: https://github.com/python-rope/rope - - - Most Python syntax from Python 2.7 up to Python 3.9 is supported. Please file bugs and contribute - patches if you encounter gaps. - - Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent @@ -51,3 +28,29 @@ Classifier: Topic :: Software Development Description-Content-Type: text/x-rst Provides-Extra: dev +License-File: COPYING + + + +.. _GitHub python-rope / rope: https://github.com/python-rope/rope + + +========================================================================= + rope -- the world's most advanced open source Python refactoring library +========================================================================= + + +Overview +======== + +`Rope`_ is the world's most advanced open source Python refactoring library +(yes, I totally stole that tagline from Postgres). + +.. _`rope`: https://github.com/python-rope/rope + + +Most Python syntax from Python 2.7 up to Python 3.9 is supported. Please file bugs and contribute +patches if you encounter gaps. + + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/rope.egg-info/SOURCES.txt new/rope-0.22.0/rope.egg-info/SOURCES.txt --- old/rope-0.21.0/rope.egg-info/SOURCES.txt 2021-10-18 12:10:40.000000000 +0200 +++ new/rope-0.22.0/rope.egg-info/SOURCES.txt 2021-11-22 16:08:12.000000000 +0100 @@ -16,6 +16,7 @@ rope.egg-info/dependency_links.txt rope.egg-info/requires.txt rope.egg-info/top_level.txt +rope/.ropeproject/config.py rope/base/__init__.py rope/base/arguments.py rope/base/ast.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/rope.egg-info/requires.txt new/rope-0.22.0/rope.egg-info/requires.txt --- old/rope-0.21.0/rope.egg-info/requires.txt 2021-10-18 12:10:40.000000000 +0200 +++ new/rope-0.22.0/rope.egg-info/requires.txt 2021-11-22 16:08:12.000000000 +0100 @@ -1,4 +1,5 @@ [dev] +build pytest pytest-timeout diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/ropetest/codeanalyzetest.py new/rope-0.22.0/ropetest/codeanalyzetest.py --- old/rope-0.21.0/ropetest/codeanalyzetest.py 2021-10-09 17:08:05.000000000 +0200 +++ new/rope-0.22.0/ropetest/codeanalyzetest.py 2021-11-21 14:25:04.000000000 +0100 @@ -13,12 +13,6 @@ class SourceLinesAdapterTest(unittest.TestCase): - def setUp(self): - super(SourceLinesAdapterTest, self).setUp() - - def tearDown(self): - super(SourceLinesAdapterTest, self).tearDown() - def test_source_lines_simple(self): to_lines = SourceLinesAdapter("line1\nline2\n") self.assertEqual("line1", to_lines.get_line(1)) @@ -51,17 +45,79 @@ class WordRangeFinderTest(unittest.TestCase): - def setUp(self): - super(WordRangeFinderTest, self).setUp() - - def tearDown(self): - super(WordRangeFinderTest, self).tearDown() - def _find_primary(self, code, offset): word_finder = worder.Worder(code) result = word_finder.get_primary_at(offset) return result + def _annotated_code(self, annotated_code): + """ + Split annotated code into raw code and annotation. + + Odd lines in `annotated_code` is the actual Python code. + + Even lines in `annotated_code` are single-char annotation for the + previous line. + + The annotation may contain one extra character which annotates the + newline/end of line character. + """ + code_lines = annotated_code.splitlines()[::2] + annotations_lines = annotated_code.splitlines()[1::2] + if len(annotations_lines) < len(code_lines): + annotations_lines.append("") + for idx, (line, line_ann) in enumerate(zip(code_lines, annotations_lines)): + newline_ann_char = 1 # for annotation of the end of line character + self.assertLessEqual( + len(line_ann), + len(line) + newline_ann_char, + msg="Extra character in annotations", + ) + line_ann = line_ann.rstrip() + line_ann += " " * (len(line) - len(line_ann)) + if len(line_ann) != len(line) + newline_ann_char: + line_ann += " " + self.assertEqual(len(line_ann), len(line) + newline_ann_char) + annotations_lines[idx] = line_ann + code, annotations = "\n".join(code_lines), "\n".join(annotations_lines) + if code[-1] != "\n": + annotations = annotations[:-1] + self.assertEqual(len(code) + code.count("\n"), len(annotations)) + return code, annotations + + def _make_offset_annotation(self, code, func): + """ + Create annotation by calling `func(offset)` for every offset in `code`. + + For example, when the annotated code looks like so: + + import a.b.c.d + ++++++++ + + This means that `func(offset)` returns True whenever offset points to + the 'a.b.c.d' part and returns False everywhere else. + """ + + def _annotation_char(offset): + ann_char = "+" if func(offset) else " " + if code[offset] == "\n": + ann_char = ann_char + "\n" + return ann_char + + return "".join([_annotation_char(offset) for offset in range(len(code))]) + + def assert_equal_annotation(self, code, expected, actual): + if expected != actual: + msg = ["Annotation does not match:\n"] + for line, line_exp, line_actual in zip( + code.splitlines(), expected.splitlines(), actual.splitlines() + ): + msg.append(" " + line + "\n") + if line_exp != line_actual: + msg.append("e " + line_exp + "\n") + msg.append("a " + line_actual + "\n") + self.fail("".join(msg)) + def test_keyword_before_parens(self): code = dedent("""\ if (a_var).an_attr: @@ -224,7 +280,33 @@ code = '"" + # var2.\n var3' self.assertEqual("var3", self._find_primary(code, 21)) - def test_import_statement_finding(self): + def test_is_import_statement(self): + code, annotations = self._annotated_code(annotated_code=dedent("""\ + import a.b.c.d + ++++++++ + from a.b import c + + import a.b.c.d as d + +++++++++++++ + from a.b import c as e + + from a.b import ( + + abc + + ) + + result = a.b.c.d.f() + + """)) + word_finder = worder.Worder(code) + self.assert_equal_annotation( + code, + annotations, + self._make_offset_annotation(code, word_finder.is_import_statement), + ) + + def test_is_import_statement_finding(self): code = dedent("""\ import mod a_var = 10 @@ -233,7 +315,7 @@ self.assertTrue(word_finder.is_import_statement(code.index("mod") + 1)) self.assertFalse(word_finder.is_import_statement(code.index("a_var") + 1)) - def test_import_statement_finding2(self): + def test_is_import_statement_finding2(self): code = dedent("""\ import a.b.c.d result = a.b.c.d.f() @@ -308,6 +390,120 @@ word_finder = worder.Worder(code) self.assertTrue(word_finder.is_assigned_here(0)) + def test_is_from_statement(self): + code, annotations = self._annotated_code(annotated_code=dedent("""\ + import a.b.c.d + + from a.b import c + +++++++++++++ + import a.b.c.d as d + + from a.b import c as e + ++++++++++++++++++ + from a.b import ( + +++++++++++++ + abc + ++++++++ + ) + ++ + result = a.b.c.d.f() + + """)) + word_finder = worder.Worder(code) + self.assert_equal_annotation( + code, + annotations, + self._make_offset_annotation(code, word_finder.is_from_statement), + ) + + def test_is_from_statement_module(self): + code, annotations = self._annotated_code(annotated_code=dedent("""\ + import a.b.c.d + + from a.b import c + +++++ + import a.b.c.d as d + + from a.b import c as e + +++++ + from a.b import ( + +++++ + abc + + ) + + result = a.b.c.d.f() + + """)) + word_finder = worder.Worder(code) + self.assert_equal_annotation( + code, + annotations, + self._make_offset_annotation(code, word_finder.is_from_statement_module), + ) + + def test_is_import_statement_aliased_module(self): + code, annotations = self._annotated_code(annotated_code=dedent("""\ + import a.b.c.d + + from a.b import c + + import a.b.c.d as d + +++++++ + from a.b import c as e + + from a.b import ( + + abc + + ) + + import mod1, \\ + + mod2 as c, mod3, mod4 as d + +++++ +++++ + result = a.b.c.d.f() + + """)) + word_finder = worder.Worder(code) + self.assert_equal_annotation( + code, + annotations, + self._make_offset_annotation( + code, word_finder.is_import_statement_aliased_module + ), + ) + + def test_is_from_aliased(self): + code, annotations = self._annotated_code(annotated_code=dedent("""\ + import a.b.c.d + + from a.b import c + + import a.b.c.d as d + + from a.b import c as e + ++ + from a.b import ( + + abc + + ) + + from a.b import mod1, \\ + + mod2 as c, mod3, mod4 as d + +++++ +++++ + result = a.b.c.d.f() + + """)) + word_finder = worder.Worder(code) + self.assert_equal_annotation( + code, + annotations, + self._make_offset_annotation(code, word_finder.is_from_aliased), + ) + def test_is_from_with_from_import_and_multiline_parens(self): code = "from mod import \\\n (f,\n g, h)\n" word_finder = worder.Worder(code) @@ -318,7 +514,31 @@ word_finder = worder.Worder(code) self.assertTrue(word_finder.is_from_statement(code.rindex("g"))) - def test_one_letter_function_keyword_arguments(self): + def test_is_function_keyword_parameter(self): + code, annotations = self._annotated_code(annotated_code=dedent("""\ + func(param=1) + ++++++ + func( + + param=1 + ++++++ + ) + + def func(param=1): + ++++++ + pass + + """)) + word_finder = worder.Worder(code) + self.assert_equal_annotation( + code, + annotations, + self._make_offset_annotation( + code, word_finder.is_function_keyword_parameter + ), + ) + + def test_one_letter_is_function_keyword_parameter(self): code = "f(p=1)\n" word_finder = worder.Worder(code) index = code.rindex("p") @@ -362,7 +582,20 @@ code.index("("), finder.find_parens_start_from_inside(len(code) - 1) ) - def test_is_on_function_keyword(self): + def test_is_on_function_call_keyword(self): + code, annotations = self._annotated_code(annotated_code=dedent("""\ + myfunc(va + +++ + """)) + + finder = worder.Worder(code) + self.assert_equal_annotation( + code, + annotations, + self._make_offset_annotation(code, finder.is_on_function_call_keyword), + ) + + def test_is_on_function_keyword_partial(self): code = "myfunc(va" finder = worder.Worder(code) self.assertTrue(finder.is_on_function_call_keyword(len(code) - 1)) @@ -474,12 +707,10 @@ def test_modules_after_from_statements(self): root_folder = self.project.root mod = testutils.create_module(self.project, "mod", root_folder) - mod.write( - dedent("""\ - def a_func(): - pass - """) - ) + mod.write(dedent("""\ + def a_func(): + pass + """)) code = "from mod import a_func\n" scope = libutils.get_string_scope(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) @@ -489,12 +720,10 @@ def test_renaming_functions_with_from_import_and_parens(self): mod1 = testutils.create_module(self.project, "mod1") - mod1.write( - dedent("""\ - def afunc(): - pass - """) - ) + mod1.write(dedent("""\ + def afunc(): + pass + """)) code = dedent("""\ from mod1 import ( afunc as func) @@ -512,12 +741,10 @@ pkg2 = testutils.create_package(self.project, "pkg2", pkg1) mod1 = testutils.create_module(self.project, "mod1", pkg1) mod2 = testutils.create_module(self.project, "mod2", pkg2) - mod1.write( - dedent("""\ - def a_func(): - pass - """) - ) + mod1.write(dedent("""\ + def a_func(): + pass + """)) code = "from ..mod1 import a_func\n" mod2.write(code) mod2_scope = self.project.get_pymodule(mod2).get_scope() @@ -597,12 +824,6 @@ class LogicalLineFinderTest(unittest.TestCase): - def setUp(self): - super(LogicalLineFinderTest, self).setUp() - - def tearDown(self): - super(LogicalLineFinderTest, self).tearDown() - def _logical_finder(self, code): return LogicalLineFinder(SourceLinesAdapter(code)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/ropetest/refactor/patchedasttest.py new/rope-0.22.0/ropetest/refactor/patchedasttest.py --- old/rope-0.21.0/ropetest/refactor/patchedasttest.py 2021-10-18 11:33:57.000000000 +0200 +++ new/rope-0.22.0/ropetest/refactor/patchedasttest.py 2021-11-21 15:46:44.000000000 +0100 @@ -1046,6 +1046,30 @@ checker = _ResultChecker(self, ast_frag) checker.check_children("Tuple", ["Num", "", ",", " ", "Num"]) + def test_tuple_with_complex_parentheses1(self): + source = "a = ( # (he\n ((((), None))))\n" + ast_frag = patchedast.get_patched_ast(source, True) + checker = _ResultChecker(self, ast_frag) + checker.check_children( + "Tuple", ["(", "", "Tuple", "", ",", " ", NameConstant, "", ")"] + ) + + def test_tuple_with_complex_parentheses2(self): + source = "a = ( # (he\n ((((('a')), ('b')))))\n" + ast_frag = patchedast.get_patched_ast(source, True) + checker = _ResultChecker(self, ast_frag) + checker.check_children( + "Tuple", ["(", "", "((", "Str", "))", ",", " (", "Str", ")", "", ")"] + ) + + def test_tuple_with_complex_parentheses3(self): + source = "a = ((), (([],), []),)" + ast_frag = patchedast.get_patched_ast(source, True) + checker = _ResultChecker(self, ast_frag) + checker.check_children( + "Tuple", ["(", "", "Tuple", "", ",", " ", "Tuple", ",", ")"] + ) + def test_one_item_tuple_node(self): source = "(1,)\n" ast_frag = patchedast.get_patched_ast(source, True) @@ -1056,7 +1080,23 @@ source = "()\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) - checker.check_children("Tuple", ["(", "", ")"]) + checker.check_children("Tuple", ["()"]) + + def test_empty_tuple_node2(self): + source = "a = ((), None)\n" + ast_frag = patchedast.get_patched_ast(source, True) + checker = _ResultChecker(self, ast_frag) + checker.check_children( + "Tuple", ["(", "", "Tuple", "", ",", " ", NameConstant, "", ")"] + ) + + def test_empty_tuple_node3(self): + source = "a = (), None\n" + ast_frag = patchedast.get_patched_ast(source, True) + checker = _ResultChecker(self, ast_frag) + checker.check_children( + "Tuple", ["Tuple", "", ",", " ", NameConstant] + ) def test_yield_node(self): source = dedent("""\ @@ -1067,6 +1107,16 @@ checker = _ResultChecker(self, ast_frag) checker.check_children("Yield", ["yield", " ", NameConstant]) + @testutils.only_for_versions_higher("3.3") + def test_yield_from_node(self): + source = dedent("""\ + def f(lst): + yield from lst + """) + ast_frag = patchedast.get_patched_ast(source, True) + checker = _ResultChecker(self, ast_frag) + checker.check_children("YieldFrom", ["yield", " ", "from", " ", "Name"]) + def test_while_node(self): source = dedent("""\ while True: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/ropetest/refactor/renametest.py new/rope-0.22.0/ropetest/refactor/renametest.py --- old/rope-0.21.0/ropetest/refactor/renametest.py 2021-10-09 17:08:05.000000000 +0200 +++ new/rope-0.22.0/ropetest/refactor/renametest.py 2021-11-14 17:35:04.000000000 +0100 @@ -1510,6 +1510,42 @@ """) self.assertEqual(refactored, expected) + def test_renaming_modules_aliased_with_dots(self): + pkg = testutils.create_package(self.project, "json") + mod1 = testutils.create_module(self.project, "utils", pkg) + + mod2 = testutils.create_module(self.project, "mod2") + mod2.write( + dedent( + """\ + import json.utils as stdlib_json_utils + """ + ) + ) + self._rename(pkg, None, "new_json") + self.assertTrue( + not mod1.exists() and self.project.find_module("new_json.utils") is not None + ) + self.assertEqual("import new_json.utils as stdlib_json_utils\n", mod2.read()) + + def test_renaming_modules_aliased_many_dots(self): + pkg = testutils.create_package(self.project, "json") + mod1 = testutils.create_module(self.project, "utils", pkg) + + mod2 = testutils.create_module(self.project, "mod2") + mod2.write( + dedent( + """\ + import json.utils.a as stdlib_json_utils + """ + ) + ) + self._rename(pkg, None, "new_json") + self.assertTrue( + not mod1.exists() and self.project.find_module("new_json.utils") is not None + ) + self.assertEqual("import new_json.utils.a as stdlib_json_utils\n", mod2.read()) + class ChangeOccurrencesTest(unittest.TestCase): def setUp(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/ropetest/refactor/restructuretest.py new/rope-0.22.0/ropetest/refactor/restructuretest.py --- old/rope-0.21.0/ropetest/refactor/restructuretest.py 2021-10-09 14:03:59.000000000 +0200 +++ new/rope-0.22.0/ropetest/refactor/restructuretest.py 2021-11-21 14:25:04.000000000 +0100 @@ -265,3 +265,28 @@ refactoring = restructure.Restructure(self.project, "${a}", "${a}") self.project.do(refactoring.get_changes()) self.assertEqual(mod_text, self.mod.read()) + + @testutils.only_for_versions_higher("3.3") + def test_yield_from(self): + mod_text = dedent("""\ + def f(lst): + yield from lst + """) + self.mod.write(mod_text) + refactoring = restructure.Restructure( + self.project, + "yield from ${a}", + dedent("""\ + for it in ${a}: + yield it"""), + ) + self.project.do(refactoring.get_changes()) + self.assertEqual( + dedent("""\ + def f(lst): + for it in lst: + yield it + """), + self.mod.read(), + ) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rope-0.21.0/setup.py new/rope-0.22.0/setup.py --- old/rope-0.21.0/setup.py 2021-10-09 14:03:59.000000000 +0200 +++ new/rope-0.22.0/setup.py 2021-11-11 14:22:16.000000000 +0100 @@ -73,6 +73,7 @@ classifiers=classifiers, extras_require={ "dev": [ + "build", "pytest", "pytest-timeout", ]
