Skip to content

Commit

Permalink
Merge branch 'master' into minor-refactorings
Browse files Browse the repository at this point in the history
  • Loading branch information
lieryan authored Mar 7, 2024
2 parents fe15b1a + 8e61a68 commit 40c05d3
Show file tree
Hide file tree
Showing 21 changed files with 320 additions and 126 deletions.
9 changes: 9 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,15 @@
"contributions": [
"code"
]
},
{
"login": "sandratsy",
"name": "Sandra Tan Shi Yun",
"avatar_url": "https://avatars.githubusercontent.com/u/26302933?v=4",
"profile": "https://github.com/sandratsy",
"contributions": [
"code"
]
}
],
"projectName": "rope",
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ __pycache__/
.Python
build/
develop-eggs/
dist/
/dist/
downloads/
eggs/
.eggs/
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# **Upcoming release**

- Check for ast.Attributes when finding occurrences in fstrings (@sandratsy)
- #777, #698 add validation to refuse Rename refactoring to a python keyword
- #730 Match on module aliases for autoimport suggestions
- #755 Remove dependency on `build` package being installed while running tests

# Release 1.12.0

Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://bamboolib.8080labs.com"><img src="https://avatars.githubusercontent.com/u/13402027?v=4?s=100" width="100px;" alt="Tobias Krabel"/><br /><sub><b>Tobias Krabel</b></sub></a><br /><a href="https://github.com/python-rope/rope/commits?author=tkrabel" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MrBago"><img src="https://avatars.githubusercontent.com/u/223219?v=4?s=100" width="100px;" alt="Bago Amirbekian"/><br /><sub><b>Bago Amirbekian</b></sub></a><br /><a href="https://github.com/python-rope/rope/commits?author=MrBago" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://mender.ai"><img src="https://avatars.githubusercontent.com/u/3324?v=4?s=100" width="100px;" alt="Ray Myers"/><br /><sub><b>Ray Myers</b></sub></a><br /><a href="https://github.com/python-rope/rope/commits?author=raymyers" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sandratsy"><img src="https://avatars.githubusercontent.com/u/26302933?v=4?s=100" width="100px;" alt="Sandra Tan Shi Yun"/><br /><sub><b>Sandra Tan Shi Yun</b></sub></a><br /><a href="https://github.com/python-rope/rope/commits?author=sandratsy" title="Code">💻</a></td>
</tr>
</tbody>
</table>
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
include README.rst COPYING setup.py MANIFEST.in CHANGELOG.md
include README.rst COPYING setup.py MANIFEST.in CHANGELOG.md ropetest-package-fixtures/external_fixturepkg/dist/external_fixturepkg-1.0.0-py3-none-any.whl ropetest-package-fixtures/external_fixturepkg/dist/external_fixturepkg-1.0.0.tar.gz
recursive-include rope *.py
recursive-include docs *.rst
recursive-include ropetest *.py
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ profile = "black"

[tool.pytest.ini_options]

testpaths = "ropetest"
python_files = [
"*test.py",
"__init__.py",
Expand Down
2 changes: 1 addition & 1 deletion rope/contrib/autoimport/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def get_package_source(
if "site-packages" in package.parts:
return Source.SITE_PACKAGE
if sys.version_info < (3, 10, 0):
if str(package).startswith(sys.prefix):
if str(package).startswith(sys.base_prefix):
return Source.STANDARD
else:
if name in sys.stdlib_module_names:
Expand Down
18 changes: 11 additions & 7 deletions rope/refactor/occurrences.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

import contextlib
import re
from typing import Iterator

from rope.base import (
ast,
Expand Down Expand Up @@ -319,7 +320,7 @@ def __init__(self, name, docs=False):
)
self.pattern = self._get_occurrence_pattern(self.name)

def find_offsets(self, source):
def find_offsets(self, source: str) -> Iterator[int]:
if not self._fast_file_query(source):
return
if self.docs:
Expand All @@ -328,22 +329,25 @@ def find_offsets(self, source):
searcher = self._re_search
yield from searcher(source)

def _re_search(self, source):
def _re_search(self, source: str) -> Iterator[int]:
for match in self.pattern.finditer(source):
if match.groupdict()["occurrence"]:
yield match.start("occurrence")
elif match.groupdict()["fstring"]:
f_string = match.groupdict()["fstring"]
for occurrence_node in self._search_in_f_string(f_string):
yield match.start("fstring") + occurrence_node.col_offset
for offset in self._search_in_f_string(f_string):
yield match.start("fstring") + offset

def _search_in_f_string(self, f_string):
def _search_in_f_string(self, f_string: str) -> Iterator[int]:
tree = ast.parse(f_string)
for node in ast.walk(tree):
if isinstance(node, ast.Name) and node.id == self.name:
yield node
yield node.col_offset
elif isinstance(node, ast.Attribute) and node.attr == self.name:
assert node.end_col_offset is not None
yield node.end_col_offset - len(self.name)

def _normal_search(self, source):
def _normal_search(self, source: str) -> Iterator[int]:
current = 0
while True:
try:
Expand Down
12 changes: 12 additions & 0 deletions rope/refactor/rename.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import warnings
from keyword import iskeyword

from rope.base import (
codeanalyze,
Expand Down Expand Up @@ -105,6 +106,7 @@ def unsure_func(value=unsure):
resources = [self.resource]
if resources is None:
resources = self.project.get_python_files()
self.validate_changes(new_name)
changes = ChangeSet(f"Renaming <{self.old_name}> to <{new_name}>")
finder = occurrences.create_finder(
self.project,
Expand All @@ -128,6 +130,16 @@ def unsure_func(value=unsure):
self._rename_module(resource, new_name, changes)
return changes

def validate_changes(
self,
new_name: str,
**_unused,
):
if iskeyword(new_name):
raise exceptions.RefactoringError(
f"Invalid refactoring target name. '{new_name}' is a Python keyword."
)

def _is_allowed_to_move(self, resources, resource):
if resource.is_folder():
try:
Expand Down
12 changes: 12 additions & 0 deletions ropetest-package-fixtures/external_fixturepkg/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# example_project

Just an example project for testing rope.


To build this package, run:

```bash
$ python -m build
```

This generates packages in `dist` folder.
Binary file not shown.
Binary file not shown.
18 changes: 18 additions & 0 deletions ropetest-package-fixtures/external_fixturepkg/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[build-system]
requires = ["flit-core >= 3.8"]
build-backend = "flit_core.buildapi"

[project]
name = "external_fixturepkg"
version = "1.0.0"
description = "Just an example project built with build/flit for testing purpose"
readme = "README.md"
requires-python = ">= 3.7"

dependencies = [ ]

[project.scripts]
pyproject-build = "build.__main__:entrypoint"

[tool.flit.sdist]
exclude = ["**/__pycache__", "**/*.egg-info"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def sample_function():
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def main() -> None:
pass


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo = None
23 changes: 16 additions & 7 deletions ropetest/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import pathlib
import sys
from subprocess import check_call

import pytest

Expand All @@ -13,13 +15,6 @@ def project():
testutils.remove_project(project)


@pytest.fixture
def project2():
project = testutils.sample_project("another_project")
yield project
testutils.remove_project(project)


@pytest.fixture
def project_path(project):
yield pathlib.Path(project.address)
Expand Down Expand Up @@ -52,3 +47,17 @@ def pkg1(project) -> resources.Folder:
@pytest.fixture
def mod2(project, pkg1) -> resources.Folder:
return testutils.create_module(project, "mod2", pkg1)


@pytest.fixture(scope="session")
def external_fixturepkg():
check_call([
sys.executable,
"-m",
"pip",
"install",
"--force-reinstall",
"ropetest-package-fixtures/external_fixturepkg/dist/external_fixturepkg-1.0.0-py3-none-any.whl",
])
yield
check_call([sys.executable, "-m", "pip", "uninstall", "--yes", "external-fixturepkg"])
13 changes: 6 additions & 7 deletions ropetest/contrib/autoimport/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,18 @@ def typing_path():


@pytest.fixture
def build_env_path():
from build import env

yield pathlib.Path(env.__file__)
def example_external_package_module_path(external_fixturepkg):
from external_fixturepkg import mod1
yield pathlib.Path(mod1.__file__)


@pytest.fixture
def build_path():
import build
def example_external_package_path(external_fixturepkg):
import external_fixturepkg

# Uses __init__.py so we need the parent

yield pathlib.Path(build.__file__).parent
yield pathlib.Path(external_fixturepkg.__file__).parent


@pytest.fixture
Expand Down
20 changes: 12 additions & 8 deletions ropetest/contrib/autoimport/utilstest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,35 @@ def test_get_package_source_not_project(mod1_path):
assert utils.get_package_source(mod1_path, None, "") == Source.UNKNOWN


def test_get_package_source_pytest(build_path):
def test_get_package_source_pytest(example_external_package_path):
# pytest is not installed as part of the standard library
# but should be installed into site_packages,
# so it should return Source.SITE_PACKAGE
assert utils.get_package_source(build_path, None, "build") == Source.SITE_PACKAGE
source = utils.get_package_source(example_external_package_path, None, "mod1")
assert source == Source.SITE_PACKAGE


def test_get_package_source_typing(typing_path):

assert utils.get_package_source(typing_path, None, "typing") == Source.STANDARD


def test_get_modname_project_no_add(mod1_path, project_path):

assert utils.get_modname_from_path(mod1_path, project_path, False) == "mod1"


def test_get_modname_single_file(typing_path):

assert utils.get_modname_from_path(typing_path, typing_path) == "typing"


def test_get_modname_folder(build_path, build_env_path):

assert utils.get_modname_from_path(build_env_path, build_path) == "build.env"
def test_get_modname_folder(
example_external_package_path,
example_external_package_module_path,
):
modname = utils.get_modname_from_path(
example_external_package_module_path,
example_external_package_path,
)
assert modname == "external_fixturepkg.mod1"


def test_get_package_tuple_sample(project_path):
Expand Down
17 changes: 10 additions & 7 deletions ropetest/contrib/autoimporttest.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,6 @@ def test_handling_builtin_modules(self):
self.importer.update_module("sys")
self.assertIn("sys", self.importer.get_modules("exit"))

def test_search_submodule(self):
self.importer.update_module("build")
import_statement = ("from build import env", "env")
self.assertIn(import_statement, self.importer.search("env", exact_match=True))
self.assertIn(import_statement, self.importer.search("en"))
self.assertIn(import_statement, self.importer.search("env"))

def test_search_module(self):
self.importer.update_module("os")
import_statement = ("import os", "os")
Expand Down Expand Up @@ -184,6 +177,16 @@ def test_skipping_directories_not_accessible_because_of_permission_error(self):
self.assertGreater(len(self.importer._dump_all()), 0)


def test_search_submodule(external_fixturepkg):
project = testutils.sample_project(extension_modules=["sys"])
importer = autoimport.AutoImport(project, observe=False)
importer.update_module("external_fixturepkg")
import_statement = ("from external_fixturepkg import mod1", "mod1")
assert import_statement in importer.search("mod1", exact_match=True)
assert import_statement in importer.search("mo")
assert import_statement in importer.search("mod1")


class AutoImportObservingTest(unittest.TestCase):
def setUp(self):
super().setUp()
Expand Down
Loading

0 comments on commit 40c05d3

Please sign in to comment.