././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3753905 rope-0.22.0/0000775000175000017500000000000000000000000012433 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637593508.0 rope-0.22.0/CHANGELOG.md0000664000175000017500000000564600000000000014257 0ustar00lieryanlieryan# **Upcoming release** ## 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) - context manager: - #387, #433 Implement extract refactoring for code containing `async with` (@lieryan) - #398, #104 Fix parsing of nested `with` statement/context manager (@climbus) - list/set/dict/generator comprehension scope issues: - #422 Added scopes for comprehension expressions as part of #293 (@climbus) - #426, #429 Added support for checking scopes by offset as part of #293 (@climbus) - #293, #430 Fix renaming global var affects list comprehension (@climbus) - #395, #315 Reuse of variable in comprehensions confuses method extraction (@climbus) - #436 Fix error `TypeError: 'PyDefinedObject' object is not subscriptable` (@lieryan) - f-string: - #303, #420 Fix inlining into f-string containing quote characters (@lieryan) - inline assignment/walrus operator: - #423 Fix `AttributeError: '_ExpressionVisitor' object has no attribute 'defineds'` (@lieryan) ## Bug fixes - #391, #376 Fix improper replacement when extracting attribute access expression with `similar=True` (@climbus) - #396 Fix improper replacement when extracting index access expression with `similar=True` (@lieryan) ## New feature - #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 (@lieryan) - #403 Remove plain 'unittest' only runner (@lieryan) # Release 0.20.1 Date: 2021-09-18 ## Bug fixes - Fix caller of `_namedexpr_last()` throwing exception due to returning unexpected list instead of boolean # 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 # Previous releases [Changelog from pre-0.10.0](https://github.com/python-rope/rope/blob/595af418e7e7e844dcce600778e1c650c2fc0ba1/docs/done.rst). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631106611.0 rope-0.22.0/COPYING0000664000175000017500000001674400000000000013502 0ustar00lieryanlieryan GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632642863.0 rope-0.22.0/MANIFEST.in0000664000175000017500000000022600000000000014171 0ustar00lieryanlieryaninclude README.rst COPYING setup.py MANIFEST.in CHANGELOG.md recursive-include rope *.py recursive-include docs *.rst recursive-include ropetest *.py ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3753905 rope-0.22.0/PKG-INFO0000664000175000017500000000353100000000000013532 0ustar00lieryanlieryanMetadata-Version: 2.1 Name: rope Version: 0.22.0 Summary: a python refactoring library... Home-page: https://github.com/python-rope/rope Author: Ali Gholami Rudi Author-email: aligrudi@users.sourceforge.net License: LGPL-3.0-or-later Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent Classifier: Environment :: X11 Applications Classifier: Environment :: Win32 (MS Windows) Classifier: Environment :: MacOS X Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633427443.0 rope-0.22.0/README.rst0000664000175000017500000000616700000000000014134 0ustar00lieryanlieryan .. _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. Getting Started =============== * `How to use Rope in my IDE or Text editor? `_ * List of features: ``_ * Overview of some of rope's features: ``_ * Using as a library: ``_ * Contributing: ``_ To change your project preferences edit ``$PROJECT_ROOT/.ropeproject/config.py`` where ``$PROJECT_ROOT`` is the root folder of your project (this file is created the first time you open a project). Why use Rope? ============= - Rope aims to provide powerful and safe refactoring - Rope is light on dependency, Rope only depends on Python itself - Unlike PyRight or PyLance, Rope does not depend on Node.js - Unlike PyLance or PyCharm, Rope is open source. - Unlike PyRight and PyLance, Rope is written in Python itself, so if you experience problems, you would be able to debug and hack it yourself in a language that you are already familiar with - In comparison to Jedi, Rope is focused on refactoring. While Jedi provides some basic refactoring capabilities, Rope supports many more advanced refactoring operations and options that Jedi does not. Bug Reports =========== Send your bug reports and feature requests at `python-rope's issue tracker`_ in Github. .. _`python-rope's issue tracker`: https://github.com/python-rope/rope/issues Maintainers =========== Current active maintainers of Rope are Matej Cepl (`@mcepl`_) and Lie Ryan (`@lieryan`_). Special Thanks ============== Many thanks the following people: - Ali Gholami Rudi (`@aligrudi`_) for initially creating the initial Rope project and most of Rope's code - Nick Smith (`@soupytwist`_) as former Rope maintainer - `all of our current and former contributors`_ - authors of editor integrations .. _`@aligrudi`: https://github.com/aligrudi .. _`@soupytwist`: https://github.com/soupytwist .. _`@lieryan`: https://github.com/lieryan .. _`@mcepl`: https://github.com/mcepl .. _`all of our current and former contributors`: https://github.com/python-rope/rope/blob/master/CONTRIBUTORS.md License ======= This program is under the terms of LGPL v3+ (GNU Lesser General Public License). Have a look at `COPYING`_ for more information. .. _`docs/rope.rst`: docs/rope.html .. _`docs/overview.rst`: docs/overview.html .. _`docs/contributing.rst`: docs/contributing.html .. _`docs/library.rst`: docs/library.html .. _`COPYING`: COPYING .. image:: https://secure.travis-ci.org/python-rope/rope.svg :alt: Build Status ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3473897 rope-0.22.0/docs/0000775000175000017500000000000000000000000013363 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632875930.0 rope-0.22.0/docs/contributing.rst0000664000175000017500000000716100000000000016631 0ustar00lieryanlieryan====================== Contributing to Rope ====================== Getting Involved! ================= Rope's main goal is being a good refactoring tool for python. It also provides some IDE helpers. If you would like to contribute, you're welcome to! How to Help Rope? ================= Rope's development happens in `python-rope's Github`_. Use `python-rope's Github Issue Tracker`_ to discuss development-related issues: * Send bug reports and request features * Submit patches for bugs or new features Use `python-rope's Github Discussion`_ for other discussions, such as: * Help using rope * Help integrating rope with text editors/tools * Discuss your ideas * Engage with the rope community .. _`python-rope's Github`: https://github.com/python-rope/rope .. _`python-rope's Github Issue Tracker`: https://github.com/python-rope/rope/issues .. _`python-rope's Github Discussion`: https://github.com/python-rope/rope/discussions Wish List ========= You are welcome to make pull requests in `python-rope's Github Issue Tracker`_. Here is a list of suggestions. Issues ------ If this is your first time contributing in rope and you don't know where to start, tickets labeled `good first issue`_ is a good place start. The `unresolved issues list`_ in Github is the latest todo list. There is also a rather outdated list in `dev/issues.rst`_. There is a section called "unresolved issues"; it contains almost every kind of task. This file will need some cleanup, thoughts, and discussions. Pickup whichever you are most interested in. If you have ideas or questions about them, don't hesitate to create a Github ticket for it. .. _`good first issue`: https://github.com/python-rope/rope/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 .. _`unresolved issues list`: https://github.com/python-rope/rope/issues .. _`dev/issues.rst`: dev/issues.rst Write Plugins For Other IDEs ---------------------------- See ropemacs_, ropevim_, eric4_ and ropeide_. .. _ropemacs: http://rope.sf.net/ropemacs.rst .. _ropevim: http://rope.sf.net/ropevim.rst .. _ropeide: http://rope.sf.net/ropeide.rst .. _eric4: http://www.die-offenbachs.de/eric/index.rst Rope Structure ============== Rope package structure: * `rope.base`: the base part of rope * `rope.refactor`: refactorings and tools used in them * `rope.contrib`: IDE helpers Have a look at ``__init__.py`` of these packages or `library.rst`_ for more information. .. _`library.rst`: library.rst Source Repository ================= Rope uses GitHub_. The repository exists at `https://github.com/python-rope/rope`_. Submitting pull requests ======================== Pull requests are welcome. Follow the instructions on GitHub_ on how to setup Git and fork the `python-rope/rope`_ repository. Once your changes are ready, send a `pull request`_ for review. Programming Style ----------------- * Follow `black codestyle`_ * Follow :PEP:`8`. * Use four spaces for indentation. * Include good unit-tests when appropriate. * Rope test suite should pass after patching .. _`black codestyle`: https://github.com/psf/black Testing ------- Rope uses `pytest`_ as a test runner per default (although the tests are strictly unittest-based), so running:: pytest -v runs all tests. Make sure to have complete test suite passing and add new tests for the changes you are providing with each new submission. All required packages for development could be installed with:: pip install -e .[dev] .. _GitHub: http://github.com/ .. _`python-rope/rope`: https://github.com/python-rope/rope .. _`pull request`: https://help.github.com/articles/using-pull-requests .. _`pytest`: https://pytest.org/ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3473897 rope-0.22.0/docs/dev/0000775000175000017500000000000000000000000014141 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632657719.0 rope-0.22.0/docs/dev/issues.rst0000664000175000017500000000750400000000000016214 0ustar00lieryanlieryan============= Rope Issues ============= NOTE: this file is severely outdated and is no longer actively used by rope to keep track of issues. It is kept only for historical purpose. Use the `Issue Tracker`_ instead .. _`Issue Tracker`: https://github.com/python-rope/rope/issues Unresolved Issues ================= * purging out less accurate callinfos when better ones appear? * using properties without calling its get? * global variable inlines * transform and extension modules * merging extract and usefunction * caching instances of PyObject * moving a group of elements together * temps might be read after body in usefunction or extract * usefunction and function returns * usefunction on methods * extracted functions should be inserted before using class bodies * adding "referenced later" wildcard argument to restructurings? * adding "change references" wildcard argument to restructurings? * ideas for more custom wildcards * custom wildcards and recursive patterns * custom restructuring wildcard patterns and replacements * not reimporting back imports after moving * importing compressed objectdb/history data? * not applying all commenting mechanisms always in codeassist * fixing try blocks before current line in code_assist * better tests for patchedast * import actions with more that one phase and filtering problems * handle long imports should work on filtered imports unconditionally? * extracting subexpressions; look at `extracttest` for more info * unignored files that are not under version control * inline fails when there is an arg mismatch * evaluate function parameter defaults in staticoi? * saving diffs instead of old contents in ChangeContents? * handling tuple parameters * extract class * analyzing function decorators * generate ... and implicit interfaces * generate method and class hierarchies * lambdas as functions; consider their parameters * renaming similarly named variables * handling the return type of ``yield`` keyword * not writing unchanged objectdb and history? To Be Reviewed ============== * review patchedast; make it faster * lots of estimations in codeanalyze in WordRangeFinder * review objectdb modules * how concluded data are held for star imports Insert Before In Restructurings =============================== Consider a restructuring like this:: pattern: ${a} if ${b} else ${c} goal: replacement before: if ${b}:\n replacement = ${a}\nelse:\n replacement = ${c} Memory Management ================= These are the places in which rope spends most of the memory it consumes: * PyCore: for storing PyModules * ObjectInfo: for storing object information * History: for storing changes We should measure the amount of memory each of them use to make decisions. Custom Restructuring Wildcards ============================== There is a need to add more custom wildcards in restructuring patterns. But adding all such needs to `similarfinder` module makes it really complex. So I think adding the ability to extend them is useful. Sometimes wildcards can be customized. For instance one might want to match the function calls only if ``p1`` is passed in the arguments. They can be specified in wildcard arguments. Since matched wildcards can appear in the goal pattern, each wildcard should have a corresponding replacement wildcard. Each replacement might be customized in each place it appears; for instance ``${mycall:-p1}`` might mean to remove ``p1`` argument. Wildcard Format --------------- All wildcards should appear as ``${name}``. The type of wildcards and their parameters can be specified using the ``args`` argument of ``Restructuring()``. Ideas: * Maybe we can put checks inside args, too:: pattern: ${project:type=rope.base.project.Project}.pycore But what should be done when a variable appears twice:: pattern: ${a:type=__builtin__.int} + ${a} Examples -------- .. ... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636636936.0 rope-0.22.0/docs/library.rst0000664000175000017500000006465100000000000015575 0ustar00lieryanlieryan========================= Using Rope As A Library ========================= If you need other features, send a feature request. Have a look at `contributing.rst`_. .. contents:: Table of Contents Quick Start =========== This section will help you get started as soon as possible. Making A Project ---------------- The first thing you should do is make a project: .. code-block:: python import rope.base.project myproject = rope.base.project.Project('/path/to/myproject') It's good to know that: * A project is a folder in the file-system. * It can contain anything. * Rope searches for python modules and packages inside a project when needed. * Refactorings only change files and folders inside the project that has been passed to them. * Out of project modules that are imported from a module inside a project are handled but never changed by refactorings. * Rope makes a rope folder inside projects. By default the name of this folder is ``.ropeproject``, but that can be changed using the constructor's ``ropefolder`` parameter. Passing ``None`` prevents rope from making this folder. * Rope uses the ``.ropeproject`` folder for things like saving object information and loading project configurations. * Project preferences can be configured by passing options to the constructor or in ``.ropeproject/config.py``. See the default ``config.py``, ``rope.base.default_config`` module, for more information. * All configurations that are available in the ``config.py`` file can be specified as keyword parameters to the ``Project`` constructor. These parameters override the ones in the ``config.py`` file. * Each project has a set of ignored resource patterns. You can use it to tell rope to ignore files and folders matching certain patterns. * The ``.ropeproject`` folder can be safely copied in other clones of a project if you don't want to lose your objectdb and history. Library Utilities ----------------- The `rope.base.libutils`_ module provides tools that make using rope as a library easier. We'll talk more about this module later. What Are These `Resource`\s? ---------------------------- In rope, files and folders in a project are accessed through ``rope.base.resources.Resource`` objects. It has two subclasses ``File`` and ``Folder``. What we care about is that refactorings and ``Change``\s (we'll talk about them later) use resources. There are two options for creating a ``Resource`` for a path in a project. The first approach uses the `Project.get_resource()`_ method. .. code-block:: python from rope.base import project myresource = myproject.get_resource('/path/to/resource') However, it's preferable to use the ``libutils.path_to_resource()`` function, because it's more flexible and offers a unified way to create resources. It takes a ``project`` and ``path`` as parameters with an optional ``type``. The ``type`` parameter, with values ``file`` or ``folder``, can create a resource for an object that doesn't exist yet. .. code-block:: python from rope.base import libutils myresource = libutils.path_to_resource(myproject, '/path/to/resource') Consider we have a resource. How can we know anything about it? The answer is to use its ``path`` and ``real_path`` attributes. ``Resource.real_path`` is the absolute path of the resource in the file-system. The ``Resource.path`` attribute contains the address of a resource relative to the project's root. Performing Refactorings ----------------------- As a short example of performing refactorings, we'll show how to extract a variable from a file. First we need the ``Resource`` object that points to a file in a project: .. code-block:: python resource = libutils.path_to_resource(myproject, '/path/to/my/module.py') Now we can make our Refactoring class: .. code-block:: python from rope.refactor.extract import ExtractVariable extractor = ExtractVariable(myproject, resource, start, end) Where ``start`` and ``end`` are the offsets of the region to extract in ``resource``. Be careful when calculating the offsets. DOS line-endings and multi-byte characters are considered to be one character. This is actually easier for IDEs, since most GUI libraries handle those cases for you. Next, the IDE ask the user to configure refactoring options, like specifying the name of the extracted variable. After that, we can calculate the changes: .. code-block:: python changes = extractor.get_changes('extracted_variable') Each refactoring returns a ``rope.base.change.Change`` object that holds the changes it made. Calculating those changes can be time consuming. See the `rope.base.taskhandle.TaskHandle`_ section for measuring its progress or interrupting it. Previewing And Performing Changes --------------------------------- As mentioned in the last section each refactoring returns a ``rope.base.change.Change`` object. Now how can we know what it contains and how to perform it? *Previewing*: You can use ``changes.get_description()`` to get a preview. It is useful when you don't care much about the format. Otherwise you can use the ``changes`` object directly. See the documentation in ``rope.base.change`` module. *Performing*: The easiest way for performing the refactoring is to use the `Project.do()`_ method: .. code-block:: python myproject.do(changes) If you want to perform the changes yourself, you have two options. Note that the main reason for performing the changes manually is handling version control systems that are not supported by rope. 1. The first approach is to use `rope.base.fscommands`_ (see `Writing A FileSystemCommands`_). The changes can be performed as before using `Project.do()`_. 2. The second approach is to perform the changes manually based on the returned ``changes`` object (again see the documentation in ``rope.base.change`` module). If this approach is used you cannot undo the refactoring using ``project.history.undo()``. *Updating Open Buffers In IDEs*: Usually editors need to reload the files changed by rope. You can use ``Change.get_changed_resources()`` to get the list of resources that need to be reloaded. Validating The Project ---------------------- When using rope as a library, you probably change the files in it in parallel (for example in IDEs). To force rope to invalidate cached information about resources that have been removed or changed outside rope, you should call the `Project.validate()`_ method. You can pass a resource to this method. For example: .. code-block:: python myproject.validate(resource) This validates all files and directories in resource. Call this function every time you want use rope (i.e., before performing refactorings). Performing Static Object Analysis --------------------------------- One of the greatest strengths of rope is its Static Object Analysis (SOA). It analyzes function calls and assignments to collect the types of objects passed to the function. Rope uses the collected data to infer the type of function parameters, return values, and the objects stored in built-in containers. The function ``rope.base.libutils.analyze_modules()`` performs SOA on all modules in the project. It is recommended that you call this function occasionally, and especially before performing large refactorings. Note that analyzing all modules of a project may take a long time. If you have ``automatic_soa`` set, which instructs rop to analyze the changed scopes of modules, then you should report the changes by calling ``rope.base.libutils.report_change()`` when saving files, as follows: .. code-block:: python # Save the new contents. old_contents = resource.read() resource.write(new_contents) # Inform rope about the change. libutils.report_change(myproject, path, old_contents) Note, however, that the use of ``automatic_soa`` is discouraged, because it may slow down saving considerably. Closing The Project ------------------- `Project.close()`_ closes a project's open resources. Always call this function when you don't need a project anymore: .. code-block:: python myproject.close() ``rope.base.libutils`` ====================== The ``rope.base.libutils`` module contains functions that make life easier for building refactoring tools. In some cases, the functions offer a unified way to access or create objects. You're encouraged to use ``rope.base.libutils`` functions whenever possible, because the APIs here may not be as volatile as class methods. ``libutils.analyze_module()`` ------------------------------ Perform static object analysis on a Python file in the project. Note, this may be a very time consuming task. .. code-block:: python libutils.analyze_module(myproject, resource) ``libutils.analyze_modules()`` ------------------------------ Perform static object analysis on all Python files in the project. Note that it might take a long time to finish. .. code-block:: python libutils.analyze_modules(myproject) ``libutils.get_string_module()`` -------------------------------- Returns a ``rope.base.pyobjects.PyModule`` object for the code string. An optional ``resource`` argument can be specified for the resource this code is associated with. If ``force_errors` is ``True``, then ``rope.base.exceptions.ModuleSyntaxError`` is raised when the code has syntax errors. Otherwise, syntax errors are silently ignored. Note that ``force_errors`` overrides the ``ignore_syntax_errors`` project configuration flag. .. code-block:: python pymodule = libutils.get_string_module(myproject, source) ``libutils.get_string_scope()`` ------------------------------- Get the ``rope.base.pyscopes.GlobalScope`` object for the code string. This is the outermost scope of the code encompassing the whole module. .. code-block:: python scope = libutils.get_string_scope(myproject, source) ``libutils.is_python_file()`` ----------------------------- Returns ``True`` if the resource is a Python file. .. code-block:: python libutils.is_python_file(myproject, resource) ``libutils.modname()`` ---------------------- Retrieves the dotted path string to the module that contains that given resource. .. code-block:: python # If resource is 'path/to/resource.py' relative to the project's root # directory, this returns the string: 'path.to.resource'. module_name = libutils.modname(resource) ``libutils.path_relative_to_project_root()`` -------------------------------------------- Retrieve the path relative to the project's root directory. .. code-block:: python # Get the path relative to the project's root directory. relpath = libutils.relative(myproject.address, path) ``libutils.path_to_resource()`` ------------------------------- Get the resource --- a file or folder --- at the given path. An optional ``type`` argument can be used if the resource doesn't yet exist. The values for ``type`` are the strings ``'file'`` or ``'folder'``. .. code-block:: python # Resource for an existing file. myfile = libutils.path_to_resource(myproject, '/path/to/file.py') # Resource for a non-existing folder. new_folder = libutils.path_to_resource(myproject, '/path/to/folder', type='folder') ``rope.base.project.Project`` ============================= You can create a project by: .. code-block:: python project = Project(root_address) Where the ``root_address`` is the root folder of your project. A project has some useful attributes. ``Project.address`` is the address of the root folder of a project. ``Project.root`` is a ``Folder`` object that points to that folder. `Project.do()` -------------- Used to commit changes returned by refactorings: .. code-block:: python project.do(changes) `Project.history` ----------------- A ``rope.base.history.History`` object. You can use its ``undo`` and ``redo`` methods for undoing or redoing changes. Note that you can use this only if you have committed your changes using rope. `Project.validate()` -------------------- When using rope as a library, you will probably change the files in that project in parallel (for example in IDEs). To force rope to validate cached information about resources that have been removed or changed outside rope, you should call ``Project.validate()``. You should pass a resource to this method. For example: .. code-block:: python project.validate(project.root) This validates all files and directories in the project and clears the cache of all recorded changes. `Project.close()` ----------------- Closes a project's open resources. Always call this function when you don't need a project anymore. Currently it closes the files used for storing object information and project history. Because some parts of these files are in memory for efficiency, not closing a project might put them in an inconsistent state. `rope.base.fscommands` ====================== The ``rope.base.fscommands`` module implements basic file system operations that rope needs to perform. The main reason for the existence of this module is supporting version control systems. Have a look at ``FileSystemCommands`` and ``SubversionCommands`` in the same module. If you need other version control systems you can write a new class that provides this interface. ``rope.base.project.Project`` accepts an ``fscommands`` argument. You can use this argument to force rope to use your new class. ``.ropeproject`` Folder ======================= Since version ``0.5``, rope makes a ``.ropeproject`` folder in the project by default for saving project configurations and data. The name of this folder is passed to the constructor if you want to change that. You can force rope not to make such a folder by passing ``None``. If such a folder exists, rope loads the ``config.py`` file in that folder. It might also use it for storing object information and history. `rope.base.pycore.PyCore` ========================= Provides useful methods for managing python modules and packages. Each project has a ``PyCore`` that can be accessed using the ``Project.pycore`` attribute. ``PyCore.run_module()`` runs a resource. When running, it collects type information to do dynamic object inference. For this reason modules run much slower. Also ``Pycore.analyze_module()`` collects object information for a module. The collected information can be used to enhance rope's static object inference. `rope.base.taskhandle.TaskHandle` ================================= A TaskHandle can be used for stopping and monitoring the progress of time consuming tasks, like some refactorings. The ``Project.do()`` and ``Refactoring.get_changes()`` methods for most refactorings take a keyword parameter called ``task_handle``. You can pass a ``TaskHandle`` object to them. A ``TaskHandle`` can be used for interrupting or observing a task. Always pass ``task_handle`` as keyword argument. It will always be the last argument, and new arguments of the refactoring are added before it. A task might consist of a few ``JobSet``\s. Each ``JobSet`` performs a few jobs. For instance calculating the changes for renaming a method in a class hierarchy has two job sets: one to find the classes for constructing the class hierarchy and another to change the occurrences. The ``TaskHandle.current_jobset()`` returns the most recent ``JobSet`` or ``None`` if none has been started. You can use the methods of ``JobSet`` for obtaining information about the current job. So you might want to do something like: .. code-block:: python import rope.base.taskhandle handle = rope.base.taskhandle.TaskHandle("Test Task") def update_progress(): jobset = handle.current_jobsets() if jobset: text = '' # getting current job set name if jobset.get_name() is not None: text += jobset.get_name() # getting active job name if jobset.get_active_job_name() is not None: text += ' : ' + jobset.get_active_job_name() # adding done percent percent = jobset.get_percent_done() if percent is not None: text += ' ... %s percent done' % percent print text handle.add_observer(update_progress) changes = renamer.get_changes('new_name', task_handle=handle) Also you can use something like this for stopping the task: .. code-block:: python def stop(): handle.stop() After calling ``stop()``, the thread that is executing the task will be interrupted by a ``rope.base.exceptions.InterruptedTaskError`` exception. Refactorings ============ Have a look at ``rope.refactor`` package and its sub-modules. For example for performing a move refactoring you can create a ``Move`` object like this: .. code-block:: python mover = Move(project, resource, offset) Where ``resource`` and ``offset`` is the location to perform the refactoring. Then you can commit the changes by it using the ``get_changes()`` method: .. code-block:: python project.do(mover.get_changes(destination)) Where the ``destination`` module/package is the destination resource for move refactoring. Other refactorings classes have a similar interface. List Of Refactorings -------------------- Here is the list of refactorings rope provides. (Note that this list might be out of date.) For more information about these refactoring see pydocs in their modules and the unit-tests in the ``ropetest/refactor/`` folder. * ``rope.refactor.rename``: Rename something in the project. See the example below. * ``rope.refactor.move``: Move a python element in the project. * ``rope.refactor.restructure``: Restructure code. See the example below. * ``rope.refactor.extract``: Extract methods/variables. * ``rope.refactor.inline``: Inline occurrences of a method/variable/parameter. * ``rope.refactor.usefunction``: Try to use a function wherever possible. * ``rope.refactor.method_object``: Transform a function or a method to a method object. * ``rope.refactor.change_signature``: Change the signature of a function/method. * ``rope.refactor.introduce_factory``: Introduce a factory for a class and changes all constructors to use it. * ``rope.refactor.introduce_parameter``: Introduce a parameter in a function. * ``rope.refactor.encapsulate_field``: Generate a getter/setter for a field and changes its occurrences to use them. * ``rope.refactor.localtofield``: Change a local variable to field. * ``rope.refactor.topackage``: Transform a module to a package with the same name. * ``rope.refactor.importutils``: Perform actions like organize imports. Refactoring Resources Parameter ------------------------------- Some refactorings, restructure and find occurrences accept an argument called ``resources``. If it is a list of `File`\s, all other resources in the project are ignored and the refactoring only analyzes them. If it is ``None`` all python modules in the project will be analyzed. Using this parameter, IDEs can let the user limit the files on which a refactoring should be applied. Examples ======== Rename ------ Using rename refactoring: .. code-block:: python # Creating a project >>> from rope.base.project import Project >>> project = Project('.') # Working with files to create a module >>> mod1 = project.root.create_file('mod1.py') >>> mod1.write('a_var = 10\n') # Alternatively you can use `generate` module. # Creating modules and packages using `generate` module >>> from rope.contrib import generate >>> pkg = generate.create_package(project, 'pkg') >>> mod2 = generate.create_module(project, 'mod2', pkg) >>> mod2.write('import mod1\nprint mod1.a_var\n') # We can use `Project.find_module` for finding modules, too >>> assert mod2 == project.find_module('pkg.mod2') # Performing rename refactoring on `mod1.a_var` >>> from rope.refactor.rename import Rename >>> changes = Rename(project, mod1, 1).get_changes('new_var') >>> project.do(changes) >>> mod1.read() u'new_var = 10\n' >>> mod2.read() u'import mod1\nprint mod1.new_var\n' # Undoing rename refactoring >>> project.history.undo() ... >>> mod1.read() u'a_var = 10\n' >>> mod2.read() u'import mod1\nprint mod1.a_var\n' # Cleaning up >>> pkg.remove() >>> mod1.remove() >>> project.close() Restructuring ------------- The example for replacing occurrences of our ``pow`` function to ``**`` operator (see the restructuring section of `overview.rst`_): .. code-block:: python # Setting up the project >>> from rope.base.project import Project >>> project = Project('.') >>> mod1 = project.root.create_file('mod1.py') >>> mod1.write('def pow(x, y):\n result = 1\n' ... ' for i in range(y):\n result *= x\n' ... ' return result\n') >>> mod2 = project.root.create_file('mod2.py') >>> mod2.write('import mod1\nprint(mod1.pow(2, 3))\n') >>> from rope.refactor import restructure >>> pattern = '${pow_func}(${param1}, ${param2})' >>> goal = '${param1} ** ${param2}' >>> args = {'pow_func': 'name=mod1.pow'} >>> restructuring = restructure.Restructure(project, pattern, goal, args) >>> project.do(restructuring.get_changes()) >>> mod2.read() u'import mod1\nprint(2 ** 3)\n' # Cleaning up >>> mod1.remove() >>> mod2.remove() >>> project.close() See code documentation and test suites for more information. .. _overview.rst: overview.rst .. _contributing.rst: contributing.rst Other Topics ============ Writing A `FileSystemCommands` ------------------------------ The ``get_changes()`` method of refactoring classes return a ``rope.base.change.Change`` object. You perform these changes by calling ``Project.do()``. But as explained above some IDEs need to perform the changes themselves. Every change to the file-system in rope is commited using an object that provides a ``rope.base.fscommands.FileSystemCommands`` interface. As explained above in `rope.base.fscommands`_ section, rope uses this interface to handle different VCSs. You can implement your own fscommands object: .. code-block:: python class MyFileSystemCommands(object): def create_file(self, path): """Create a new file""" # ... def create_folder(self, path): """Create a new folder""" # ... def move(self, path, new_location): """Move resource at `path` to `new_location`""" # ... def remove(self, path): """Remove resource""" # ... def write(self, path, data): """Write `data` to file at `path`""" # ... def read(self, path): """Read `data` from file at `path`""" # ... And you can create a project like this: .. code-block:: python my_fscommands = MyFileSystemCommands() project = rope.base.project.Project('~/myproject', fscommands=my_fscommands) `rope.contrib.codeassist` ------------------------- The ``rope.contrib`` package contains modules that use rope base parts and provide useful features. ``rope.contrib.codeassist`` module can be used in IDEs: .. code-block:: python from rope.ide import codeassist # Get the proposals; you might want to pass a Resource proposals = codeassist.code_assist(project, source_code, offset) # Sorting proposals; for changing the order see pydoc proposals = codeassist.sorted_proposals(proposals) # Where to insert the completions starting_offset = codeassist.starting_offset(source_code, offset) # Applying a proposal proposal = proposals[x] replacement = proposal.name new_source_code = (source_code[:starting_offset] + replacement + source_code[offset:]) ``maxfixes`` parameter of ``code_assist`` decides how many syntax errors to fix. The default value is one. For instance: .. code-block:: python def f(): g(my^ myvariable = None def g(p): invalid syntax ... will report ``myvariable``, only if ``maxfixes`` is greater than 1. ``later_locals``, if ``True``, forces rope to propose names that are defined later in current scope. It is ``True`` by default. For instance: .. code-block:: python def f(): my^ myvariable = None will not report ``myvariable``, if ``later_locals`` is ``False``. See pydocs and source code for more information (other functions in this module might be interesting, too; like ``get_doc``, ``get_definition_location``). `rope.contrib.findit` --------------------- ``findit`` module provides ``find_occurrences()`` for finding occurrences of a name. Also the ``find_implementations()`` function finds the places in which a method is overridden. `rope.contrib.autoimport` ------------------------- This module can be used to find the modules that provide a name. IDEs can use this module to auto-import names. ``AutoImport.get_modules()`` returns the list of modules with the given global name. ``AutoImport.import_assist()`` tries to find the modules that have a global name that starts with the given prefix. Cross-Project Refactorings -------------------------- ``rope.refactor.multiproject`` can be used to perform a refactoring across multiple projects. Usually refactorings have a main project. That is the project that contains the definition of the changing python name. Other projects depend on the main one, and the uses of the changed name in them should be updated. Each refactoring changes only one project (the project passed to its constructor). But we can use the ``MultiProjectRefactoring`` proxy to perform a refactoring on other projects, too. First we need to create a multi-project refactoring constructor. As an example consider that we want to perform a rename refactoring: .. code-block:: python from rope.refactor import multiproject, rename CrossRename = multiproject.MultiProjectRefactoring(rename.Rename, projects) Here ``projects`` is the list of dependant projects. It does not include the main project. The first argument is the refactoring class (such as ``Rename``) or factory function (like ``create_move``). Next we can construct the refactoring: .. code-block:: python renamer = CrossRename(project, resource, offset) We create the rename refactoring as we do for normal refactoings. Note that ``project`` is the main project. As mentioned above, other projects use the main project. Rope automatically adds the main project to the python path of other projects. Finally we can calculate the changes. But instead of calling ``get_changes()`` (which returns main project changes only), we can call ``get_all_changes()`` with the same arguments. It returns a list of ``(project, changes)`` tuples. You can perform them manually by calling ``project.do(changes)`` for each tuple, or use ``multiproject.perform()``: .. code-block:: python project_and_changes = renamer.get_all_changes('newname') multiproject.perform(project_and_changes) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633427443.0 rope-0.22.0/docs/overview.rst0000664000175000017500000007267600000000000016005 0ustar00lieryanlieryan=============== Rope Overview =============== The purpose of this file is to give an overview of some of rope's features. It is incomplete. And some of the features shown here are old and do not show what rope can do in extremes. So if you really want to feel the power of rope try its features and see its unit tests. This file is more suitable for the users. Developers who plan to use rope as a library might find library.rst_ more useful. .. contents:: Table of Contents .. _library.rst: library.rst ``.ropeproject`` Folder ======================= Rope uses a folder inside projects for holding project configuration and data. Its default name is ``.ropeproject``, but it can be changed (you can even tell rope not to create this folder). Currently it is used for things such as: * There is a ``config.py`` file in this folder in which you can change project configurations. Have look at the default ``config.py`` file (is created when it does not exist) for more information. * It can be used for saving project history, so that the next time you open the project you can undo past changes. * It can be used for saving object information to help rope object inference. * It can be used for saving global names cache which is used in auto-import. You can change what to save and what not to in the ``config.py`` file. Key bindings ============ Rope is a library that is used in many IDE and Text Editors to perform refactoring on Python coe. This page documents the details of the refactoring operations but you would need consult the documentation for your IDE/Text Editor client integration for the specific key bindings that are used by those IDE/Text Editors. Refactorings ============ This section shows some random refactorings that you can perform using rope. Renaming Attributes ------------------- Consider we have: .. code-block:: python class AClass(object): def __init__(self): self.an_attr = 1 def a_method(self, arg): print(self.an_attr, arg) a_var = AClass() a_var.a_method(a_var.an_attr) After renaming ``an_attr`` to ``new_attr`` and ``a_method`` to ``new_method`` we'll have: .. code-block:: python class AClass(object): def __init__(self): self.new_attr = 1 def new_method(self, arg): print(self.new_attr, arg) a_var = AClass() a_var.new_method(a_var.new_attr) Renaming Function Keyword Parameters ------------------------------------ On: .. code-block:: python def a_func(a_param): print(a_param) a_func(a_param=10) a_func(10) performing rename refactoring on any occurrence of ``a_param`` will result in: .. code-block:: python def a_func(new_param): print(new_param) a_func(new_param=10) a_func(10) Renaming modules ---------------- Consider the project tree is something like:: root/ mod1.py mod2.py ``mod1.py`` contains: .. code-block:: python import mod2 from mod2 import AClass mod2.a_func() a_var = AClass() After performing rename refactoring one of the ``mod2`` occurrences in `mod1` we'll get: .. code-block:: python import newmod from newmod import AClass newmod.a_func() a_var = AClass() and the new project tree would be:: root/ mod1.py newmod.py Renaming Occurrences In Strings And Comments -------------------------------------------- You can tell rope to rename all occurrences of a name in comments and strings. This can be done by passing ``docs=True`` to `Rename.get_changes()` method. Rope renames names in comments and strings only where the name is visible. For example in: .. code-block:: python def f(): a_var = 1 # INFO: I'm printing `a_var` print('a_var = %s' % a_var) # f prints a_var after we rename the `a_var` local variable in `f()` to `new_var` we would get: .. code-block:: python def f(): new_var = 1 # INFO: I'm printing `new_var` print('new_var = %s' % new_var) # f prints a_var This makes it safe to assume that this option does not perform wrong renames most of the time. This also changes occurrences inside evaluated strings: .. code-block:: python def func(): print('func() called') eval('func()') After renaming ``func`` to ``newfunc`` we should have: .. code-block:: python def newfunc(): print('newfunc() called') eval('newfunc()') Rename When Unsure ------------------ This option tells rope to rename when it doesn't know whether it is an exact match or not. For example after renaming `C.a_func` when the 'rename when unsure' option is set in: .. code-block:: python class C(object): def a_func(self): pass def a_func(arg): arg.a_func() C().a_func() we would have: .. code-block:: python class C(object): def new_func(self): pass def a_func(arg): arg.new_func() C().new_func() Note that the global ``a_func`` was not renamed because we are sure that it is not a match. But when using this option there might be some unexpected renames. So only use this option when the name is almost unique and is not defined in other places. Move Method Refactoring ----------------------- It happens when you perform move refactoring on a method of a class. In this refactoring, a method of a class is moved to the class of one of its attributes. The old method will call the new method. If you want to change all of the occurrences of the old method to use the new method you can inline it afterwards. For instance if you perform move method on ``a_method`` in: .. code-block:: python class A(object): pass class B(object): def __init__(self): self.attr = A() def a_method(self): pass b = B() b.a_method() You will be asked for the destination field and the name of the new method. If you use ``attr`` and ``new_method`` in these fields and press enter, you'll have: .. code-block:: python class A(object): def new_method(self): pass class B(object): def __init__(self): self.attr = A() def a_method(self): return self.attr.new_method() b = B() b.a_method() Now if you want to change the occurrences of ``B.a_method()`` to use ``A.new_method()``, you can inline ``B.a_method()``: .. code-block:: python class A(object): def new_method(self): pass class B(object): def __init__(self): self.attr = A() b = B() b.attr.new_method() Moving Fields ------------- Rope does not have a separate refactoring for moving fields. Rope's refactorings are very flexible, though. You can use the rename refactoring to move fields. For instance: .. code-block:: python class A(object): pass class B(object): def __init__(self): self.a = A() self.attr = 1 b = B() print(b.attr) consider we want to move ``attr`` to ``A``. We can do that by renaming ``attr`` to ``a.attr``: .. code-block:: python class A(object): pass class B(object): def __init__(self): self.a = A() self.a.attr = 1 b = B() print(b.a.attr) You can move the definition of ``attr`` manually. Extract Method -------------- In these examples ``${region_start}`` and ``${region_end}`` show the selected region for extraction: .. code-block:: python def a_func(): a = 1 b = 2 * a c = ${region_start}a * 2 + b * 3${region_end} After performing extract method we'll have: .. code-block:: python def a_func(): a = 1 b = 2 * a c = new_func(a, b) def new_func(a, b): return a * 2 + b * 3 For multi-line extractions if we have: .. code-block:: python def a_func(): a = 1 ${region_start}b = 2 * a c = a * 2 + b * 3${region_end} print(b, c) After performing extract method we'll have: .. code-block:: python def a_func(): a = 1 b, c = new_func(a) print(b, c) def new_func(a): b = 2 * a c = a * 2 + b * 3 return b, c Extracting Similar Expressions/Statements ----------------------------------------- When performing extract method or local variable refactorings you can tell rope to extract similar expressions/statements. For instance in: .. code-block:: python if True: x = 2 * 3 else: x = 2 * 3 + 1 Extracting ``2 * 3`` will result in: .. code-block:: python six = 2 * 3 if True: x = six else: x = six + 1 Extract Regular Method into staticmethod/classmethod ---------------------------------------------------- If you prefix the extracted method name with `@` or `$`, the generated method will be created as a `classmethod` and `staticmethod` respectively. For instance in: .. code-block:: python class A(object): def f(self, a): b = a * 2 if you select ``a * 2`` for method extraction and name the method ``@new_method``, you'll get: .. code-block:: python class A(object): def f(self, a): b = A.twice(a) @classmethod def new_method(cls, a): return a * 2 Similarly, you can prefix the name with `$` to create a staticmethod instead. Extract Method In staticmethods/classmethods -------------------------------------------- The extract method refactoring has been enhanced to handle static and class methods better. For instance in: .. code-block:: python class A(object): @staticmethod def f(a): b = a * 2 if you extract ``a * 2`` as a method you'll get: .. code-block:: python class A(object): @staticmethod def f(a): b = A.twice(a) @staticmethod def twice(a): return a * 2 Inline Method Refactoring ------------------------- Inline method refactoring can add imports when necessary. For instance consider ``mod1.py`` is: .. code-block:: python import sys class C(object): pass def do_something(): print(sys.version) return C() and ``mod2.py`` is: .. code-block:: python import mod1 c = mod1.do_something() After inlining ``do_something``, ``mod2.py`` would be: .. code-block:: python import mod1 import sys print(sys.version) c = mod1.C() Rope can inline methods, too: .. code-block:: python class C(object): var = 1 def f(self, p): result = self.var + pn return result c = C() x = c.f(1) After inlining ``C.f()``, we'll have: .. code-block:: python class C(object): var = 1 c = C() result = c.var + pn x = result As another example we will inline a ``classmethod``: .. code-block:: python class C(object): @classmethod def say_hello(cls, name): return 'Saying hello to %s from %s' % (name, cls.__name__) hello = C.say_hello('Rope') Inlining ``say_hello`` will result in: .. code-block:: python class C(object): pass hello = 'Saying hello to %s from %s' % ('Rope', C.__name__) Inlining Parameters ------------------- ``rope.refactor.inline.create_inline()`` creates an ``InlineParameter`` object when performed on a parameter. It passes the default value of the parameter wherever its function is called without passing it. For instance in: .. code-block:: python def f(p1=1, p2=1): pass f(3) f() f(3, 4) after inlining p2 parameter will have: .. code-block:: python def f(p1=1, p2=2): pass f(3, 2) f(p2=2) f(3, 4) Use Function Refactoring ------------------------ It tries to find the places in which a function can be used and changes the code to call it instead. For instance if mod1 is: .. code-block:: python def square(p): return p ** 2 my_var = 3 ** 2 and mod2 is: .. code-block:: python another_var = 4 ** 2 if we perform "use function" on square function, mod1 will be: .. code-block:: python def square(p): return p ** 2 my_var = square(3) and mod2 will be: .. code-block:: python import mod1 another_var = mod1.square(4) Automatic Default Insertion In Change Signature ----------------------------------------------- The ``rope.refactor.change_signature.ArgumentReorderer`` signature changer takes a parameter called ``autodef``. If not ``None``, its value is used whenever rope needs to insert a default for a parameter (that happens when an argument without default is moved after another that has a default value). For instance in: .. code-block:: python def f(p1, p2=2): pass if we reorder using: .. code-block:: python changers = [ArgumentReorderer([1, 0], autodef='1')] will result in: .. code-block:: python def f(p2=2, p1=1): pass Sorting Imports --------------- Organize imports sorts imports, too. It does that according to :PEP:`8`:: [__future__ imports] [standard imports] [third-party imports] [project imports] [the rest of module] Handling Long Imports --------------------- ``Handle long imports`` command trys to make long imports look better by transforming ``import pkg1.pkg2.pkg3.pkg4.mod1`` to ``from pkg1.pkg2.pkg3.pkg4 import mod1``. Long imports can be identified either by having lots of dots or being very long. The default configuration considers imported modules with more than 2 dots or with more than 27 characters to be long. Stoppable Refactorings ---------------------- Some refactorings might take a long time to finish (based on the size of your project). The ``get_changes()`` method of these refactorings take a parameter called ``task_handle``. If you want to monitor or stop these refactoring you can pass a ``rope.refactor.taskhandle.TaskHandle`` to this method. See ``rope.refactor.taskhandle`` module for more information. Basic Implicit Interfaces ------------------------- Implicit interfaces are the interfaces that you don't explicitly define; But you expect a group of classes to have some common attributes. These interfaces are very common in dynamic languages; Since we only have implementation inheritance and not interface inheritance. For instance: .. code-block:: python class A(object): def count(self): pass class B(object): def count(self): pass def count_for(arg): return arg.count() count_for(A()) count_for(B()) Here we know that there is an implicit interface defined by the function ``count_for`` that provides ``count()``. Here when we rename ``A.count()`` we expect ``B.count()`` to be renamed, too. Currently rope supports a basic form of implicit interfaces. When you try to rename an attribute of a parameter, rope renames that attribute for all objects that have been passed to that function in different call sites. That is renaming the occurrence of ``count`` in ``count_for`` function to ``newcount`` will result in: .. code-block:: python class A(object): def newcount(self): pass class B(object): def newcount(self): pass def count_for(arg): return arg.newcount() count_for(A()) count_for(B()) This also works for change method signature. Note that this feature relies on rope's object analysis mechanisms to find out the parameters that are passed to a function. Restructurings -------------- ``rope.refactor.restructure`` can be used for performing restructurings. A restructuring is a program transformation; not as well defined as other refactorings like rename. In this section, we'll see some examples. After this example you might like to have a look at: * ``rope.refactor.restructure`` for more examples and features not described here like adding imports to changed modules. * ``rope.refactor.wildcards`` for an overview of the arguments the default wildcard supports. Finally, restructurings can be improved in many ways (for instance adding new wildcards). You might like to discuss your ideas in the `Github Discussion`_. .. _`Github Discussion`: https://github.com/python-rope/rope/discussions Example 1 ''''''''' In its basic form we have a pattern and a goal. Consider we were not aware of the ``**`` operator and wrote our own: .. code-block:: python def pow(x, y): result = 1 for i in range(y): result *= x return result print(pow(2, 3)) Now that we know ``**`` exists we want to use it wherever ``pow`` is used (there might be hundreds of them!). We can use a pattern like:: pattern: pow(${param1}, ${param2}) Goal can be something like:: goal: ${param1} ** ${param2} Note that ``${...}`` can be used to match expressions. By default every expression at that point will match. You can use the matched names in goal and they will be replaced with the string that was matched in each occurrence. So the outcome of our restructuring will be: .. code-block:: python def pow(x, y): result = 1 for i in range(y): result *= x return result print(2 ** 3) It seems to be working but what if ``pow`` is imported in some module or we have some other function defined in some other module that uses the same name and we don't want to change it. Wildcard arguments come to rescue. Wildcard arguments is a mapping; Its keys are wildcard names that appear in the pattern (the names inside ``${...}``). The values are the parameters that are passed to wildcard matchers. The arguments a wildcard takes is based on its type. For checking the type of a wildcard, we can pass ``type=value`` as an argument; ``value`` should be resolved to a python variable (or reference). For instance for specifying ``pow`` in this example we can use ``mod.pow``. As you see, this string should start from module name. For referencing python builtin types and functions you can use ``__builtin__`` module (for instance ``__builtin__.int``). For solving the mentioned problem, we change our ``pattern``. But ``goal`` remains the same:: pattern: ${pow_func}(${param1}, ${param2}) goal: ${param1} ** ${param2} Consider the name of the module containing our ``pow`` function is ``mod``. ``args`` can be:: pow_func: name=mod.pow If we need to pass more arguments to a wildcard matcher we can use ``,`` to separate them. Such as ``name: type=mod.MyClass,exact``. This restructuring handles aliases like in: .. code-block:: python mypow = pow result = mypow(2, 3) Transforms into: .. code-block:: python mypow = pow result = 2 ** 3 If we want to ignore aliases we can pass ``exact`` as another wildcard argument:: pattern: ${pow}(${param1}, ${param2}) goal: ${param1} ** ${param2} args: pow: name=mod.pow, exact ``${name}``, by default, matches every expression at that point; if ``exact`` argument is passed to a wildcard only the specified name will match (for instance, if ``exact`` is specified , ``${name}`` matches ``name`` and ``x.name`` but not ``var`` nor ``(1 + 2)`` while a normal ``${name}`` can match all of them). For performing this refactoring using rope library see `library.rst`_. Example 2 ''''''''' As another example consider: .. code-block:: python class A(object): def f(self, p1, p2): print(p1) print(p2) a = A() a.f(1, 2) Later we decide that ``A.f()`` is doing too much and we want to divide it to ``A.f1()`` and ``A.f2()``: .. code-block:: python class A(object): def f(self, p1, p2): print(p1) print(p2) def f1(self, p): print(p) def f2(self, p): print(p) a = A() a.f(1, 2) But who's going to fix all those nasty occurrences (actually this situation can be handled using inline method refactoring but this is just an example; consider inline refactoring is not implemented yet!). Restructurings come to rescue:: pattern: ${inst}.f(${p1}, ${p2}) goal: ${inst}.f1(${p1}) ${inst}.f2(${p2}) args: inst: type=mod.A After performing we will have: .. code-block:: python class A(object): def f(self, p1, p2): print(p1) print(p2) def f1(self, p): print(p) def f2(self, p): print(p) a = A() a.f1(1) a.f2(2) Example 3 ''''''''' If you like to replace every occurrences of ``x.set(y)`` with ``x = y`` when x is an instance of ``mod.A`` in: .. code-block:: python from mod import A a = A() b = A() a.set(b) We can perform a restructuring with these information:: pattern: ${x}.set(${y}) goal: ${x} = ${y} args: x: type=mod.A After performing the above restructuring we'll have: .. code-block:: python from mod import A a = A() b = A() a = b Note that ``mod.py`` contains something like: .. code-block:: python class A(object): def set(self, arg): pass Issues '''''' Pattern names can appear only at the start of an expression. For instance ``var.${name}`` is invalid. These situations can usually be fixed by specifying good checks, for example on the type of `var` and using a ``${var}.name``. Object Inference ================ This section is a bit out of date. Static object inference can do more than described here (see unittests). Hope to update this someday! Static Object Inference ----------------------- .. code-block:: python class AClass(object): def __init__(self): self.an_attr = 1 def call_a_func(self): return a_func() def a_func(): return AClass() a_var = a_func() #a_var.${codeassist} another_var = a_var #another_var.${codeassist} #another_var.call_a_func().${codeassist} Basic support for builtin types: .. code-block:: python a_list = [AClass(), AClass()] for x in a_list: pass #x.${codeassist} #a_list.pop().${codeassist} a_dict = ['text': AClass()] for key, value in a_dict.items(): pass #key.${codeassist} #value.${codeassist} Enhanced static returned object inference: .. code-block:: python class C(object): def c_func(self): return [''] def a_func(arg): return arg.c_func() a_var = a_func(C()) Here rope knows that the type of a_var is a ``list`` that holds ``str``\s. Supporting generator functions: .. code-block:: python class C(object): pass def a_generator(): yield C() for c in a_generator(): a_var = c Here the objects ``a_var`` and ``c`` hold are known. Rope collects different types of data during SOA, like per name data for builtin container types: .. code-block:: python l1 = [C()] var1 = l1.pop() l2 = [] l2.append(C()) var2 = l2.pop() Here rope can easily infer the type of ``var1``. But for knowing the type of ``var2``, it needs to analyze the items inserted into ``l2`` which might happen in other modules. Rope can do that by running SOA on that module. You might be wondering is there any reason for using DOA instead of SOA. The answer is that DOA might be more accurate and handles complex and dynamic situations. For example in: .. code-block:: python def f(arg): return eval(arg) a_var = f('C') SOA can no way conclude the object ``a_var`` holds but it is really trivial for DOA. What's more SOA only analyzes calls in one module while DOA analyzes any call that happens when running a module. That is, for achieving the same result as DOA you might need to run SOA on more than one module and more than once (not considering dynamic situations.) One advantage of SOA is that it is much faster than DOA. Dynamic Object Analysis ----------------------- ``PyCore.run_module()`` runs a module and collects object information if ``perform_doa`` project config is set. Since as the program runs rope gathers type information, the program runs much slower. After the program is run, you can get better code assists and some of the refactorings perform much better. ``mod1.py``: .. code-block:: python def f1(param): pass #param.${codeassist} #f2(param).${codeassist} def f2(param): #param.${codeassist} return param Using code assist in specified places does not give any information and there is actually no information about the return type of ``f2`` or ``param`` parameter of ``f1``. ``mod2.py``: .. code-block:: python import mod1 class A(object): def a_method(self): pass a_var = A() mod1.f1(a_var) Retry those code assists after performing DOA on ``mod2`` module. Builtin Container Types ''''''''''''''''''''''' Builtin types can be handled in a limited way, too: .. code-block:: python class A(object): def a_method(self): pass def f1(): result = [] result.append(A()) return result returned = f() #returned[0].${codeassist} Test the the proposed completions after running this module. Guessing Function Returned Value Based On Parameters ---------------------------------------------------- ``mod1.py``: .. code-block:: python class C1(object): def c1_func(self): pass class C2(object): def c2_func(self): pass def func(arg): if isinstance(arg, C1): return C2() else: return C1() func(C1()) func(C2()) After running ``mod1`` either SOA or DOA on this module you can test: ``mod2.py``: .. code-block:: python import mod1 arg = mod1.C1() a_var = mod1.func(arg) a_var.${codeassist} mod1.func(mod1.C2()).${codeassist} Automatic SOA ------------- When turned on, it analyzes the changed scopes of a file when saving for obtaining object information; So this might make saving files a bit more time consuming. By default, this feature is turned on, but you can turn it off by editing your project ``config.py`` file, though that is not recommended. Validating Object DB -------------------- Since files on disk change over time project objectdb might hold invalid information. Currently there is a basic incremental objectdb validation that can be used to remove or fix out of date information. Rope uses this feature by default but you can disable it by editing ``config.py``. Type Hinting ------------ Currently supported type hinting for: - function parameter type, using function doctring (:type or @type) - function return type, using function doctring (:rtype or @rtype) - class attribute type, using class docstring (:type or @type). Attribute should by set to None or NotImplemented in class. - any assignment, using type comments of PEP 0484 (in limited form). If rope cannot detect the type of a function argument correctly (due to the dynamic nature of Python), you can help it by hinting the type using one of the following docstring syntax styles. **Sphinx style** http://sphinx-doc.org/domains.html#info-field-lists :: def myfunction(node, foo): """Do something with a ``node``. :type node: ProgramNode :param str foo: foo parameter description """ node.| # complete here **Epydoc** http://epydoc.sourceforge.net/manual-fields.html :: def myfunction(node): """Do something with a ``node``. @type node: ProgramNode """ node.| # complete here **Numpydoc** https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt In order to support the numpydoc format, you need to install the `numpydoc `__ package. :: def foo(var1, var2, long_var_name='hi'): r"""A one-line summary that does not use variable names or the function name. ... Parameters ---------- var1 : array_like Array_like means all those objects -- lists, nested lists, etc. -- that can be converted to an array. We can also refer to variables like `var1`. var2 : int The type above can either refer to an actual Python type (e.g. ``int``), or describe the type of the variable in more detail, e.g. ``(N,) ndarray`` or ``array_like``. long_variable_name : {'hi', 'ho'}, optional Choices in brackets, default first when optional. ... """ var2.| # complete here **PEP 0484** https://www.python.org/dev/peps/pep-0484/#type-comments :: class Sample(object): def __init__(self): self.x = None # type: random.Random self.x.| # complete here Supported syntax of type hinting '''''''''''''''''''''''''''''''' Currently rope supports the following syntax of type-hinting. Parametrized objects: - Foo - foo.bar.Baz - list[Foo] or list[foo.bar.Baz] etc. - set[Foo] - tuple[Foo] - dict[Foo, Bar] - collections.Iterable[Foo] - collections.Iterator[Foo] Nested expressions also allowed: - collections.Iterable[list[Foo]] TODO: Callable objects: - (Foo, Bar) -> Baz Multiple interfaces implementation: - Foo | Bar 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 using ``source_folders`` project config. Note that rope guesses project source folders correctly most of the time. You can also extend python path using ``python_path`` config. Version Control Systems Support =============================== When performing refactorings some files might need to be moved (when renaming a module) or new files might be created. When using a VCS, rope detects and uses it to perform file system actions. Currently Mercurial_, GIT_, Darcs_ and SVN (using pysvn_ library) are supported. They are selected based on dot files in project root directory. For instance, Mercurial will be used if `mercurial` module is available and there is a ``.hg`` folder in project root. Rope assumes either all files are under version control in a project or there is no version control at all. Also don't forget to commit your changes yourself, rope doesn't do that. Adding support for other VCSs is easy; have a look at `library.rst`_. .. _pysvn: http://pysvn.tigris.org .. _Mercurial: http://selenic.com/mercurial .. _GIT: http://git.or.cz .. _darcs: http://darcs.net ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636636936.0 rope-0.22.0/docs/release-process.rst0000664000175000017500000000057400000000000017217 0ustar00lieryanlieryan1. Ensure tickets assigned to Milestones are up to date 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 -m build`` 6. ``twine upload -s dist/rope-$VERSION.{tar.gz,whl}`` 7. Publish to Discussions Announcement ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1599360855.0 rope-0.22.0/docs/rope.rst0000664000175000017500000000270400000000000015065 0ustar00lieryanlieryanFeatures ======== Features implemented so far: * Refactorings * Rename everything! * Extract method/local variable * Move class/function/module/package/method * Inline method/local variable/parameter * Restructuring (like converting ``${a}.f(${b})`` to ``${b}.g(${a})`` where ``a: type=mymod.A``) * Introduce factory * Change method signature * Transform module to package * Encapsulate field * Replace method with method object * And a few others * Refactoring Features * Extracting similar statements in extract refactorings * Fixing imports when needed * Previewing refactorings * Undo/redo refactorings * Stopping refactorings * Cross-project refactorings * Basic implicit interfaces handling in rename and change signature * Mercurial_, GIT_, Darcs_ and SVN (pysvn_ library) support in refactorings * IDE helpers * Auto-completion * Definition location * Get pydoc * Find occurrences * Organize imports (remove unused and duplicate imports and sort them) * Generating python elements * Object Inference * Static and dynamic object analysis * Handling built-in container types * Saving object information on disk and validating them * Type hints using docstring or type comments PEP 0484 For more information see `overview.rst`_. .. _overview.rst: overview.rst .. _pysvn: http://pysvn.tigris.org .. _Mercurial: http://selenic.com/mercurial .. _GIT: http://git.or.cz .. _darcs: http://darcs.net ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637501450.0 rope-0.22.0/pyproject.toml0000664000175000017500000000022600000000000015347 0ustar00lieryanlieryan[tool.black] target-version = ['py27', 'py33', 'py34', 'py35', 'py36', 'py37', 'py38', 'py39'] include = 'rope/.*\.pyi?$' force-exclude = 'ropetest' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3513896 rope-0.22.0/rope/0000775000175000017500000000000000000000000013400 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3513896 rope-0.22.0/rope/.ropeproject/0000775000175000017500000000000000000000000016012 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636638665.0 rope-0.22.0/rope/.ropeproject/config.py0000664000175000017500000001140300000000000017630 0ustar00lieryanlieryan# 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 import ` 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! ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637593538.0 rope-0.22.0/rope/__init__.py0000664000175000017500000000167000000000000015515 0ustar00lieryanlieryan"""rope, a python refactoring library""" INFO = __doc__ VERSION = "0.22.0" COPYRIGHT = """\ Copyright (C) 2021-2021 Lie Ryan Copyright (C) 2019-2021 Matej Cepl Copyright (C) 2015-2018 Nicholas Smith Copyright (C) 2014-2015 Matej Cepl Copyright (C) 2006-2012 Ali Gholami Rudi Copyright (C) 2009-2012 Anton Gritsay This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see .""" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3553898 rope-0.22.0/rope/base/0000775000175000017500000000000000000000000014312 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/__init__.py0000664000175000017500000000024100000000000016420 0ustar00lieryanlieryan"""Base rope package This package contains rope core modules that are used by other modules and packages. """ __all__ = ["project", "libutils", "exceptions"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/arguments.py0000664000175000017500000000635200000000000016677 0ustar00lieryanlieryanimport rope.base.evaluate from rope.base import ast class Arguments(object): """A class for evaluating parameters passed to a function You can use the `create_arguments` factory. It handles implicit first arguments. """ def __init__(self, args, scope): self.args = args self.scope = scope self.instance = None def get_arguments(self, parameters): result = [] for pyname in self.get_pynames(parameters): if pyname is None: result.append(None) else: result.append(pyname.get_object()) return result def get_pynames(self, parameters): result = [None] * max(len(parameters), len(self.args)) for index, arg in enumerate(self.args): if isinstance(arg, ast.keyword) and arg.arg in parameters: result[parameters.index(arg.arg)] = self._evaluate(arg.value) else: result[index] = self._evaluate(arg) return result def get_instance_pyname(self): if self.args: return self._evaluate(self.args[0]) def _evaluate(self, ast_node): return rope.base.evaluate.eval_node(self.scope, ast_node) def create_arguments(primary, pyfunction, call_node, scope): """A factory for creating `Arguments`""" args = list(call_node.args) args.extend(call_node.keywords) called = call_node.func # XXX: Handle constructors if _is_method_call(primary, pyfunction) and isinstance(called, ast.Attribute): args.insert(0, called.value) return Arguments(args, scope) class ObjectArguments(object): def __init__(self, pynames): self.pynames = pynames def get_arguments(self, parameters): result = [] for pyname in self.pynames: if pyname is None: result.append(None) else: result.append(pyname.get_object()) return result def get_pynames(self, parameters): return self.pynames def get_instance_pyname(self): return self.pynames[0] class MixedArguments(object): def __init__(self, pyname, arguments, scope): """`argumens` is an instance of `Arguments`""" self.pyname = pyname self.args = arguments def get_pynames(self, parameters): return [self.pyname] + self.args.get_pynames(parameters[1:]) def get_arguments(self, parameters): result = [] for pyname in self.get_pynames(parameters): if pyname is None: result.append(None) else: result.append(pyname.get_object()) return result def get_instance_pyname(self): return self.pyname def _is_method_call(primary, pyfunction): if primary is None: return False pyobject = primary.get_object() if ( isinstance(pyobject.get_type(), rope.base.pyobjects.PyClass) and isinstance(pyfunction, rope.base.pyobjects.PyFunction) and isinstance(pyfunction.parent, rope.base.pyobjects.PyClass) ): return True if isinstance( pyobject.get_type(), rope.base.pyobjects.AbstractClass ) and isinstance(pyfunction, rope.base.builtins.BuiltinFunction): return True return False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/ast.py0000664000175000017500000000437700000000000015466 0ustar00lieryanlieryanfrom __future__ import absolute_import import ast from ast import * from rope.base import fscommands try: unicode except NameError: unicode = str def parse(source, filename=""): # NOTE: the raw string should be given to `compile` function if isinstance(source, unicode): source = fscommands.unicode_to_file_data(source) if b"\r" in source: source = source.replace(b"\r\n", b"\n").replace(b"\r", b"\n") if not source.endswith(b"\n"): source += b"\n" try: return ast.parse(source, filename="") except (TypeError, ValueError) as e: error = SyntaxError() error.lineno = 1 error.filename = filename error.msg = str(e) raise error def walk(node, walker): """Walk the syntax tree""" method_name = "_" + node.__class__.__name__ method = getattr(walker, method_name, None) if method is not None: if isinstance(node, ast.ImportFrom) and node.module is None: # In python < 2.7 ``node.module == ''`` for relative imports # but for python 2.7 it is None. Generalizing it to ''. node.module = "" return method(node) for child in get_child_nodes(node): walk(child, walker) def get_child_nodes(node): if isinstance(node, ast.Module): return node.body result = [] if node._fields is not None: for name in node._fields: child = getattr(node, name) if isinstance(child, list): for entry in child: if isinstance(entry, ast.AST): result.append(entry) if isinstance(child, ast.AST): result.append(child) return result def call_for_nodes(node, callback, recursive=False): """If callback returns `True` the child nodes are skipped""" result = callback(node) if recursive and not result: for child in get_child_nodes(node): call_for_nodes(child, callback, recursive) def get_children(node): result = [] if node._fields is not None: for name in node._fields: if name in ["lineno", "col_offset"]: continue child = getattr(node, name) result.append(child) return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/astutils.py0000664000175000017500000000316200000000000016536 0ustar00lieryanlieryanfrom rope.base import ast def get_name_levels(node): """Return a list of ``(name, level)`` tuples for assigned names The `level` is `None` for simple assignments and is a list of numbers for tuple assignments for example in:: a, (b, c) = x The levels for for `a` is ``[0]``, for `b` is ``[1, 0]`` and for `c` is ``[1, 1]``. """ visitor = _NodeNameCollector() ast.walk(node, visitor) return visitor.names class _NodeNameCollector(object): def __init__(self, levels=None): self.names = [] self.levels = levels self.index = 0 def _add_node(self, node): new_levels = [] if self.levels is not None: new_levels = list(self.levels) new_levels.append(self.index) self.index += 1 self._added(node, new_levels) def _added(self, node, levels): if hasattr(node, "id"): self.names.append((node.id, levels)) def _Name(self, node): self._add_node(node) def _ExceptHandler(self, node): self.names.append((node.name, [])) def _Tuple(self, node): new_levels = [] if self.levels is not None: new_levels = list(self.levels) new_levels.append(self.index) self.index += 1 visitor = _NodeNameCollector(new_levels) for child in ast.get_child_nodes(node): ast.walk(child, visitor) self.names.extend(visitor.names) def _Subscript(self, node): self._add_node(node) def _Attribute(self, node): self._add_node(node) def _Slice(self, node): self._add_node(node) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/base/builtins.py0000664000175000017500000006430100000000000016521 0ustar00lieryanlieryan"""This module trys to support builtin types and functions.""" import inspect import io try: raw_input except NameError: raw_input = input import rope.base.evaluate from rope.base.utils import pycompat from rope.base import pynames, pyobjects, arguments, utils class BuiltinModule(pyobjects.AbstractModule): def __init__(self, name, pycore=None, initial={}): super(BuiltinModule, self).__init__() self.name = name self.pycore = pycore self.initial = initial parent = None def get_attributes(self): return self.attributes def get_doc(self): if self.module: return self.module.__doc__ def get_name(self): return self.name.split(".")[-1] @property @utils.saveit def attributes(self): result = _object_attributes(self.module, self) result.update(self.initial) if self.pycore is not None: submodules = self.pycore._builtin_submodules(self.name) for name, module in submodules.items(): result[name] = rope.base.builtins.BuiltinName(module) return result @property @utils.saveit def module(self): try: result = __import__(self.name) for token in self.name.split(".")[1:]: result = getattr(result, token, None) return result except ImportError: return class _BuiltinElement(object): def __init__(self, builtin, parent=None): self.builtin = builtin self._parent = parent def get_doc(self): if self.builtin: return getattr(self.builtin, "__doc__", None) def get_name(self): if self.builtin: return getattr(self.builtin, "__name__", None) @property def parent(self): if self._parent is None: return builtins return self._parent class BuiltinClass(_BuiltinElement, pyobjects.AbstractClass): def __init__(self, builtin, attributes, parent=None): _BuiltinElement.__init__(self, builtin, parent) pyobjects.AbstractClass.__init__(self) self.initial = attributes @utils.saveit def get_attributes(self): result = _object_attributes(self.builtin, self) result.update(self.initial) return result def get_module(self): return builtins class BuiltinFunction(_BuiltinElement, pyobjects.AbstractFunction): def __init__( self, returned=None, function=None, builtin=None, argnames=[], parent=None ): _BuiltinElement.__init__(self, builtin, parent) pyobjects.AbstractFunction.__init__(self) self.argnames = argnames self.returned = returned self.function = function def get_returned_object(self, args): if self.function is not None: return self.function(_CallContext(self.argnames, args)) else: return self.returned def get_param_names(self, special_args=True): return self.argnames class BuiltinUnknown(_BuiltinElement, pyobjects.PyObject): def __init__(self, builtin): super(BuiltinUnknown, self).__init__(pyobjects.get_unknown()) self.builtin = builtin self.type = pyobjects.get_unknown() def get_name(self): return getattr(type(self.builtin), "__name__", None) @utils.saveit def get_attributes(self): return _object_attributes(self.builtin, self) def _object_attributes(obj, parent): attributes = {} for name in dir(obj): if name == "None": continue try: child = getattr(obj, name) except AttributeError: # descriptors are allowed to raise AttributeError # even if they are in dir() continue pyobject = None if inspect.isclass(child): pyobject = BuiltinClass(child, {}, parent=parent) elif inspect.isroutine(child): pyobject = BuiltinFunction(builtin=child, parent=parent) else: pyobject = BuiltinUnknown(builtin=child) attributes[name] = BuiltinName(pyobject) return attributes def _create_builtin_type_getter(cls): def _get_builtin(*args): if not hasattr(cls, "_generated"): cls._generated = {} if args not in cls._generated: cls._generated[args] = cls(*args) return cls._generated[args] return _get_builtin def _create_builtin_getter(cls): type_getter = _create_builtin_type_getter(cls) def _get_builtin(*args): return pyobjects.PyObject(type_getter(*args)) return _get_builtin class _CallContext(object): def __init__(self, argnames, args): self.argnames = argnames self.args = args def _get_scope_and_pyname(self, pyname): if pyname is not None and isinstance(pyname, pynames.AssignedName): pymodule, lineno = pyname.get_definition_location() if pymodule is None: return None, None if lineno is None: lineno = 1 scope = pymodule.get_scope().get_inner_scope_for_line(lineno) name = None while name is None and scope is not None: for current in scope.get_names(): if scope[current] is pyname: name = current break else: scope = scope.parent return scope, name return None, None def get_argument(self, name): if self.args: args = self.args.get_arguments(self.argnames) return args[self.argnames.index(name)] def get_pyname(self, name): if self.args: args = self.args.get_pynames(self.argnames) if name in self.argnames: return args[self.argnames.index(name)] def get_arguments(self, argnames): if self.args: return self.args.get_arguments(argnames) def get_pynames(self, argnames): if self.args: return self.args.get_pynames(argnames) def get_per_name(self): if self.args is None: return None pyname = self.args.get_instance_pyname() scope, name = self._get_scope_and_pyname(pyname) if name is not None: pymodule = pyname.get_definition_location()[0] return pymodule.pycore.object_info.get_per_name(scope, name) return None def save_per_name(self, value): if self.args is None: return None pyname = self.args.get_instance_pyname() scope, name = self._get_scope_and_pyname(pyname) if name is not None: pymodule = pyname.get_definition_location()[0] pymodule.pycore.object_info.save_per_name(scope, name, value) class _AttributeCollector(object): def __init__(self, type): self.attributes = {} self.type = type def __call__( self, name, returned=None, function=None, argnames=["self"], check_existence=True, parent=None, ): try: builtin = getattr(self.type, name) except AttributeError: if check_existence: raise builtin = None self.attributes[name] = BuiltinName( BuiltinFunction( returned=returned, function=function, argnames=argnames, builtin=builtin, parent=parent, ) ) def __setitem__(self, name, value): self.attributes[name] = value class List(BuiltinClass): def __init__(self, holding=None): self.holding = holding collector = _AttributeCollector(list) collector("__iter__", function=self._iterator_get, parent=self) collector("__new__", function=self._new_list, parent=self) # Adding methods collector( "append", function=self._list_add, argnames=["self", "value"], parent=self ) collector( "__setitem__", function=self._list_add, argnames=["self", "index", "value"], parent=self, ) collector( "insert", function=self._list_add, argnames=["self", "index", "value"], parent=self, ) collector( "extend", function=self._self_set, argnames=["self", "iterable"], parent=self, ) # Getting methods collector("__getitem__", function=self._list_get, parent=self) collector("pop", function=self._list_get, parent=self) try: collector("__getslice__", function=self._list_get) except AttributeError: pass super(List, self).__init__(list, collector.attributes) def _new_list(self, args): return _create_builtin(args, get_list) def _list_add(self, context): if self.holding is not None: return holding = context.get_argument("value") if holding is not None and holding != pyobjects.get_unknown(): context.save_per_name(holding) def _self_set(self, context): if self.holding is not None: return iterable = context.get_pyname("iterable") holding = _infer_sequence_for_pyname(iterable) if holding is not None and holding != pyobjects.get_unknown(): context.save_per_name(holding) def _list_get(self, context): if self.holding is not None: args = context.get_arguments(["self", "key"]) if ( len(args) > 1 and args[1] is not None and args[1].get_type() == builtins["slice"].get_object() ): return get_list(self.holding) return self.holding return context.get_per_name() def _iterator_get(self, context): return get_iterator(self._list_get(context)) def _self_get(self, context): return get_list(self._list_get(context)) get_list = _create_builtin_getter(List) get_list_type = _create_builtin_type_getter(List) class Dict(BuiltinClass): def __init__(self, keys=None, values=None): self.keys = keys self.values = values collector = _AttributeCollector(dict) collector("__new__", function=self._new_dict, parent=self) collector("__setitem__", function=self._dict_add, parent=self) collector("popitem", function=self._item_get, parent=self) collector("pop", function=self._value_get, parent=self) collector("get", function=self._key_get, parent=self) collector("keys", function=self._key_list, parent=self) collector("values", function=self._value_list, parent=self) collector("items", function=self._item_list, parent=self) collector("copy", function=self._self_get, parent=self) collector("__getitem__", function=self._value_get, parent=self) collector("__iter__", function=self._key_iter, parent=self) collector("update", function=self._self_set, parent=self) super(Dict, self).__init__(dict, collector.attributes) def _new_dict(self, args): def do_create(holding=None): if holding is None: return get_dict() type = holding.get_type() if isinstance(type, Tuple) and len(type.get_holding_objects()) == 2: return get_dict(*type.get_holding_objects()) return _create_builtin(args, do_create) def _dict_add(self, context): if self.keys is not None: return key, value = context.get_arguments(["self", "key", "value"])[1:] if key is not None and key != pyobjects.get_unknown(): context.save_per_name(get_tuple(key, value)) def _item_get(self, context): if self.keys is not None: return get_tuple(self.keys, self.values) item = context.get_per_name() if item is None or not isinstance(item.get_type(), Tuple): return get_tuple(self.keys, self.values) return item def _value_get(self, context): item = self._item_get(context).get_type() return item.get_holding_objects()[1] def _key_get(self, context): item = self._item_get(context).get_type() return item.get_holding_objects()[0] def _value_list(self, context): return get_list(self._value_get(context)) def _key_list(self, context): return get_list(self._key_get(context)) def _item_list(self, context): return get_list(self._item_get(context)) def _value_iter(self, context): return get_iterator(self._value_get(context)) def _key_iter(self, context): return get_iterator(self._key_get(context)) def _item_iter(self, context): return get_iterator(self._item_get(context)) def _self_get(self, context): item = self._item_get(context).get_type() key, value = item.get_holding_objects()[:2] return get_dict(key, value) def _self_set(self, context): if self.keys is not None: return new_dict = context.get_pynames(["self", "d"])[1] if new_dict and isinstance(new_dict.get_object().get_type(), Dict): args = arguments.ObjectArguments([new_dict]) items = ( new_dict.get_object()["popitem"].get_object().get_returned_object(args) ) context.save_per_name(items) else: holding = _infer_sequence_for_pyname(new_dict) if holding is not None and isinstance(holding.get_type(), Tuple): context.save_per_name(holding) get_dict = _create_builtin_getter(Dict) get_dict_type = _create_builtin_type_getter(Dict) class Tuple(BuiltinClass): def __init__(self, *objects): self.objects = objects first = None if objects: first = objects[0] attributes = { "__getitem__": BuiltinName( BuiltinFunction(first) ), # TODO: add slice support "__getslice__": BuiltinName(BuiltinFunction(pyobjects.PyObject(self))), "__new__": BuiltinName(BuiltinFunction(function=self._new_tuple)), "__iter__": BuiltinName(BuiltinFunction(get_iterator(first))), } super(Tuple, self).__init__(tuple, attributes) def get_holding_objects(self): return self.objects def _new_tuple(self, args): return _create_builtin(args, get_tuple) get_tuple = _create_builtin_getter(Tuple) get_tuple_type = _create_builtin_type_getter(Tuple) class Set(BuiltinClass): def __init__(self, holding=None): self.holding = holding collector = _AttributeCollector(set) collector("__new__", function=self._new_set) self_methods = [ "copy", "difference", "intersection", "symmetric_difference", "union", ] for method in self_methods: collector(method, function=self._self_get, parent=self) collector("add", function=self._set_add, parent=self) collector("update", function=self._self_set, parent=self) collector("update", function=self._self_set, parent=self) collector("symmetric_difference_update", function=self._self_set, parent=self) collector("difference_update", function=self._self_set, parent=self) collector("pop", function=self._set_get, parent=self) collector("__iter__", function=self._iterator_get, parent=self) super(Set, self).__init__(set, collector.attributes) def _new_set(self, args): return _create_builtin(args, get_set) def _set_add(self, context): if self.holding is not None: return holding = context.get_arguments(["self", "value"])[1] if holding is not None and holding != pyobjects.get_unknown(): context.save_per_name(holding) def _self_set(self, context): if self.holding is not None: return iterable = context.get_pyname("iterable") holding = _infer_sequence_for_pyname(iterable) if holding is not None and holding != pyobjects.get_unknown(): context.save_per_name(holding) def _set_get(self, context): if self.holding is not None: return self.holding return context.get_per_name() def _iterator_get(self, context): return get_iterator(self._set_get(context)) def _self_get(self, context): return get_list(self._set_get(context)) get_set = _create_builtin_getter(Set) get_set_type = _create_builtin_type_getter(Set) class Str(BuiltinClass): def __init__(self): self_object = pyobjects.PyObject(self) collector = _AttributeCollector(str) collector("__iter__", get_iterator(self_object), check_existence=False) self_methods = [ "__getitem__", "capitalize", "center", "encode", "expandtabs", "join", "ljust", "lower", "lstrip", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "translate", "upper", "zfill", ] for method in self_methods: collector(method, self_object, parent=self) py2_self_methods = ["__getslice__", "decode"] for method in py2_self_methods: try: collector(method, self_object) except AttributeError: pass for method in ["rsplit", "split", "splitlines"]: collector(method, get_list(self_object), parent=self) super(Str, self).__init__(str, collector.attributes) def get_doc(self): return str.__doc__ get_str = _create_builtin_getter(Str) get_str_type = _create_builtin_type_getter(Str) class BuiltinName(pynames.PyName): def __init__(self, pyobject): self.pyobject = pyobject def get_object(self): return self.pyobject def get_definition_location(self): return (None, None) class Iterator(pyobjects.AbstractClass): def __init__(self, holding=None): super(Iterator, self).__init__() self.holding = holding self.attributes = { "next": BuiltinName(BuiltinFunction(self.holding)), "__iter__": BuiltinName(BuiltinFunction(self)), } def get_attributes(self): return self.attributes def get_returned_object(self, args): return self.holding get_iterator = _create_builtin_getter(Iterator) class Generator(pyobjects.AbstractClass): def __init__(self, holding=None): super(Generator, self).__init__() self.holding = holding self.attributes = { "next": BuiltinName(BuiltinFunction(self.holding)), "__iter__": BuiltinName(BuiltinFunction(get_iterator(self.holding))), "close": BuiltinName(BuiltinFunction()), "send": BuiltinName(BuiltinFunction()), "throw": BuiltinName(BuiltinFunction()), } def get_attributes(self): return self.attributes def get_returned_object(self, args): return self.holding get_generator = _create_builtin_getter(Generator) class File(BuiltinClass): def __init__(self, filename=None, mode="r", *args): self.filename = filename self.mode = mode self.args = args str_object = get_str() str_list = get_list(get_str()) attributes = {} def add(name, returned=None, function=None): builtin = getattr(io.TextIOBase, name, None) attributes[name] = BuiltinName( BuiltinFunction(returned=returned, function=function, builtin=builtin) ) add("__iter__", get_iterator(str_object)) add("__enter__", returned=pyobjects.PyObject(self)) for method in ["next", "read", "readline", "readlines"]: add(method, str_list) for method in [ "close", "flush", "lineno", "isatty", "seek", "tell", "truncate", "write", "writelines", ]: add(method) super(File, self).__init__(open, attributes) get_file = _create_builtin_getter(File) get_file_type = _create_builtin_type_getter(File) class Property(BuiltinClass): def __init__(self, fget=None, fset=None, fdel=None, fdoc=None): self._fget = fget self._fdoc = fdoc attributes = { "fget": BuiltinName(BuiltinFunction()), "fset": BuiltinName(pynames.UnboundName()), "fdel": BuiltinName(pynames.UnboundName()), "__new__": BuiltinName(BuiltinFunction(function=_property_function)), } super(Property, self).__init__(property, attributes) def get_property_object(self, args): if isinstance(self._fget, pyobjects.AbstractFunction): return self._fget.get_returned_object(args) def _property_function(args): parameters = args.get_arguments(["fget", "fset", "fdel", "fdoc"]) return pyobjects.PyObject(Property(parameters[0])) class Lambda(pyobjects.AbstractFunction): def __init__(self, node, scope): super(Lambda, self).__init__() self.node = node self.arguments = node.args self.scope = scope def get_returned_object(self, args): result = rope.base.evaluate.eval_node(self.scope, self.node.body) if result is not None: return result.get_object() else: return pyobjects.get_unknown() def get_module(self): return self.parent.get_module() def get_scope(self): return self.scope def get_kind(self): return "lambda" def get_ast(self): return self.node def get_attributes(self): return {} def get_name(self): return "lambda" def get_param_names(self, special_args=True): result = [ pycompat.get_ast_arg_arg(node) for node in self.arguments.args if isinstance(node, pycompat.ast_arg_type) ] if self.arguments.vararg: result.append("*" + pycompat.get_ast_arg_arg(self.arguments.vararg)) if self.arguments.kwarg: result.append("**" + pycompat.get_ast_arg_arg(self.arguments.kwarg)) return result @property def parent(self): return self.scope.pyobject class BuiltinObject(BuiltinClass): def __init__(self): super(BuiltinObject, self).__init__(object, {}) class BuiltinType(BuiltinClass): def __init__(self): super(BuiltinType, self).__init__(type, {}) def _infer_sequence_for_pyname(pyname): if pyname is None: return None seq = pyname.get_object() args = arguments.ObjectArguments([pyname]) if "__iter__" in seq: obj = seq["__iter__"].get_object() if not isinstance(obj, pyobjects.AbstractFunction): return None iter = obj.get_returned_object(args) if iter is not None and "next" in iter: holding = iter["next"].get_object().get_returned_object(args) return holding def _create_builtin(args, creator): passed = args.get_pynames(["sequence"])[0] if passed is None: holding = None else: holding = _infer_sequence_for_pyname(passed) if holding is not None: return creator(holding) else: return creator() def _open_function(args): return _create_builtin(args, get_file) def _range_function(args): return get_list() def _reversed_function(args): return _create_builtin(args, get_iterator) def _sorted_function(args): return _create_builtin(args, get_list) def _super_function(args): passed_class, passed_self = args.get_arguments(["type", "self"]) if passed_self is None: return passed_class else: # pyclass = passed_self.get_type() pyclass = passed_class if isinstance(pyclass, pyobjects.AbstractClass): supers = pyclass.get_superclasses() if supers: return pyobjects.PyObject(supers[0]) return passed_self def _zip_function(args): args = args.get_pynames(["sequence"]) objects = [] for seq in args: if seq is None: holding = None else: holding = _infer_sequence_for_pyname(seq) objects.append(holding) tuple = get_tuple(*objects) return get_list(tuple) def _enumerate_function(args): passed = args.get_pynames(["sequence"])[0] if passed is None: holding = None else: holding = _infer_sequence_for_pyname(passed) tuple = get_tuple(None, holding) return get_iterator(tuple) def _iter_function(args): passed = args.get_pynames(["sequence"])[0] if passed is None: holding = None else: holding = _infer_sequence_for_pyname(passed) return get_iterator(holding) def _input_function(args): return get_str() _initial_builtins = { "list": BuiltinName(get_list_type()), "dict": BuiltinName(get_dict_type()), "tuple": BuiltinName(get_tuple_type()), "set": BuiltinName(get_set_type()), "str": BuiltinName(get_str_type()), "file": BuiltinName(get_file_type()), "open": BuiltinName(BuiltinFunction(function=_open_function, builtin=open)), "unicode": BuiltinName(get_str_type()), "range": BuiltinName(BuiltinFunction(function=_range_function, builtin=range)), "reversed": BuiltinName( BuiltinFunction(function=_reversed_function, builtin=reversed) ), "sorted": BuiltinName(BuiltinFunction(function=_sorted_function, builtin=sorted)), "super": BuiltinName(BuiltinFunction(function=_super_function, builtin=super)), "property": BuiltinName( BuiltinFunction(function=_property_function, builtin=property) ), "zip": BuiltinName(BuiltinFunction(function=_zip_function, builtin=zip)), "enumerate": BuiltinName( BuiltinFunction(function=_enumerate_function, builtin=enumerate) ), "object": BuiltinName(BuiltinObject()), "type": BuiltinName(BuiltinType()), "iter": BuiltinName(BuiltinFunction(function=_iter_function, builtin=iter)), "raw_input": BuiltinName( BuiltinFunction(function=_input_function, builtin=raw_input) ), } builtins = BuiltinModule(pycompat.builtins.__name__, initial=_initial_builtins) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/base/change.py0000664000175000017500000003225600000000000016121 0ustar00lieryanlieryanimport datetime import difflib import os import time import rope.base.fscommands from rope.base import taskhandle, exceptions, utils class Change(object): """The base class for changes Rope refactorings return `Change` objects. They can be previewed, committed or undone. """ def do(self, job_set=None): """Perform the change .. note:: Do use this directly. Use `Project.do()` instead. """ def undo(self, job_set=None): """Perform the change .. note:: Do use this directly. Use `History.undo()` instead. """ def get_description(self): """Return the description of this change This can be used for previewing the changes. """ return str(self) def get_changed_resources(self): """Return the list of resources that will be changed""" return [] @property @utils.saveit def _operations(self): return _ResourceOperations(self.resource.project) class ChangeSet(Change): """A collection of `Change` objects This class holds a collection of changes. This class provides these fields: * `changes`: the list of changes * `description`: the goal of these changes """ def __init__(self, description, timestamp=None): self.changes = [] self.description = description self.time = timestamp def do(self, job_set=taskhandle.NullJobSet()): try: done = [] for change in self.changes: change.do(job_set) done.append(change) self.time = time.time() except Exception: for change in done: change.undo() raise def undo(self, job_set=taskhandle.NullJobSet()): try: done = [] for change in reversed(self.changes): change.undo(job_set) done.append(change) except Exception: for change in done: change.do() raise def add_change(self, change): self.changes.append(change) def get_description(self): result = [str(self) + ":\n\n\n"] for change in self.changes: result.append(change.get_description()) result.append("\n") return "".join(result) def __str__(self): if self.time is not None: date = datetime.datetime.fromtimestamp(self.time) if date.date() == datetime.date.today(): string_date = "today" elif date.date() == (datetime.date.today() - datetime.timedelta(1)): string_date = "yesterday" elif date.year == datetime.date.today().year: string_date = date.strftime("%b %d") else: string_date = date.strftime("%d %b, %Y") string_time = date.strftime("%H:%M:%S") string_time = "%s %s " % (string_date, string_time) return self.description + " - " + string_time return self.description def get_changed_resources(self): result = set() for change in self.changes: result.update(change.get_changed_resources()) return result def _handle_job_set(function): """A decorator for handling `taskhandle.JobSet` A decorator for handling `taskhandle.JobSet` for `do` and `undo` methods of `Change`. """ def call(self, job_set=taskhandle.NullJobSet()): job_set.started_job(str(self)) function(self) job_set.finished_job() return call class ChangeContents(Change): """A class to change the contents of a file Fields: * `resource`: The `rope.base.resources.File` to change * `new_contents`: What to write in the file """ def __init__(self, resource, new_contents, old_contents=None): self.resource = resource # IDEA: Only saving diffs; possible problems when undo/redoing self.new_contents = new_contents self.old_contents = old_contents @_handle_job_set def do(self): if self.old_contents is None: self.old_contents = self.resource.read() self._operations.write_file(self.resource, self.new_contents) @_handle_job_set def undo(self): if self.old_contents is None: raise exceptions.HistoryError("Undoing a change that is not performed yet!") self._operations.write_file(self.resource, self.old_contents) def __str__(self): return "Change <%s>" % self.resource.path def get_description(self): new = self.new_contents old = self.old_contents if old is None: if self.resource.exists(): old = self.resource.read() else: old = "" result = difflib.unified_diff( old.splitlines(True), new.splitlines(True), "a/" + self.resource.path, "b/" + self.resource.path, ) return "".join(list(result)) def get_changed_resources(self): return [self.resource] class MoveResource(Change): """Move a resource to a new location Fields: * `resource`: The `rope.base.resources.Resource` to move * `new_resource`: The destination for move; It is the moved resource not the folder containing that resource. """ def __init__(self, resource, new_location, exact=False): self.project = resource.project self.resource = resource if not exact: new_location = _get_destination_for_move(resource, new_location) if resource.is_folder(): self.new_resource = self.project.get_folder(new_location) else: self.new_resource = self.project.get_file(new_location) @_handle_job_set def do(self): self._operations.move(self.resource, self.new_resource) @_handle_job_set def undo(self): self._operations.move(self.new_resource, self.resource) def __str__(self): return "Move <%s>" % self.resource.path def get_description(self): return "rename from %s\nrename to %s" % ( self.resource.path, self.new_resource.path, ) def get_changed_resources(self): return [self.resource, self.new_resource] class CreateResource(Change): """A class to create a resource Fields: * `resource`: The resource to create """ def __init__(self, resource): self.resource = resource @_handle_job_set def do(self): self._operations.create(self.resource) @_handle_job_set def undo(self): self._operations.remove(self.resource) def __str__(self): return "Create Resource <%s>" % (self.resource.path) def get_description(self): return "new file %s" % (self.resource.path) def get_changed_resources(self): return [self.resource] def _get_child_path(self, parent, name): if parent.path == "": return name else: return parent.path + "/" + name class CreateFolder(CreateResource): """A class to create a folder See docs for `CreateResource`. """ def __init__(self, parent, name): resource = parent.project.get_folder(self._get_child_path(parent, name)) super(CreateFolder, self).__init__(resource) class CreateFile(CreateResource): """A class to create a file See docs for `CreateResource`. """ def __init__(self, parent, name): resource = parent.project.get_file(self._get_child_path(parent, name)) super(CreateFile, self).__init__(resource) class RemoveResource(Change): """A class to remove a resource Fields: * `resource`: The resource to be removed """ def __init__(self, resource): self.resource = resource @_handle_job_set def do(self): self._operations.remove(self.resource) # TODO: Undoing remove operations @_handle_job_set def undo(self): raise NotImplementedError("Undoing `RemoveResource` is not implemented yet.") def __str__(self): return "Remove <%s>" % (self.resource.path) def get_changed_resources(self): return [self.resource] def count_changes(change): """Counts the number of basic changes a `Change` will make""" if isinstance(change, ChangeSet): result = 0 for child in change.changes: result += count_changes(child) return result return 1 def create_job_set(task_handle, change): return task_handle.create_jobset(str(change), count_changes(change)) class _ResourceOperations(object): def __init__(self, project): self.project = project self.fscommands = project.fscommands self.direct_commands = rope.base.fscommands.FileSystemCommands() def _get_fscommands(self, resource): if self.project.is_ignored(resource): return self.direct_commands return self.fscommands def write_file(self, resource, contents): data = rope.base.fscommands.unicode_to_file_data(contents) fscommands = self._get_fscommands(resource) fscommands.write(resource.real_path, data) for observer in list(self.project.observers): observer.resource_changed(resource) def move(self, resource, new_resource): fscommands = self._get_fscommands(resource) fscommands.move(resource.real_path, new_resource.real_path) for observer in list(self.project.observers): observer.resource_moved(resource, new_resource) def create(self, resource): if resource.is_folder(): self._create_resource(resource.path, kind="folder") else: self._create_resource(resource.path) for observer in list(self.project.observers): observer.resource_created(resource) def remove(self, resource): fscommands = self._get_fscommands(resource) fscommands.remove(resource.real_path) for observer in list(self.project.observers): observer.resource_removed(resource) def _create_resource(self, file_name, kind="file"): resource_path = self.project._get_resource_path(file_name) if os.path.exists(resource_path): raise exceptions.RopeError("Resource <%s> already exists" % resource_path) resource = self.project.get_file(file_name) if not resource.parent.exists(): raise exceptions.ResourceNotFoundError( "Parent folder of <%s> does not exist" % resource.path ) fscommands = self._get_fscommands(resource) try: if kind == "file": fscommands.create_file(resource_path) else: fscommands.create_folder(resource_path) except IOError as e: raise exceptions.RopeError(e) def _get_destination_for_move(resource, destination): dest_path = resource.project._get_resource_path(destination) if os.path.isdir(dest_path): if destination != "": return destination + "/" + resource.name else: return resource.name return destination class ChangeToData(object): def convertChangeSet(self, change): description = change.description changes = [] for child in change.changes: changes.append(self(child)) return (description, changes, change.time) def convertChangeContents(self, change): return (change.resource.path, change.new_contents, change.old_contents) def convertMoveResource(self, change): return (change.resource.path, change.new_resource.path) def convertCreateResource(self, change): return (change.resource.path, change.resource.is_folder()) def convertRemoveResource(self, change): return (change.resource.path, change.resource.is_folder()) def __call__(self, change): change_type = type(change) if change_type in (CreateFolder, CreateFile): change_type = CreateResource method = getattr(self, "convert" + change_type.__name__) return (change_type.__name__, method(change)) class DataToChange(object): def __init__(self, project): self.project = project def makeChangeSet(self, description, changes, time=None): result = ChangeSet(description, time) for child in changes: result.add_change(self(child)) return result def makeChangeContents(self, path, new_contents, old_contents): resource = self.project.get_file(path) return ChangeContents(resource, new_contents, old_contents) def makeMoveResource(self, old_path, new_path): resource = self.project.get_file(old_path) return MoveResource(resource, new_path, exact=True) def makeCreateResource(self, path, is_folder): if is_folder: resource = self.project.get_folder(path) else: resource = self.project.get_file(path) return CreateResource(resource) def makeRemoveResource(self, path, is_folder): if is_folder: resource = self.project.get_folder(path) else: resource = self.project.get_file(path) return RemoveResource(resource) def __call__(self, data): method = getattr(self, "make" + data[0]) return method(*data[1]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633864767.0 rope-0.22.0/rope/base/codeanalyze.py0000664000175000017500000002706600000000000017175 0ustar00lieryanlieryanimport bisect import re import token import tokenize class ChangeCollector(object): def __init__(self, text): self.text = text self.changes = [] def add_change(self, start, end, new_text=None): if new_text is None: new_text = self.text[start:end] self.changes.append((start, end, new_text)) def get_changed(self): if not self.changes: return None self.changes.sort(key=lambda x: x[:2]) pieces = [] last_changed = 0 for change in self.changes: start, end, text = change pieces.append(self.text[last_changed:start] + text) last_changed = end if last_changed < len(self.text): pieces.append(self.text[last_changed:]) result = "".join(pieces) if result != self.text: return result class SourceLinesAdapter(object): """Adapts source to Lines interface Note: The creation of this class is expensive. """ def __init__(self, source_code): self.code = source_code self.starts = None self._initialize_line_starts() def _initialize_line_starts(self): self.starts = [] self.starts.append(0) try: i = 0 while True: i = self.code.index("\n", i) + 1 self.starts.append(i) except ValueError: pass self.starts.append(len(self.code) + 1) def get_line(self, lineno): return self.code[self.starts[lineno - 1] : self.starts[lineno] - 1] def length(self): return len(self.starts) - 1 def get_line_number(self, offset): return bisect.bisect(self.starts, offset) def get_line_start(self, lineno): return self.starts[lineno - 1] def get_line_end(self, lineno): return self.starts[lineno] - 1 class ArrayLinesAdapter(object): def __init__(self, lines): self.lines = lines def get_line(self, line_number): return self.lines[line_number - 1] def length(self): return len(self.lines) class LinesToReadline(object): def __init__(self, lines, start): self.lines = lines self.current = start def readline(self): if self.current <= self.lines.length(): self.current += 1 return self.lines.get_line(self.current - 1) + "\n" return "" def __call__(self): return self.readline() class _CustomGenerator(object): def __init__(self, lines): self.lines = lines self.in_string = "" self.open_count = 0 self.continuation = False def __call__(self): size = self.lines.length() result = [] i = 1 while i <= size: while i <= size and not self.lines.get_line(i).strip(): i += 1 if i <= size: start = i while True: line = self.lines.get_line(i) self._analyze_line(line) if ( not (self.continuation or self.open_count or self.in_string) or i == size ): break i += 1 result.append((start, i)) i += 1 return result # Matches all backslashes before the token, to detect escaped quotes _main_tokens = re.compile(r'(\\*)((\'\'\'|"""|\'|")|#|\[|\]|\{|\}|\(|\))') def _analyze_line(self, line): token = None for match in self._main_tokens.finditer(line): prefix = match.group(1) token = match.group(2) # Skip any tokens which are escaped if len(prefix) % 2 == 1: continue if token in ["'''", '"""', "'", '"']: if not self.in_string: self.in_string = token elif self.in_string == token or ( self.in_string in ['"', "'"] and token == 3 * self.in_string ): self.in_string = "" if self.in_string: continue if token == "#": break if token in "([{": self.open_count += 1 elif token in ")]}": self.open_count -= 1 if line and token != "#" and line.endswith("\\"): self.continuation = True else: self.continuation = False def custom_generator(lines): return _CustomGenerator(lines)() class LogicalLineFinder(object): def __init__(self, lines): self.lines = lines def logical_line_in(self, line_number): indents = count_line_indents(self.lines.get_line(line_number)) tries = 0 while True: block_start = get_block_start(self.lines, line_number, indents) try: return self._block_logical_line(block_start, line_number) except IndentationError as e: tries += 1 if tries == 5: raise e lineno = e.lineno + block_start - 1 indents = count_line_indents(self.lines.get_line(lineno)) def generate_starts(self, start_line=1, end_line=None): for start, end in self.generate_regions(start_line, end_line): yield start def generate_regions(self, start_line=1, end_line=None): # XXX: `block_start` should be at a better position! block_start = 1 readline = LinesToReadline(self.lines, block_start) try: for start, end in self._logical_lines(readline): real_start = start + block_start - 1 real_start = self._first_non_blank(real_start) if end_line is not None and real_start >= end_line: break real_end = end + block_start - 1 if real_start >= start_line: yield (real_start, real_end) except tokenize.TokenError: pass def _block_logical_line(self, block_start, line_number): readline = LinesToReadline(self.lines, block_start) shifted = line_number - block_start + 1 region = self._calculate_logical(readline, shifted) start = self._first_non_blank(region[0] + block_start - 1) if region[1] is None: end = self.lines.length() else: end = region[1] + block_start - 1 return start, end def _calculate_logical(self, readline, line_number): last_end = 1 try: for start, end in self._logical_lines(readline): if line_number <= end: return (start, end) last_end = end + 1 except tokenize.TokenError as e: current = e.args[1][0] return (last_end, max(last_end, current - 1)) return (last_end, None) def _logical_lines(self, readline): last_end = 1 for current_token in tokenize.generate_tokens(readline): current = current_token[2][0] if current_token[0] == token.NEWLINE: yield (last_end, current) last_end = current + 1 def _first_non_blank(self, line_number): current = line_number while current < self.lines.length(): line = self.lines.get_line(current).strip() if line and not line.startswith("#"): return current current += 1 return current def tokenizer_generator(lines): return LogicalLineFinder(lines).generate_regions() class CachingLogicalLineFinder(object): def __init__(self, lines, generate=custom_generator): self.lines = lines self._generate = generate _starts = None @property def starts(self): if self._starts is None: self._init_logicals() return self._starts _ends = None @property def ends(self): if self._ends is None: self._init_logicals() return self._ends def _init_logicals(self): """Should initialize _starts and _ends attributes""" size = self.lines.length() + 1 self._starts = [None] * size self._ends = [None] * size for start, end in self._generate(self.lines): self._starts[start] = True self._ends[end] = True def logical_line_in(self, line_number): start = line_number while start > 0 and not self.starts[start]: start -= 1 if start == 0: try: start = self.starts.index(True, line_number) except ValueError: return (line_number, line_number) return (start, self.ends.index(True, start)) def generate_starts(self, start_line=1, end_line=None): if end_line is None: end_line = self.lines.length() for index in range(start_line, end_line): if self.starts[index]: yield index def get_block_start(lines, lineno, maximum_indents=80): """Approximate block start""" pattern = get_block_start_patterns() for i in range(lineno, 0, -1): match = pattern.search(lines.get_line(i)) if ( match is not None and count_line_indents(lines.get_line(i)) <= maximum_indents ): striped = match.string.lstrip() # Maybe we're in a list comprehension or generator expression if i > 1 and striped.startswith("if") or striped.startswith("for"): bracs = 0 for j in range(i, min(i + 5, lines.length() + 1)): for c in lines.get_line(j): if c == "#": break if c in "[(": bracs += 1 if c in ")]": bracs -= 1 if bracs < 0: break if bracs < 0: break if bracs < 0: continue return i return 1 _block_start_pattern = None def get_block_start_patterns(): global _block_start_pattern if not _block_start_pattern: pattern = ( "^\\s*(((def|class|if|elif|except|for|while|with)\\s)|" "((try|else|finally|except)\\s*:))" ) _block_start_pattern = re.compile(pattern, re.M) return _block_start_pattern def count_line_indents(line): indents = 0 for char in line: if char == " ": indents += 1 elif char == "\t": indents += 8 else: return indents return 0 def get_string_pattern_with_prefix(prefix, prefix_group_name=None): longstr = r'"""(\\.|"(?!"")|\\\n|[^"\\])*"""' shortstr = r'"(\\.|\\\n|[^"\\\n])*"' if prefix_group_name is not None: pattern = "(?P<%s>%%s)(%%s)" % prefix_group_name else: pattern = "%s(%s)" return pattern % ( prefix, "|".join( [ longstr, longstr.replace('"', "'"), shortstr, shortstr.replace('"', "'"), ] ), ) def get_string_pattern(): prefix = r"(? import ` 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! ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633802493.0 rope-0.22.0/rope/base/evaluate.py0000664000175000017500000003166600000000000016506 0ustar00lieryanlieryanfrom operator import itemgetter import rope.base.builtins import rope.base.pynames import rope.base.pyobjects from rope.base import ast, astutils, exceptions, pyobjects, arguments, worder from rope.base.utils import pycompat BadIdentifierError = exceptions.BadIdentifierError def eval_location(pymodule, offset): """Find the pyname at the offset""" return eval_location2(pymodule, offset)[1] def eval_location2(pymodule, offset): """Find the primary and pyname at offset""" pyname_finder = ScopeNameFinder(pymodule) return pyname_finder.get_primary_and_pyname_at(offset) def eval_node(scope, node): """Evaluate a `ast.AST` node and return a PyName Return `None` if the expression cannot be evaluated. """ return eval_node2(scope, node)[1] def eval_node2(scope, node): evaluator = StatementEvaluator(scope) ast.walk(node, evaluator) return evaluator.old_result, evaluator.result def eval_str(holding_scope, name): return eval_str2(holding_scope, name)[1] def eval_str2(holding_scope, name): try: # parenthesizing for handling cases like 'a_var.\nattr' node = ast.parse("(%s)" % name) except SyntaxError: raise BadIdentifierError("Not a resolvable python identifier selected.") return eval_node2(holding_scope, node) class ScopeNameFinder(object): def __init__(self, pymodule): self.module_scope = pymodule.get_scope() self.lines = pymodule.lines self.worder = worder.Worder(pymodule.source_code, True) def _is_defined_in_class_body(self, holding_scope, offset, lineno): if ( lineno == holding_scope.get_start() and holding_scope.parent is not None and holding_scope.parent.get_kind() == "Class" and self.worder.is_a_class_or_function_name_in_header(offset) ): return True if ( lineno != holding_scope.get_start() and holding_scope.get_kind() == "Class" and self.worder.is_name_assigned_in_class_body(offset) ): return True return False def _is_function_name_in_function_header(self, scope, offset, lineno): if ( scope.get_start() <= lineno <= scope.get_body_start() and scope.get_kind() == "Function" and self.worder.is_a_class_or_function_name_in_header(offset) ): return True return False def get_pyname_at(self, offset): return self.get_primary_and_pyname_at(offset)[1] def get_primary_and_pyname_at(self, offset): lineno = self.lines.get_line_number(offset) holding_scope = self.module_scope.get_inner_scope_for_offset(offset) # function keyword parameter if self.worder.is_function_keyword_parameter(offset): keyword_name = self.worder.get_word_at(offset) pyobject = self.get_enclosing_function(offset) if isinstance(pyobject, pyobjects.PyFunction): return (None, pyobject.get_parameters().get(keyword_name, None)) # class body if self._is_defined_in_class_body(holding_scope, offset, lineno): class_scope = holding_scope if lineno == holding_scope.get_start(): class_scope = holding_scope.parent name = self.worder.get_primary_at(offset).strip() try: return (None, class_scope.pyobject[name]) except rope.base.exceptions.AttributeNotFoundError: return (None, None) # function header if self._is_function_name_in_function_header(holding_scope, offset, lineno): name = self.worder.get_primary_at(offset).strip() return (None, holding_scope.parent[name]) # module in a from statement or an imported name that is aliased if self.worder.is_from_statement_module( offset ) or self.worder.is_import_statement_aliased_module(offset): module = self.worder.get_primary_at(offset) module_pyname = self._find_module(module) return (None, module_pyname) if self.worder.is_from_aliased(offset): name = self.worder.get_from_aliased(offset) else: name = self.worder.get_primary_at(offset) return eval_str2(holding_scope, name) def get_enclosing_function(self, offset): function_parens = self.worder.find_parens_start_from_inside(offset) try: function_pyname = self.get_pyname_at(function_parens - 1) except BadIdentifierError: function_pyname = None if function_pyname is not None: pyobject = function_pyname.get_object() if isinstance(pyobject, pyobjects.AbstractFunction): return pyobject elif ( isinstance(pyobject, pyobjects.AbstractClass) and "__init__" in pyobject ): return pyobject["__init__"].get_object() elif "__call__" in pyobject: return pyobject["__call__"].get_object() return None def _find_module(self, module_name): dots = 0 while module_name[dots] == ".": dots += 1 return rope.base.pynames.ImportedModule( self.module_scope.pyobject, module_name[dots:], dots ) class StatementEvaluator(object): def __init__(self, scope): self.scope = scope self.result = None self.old_result = None def _Name(self, node): self.result = self.scope.lookup(node.id) def _Attribute(self, node): pyname = eval_node(self.scope, node.value) if pyname is None: pyname = rope.base.pynames.UnboundName() self.old_result = pyname if pyname.get_object() != rope.base.pyobjects.get_unknown(): try: self.result = pyname.get_object()[node.attr] except exceptions.AttributeNotFoundError: self.result = None def _Call(self, node): primary, pyobject = self._get_primary_and_object_for_node(node.func) if pyobject is None: return def _get_returned(pyobject): args = arguments.create_arguments(primary, pyobject, node, self.scope) return pyobject.get_returned_object(args) if isinstance(pyobject, rope.base.pyobjects.AbstractClass): result = None if "__new__" in pyobject: new_function = pyobject["__new__"].get_object() result = _get_returned(new_function) if result is None or result == rope.base.pyobjects.get_unknown(): result = rope.base.pyobjects.PyObject(pyobject) self.result = rope.base.pynames.UnboundName(pyobject=result) return pyfunction = None if isinstance(pyobject, rope.base.pyobjects.AbstractFunction): pyfunction = pyobject elif "__call__" in pyobject: pyfunction = pyobject["__call__"].get_object() if pyfunction is not None: self.result = rope.base.pynames.UnboundName( pyobject=_get_returned(pyfunction) ) def _Str(self, node): self.result = rope.base.pynames.UnboundName( pyobject=rope.base.builtins.get_str() ) def _Num(self, node): type_name = type(node.n).__name__ self.result = self._get_builtin_name(type_name) def _Constant(self, node): type_name = type(node.n).__name__ try: self.result = self._get_builtin_name(type_name) except exceptions.AttributeNotFoundError: # XXX: Right way to fix this is to add missing NoneType to builtins? pass def _get_builtin_name(self, type_name): pytype = rope.base.builtins.builtins[type_name].get_object() return rope.base.pynames.UnboundName(rope.base.pyobjects.PyObject(pytype)) def _BinOp(self, node): self.result = rope.base.pynames.UnboundName( self._get_object_for_node(node.left) ) def _BoolOp(self, node): pyobject = self._get_object_for_node(node.values[0]) if pyobject is None: pyobject = self._get_object_for_node(node.values[1]) self.result = rope.base.pynames.UnboundName(pyobject) def _Repr(self, node): self.result = self._get_builtin_name("str") def _UnaryOp(self, node): self.result = rope.base.pynames.UnboundName( self._get_object_for_node(node.operand) ) def _Compare(self, node): self.result = self._get_builtin_name("bool") def _Dict(self, node): keys = None values = None if node.keys and node.keys[0]: keys, values = next( iter(filter(itemgetter(0), zip(node.keys, node.values))), (None, None) ) if keys: keys = self._get_object_for_node(keys) if values: values = self._get_object_for_node(values) self.result = rope.base.pynames.UnboundName( pyobject=rope.base.builtins.get_dict(keys, values) ) def _List(self, node): holding = None if node.elts: holding = self._get_object_for_node(node.elts[0]) self.result = rope.base.pynames.UnboundName( pyobject=rope.base.builtins.get_list(holding) ) def _ListComp(self, node): pyobject = self._what_does_comprehension_hold(node) self.result = rope.base.pynames.UnboundName( pyobject=rope.base.builtins.get_list(pyobject) ) def _GeneratorExp(self, node): pyobject = self._what_does_comprehension_hold(node) self.result = rope.base.pynames.UnboundName( pyobject=rope.base.builtins.get_iterator(pyobject) ) def _what_does_comprehension_hold(self, node): scope = self._make_comprehension_scope(node) pyname = eval_node(scope, node.elt) return pyname.get_object() if pyname is not None else None def _make_comprehension_scope(self, node): scope = self.scope module = scope.pyobject.get_module() names = {} for comp in node.generators: new_names = _get_evaluated_names( comp.target, comp.iter, module, ".__iter__().next()", node.lineno ) names.update(new_names) return rope.base.pyscopes.TemporaryScope(scope.pycore, scope, names) def _Tuple(self, node): objects = [] if len(node.elts) < 4: for stmt in node.elts: pyobject = self._get_object_for_node(stmt) objects.append(pyobject) else: objects.append(self._get_object_for_node(node.elts[0])) self.result = rope.base.pynames.UnboundName( pyobject=rope.base.builtins.get_tuple(*objects) ) def _get_object_for_node(self, stmt): pyname = eval_node(self.scope, stmt) pyobject = None if pyname is not None: pyobject = pyname.get_object() return pyobject def _get_primary_and_object_for_node(self, stmt): primary, pyname = eval_node2(self.scope, stmt) pyobject = None if pyname is not None: pyobject = pyname.get_object() return primary, pyobject def _Subscript(self, node): if isinstance(node.slice, ast.Index): self._call_function(node.value, "__getitem__", [node.slice.value]) elif isinstance(node.slice, ast.Slice): self._call_function(node.value, "__getitem__", [node.slice]) elif isinstance(node.slice, ast.expr): self._call_function(node.value, "__getitem__", [node.value]) def _Slice(self, node): self.result = self._get_builtin_name("slice") def _call_function(self, node, function_name, other_args=None): pyname = eval_node(self.scope, node) if pyname is not None: pyobject = pyname.get_object() else: return if function_name in pyobject: called = pyobject[function_name].get_object() if not called or not isinstance(called, pyobjects.AbstractFunction): return args = [node] if other_args: args += other_args arguments_ = arguments.Arguments(args, self.scope) self.result = rope.base.pynames.UnboundName( pyobject=called.get_returned_object(arguments_) ) def _Lambda(self, node): self.result = rope.base.pynames.UnboundName( pyobject=rope.base.builtins.Lambda(node, self.scope) ) def _get_evaluated_names(targets, assigned, module, evaluation, lineno): result = {} for name, levels in astutils.get_name_levels(targets): assignment = rope.base.pynames.AssignmentValue(assigned, levels, evaluation) # XXX: this module should not access `rope.base.pynamesdef`! pyname = rope.base.pynamesdef.AssignedName(lineno, module) pyname.assignments.append(assignment) result[name] = pyname return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/base/exceptions.py0000664000175000017500000000265400000000000017054 0ustar00lieryanlieryanclass RopeError(Exception): """Base exception for rope""" class ResourceNotFoundError(RopeError): """Resource not found exception""" class RefactoringError(RopeError): """Errors for performing a refactoring""" class InterruptedTaskError(RopeError): """The task has been interrupted""" class HistoryError(RopeError): """Errors for history undo/redo operations""" class ModuleNotFoundError(RopeError): """Module not found exception""" class AttributeNotFoundError(RopeError): """Attribute not found exception""" class NameNotFoundError(RopeError): """Name not found exception""" class BadIdentifierError(RopeError): """The name cannot be resolved""" class ModuleSyntaxError(RopeError): """Module has syntax errors The `filename` and `lineno` fields indicate where the error has occurred. """ def __init__(self, filename, lineno, message): self.filename = filename self.lineno = lineno self.message_ = message super(ModuleSyntaxError, self).__init__( "Syntax error in file <%s> line <%s>: %s" % (filename, lineno, message) ) class ModuleDecodeError(RopeError): """Cannot decode module""" def __init__(self, filename, message): self.filename = filename self.message_ = message super(ModuleDecodeError, self).__init__( "Cannot decode file <%s>: %s" % (filename, message) ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636636936.0 rope-0.22.0/rope/base/fscommands.py0000664000175000017500000002011600000000000017016 0ustar00lieryanlieryan"""Project file system commands. This modules implements file system operations used by rope. Different version control systems can be supported by implementing the interface provided by `FileSystemCommands` class. See `SubversionCommands` and `MercurialCommands` for example. """ import re import os import shutil import subprocess import rope.base.utils.pycompat as pycompat try: unicode except NameError: unicode = str def create_fscommands(root): dirlist = os.listdir(root) commands = { ".hg": MercurialCommands, ".svn": SubversionCommands, ".git": GITCommands, "_svn": SubversionCommands, "_darcs": DarcsCommands, } for key in commands: if key in dirlist: try: return commands[key](root) except (ImportError, OSError): pass return FileSystemCommands() class FileSystemCommands(object): def create_file(self, path): open(path, "w").close() def create_folder(self, path): os.mkdir(path) def move(self, path, new_location): shutil.move(path, new_location) def remove(self, path): if os.path.isfile(path): os.remove(path) else: shutil.rmtree(path) def write(self, path, data): file_ = open(path, "wb") try: file_.write(data) finally: file_.close() def read(self, path): with open(path, "rb") as handle: return handle.read() class SubversionCommands(object): def __init__(self, *args): self.normal_actions = FileSystemCommands() import pysvn self.client = pysvn.Client() def create_file(self, path): self.normal_actions.create_file(path) self.client.add(path, force=True) def create_folder(self, path): self.normal_actions.create_folder(path) self.client.add(path, force=True) def move(self, path, new_location): self.client.move(path, new_location, force=True) def remove(self, path): self.client.remove(path, force=True) def write(self, path, data): self.normal_actions.write(path, data) def read(self, path): return self.normal_actions.read(path) class MercurialCommands(object): def __init__(self, root): self.hg = self._import_mercurial() self.normal_actions = FileSystemCommands() try: self.ui = self.hg.ui.ui( verbose=False, debug=False, quiet=True, interactive=False, traceback=False, report_untrusted=False, ) except: self.ui = self.hg.ui.ui() self.ui.setconfig("ui", "interactive", "no") self.ui.setconfig("ui", "debug", "no") self.ui.setconfig("ui", "traceback", "no") self.ui.setconfig("ui", "verbose", "no") self.ui.setconfig("ui", "report_untrusted", "no") self.ui.setconfig("ui", "quiet", "yes") self.repo = self.hg.hg.repository(self.ui, root) def _import_mercurial(self): import mercurial.commands import mercurial.hg import mercurial.ui return mercurial def create_file(self, path): self.normal_actions.create_file(path) self.hg.commands.add(self.ui, self.repo, path) def create_folder(self, path): self.normal_actions.create_folder(path) def move(self, path, new_location): self.hg.commands.rename(self.ui, self.repo, path, new_location, after=False) def remove(self, path): self.hg.commands.remove(self.ui, self.repo, path) def write(self, path, data): self.normal_actions.write(path, data) def read(self, path): return self.normal_actions.read(path) class GITCommands(object): def __init__(self, root): self.root = root self._do(["version"]) self.normal_actions = FileSystemCommands() def create_file(self, path): self.normal_actions.create_file(path) self._do(["add", self._in_dir(path)]) def create_folder(self, path): self.normal_actions.create_folder(path) def move(self, path, new_location): self._do(["mv", self._in_dir(path), self._in_dir(new_location)]) def remove(self, path): self._do(["rm", self._in_dir(path)]) def write(self, path, data): # XXX: should we use ``git add``? self.normal_actions.write(path, data) def read(self, path): return self.normal_actions.read(path) def _do(self, args): _execute(["git"] + args, cwd=self.root) def _in_dir(self, path): if path.startswith(self.root): return path[len(self.root) + 1 :] return self.root class DarcsCommands(object): def __init__(self, root): self.root = root self.normal_actions = FileSystemCommands() def create_file(self, path): self.normal_actions.create_file(path) self._do(["add", path]) def create_folder(self, path): self.normal_actions.create_folder(path) self._do(["add", path]) def move(self, path, new_location): self._do(["mv", path, new_location]) def remove(self, path): self.normal_actions.remove(path) def read(self, path): return self.normal_actions.read(path) def write(self, path, data): self.normal_actions.write(path, data) def _do(self, args): _execute(["darcs"] + args, cwd=self.root) def _execute(args, cwd=None): process = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE) process.wait() return process.returncode def unicode_to_file_data(contents, encoding=None): if not isinstance(contents, unicode): return contents if encoding is None: encoding = read_str_coding(contents) if encoding is not None: return contents.encode(encoding) try: return contents.encode() except UnicodeEncodeError: return contents.encode("utf-8") def file_data_to_unicode(data, encoding=None): result = _decode_data(data, encoding) if "\r" in result: result = result.replace("\r\n", "\n").replace("\r", "\n") return result def _decode_data(data, encoding): if isinstance(data, unicode): return data if encoding is None: encoding = read_str_coding(data) if encoding is None: # there is no encoding tip, we need to guess. # PEP263 says that "encoding not explicitly defined" means it is ascii, # but we will use utf8 instead since utf8 fully covers ascii and btw is # the only non-latin sane encoding. encoding = "utf-8" try: return data.decode(encoding) except (UnicodeError, LookupError): # fallback to latin1: it should never fail return data.decode("latin1") def read_str_coding(source): # as defined by PEP-263 (https://www.python.org/dev/peps/pep-0263/) CODING_LINE_PATTERN = b"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)" if type(source) == bytes: newline = b"\n" CODING_LINE_PATTERN = re.compile(CODING_LINE_PATTERN) else: newline = "\n" CODING_LINE_PATTERN = re.compile(CODING_LINE_PATTERN.decode("ascii")) for line in source.split(newline, 2)[:2]: if re.match(CODING_LINE_PATTERN, line): return _find_coding(line) else: return def _find_coding(text): if isinstance(text, pycompat.str): text = text.encode("utf-8") coding = b"coding" to_chr = chr if pycompat.PY3 else lambda x: x try: start = text.index(coding) + len(coding) if text[start] not in b"=:": return start += 1 while start < len(text) and to_chr(text[start]).isspace(): start += 1 end = start while end < len(text): c = text[end] if not to_chr(c).isalnum() and c not in b"-_": break end += 1 result = text[start:end] if isinstance(result, bytes): result = result.decode("utf-8") return result except ValueError: pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/history.py0000664000175000017500000002037500000000000016374 0ustar00lieryanlieryanfrom rope.base import exceptions, change, taskhandle class History(object): """A class that holds project history""" def __init__(self, project, maxundos=None): self.project = project self._undo_list = [] self._redo_list = [] self._maxundos = maxundos self._load_history() self.project.data_files.add_write_hook(self.write) self.current_change = None def _load_history(self): if self.save: result = self.project.data_files.read_data( "history", compress=self.compress, import_=True ) if result is not None: to_change = change.DataToChange(self.project) for data in result[0]: self._undo_list.append(to_change(data)) for data in result[1]: self._redo_list.append(to_change(data)) def do(self, changes, task_handle=taskhandle.NullTaskHandle()): """Perform the change and add it to the `self.undo_list` Note that uninteresting changes (changes to ignored files) will not be appended to `self.undo_list`. """ try: self.current_change = changes changes.do(change.create_job_set(task_handle, changes)) finally: self.current_change = None if self._is_change_interesting(changes): self.undo_list.append(changes) self._remove_extra_items() del self.redo_list[:] def _remove_extra_items(self): if len(self.undo_list) > self.max_undos: del self.undo_list[0 : len(self.undo_list) - self.max_undos] def _is_change_interesting(self, changes): for resource in changes.get_changed_resources(): if not self.project.is_ignored(resource): return True return False def undo(self, change=None, drop=False, task_handle=taskhandle.NullTaskHandle()): """Redo done changes from the history When `change` is `None`, the last done change will be undone. If change is not `None` it should be an item from `self.undo_list`; this change and all changes that depend on it will be undone. In both cases the list of undone changes will be returned. If `drop` is `True`, the undone change will not be appended to the redo list. """ if not self._undo_list: raise exceptions.HistoryError("Undo list is empty") if change is None: change = self.undo_list[-1] dependencies = self._find_dependencies(self.undo_list, change) self._move_front(self.undo_list, dependencies) self._perform_undos(len(dependencies), task_handle) result = self.redo_list[-len(dependencies) :] if drop: del self.redo_list[-len(dependencies) :] return result def redo(self, change=None, task_handle=taskhandle.NullTaskHandle()): """Redo undone changes from the history When `change` is `None`, the last undone change will be redone. If change is not `None` it should be an item from `self.redo_list`; this change and all changes that depend on it will be redone. In both cases the list of redone changes will be returned. """ if not self.redo_list: raise exceptions.HistoryError("Redo list is empty") if change is None: change = self.redo_list[-1] dependencies = self._find_dependencies(self.redo_list, change) self._move_front(self.redo_list, dependencies) self._perform_redos(len(dependencies), task_handle) return self.undo_list[-len(dependencies) :] def _move_front(self, change_list, changes): for change in changes: change_list.remove(change) change_list.append(change) def _find_dependencies(self, change_list, change): index = change_list.index(change) return _FindChangeDependencies(change_list[index:])() def _perform_undos(self, count, task_handle): for i in range(count): self.current_change = self.undo_list[-1] try: job_set = change.create_job_set(task_handle, self.current_change) self.current_change.undo(job_set) finally: self.current_change = None self.redo_list.append(self.undo_list.pop()) def _perform_redos(self, count, task_handle): for i in range(count): self.current_change = self.redo_list[-1] try: job_set = change.create_job_set(task_handle, self.current_change) self.current_change.do(job_set) finally: self.current_change = None self.undo_list.append(self.redo_list.pop()) def contents_before_current_change(self, file): if self.current_change is None: return None result = self._search_for_change_contents([self.current_change], file) if result is not None: return result if file.exists() and not file.is_folder(): return file.read() else: return None def _search_for_change_contents(self, change_list, file): for change_ in reversed(change_list): if isinstance(change_, change.ChangeSet): result = self._search_for_change_contents(change_.changes, file) if result is not None: return result if isinstance(change_, change.ChangeContents) and change_.resource == file: return change_.old_contents def write(self): if self.save: data = [] to_data = change.ChangeToData() self._remove_extra_items() data.append([to_data(change_) for change_ in self.undo_list]) data.append([to_data(change_) for change_ in self.redo_list]) self.project.data_files.write_data("history", data, compress=self.compress) def get_file_undo_list(self, resource): result = [] for change in self.undo_list: if resource in change.get_changed_resources(): result.append(change) return result def __str__(self): return "History holds %s changes in memory" % ( len(self.undo_list) + len(self.redo_list) ) undo_list = property(lambda self: self._undo_list) redo_list = property(lambda self: self._redo_list) @property def tobe_undone(self): """The last done change if available, `None` otherwise""" if self.undo_list: return self.undo_list[-1] @property def tobe_redone(self): """The last undone change if available, `None` otherwise""" if self.redo_list: return self.redo_list[-1] @property def max_undos(self): if self._maxundos is None: return self.project.prefs.get("max_history_items", 100) else: return self._maxundos @property def save(self): return self.project.prefs.get("save_history", False) @property def compress(self): return self.project.prefs.get("compress_history", False) def clear(self): """Forget all undo and redo information""" del self.undo_list[:] del self.redo_list[:] class _FindChangeDependencies(object): def __init__(self, change_list): self.change = change_list[0] self.change_list = change_list self.changed_resources = set(self.change.get_changed_resources()) def __call__(self): result = [self.change] for change in self.change_list[1:]: if self._depends_on(change, result): result.append(change) self.changed_resources.update(change.get_changed_resources()) return result def _depends_on(self, changes, result): for resource in changes.get_changed_resources(): if resource is None: continue if resource in self.changed_resources: return True for changed in self.changed_resources: if resource.is_folder() and resource.contains(changed): return True if changed.is_folder() and changed.contains(resource): return True return False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/libutils.py0000664000175000017500000000745000000000000016521 0ustar00lieryanlieryan"""A few useful functions for using rope as a library""" import os.path import rope.base.project import rope.base.pycore from rope.base import pyobjectsdef from rope.base import utils from rope.base import taskhandle def path_to_resource(project, path, type=None): """Get the resource at path You only need to specify `type` if `path` does not exist. It can be either 'file' or 'folder'. If the type is `None` it is assumed that the resource already exists. Note that this function uses `Project.get_resource()`, `Project.get_file()`, and `Project.get_folder()` methods. """ project_path = path_relative_to_project_root(project, path) if project_path is None: project_path = rope.base.project._realpath(path) project = rope.base.project.get_no_project() if type is None: return project.get_resource(project_path) if type == "file": return project.get_file(project_path) if type == "folder": return project.get_folder(project_path) return None def path_relative_to_project_root(project, path): return relative(project.address, path) @utils.deprecated() def relative(root, path): root = rope.base.project._realpath(root).replace(os.path.sep, "/") path = rope.base.project._realpath(path).replace(os.path.sep, "/") if path == root: return "" if path.startswith(root + "/"): return path[len(root) + 1 :] def report_change(project, path, old_content): """Report that the contents of file at `path` was changed The new contents of file is retrieved by reading the file. """ resource = path_to_resource(project, path) if resource is None: return for observer in list(project.observers): observer.resource_changed(resource) if project.pycore.automatic_soa: rope.base.pycore.perform_soa_on_changed_scopes(project, resource, old_content) def analyze_module(project, resource): """Perform static object analysis on a python file in the project Note that this might be really time consuming. """ project.pycore.analyze_module(resource) def analyze_modules(project, task_handle=taskhandle.NullTaskHandle()): """Perform static object analysis on all python files in the project Note that this might be really time consuming. """ resources = project.get_python_files() job_set = task_handle.create_jobset("Analyzing Modules", len(resources)) for resource in resources: job_set.started_job(resource.path) analyze_module(project, resource) job_set.finished_job() def get_string_module(project, code, resource=None, force_errors=False): """Returns a `PyObject` object for the given code If `force_errors` is `True`, `exceptions.ModuleSyntaxError` is raised if module has syntax errors. This overrides ``ignore_syntax_errors`` project config. """ return pyobjectsdef.PyModule( project.pycore, code, resource, force_errors=force_errors ) def get_string_scope(project, code, resource=None): """Returns a `Scope` object for the given code""" return get_string_module(project, code, resource).get_scope() def is_python_file(project, resource): return project.pycore.is_python_file(resource) def modname(resource): if resource.is_folder(): module_name = resource.name source_folder = resource.parent elif resource.name == "__init__.py": module_name = resource.parent.name source_folder = resource.parent.parent else: module_name = resource.name[:-3] source_folder = resource.parent while source_folder != source_folder.parent and source_folder.has_child( "__init__.py" ): module_name = source_folder.name + "." + module_name source_folder = source_folder.parent return module_name ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3553898 rope-0.22.0/rope/base/oi/0000775000175000017500000000000000000000000014721 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631106611.0 rope-0.22.0/rope/base/oi/__init__.py0000664000175000017500000000322200000000000017031 0ustar00lieryanlieryan"""Rope object analysis and inference package Rope makes some simplifying assumptions about a python program. It assumes that a program only performs assignments and function calls. Tracking assignments is simple and `PyName` objects handle that. The main problem is function calls. Rope uses these two approaches for obtaining call information: * Static object analysis: `rope.base.pycore.PyCore.analyze_module()` It can analyze modules to obtain information about functions. This is done by analyzing function calls in a module or scope. Currently SOA analyzes the scopes that are changed while saving or when the user asks to analyze a module. That is mainly because static analysis is time-consuming. * Dynamic object analysis: `rope.base.pycore.PyCore.run_module()` When you run a module or your testsuite, when DOA is enabled, it collects information about parameters passed to and objects returned from functions. The main problem with this approach is that it is quite slow; Not when looking up the information but when collecting them. An instance of `rope.base.oi.objectinfo.ObjectInfoManager` can be used for accessing these information. It saves the data in a `rope.base.oi.objectdb.ObjectDB` internally. Now if our objectdb does not know anything about a function and we need the value returned by it, static object inference, SOI, comes into play. It analyzes function body and tries to infer the object that is returned from it (we usually need the returned value for the given parameter objects). Rope might collect and store information for other `PyName`, too. For instance rope stores the object builtin containers hold. """ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/doa.py0000664000175000017500000001631700000000000016046 0ustar00lieryanlieryanimport base64 import hashlib import hmac try: import cPickle as pickle except ImportError: import pickle import marshal import os import socket import subprocess import sys import tempfile import threading def _compat_compare_digest(a, b): """Implementation of hmac.compare_digest for python < 2.7.7. This function uses an approach designed to prevent timing analysis by avoiding content-based short circuiting behaviour, making it appropriate for cryptography. """ if len(a) != len(b): return False # Computes the bitwise difference of all characters in the two strings # before returning whether or not they are equal. difference = 0 for (a_char, b_char) in zip(a, b): difference |= ord(a_char) ^ ord(b_char) return difference == 0 try: from hmac import compare_digest except ImportError: compare_digest = _compat_compare_digest class PythonFileRunner(object): """A class for running python project files""" def __init__( self, pycore, file_, args=None, stdin=None, stdout=None, analyze_data=None ): self.pycore = pycore self.file = file_ self.analyze_data = analyze_data self.observers = [] self.args = args self.stdin = stdin self.stdout = stdout def run(self): """Execute the process""" env = dict(os.environ) file_path = self.file.real_path path_folders = ( self.pycore.project.get_source_folders() + self.pycore.project.get_python_path_folders() ) env["PYTHONPATH"] = os.pathsep.join(folder.real_path for folder in path_folders) runmod_path = self.pycore.project.find_module("rope.base.oi.runmod").real_path self.receiver = None self._init_data_receiving() send_info = "-" if self.receiver: send_info = self.receiver.get_send_info() args = [ sys.executable, runmod_path, send_info, self.pycore.project.address, self.file.real_path, ] if self.analyze_data is None: del args[1:4] if self.args is not None: args.extend(self.args) self.process = subprocess.Popen( executable=sys.executable, args=args, env=env, cwd=os.path.split(file_path)[0], stdin=self.stdin, stdout=self.stdout, stderr=self.stdout, close_fds=os.name != "nt", ) def _init_data_receiving(self): if self.analyze_data is None: return # Disabling FIFO data transfer due to blocking when running # unittests in the GUI. # XXX: Handle FIFO data transfer for `rope.ui.testview` if True or os.name == "nt": self.receiver = _SocketReceiver() else: self.receiver = _FIFOReceiver() self.receiving_thread = threading.Thread(target=self._receive_information) self.receiving_thread.setDaemon(True) self.receiving_thread.start() def _receive_information(self): # temp = open('/dev/shm/info', 'wb') for data in self.receiver.receive_data(): self.analyze_data(data) # temp.write(str(data) + '\n') # temp.close() for observer in self.observers: observer() def wait_process(self): """Wait for the process to finish""" self.process.wait() if self.analyze_data: self.receiving_thread.join() def kill_process(self): """Stop the process""" if self.process.poll() is not None: return try: if hasattr(self.process, "terminate"): self.process.terminate() elif os.name != "nt": os.kill(self.process.pid, 9) else: import ctypes handle = int(self.process._handle) ctypes.windll.kernel32.TerminateProcess(handle, -1) except OSError: pass def add_finishing_observer(self, observer): """Notify this observer when execution finishes""" self.observers.append(observer) class _MessageReceiver(object): def receive_data(self): pass def get_send_info(self): pass class _SocketReceiver(_MessageReceiver): def __init__(self): self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.data_port = 3037 self.key = os.urandom(32) while self.data_port < 4000: try: self.server_socket.bind(("localhost", self.data_port)) break except socket.error: self.data_port += 1 self.server_socket.listen(1) def get_send_info(self): return "%d:%s" % (self.data_port, base64.b64encode(self.key).decode("utf-8")) def receive_data(self): conn, addr = self.server_socket.accept() self.server_socket.close() my_file = conn.makefile("rb") while True: # Received messages must meet the following criteria: # 1. Must be contained on a single line. # 2. Must be prefixed with a base64 encoded sha256 message digest # of the base64 encoded pickle data. # 3. Message digest must be computed using the correct key. # # Any messages received that do not meet these criteria will never # be unpickled and will be dropped silently. try: buf = my_file.readline() if len(buf) == 0: break try: digest_end = buf.index(b":") buf_digest = base64.b64decode(buf[:digest_end]) buf_data = buf[digest_end + 1 : -1] decoded_buf_data = base64.b64decode(buf_data) except: # Corrupted data; the payload cannot be trusted and just has # to be dropped. See CVE-2014-3539. continue digest = hmac.new(self.key, buf_data, hashlib.sha256).digest() if not compare_digest(buf_digest, digest): # Signature mismatch; the payload cannot be trusted and just # has to be dropped. See CVE-2014-3539. continue yield pickle.loads(decoded_buf_data) except EOFError: break my_file.close() conn.close() class _FIFOReceiver(_MessageReceiver): def __init__(self): # XXX: this is insecure and might cause race conditions self.file_name = self._get_file_name() os.mkfifo(self.file_name) def _get_file_name(self): prefix = tempfile.gettempdir() + "/__rope_" i = 0 while os.path.exists(prefix + str(i).rjust(4, "0")): i += 1 return prefix + str(i).rjust(4, "0") def get_send_info(self): return self.file_name def receive_data(self): my_file = open(self.file_name, "rb") while True: try: yield marshal.load(my_file) except EOFError: break my_file.close() os.remove(self.file_name) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/memorydb.py0000664000175000017500000000576400000000000017125 0ustar00lieryanlieryanfrom rope.base.oi import objectdb class MemoryDB(objectdb.FileDict): def __init__(self, project, persist=None): self.project = project self._persist = persist self.files = self self._load_files() self.project.data_files.add_write_hook(self.write) def _load_files(self): self._files = {} if self.persist: result = self.project.data_files.read_data( "objectdb", compress=self.compress, import_=True ) if result is not None: self._files = result def keys(self): return self._files.keys() def __iter__(self): for f in self._files: yield f def __len__(self): return len(self._files) def __setitem__(self): raise NotImplementedError() def __contains__(self, key): return key in self._files def __getitem__(self, key): return FileInfo(self._files[key]) def create(self, path): self._files[path] = {} def rename(self, file, newfile): if file not in self._files: return self._files[newfile] = self._files[file] del self[file] def __delitem__(self, file): del self._files[file] def write(self): if self.persist: self.project.data_files.write_data("objectdb", self._files, self.compress) @property def compress(self): return self.project.prefs.get("compress_objectdb", False) @property def persist(self): if self._persist is not None: return self._persist else: return self.project.prefs.get("save_objectdb", False) class FileInfo(objectdb.FileInfo): def __init__(self, scopes): self.scopes = scopes def create_scope(self, key): self.scopes[key] = ScopeInfo() def keys(self): return self.scopes.keys() def __contains__(self, key): return key in self.scopes def __getitem__(self, key): return self.scopes[key] def __delitem__(self, key): del self.scopes[key] def __iter__(self): for s in self.scopes: yield s def __len__(self): return len(self.scopes) def __setitem__(self): raise NotImplementedError() class ScopeInfo(objectdb.ScopeInfo): def __init__(self): self.call_info = {} self.per_name = {} def get_per_name(self, name): return self.per_name.get(name, None) def save_per_name(self, name, value): self.per_name[name] = value def get_returned(self, parameters): return self.call_info.get(parameters, None) def get_call_infos(self): for args, returned in self.call_info.items(): yield objectdb.CallInfo(args, returned) def add_call(self, parameters, returned): self.call_info[parameters] = returned def __getstate__(self): return (self.call_info, self.per_name) def __setstate__(self, data): self.call_info, self.per_name = data ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/objectdb.py0000664000175000017500000001101700000000000017047 0ustar00lieryanlieryanfrom __future__ import print_function class ObjectDB(object): def __init__(self, db, validation): self.db = db self.validation = validation self.observers = [] self.files = db.files def validate_files(self): for file in list(self.files): if not self.validation.is_file_valid(file): del self.files[file] self._file_removed(file) def validate_file(self, file): if file not in self.files: return for key in list(self.files[file]): if not self.validation.is_scope_valid(file, key): del self.files[file][key] def file_moved(self, file, newfile): if file not in self.files: return self.files.rename(file, newfile) self._file_removed(file) self._file_added(newfile) def get_files(self): return self.files.keys() def get_returned(self, path, key, args): scope_info = self._get_scope_info(path, key, readonly=True) result = scope_info.get_returned(args) if self.validation.is_value_valid(result): return result def get_pername(self, path, key, name): scope_info = self._get_scope_info(path, key, readonly=True) result = scope_info.get_per_name(name) if self.validation.is_value_valid(result): return result def get_callinfos(self, path, key): scope_info = self._get_scope_info(path, key, readonly=True) return scope_info.get_call_infos() def add_callinfo(self, path, key, args, returned): scope_info = self._get_scope_info(path, key, readonly=False) old_returned = scope_info.get_returned(args) if self.validation.is_more_valid(returned, old_returned): scope_info.add_call(args, returned) def add_pername(self, path, key, name, value): scope_info = self._get_scope_info(path, key, readonly=False) old_value = scope_info.get_per_name(name) if self.validation.is_more_valid(value, old_value): scope_info.save_per_name(name, value) def add_file_list_observer(self, observer): self.observers.append(observer) def write(self): self.db.write() def _get_scope_info(self, path, key, readonly=True): if path not in self.files: if readonly: return _NullScopeInfo() self.files.create(path) self._file_added(path) if key not in self.files[path]: if readonly: return _NullScopeInfo() self.files[path].create_scope(key) result = self.files[path][key] if isinstance(result, dict): print(self.files, self.files[path], self.files[path][key]) return result def _file_removed(self, path): for observer in self.observers: observer.removed(path) def _file_added(self, path): for observer in self.observers: observer.added(path) def __str__(self): scope_count = 0 for file_dict in self.files.values(): scope_count += len(file_dict) return "ObjectDB holds %s file and %s scope infos" % ( len(self.files), scope_count, ) class _NullScopeInfo(object): def __init__(self, error_on_write=True): self.error_on_write = error_on_write def get_per_name(self, name): pass def save_per_name(self, name, value): if self.error_on_write: raise NotImplementedError() def get_returned(self, parameters): pass def get_call_infos(self): return [] def add_call(self, parameters, returned): if self.error_on_write: raise NotImplementedError() class FileInfo(dict): def create_scope(self, key): pass class FileDict(dict): def create(self, key): pass def rename(self, key, new_key): pass class ScopeInfo(object): def get_per_name(self, name): pass def save_per_name(self, name, value): pass def get_returned(self, parameters): pass def get_call_infos(self): pass def add_call(self, parameters, returned): pass class CallInfo(object): def __init__(self, args, returned): self.args = args self.returned = returned def get_parameters(self): return self.args def get_returned(self): return self.returned class FileListObserver(object): def added(self, path): pass def removed(self, path): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632659867.0 rope-0.22.0/rope/base/oi/objectinfo.py0000664000175000017500000002071600000000000017423 0ustar00lieryanlieryanimport warnings from rope.base import exceptions, resourceobserver from rope.base.oi import objectdb, memorydb, transform class ObjectInfoManager(object): """Stores object information It uses an instance of `objectdb.ObjectDB` for storing information. """ def __init__(self, project): self.project = project self.to_textual = transform.PyObjectToTextual(project) self.to_pyobject = transform.TextualToPyObject(project) self.doi_to_pyobject = transform.DOITextualToPyObject(project) self._init_objectdb() if project.prefs.get("validate_objectdb", False): self._init_validation() def _init_objectdb(self): dbtype = self.project.get_prefs().get("objectdb_type", None) persist = None if dbtype is not None: warnings.warn( '"objectdb_type" project config is deprecated;\n' 'Use "save_objectdb" instead in your project ' 'config file.\n(".ropeproject/config.py" by default)\n', DeprecationWarning, ) if dbtype != "memory" and self.project.ropefolder is not None: persist = True self.validation = TextualValidation(self.to_pyobject) db = memorydb.MemoryDB(self.project, persist=persist) self.objectdb = objectdb.ObjectDB(db, self.validation) def _init_validation(self): self.objectdb.validate_files() observer = resourceobserver.ResourceObserver( changed=self._resource_changed, moved=self._resource_moved, removed=self._resource_moved, ) files = [] for path in self.objectdb.get_files(): resource = self.to_pyobject.path_to_resource(path) if resource is not None and resource.project == self.project: files.append(resource) self.observer = resourceobserver.FilteredResourceObserver(observer, files) self.objectdb.add_file_list_observer(_FileListObserver(self)) self.project.add_observer(self.observer) def _resource_changed(self, resource): try: self.objectdb.validate_file(self.to_textual.resource_to_path(resource)) except exceptions.ModuleSyntaxError: pass def _resource_moved(self, resource, new_resource=None): self.observer.remove_resource(resource) if new_resource is not None: old = self.to_textual.resource_to_path(resource) new = self.to_textual.resource_to_path(new_resource) self.objectdb.file_moved(old, new) self.observer.add_resource(new_resource) def get_returned(self, pyobject, args): result = self.get_exact_returned(pyobject, args) if result is not None: return result path, key = self._get_scope(pyobject) if path is None: return None for call_info in self.objectdb.get_callinfos(path, key): returned = call_info.get_returned() if returned and returned[0] not in ("unknown", "none"): result = returned break if result is None: result = returned if result is not None: return self.to_pyobject(result) def get_exact_returned(self, pyobject, args): path, key = self._get_scope(pyobject) if path is not None: returned = self.objectdb.get_returned( path, key, self._args_to_textual(pyobject, args) ) if returned is not None: return self.to_pyobject(returned) def _args_to_textual(self, pyfunction, args): parameters = list(pyfunction.get_param_names(special_args=False)) arguments = args.get_arguments(parameters)[: len(parameters)] textual_args = tuple([self.to_textual(arg) for arg in arguments]) return textual_args def get_parameter_objects(self, pyobject): path, key = self._get_scope(pyobject) if path is None: return None arg_count = len(pyobject.get_param_names(special_args=False)) unknowns = arg_count parameters = [None] * arg_count for call_info in self.objectdb.get_callinfos(path, key): args = call_info.get_parameters() for index, arg in enumerate(args[:arg_count]): old = parameters[index] if self.validation.is_more_valid(arg, old): parameters[index] = arg if self.validation.is_value_valid(arg): unknowns -= 1 if unknowns == 0: break if unknowns < arg_count: return [self.to_pyobject(parameter) for parameter in parameters] def get_passed_objects(self, pyfunction, parameter_index): path, key = self._get_scope(pyfunction) if path is None: return [] result = [] for call_info in self.objectdb.get_callinfos(path, key): args = call_info.get_parameters() if len(args) > parameter_index: parameter = self.to_pyobject(args[parameter_index]) if parameter is not None: result.append(parameter) return result def doa_data_received(self, data): def doi_to_normal(textual): pyobject = self.doi_to_pyobject(textual) return self.to_textual(pyobject) function = doi_to_normal(data[0]) args = tuple([doi_to_normal(textual) for textual in data[1]]) returned = doi_to_normal(data[2]) if function[0] == "defined" and len(function) == 3: self._save_data(function, args, returned) def function_called(self, pyfunction, params, returned=None): function_text = self.to_textual(pyfunction) params_text = tuple([self.to_textual(param) for param in params]) returned_text = ("unknown",) if returned is not None: returned_text = self.to_textual(returned) self._save_data(function_text, params_text, returned_text) def save_per_name(self, scope, name, data): path, key = self._get_scope(scope.pyobject) if path is not None: self.objectdb.add_pername(path, key, name, self.to_textual(data)) def get_per_name(self, scope, name): path, key = self._get_scope(scope.pyobject) if path is not None: result = self.objectdb.get_pername(path, key, name) if result is not None: return self.to_pyobject(result) def _save_data(self, function, args, returned=("unknown",)): self.objectdb.add_callinfo(function[1], function[2], args, returned) def _get_scope(self, pyobject): resource = pyobject.get_module().get_resource() if resource is None: return None, None textual = self.to_textual(pyobject) if textual[0] == "defined": path = textual[1] if len(textual) == 3: key = textual[2] else: key = "" return path, key return None, None def sync(self): self.objectdb.sync() def __str__(self): return str(self.objectdb) class TextualValidation(object): def __init__(self, to_pyobject): self.to_pyobject = to_pyobject def is_value_valid(self, value): # ???: Should none and unknown be considered valid? if value is None or value[0] in ("none", "unknown"): return False return self.to_pyobject(value) is not None def is_more_valid(self, new, old): if old is None: return True return new[0] not in ("unknown", "none") def is_file_valid(self, path): return self.to_pyobject.path_to_resource(path) is not None def is_scope_valid(self, path, key): if key == "": textual = ("defined", path) else: textual = ("defined", path, key) return self.to_pyobject(textual) is not None class _FileListObserver(object): def __init__(self, object_info): self.object_info = object_info self.observer = self.object_info.observer self.to_pyobject = self.object_info.to_pyobject def removed(self, path): resource = self.to_pyobject.path_to_resource(path) if resource is not None: self.observer.remove_resource(resource) def added(self, path): resource = self.to_pyobject.path_to_resource(path) if resource is not None: self.observer.add_resource(resource) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/runmod.py0000664000175000017500000002054500000000000016605 0ustar00lieryanlieryandef __rope_start_everything(): import os import sys import socket try: import cPickle as pickle except ImportError: import pickle import marshal import inspect import types import threading import rope.base.utils.pycompat as pycompat import base64 import hashlib import hmac class _MessageSender(object): def send_data(self, data): pass class _SocketSender(_MessageSender): def __init__(self, port, key): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("127.0.0.1", port)) self.my_file = s.makefile("wb") self.key = base64.b64decode(key) def send_data(self, data): if not self.my_file.closed: pickled_data = base64.b64encode( pickle.dumps(data, pickle.HIGHEST_PROTOCOL) ) dgst = hmac.new(self.key, pickled_data, hashlib.sha256).digest() self.my_file.write(base64.b64encode(dgst) + b":" + pickled_data + b"\n") def close(self): self.my_file.close() class _FileSender(_MessageSender): def __init__(self, file_name): self.my_file = open(file_name, "wb") def send_data(self, data): if not self.my_file.closed: marshal.dump(data, self.my_file) def close(self): self.my_file.close() def _cached(func): cache = {} def newfunc(self, arg): if arg in cache: return cache[arg] result = func(self, arg) cache[arg] = result return result return newfunc class _FunctionCallDataSender(object): def __init__(self, send_info, project_root): self.project_root = project_root if send_info[0].isdigit(): port, key = send_info.split(":", 1) self.sender = _SocketSender(int(port), key) else: self.sender = _FileSender(send_info) def global_trace(frame, event, arg): # HACK: Ignoring out->in calls # This might lose some information if self._is_an_interesting_call(frame): return self.on_function_call sys.settrace(global_trace) threading.settrace(global_trace) def on_function_call(self, frame, event, arg): if event != "return": return args = [] returned = ("unknown",) code = frame.f_code for argname in code.co_varnames[: code.co_argcount]: try: argvalue = self._object_to_persisted_form(frame.f_locals[argname]) args.append(argvalue) except (TypeError, AttributeError): args.append(("unknown",)) try: returned = self._object_to_persisted_form(arg) except (TypeError, AttributeError): pass try: data = ( self._object_to_persisted_form(frame.f_code), tuple(args), returned, ) self.sender.send_data(data) except (TypeError): pass return self.on_function_call def _is_an_interesting_call(self, frame): # if frame.f_code.co_name in ['?', '']: # return False # return not frame.f_back or # not self._is_code_inside_project(frame.f_back.f_code) if not self._is_code_inside_project(frame.f_code) and ( not frame.f_back or not self._is_code_inside_project(frame.f_back.f_code) ): return False return True def _is_code_inside_project(self, code): source = self._path(code.co_filename) return ( source is not None and os.path.exists(source) and _realpath(source).startswith(self.project_root) ) @_cached def _get_persisted_code(self, object_): source = self._path(object_.co_filename) if not os.path.exists(source): raise TypeError("no source") return ("defined", _realpath(source), str(object_.co_firstlineno)) @_cached def _get_persisted_class(self, object_): try: return ( "defined", _realpath(inspect.getsourcefile(object_)), object_.__name__, ) except (TypeError, AttributeError): return ("unknown",) def _get_persisted_builtin(self, object_): if isinstance(object_, pycompat.string_types): return ("builtin", "str") if isinstance(object_, list): holding = None if len(object_) > 0: holding = object_[0] return ("builtin", "list", self._object_to_persisted_form(holding)) if isinstance(object_, dict): keys = None values = None if len(object_) > 0: # @todo - fix it properly, why is __locals__ being # duplicated ? keys = [key for key in object_.keys() if key != "__locals__"][0] values = object_[keys] return ( "builtin", "dict", self._object_to_persisted_form(keys), self._object_to_persisted_form(values), ) if isinstance(object_, tuple): objects = [] if len(object_) < 3: for holding in object_: objects.append(self._object_to_persisted_form(holding)) else: objects.append(self._object_to_persisted_form(object_[0])) return tuple(["builtin", "tuple"] + objects) if isinstance(object_, set): holding = None if len(object_) > 0: for o in object_: holding = o break return ("builtin", "set", self._object_to_persisted_form(holding)) return ("unknown",) def _object_to_persisted_form(self, object_): if object_ is None: return ("none",) if isinstance(object_, types.CodeType): return self._get_persisted_code(object_) if isinstance(object_, types.FunctionType): return self._get_persisted_code(object_.__code__) if isinstance(object_, types.MethodType): return self._get_persisted_code(object_.__func__.__code__) if isinstance(object_, types.ModuleType): return self._get_persisted_module(object_) if isinstance(object_, pycompat.string_types + (list, dict, tuple, set)): return self._get_persisted_builtin(object_) if isinstance(object_, type): return self._get_persisted_class(object_) return ("instance", self._get_persisted_class(type(object_))) @_cached def _get_persisted_module(self, object_): path = self._path(object_.__file__) if path and os.path.exists(path): return ("defined", _realpath(path)) return ("unknown",) def _path(self, path): if path.endswith(".pyc"): path = path[:-1] if path.endswith(".py"): return path def close(self): self.sender.close() sys.settrace(None) def _realpath(path): return os.path.realpath(os.path.abspath(os.path.expanduser(path))) send_info = sys.argv[1] project_root = sys.argv[2] file_to_run = sys.argv[3] run_globals = globals() run_globals.update( {"__name__": "__main__", "__builtins__": __builtins__, "__file__": file_to_run} ) if send_info != "-": data_sender = _FunctionCallDataSender(send_info, project_root) del sys.argv[1:4] pycompat.execfile(file_to_run, run_globals) if send_info != "-": data_sender.close() if __name__ == "__main__": __rope_start_everything() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/base/oi/soa.py0000664000175000017500000001355100000000000016062 0ustar00lieryanlieryanimport rope.base.ast import rope.base.oi.soi import rope.base.pynames from rope.base import pyobjects, evaluate, astutils, arguments def analyze_module(pycore, pymodule, should_analyze, search_subscopes, followed_calls): """Analyze `pymodule` for static object inference Analyzes scopes for collecting object information. The analysis starts from inner scopes. """ _analyze_node(pycore, pymodule, should_analyze, search_subscopes, followed_calls) def _analyze_node(pycore, pydefined, should_analyze, search_subscopes, followed_calls): if search_subscopes(pydefined): for scope in pydefined.get_scope().get_scopes(): _analyze_node( pycore, scope.pyobject, should_analyze, search_subscopes, followed_calls ) if should_analyze(pydefined): new_followed_calls = max(0, followed_calls - 1) return_true = lambda pydefined: True return_false = lambda pydefined: False def _follow(pyfunction): _analyze_node( pycore, pyfunction, return_true, return_false, new_followed_calls ) if not followed_calls: _follow = None visitor = SOAVisitor(pycore, pydefined, _follow) for child in rope.base.ast.get_child_nodes(pydefined.get_ast()): rope.base.ast.walk(child, visitor) class SOAVisitor(object): def __init__(self, pycore, pydefined, follow_callback=None): self.pycore = pycore self.pymodule = pydefined.get_module() self.scope = pydefined.get_scope() self.follow = follow_callback def _FunctionDef(self, node): pass def _ClassDef(self, node): pass def _Call(self, node): for child in rope.base.ast.get_child_nodes(node): rope.base.ast.walk(child, self) primary, pyname = evaluate.eval_node2(self.scope, node.func) if pyname is None: return pyfunction = pyname.get_object() if isinstance(pyfunction, pyobjects.AbstractFunction): args = arguments.create_arguments(primary, pyfunction, node, self.scope) elif isinstance(pyfunction, pyobjects.PyClass): pyclass = pyfunction if "__init__" in pyfunction: pyfunction = pyfunction["__init__"].get_object() pyname = rope.base.pynames.UnboundName(pyobjects.PyObject(pyclass)) args = self._args_with_self(primary, pyname, pyfunction, node) elif "__call__" in pyfunction: pyfunction = pyfunction["__call__"].get_object() args = self._args_with_self(primary, pyname, pyfunction, node) else: return self._call(pyfunction, args) def _args_with_self(self, primary, self_pyname, pyfunction, node): base_args = arguments.create_arguments(primary, pyfunction, node, self.scope) return arguments.MixedArguments(self_pyname, base_args, self.scope) def _call(self, pyfunction, args): if isinstance(pyfunction, pyobjects.PyFunction): if self.follow is not None: before = self._parameter_objects(pyfunction) self.pycore.object_info.function_called( pyfunction, args.get_arguments(pyfunction.get_param_names()) ) pyfunction._set_parameter_pyobjects(None) if self.follow is not None: after = self._parameter_objects(pyfunction) if after != before: self.follow(pyfunction) # XXX: Maybe we should not call every builtin function if isinstance(pyfunction, rope.base.builtins.BuiltinFunction): pyfunction.get_returned_object(args) def _parameter_objects(self, pyfunction): result = [] for i in range(len(pyfunction.get_param_names(False))): result.append(pyfunction.get_parameter(i)) return result def _AnnAssign(self, node): for child in rope.base.ast.get_child_nodes(node): rope.base.ast.walk(child, self) visitor = _SOAAssignVisitor() nodes = [] rope.base.ast.walk(node.target, visitor) nodes.extend(visitor.nodes) self._evaluate_assign_value(node, nodes, type_hint=node.annotation) def _Assign(self, node): for child in rope.base.ast.get_child_nodes(node): rope.base.ast.walk(child, self) visitor = _SOAAssignVisitor() nodes = [] for child in node.targets: rope.base.ast.walk(child, visitor) nodes.extend(visitor.nodes) self._evaluate_assign_value(node, nodes) def _evaluate_assign_value(self, node, nodes, type_hint=False): for subscript, levels in nodes: instance = evaluate.eval_node(self.scope, subscript.value) args_pynames = [evaluate.eval_node(self.scope, subscript.slice)] value = rope.base.oi.soi._infer_assignment( rope.base.pynames.AssignmentValue( node.value, levels, type_hint=type_hint ), self.pymodule, ) args_pynames.append(rope.base.pynames.UnboundName(value)) if instance is not None and value is not None: pyobject = instance.get_object() if "__setitem__" in pyobject: pyfunction = pyobject["__setitem__"].get_object() args = arguments.ObjectArguments([instance] + args_pynames) self._call(pyfunction, args) # IDEA: handle `__setslice__`, too class _SOAAssignVisitor(astutils._NodeNameCollector): def __init__(self): super(_SOAAssignVisitor, self).__init__() self.nodes = [] def _added(self, node, levels): if isinstance(node, rope.base.ast.Subscript) and isinstance( node.slice, (rope.base.ast.Index, rope.base.ast.expr) ): self.nodes.append((node, levels)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/soi.py0000664000175000017500000001743400000000000016076 0ustar00lieryanlieryan"""A module for inferring objects For more information see the documentation in `rope.base.oi` package. """ import rope.base.builtins import rope.base.pynames import rope.base.pyobjects from rope.base import evaluate, utils, arguments from rope.base.oi.type_hinting.factory import get_type_hinting_factory _ignore_inferred = utils.ignore_exception(rope.base.pyobjects.IsBeingInferredError) @_ignore_inferred def infer_returned_object(pyfunction, args): """Infer the `PyObject` this `PyFunction` returns after calling""" object_info = pyfunction.pycore.object_info result = object_info.get_exact_returned(pyfunction, args) if result is not None: return result result = _infer_returned(pyfunction, args) if result is not None: if args and pyfunction.get_module().get_resource() is not None: params = args.get_arguments(pyfunction.get_param_names(special_args=False)) object_info.function_called(pyfunction, params, result) return result result = object_info.get_returned(pyfunction, args) if result is not None: return result hint_return = get_type_hinting_factory( pyfunction.pycore.project ).make_return_provider() type_ = hint_return(pyfunction) if type_ is not None: return rope.base.pyobjects.PyObject(type_) @_ignore_inferred def infer_parameter_objects(pyfunction): """Infer the `PyObject` of parameters of this `PyFunction`""" object_info = pyfunction.pycore.object_info result = object_info.get_parameter_objects(pyfunction) if result is None: result = _parameter_objects(pyfunction) _handle_first_parameter(pyfunction, result) return result def _handle_first_parameter(pyobject, parameters): kind = pyobject.get_kind() if parameters is None or kind not in ["method", "classmethod"]: pass if not parameters: if not pyobject.get_param_names(special_args=False): return parameters.append(rope.base.pyobjects.get_unknown()) if kind == "method": parameters[0] = rope.base.pyobjects.PyObject(pyobject.parent) if kind == "classmethod": parameters[0] = pyobject.parent @_ignore_inferred def infer_assigned_object(pyname): if not pyname.assignments: return for assignment in reversed(pyname.assignments): result = _infer_assignment(assignment, pyname.module) if ( isinstance(result, rope.base.builtins.BuiltinUnknown) and result.get_name() == "NotImplementedType" ): break elif result == rope.base.pyobjects.get_unknown(): break elif result is not None: return result hint_assignment = get_type_hinting_factory( pyname.module.pycore.project ).make_assignment_provider() hinting_result = hint_assignment(pyname) if hinting_result is not None: return rope.base.pyobjects.PyObject(hinting_result) return result def get_passed_objects(pyfunction, parameter_index): object_info = pyfunction.pycore.object_info result = object_info.get_passed_objects(pyfunction, parameter_index) if not result: statically_inferred = _parameter_objects(pyfunction) if len(statically_inferred) > parameter_index: result.append(statically_inferred[parameter_index]) return result def _infer_returned(pyobject, args): if args: # HACK: Setting parameter objects manually # This is not thread safe and might cause problems if `args` # does not come from a good call site pyobject.get_scope().invalidate_data() pyobject._set_parameter_pyobjects( args.get_arguments(pyobject.get_param_names(special_args=False)) ) scope = pyobject.get_scope() if not scope._get_returned_asts(): return maxtries = 3 for returned_node in reversed(scope._get_returned_asts()[-maxtries:]): try: resulting_pyname = evaluate.eval_node(scope, returned_node) if resulting_pyname is None: continue pyobject = resulting_pyname.get_object() if pyobject == rope.base.pyobjects.get_unknown(): continue if not scope._is_generator(): return pyobject else: return rope.base.builtins.get_generator(pyobject) except rope.base.pyobjects.IsBeingInferredError: pass def _parameter_objects(pyobject): result = [] params = pyobject.get_param_names(special_args=False) hint_param = get_type_hinting_factory(pyobject.pycore.project).make_param_provider() for name in params: type_ = hint_param(pyobject, name) if type_ is not None: result.append(rope.base.pyobjects.PyObject(type_)) else: result.append(rope.base.pyobjects.get_unknown()) return result # handling `rope.base.pynames.AssignmentValue` @_ignore_inferred def _infer_assignment(assignment, pymodule): result = _follow_pyname(assignment, pymodule) if result is None: return None pyname, pyobject = result pyobject = _follow_evaluations(assignment, pyname, pyobject) if pyobject is None: return None return _follow_levels(assignment, pyobject) def _follow_levels(assignment, pyobject): for index in assignment.levels: if isinstance(pyobject.get_type(), rope.base.builtins.Tuple): holdings = pyobject.get_type().get_holding_objects() if holdings: pyobject = holdings[min(len(holdings) - 1, index)] else: pyobject = None elif isinstance(pyobject.get_type(), rope.base.builtins.List): pyobject = pyobject.get_type().holding else: pyobject = None if pyobject is None: break return pyobject @_ignore_inferred def _follow_pyname(assignment, pymodule, lineno=None): assign_node = assignment.type_hint or assignment.ast_node if lineno is None: lineno = _get_lineno_for_node(assign_node) holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno) pyname = evaluate.eval_node(holding_scope, assign_node) if pyname is not None: result = pyname.get_object() if ( isinstance(result.get_type(), rope.base.builtins.Property) and holding_scope.get_kind() == "Class" ): arg = rope.base.pynames.UnboundName( rope.base.pyobjects.PyObject(holding_scope.pyobject) ) return pyname, result.get_type().get_property_object( arguments.ObjectArguments([arg]) ) return pyname, result @_ignore_inferred def _follow_evaluations(assignment, pyname, pyobject): new_pyname = pyname tokens = assignment.evaluation.split(".") for token in tokens: call = token.endswith("()") if call: token = token[:-2] if token: pyname = new_pyname new_pyname = _get_attribute(pyobject, token) if new_pyname is not None: pyobject = new_pyname.get_object() if pyobject is not None and call: if isinstance(pyobject, rope.base.pyobjects.AbstractFunction): args = arguments.ObjectArguments([pyname]) pyobject = pyobject.get_returned_object(args) else: pyobject = None if pyobject is None: break if pyobject is not None and assignment.assign_type: return rope.base.pyobjects.PyObject(pyobject) return pyobject def _get_lineno_for_node(assign_node): if hasattr(assign_node, "lineno") and assign_node.lineno is not None: return assign_node.lineno return 1 def _get_attribute(pyobject, name): if pyobject is not None and name in pyobject: return pyobject[name] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/base/oi/transform.py0000664000175000017500000002321600000000000017312 0ustar00lieryanlieryan"""Provides classes for persisting `PyObject`""" import os import re import rope.base.builtins from rope.base import exceptions class PyObjectToTextual(object): """For transforming `PyObject` to textual form This can be used for storing `PyObjects` in files. Use `TextualToPyObject` for converting back. """ def __init__(self, project): self.project = project def transform(self, pyobject): """Transform a `PyObject` to textual form""" if pyobject is None: return ("none",) object_type = type(pyobject) try: method = getattr(self, object_type.__name__ + "_to_textual") return method(pyobject) except AttributeError: return ("unknown",) def __call__(self, pyobject): return self.transform(pyobject) def PyObject_to_textual(self, pyobject): if isinstance(pyobject.get_type(), rope.base.pyobjects.AbstractClass): result = self.transform(pyobject.get_type()) if result[0] == "defined": return ("instance", result) return result return ("unknown",) def PyFunction_to_textual(self, pyobject): return self._defined_to_textual(pyobject) def PyClass_to_textual(self, pyobject): return self._defined_to_textual(pyobject) def _defined_to_textual(self, pyobject): address = [] while pyobject.parent is not None: address.insert(0, pyobject.get_name()) pyobject = pyobject.parent return ( "defined", self._get_pymodule_path(pyobject.get_module()), ".".join(address), ) def PyModule_to_textual(self, pyobject): return ("defined", self._get_pymodule_path(pyobject)) def PyPackage_to_textual(self, pyobject): return ("defined", self._get_pymodule_path(pyobject)) def List_to_textual(self, pyobject): return ("builtin", "list", self.transform(pyobject.holding)) def Dict_to_textual(self, pyobject): return ( "builtin", "dict", self.transform(pyobject.keys), self.transform(pyobject.values), ) def Tuple_to_textual(self, pyobject): objects = [ self.transform(holding) for holding in pyobject.get_holding_objects() ] return tuple(["builtin", "tuple"] + objects) def Set_to_textual(self, pyobject): return ("builtin", "set", self.transform(pyobject.holding)) def Iterator_to_textual(self, pyobject): return ("builtin", "iter", self.transform(pyobject.holding)) def Generator_to_textual(self, pyobject): return ("builtin", "generator", self.transform(pyobject.holding)) def Str_to_textual(self, pyobject): return ("builtin", "str") def File_to_textual(self, pyobject): return ("builtin", "file") def BuiltinFunction_to_textual(self, pyobject): return ("builtin", "function", pyobject.get_name()) def _get_pymodule_path(self, pymodule): return self.resource_to_path(pymodule.get_resource()) def resource_to_path(self, resource): if resource.project == self.project: return resource.path else: return resource.real_path class TextualToPyObject(object): """For transforming textual form to `PyObject`""" def __init__(self, project, allow_in_project_absolutes=False): self.project = project def __call__(self, textual): return self.transform(textual) def transform(self, textual): """Transform an object from textual form to `PyObject`""" if textual is None: return None type = textual[0] try: method = getattr(self, type + "_to_pyobject") return method(textual) except AttributeError: return None def builtin_to_pyobject(self, textual): method = getattr(self, "builtin_%s_to_pyobject" % textual[1], None) if method is not None: return method(textual) def builtin_str_to_pyobject(self, textual): return rope.base.builtins.get_str() def builtin_list_to_pyobject(self, textual): holding = self.transform(textual[2]) return rope.base.builtins.get_list(holding) def builtin_dict_to_pyobject(self, textual): keys = self.transform(textual[2]) values = self.transform(textual[3]) return rope.base.builtins.get_dict(keys, values) def builtin_tuple_to_pyobject(self, textual): objects = [] for holding in textual[2:]: objects.append(self.transform(holding)) return rope.base.builtins.get_tuple(*objects) def builtin_set_to_pyobject(self, textual): holding = self.transform(textual[2]) return rope.base.builtins.get_set(holding) def builtin_iter_to_pyobject(self, textual): holding = self.transform(textual[2]) return rope.base.builtins.get_iterator(holding) def builtin_generator_to_pyobject(self, textual): holding = self.transform(textual[2]) return rope.base.builtins.get_generator(holding) def builtin_file_to_pyobject(self, textual): return rope.base.builtins.get_file() def builtin_function_to_pyobject(self, textual): if textual[2] in rope.base.builtins.builtins: return rope.base.builtins.builtins[textual[2]].get_object() def unknown_to_pyobject(self, textual): return None def none_to_pyobject(self, textual): return None def _module_to_pyobject(self, textual): path = textual[1] return self._get_pymodule(path) def _hierarchical_defined_to_pyobject(self, textual): path = textual[1] names = textual[2].split(".") pymodule = self._get_pymodule(path) pyobject = pymodule for name in names: if pyobject is None: return None if isinstance(pyobject, rope.base.pyobjects.PyDefinedObject): try: pyobject = pyobject.get_scope()[name].get_object() except exceptions.NameNotFoundError: return None else: return None return pyobject def defined_to_pyobject(self, textual): if len(textual) == 2 or textual[2] == "": return self._module_to_pyobject(textual) else: return self._hierarchical_defined_to_pyobject(textual) def instance_to_pyobject(self, textual): type = self.transform(textual[1]) if type is not None: return rope.base.pyobjects.PyObject(type) def _get_pymodule(self, path): resource = self.path_to_resource(path) if resource is not None: return self.project.get_pymodule(resource) def path_to_resource(self, path): try: root = self.project.address if not os.path.isabs(path): return self.project.get_resource(path) if path == root or path.startswith(root + os.sep): # INFO: This is a project file; should not be absolute return None import rope.base.project return rope.base.project.get_no_project().get_resource(path) except exceptions.ResourceNotFoundError: return None class DOITextualToPyObject(TextualToPyObject): """For transforming textual form to `PyObject` The textual form DOI uses is different from rope's standard textual form. The reason is that we cannot find the needed information by analyzing live objects. This class can be used to transform DOI textual form to `PyObject` and later we can convert it to standard textual form using `TextualToPyObject` class. """ def _function_to_pyobject(self, textual): path = textual[1] lineno = int(textual[2]) pymodule = self._get_pymodule(path) if pymodule is not None: scope = pymodule.get_scope() inner_scope = scope.get_inner_scope_for_line(lineno) return inner_scope.pyobject def _class_to_pyobject(self, textual): path, name = textual[1:] pymodule = self._get_pymodule(path) if pymodule is None: return None module_scope = pymodule.get_scope() suspected = None if name in module_scope.get_names(): suspected = module_scope[name].get_object() if suspected is not None and isinstance(suspected, rope.base.pyobjects.PyClass): return suspected else: lineno = self._find_occurrence(name, pymodule.get_resource().read()) if lineno is not None: inner_scope = module_scope.get_inner_scope_for_line(lineno) return inner_scope.pyobject def defined_to_pyobject(self, textual): if len(textual) == 2: return self._module_to_pyobject(textual) else: if textual[2].isdigit(): result = self._function_to_pyobject(textual) else: result = self._class_to_pyobject(textual) if not isinstance(result, rope.base.pyobjects.PyModule): return result def _find_occurrence(self, name, source): pattern = re.compile(r"^\s*class\s*" + name + r"\b") lines = source.split("\n") for i in range(len(lines)): if pattern.match(lines[i]): return i + 1 def path_to_resource(self, path): import rope.base.libutils relpath = rope.base.libutils.path_relative_to_project_root(self.project, path) if relpath is not None: path = relpath return super(DOITextualToPyObject, self).path_to_resource(path) ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1637593692.35939 rope-0.22.0/rope/base/oi/type_hinting/0000775000175000017500000000000000000000000017422 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631106611.0 rope-0.22.0/rope/base/oi/type_hinting/__init__.py0000664000175000017500000000000000000000000021521 0ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/evaluate.py0000664000175000017500000002171100000000000021604 0ustar00lieryanlieryan# Based on super lightweight Simple Top-Down Parser from http://effbot.org/zone/simple-top-down-parsing.htm # and https://bitbucket.org/emacsway/sqlbuilder/src/default/sqlbuilder/smartsql/contrib/evaluate.py import re from rope.base.utils import pycompat from rope.base.oi.type_hinting import utils from rope.base import utils as base_utils class SymbolBase(object): name = None # node/token type name def __init__(self): self.value = None # used by name and literals self.first = None self.second = None self.third = None # used by tree nodes def nud(self, parser): raise SyntaxError("Syntax error (%r)." % self.name) def led(self, left, parser): raise SyntaxError("Unknown operator (%r)." % self.name) def evaluate(self, pyobject): raise NotImplementedError(self.name, self) def __repr__(self): if self.name == "(name)": return "(%s %s)" % (self.name[1:-1], self.value) out = [repr(self.name), self.first, self.second, self.third] out = [str(i) for i in out if i] return "(" + " ".join(out) + ")" class SymbolTable(object): def multi(func): def _inner(self, names, *a, **kw): for name in names.split(): func(self, name, *a, **kw) return _inner def __init__(self): self.symbol_table = {} def get(self, name, default=None): return self.symbol_table.get(name, default) def __getitem__(self, name): return self.symbol_table[name] def __iter__(self): return iter(self.symbol_table) def symbol(self, name, bp=0): try: s = self.symbol_table[name] except KeyError: class S(SymbolBase): pass s = S s.__name__ = "symbol-" + name # for debugging s.name = name s.lbp = bp self.symbol_table[name] = s else: s.lbp = max(bp, s.lbp) return s @multi def infix(self, name, bp): symbol = self.symbol(name, bp) @method(symbol) def led(self, left, parser): self.first = left self.second = parser.expression(bp) return self @multi def infix_r(self, name, bp): symbol = self.symbol(name, bp) @method(symbol) def led(self, left, parser): self.first = left self.second = parser.expression(bp - 0.1) return self def ternary(self, name, name2, bp): symbol = self.symbol(name, bp) symbol2 = self.symbol(name2) @method(symbol) def led(self, left, parser): self.first = left self.second = parser.expression(symbol2.lbp) parser.advance(symbol2.name) self.third = parser.expression(symbol2.lbp + 0.1) return self @multi def prefix(self, name, bp): symbol = self.symbol(name, bp) @method(symbol) def nud(self, parser): self.first = parser.expression(bp) return self @multi def postfix(self, name, bp): symbol = self.symbol(name, bp) @method(symbol) def led(self, left, parser): self.first = left return self multi = staticmethod(multi) # Just for code checker symbol_table = SymbolTable() class Lexer(object): _token_pattern = re.compile( r""" \s* (?: ( [,()\[\]|] | -> | (?<=\s)(?:or)\b ) # operator | ([a-zA-Z](?:\w|\.)*) # name ) """, re.U | re.S | re.X, ) def __init__(self, symbol_table): self.symbol_table = symbol_table def tokenize(self, program): for name, value in self._tokenize_expr(program): symbol = symbol_table.get(value) if symbol: s = symbol() elif name == "(name)": symbol = symbol_table[name] s = symbol() s.value = value else: raise SyntaxError( "Unknown operator ({0}). Possible operators are {1!r}".format( value, list(self.symbol_table) ) ) yield s def _tokenize_expr(self, program): if isinstance(program, bytes): program = program.decode("utf-8") # import pprint; pprint.pprint(self._token_pattern.findall(program)) for operator, name in self._token_pattern.findall(program): if operator: yield "(operator)", operator elif name: yield "(name)", name else: raise SyntaxError yield "(end)", "(end)" class Parser(object): token = None next = None def __init__(self, lexer): self.lexer = lexer def parse(self, program): generator = self.lexer.tokenize(program) try: self.next = generator.__next__ # PY3 except AttributeError: self.next = generator.next self.token = self.next() return self.expression() def expression(self, rbp=0): t = self.token self.token = self.next() left = t.nud(self) while rbp < self.token.lbp: t = self.token self.token = self.next() left = t.led(left, self) return left def advance(self, name=None): if name and self.token.name != name: raise SyntaxError( "Expected {0!r} but found {1!r}".format(name, self.token.name) ) self.token = self.next() def method(s): assert issubclass(s, SymbolBase) def bind(fn): setattr(s, fn.__name__, fn) return fn return bind symbol, infix, infix_r, prefix, postfix, ternary = ( symbol_table.symbol, symbol_table.infix, symbol_table.infix_r, symbol_table.prefix, symbol_table.postfix, symbol_table.ternary, ) symbol("(", 270) symbol(")") symbol("[", 250) # Parameters symbol("]") symbol("->", 230) infix("|", 170) infix("or", 170) symbol(",") symbol("(name)") symbol("(end)") @method(symbol("(name)")) def nud(self, parser): return self @method(symbol("(name)")) def evaluate(self, pyobject): return utils.resolve_type(self.value, pyobject) # Parametrized objects @method(symbol("[")) def led(self, left, parser): self.first = left self.second = [] if parser.token.name != "]": while 1: if parser.token.name == "]": break self.second.append(parser.expression()) if parser.token.name != ",": break parser.advance(",") parser.advance("]") return self @method(symbol("[")) def evaluate(self, pyobject): return utils.parametrize_type( self.first.evaluate(pyobject), *[i.evaluate(pyobject) for i in self.second] ) # Anonymous Function Calls @method(symbol("(")) def nud(self, parser): self.second = [] if parser.token.name != ")": while 1: self.second.append(parser.expression()) if parser.token.name != ",": break parser.advance(",") parser.advance(")") parser.advance("->") self.third = parser.expression(symbol("->").lbp + 0.1) return self # Function Calls @method(symbol("(")) def led(self, left, parser): self.first = left self.second = [] if parser.token.name != ")": while 1: self.second.append(parser.expression()) if parser.token.name != ",": break parser.advance(",") parser.advance(")") parser.advance("->") self.third = parser.expression(symbol("->").lbp + 0.1) return self @method(symbol("(")) def evaluate(self, pyobject): # TODO: Implement me raise NotImplementedError @method(symbol("or")) @method(symbol("|")) def evaluate(self, pyobject): # TODO: Implement me raise NotImplementedError class Compiler(object): parser_factory = Parser lexer_factory = Lexer symbol_table = symbol_table def _make_parser(self): return self.parser_factory(self.lexer_factory(self.symbol_table)) @base_utils.cached(500) def __call__(self, program): """ :type program: str :rtype: rope.base.oi.type_hinting.evaluate.SymbolBase """ return self._make_parser().parse(program) compile = Compiler() class Evaluator(object): compile = compile def __call__(self, program, pyobject): """Evaluates the program string or AST :type program: str or rope.base.oi.type_hinting.evaluate.SymbolBase :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ ast = ( self.compile(program) if isinstance(program, pycompat.string_types) else program ) return ast.evaluate(pyobject) evaluate = Evaluator() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/factory.py0000664000175000017500000000520100000000000021441 0ustar00lieryanlieryanfrom rope.base.oi.type_hinting import interfaces from rope.base.oi.type_hinting.providers import ( composite, inheritance, docstrings, numpydocstrings, pep0484_type_comments, ) from rope.base.oi.type_hinting.resolvers import composite as composite_resolvers, types from rope.base import utils class TypeHintingFactory(interfaces.ITypeHintingFactory): @utils.saveit def make_param_provider(self): providers = [ docstrings.ParamProvider( docstrings.DocstringParamParser(), self.make_resolver() ), docstrings.ParamProvider( numpydocstrings.NumPyDocstringParamParser(), self.make_resolver() ), ] return inheritance.ParamProvider(composite.ParamProvider(*providers)) @utils.saveit def make_return_provider(self): providers = [ docstrings.ReturnProvider( docstrings.DocstringReturnParser(), self.make_resolver() ), ] return inheritance.ReturnProvider(composite.ReturnProvider(*providers)) @utils.saveit def make_assignment_provider(self): providers = [ pep0484_type_comments.AssignmentProvider(self.make_resolver()), docstrings.AssignmentProvider( docstrings.DocstringParamParser(), self.make_resolver() ), docstrings.AssignmentProvider( numpydocstrings.NumPyDocstringParamParser(), self.make_resolver() ), ] return inheritance.AssignmentProvider(composite.AssignmentProvider(*providers)) @utils.saveit def make_resolver(self): """ :rtype: rope.base.oi.type_hinting.resolvers.interfaces.IResolver """ resolvers = [ types.Resolver(), ] return composite_resolvers.Resolver(*resolvers) default_type_hinting_factory = TypeHintingFactory() class TypeHintingFactoryAccessor(object): def __call__(self, project): """ :type project: rope.base.project.Project :rtype: rope.base.oi.type_hinting.interfaces.ITypeHintingFactory """ factory_location = project.get_prefs().get( "type_hinting_factory", "rope.base.oi.type_hinting.factory.default_type_hinting_factory", ) return self._get_factory(factory_location) @utils.cached(10) def _get_factory(self, factory_location): """ :type factory_location: str :rtype: rope.base.oi.type_hinting.interfaces.ITypeHintingFactory """ return utils.resolve(factory_location) get_type_hinting_factory = TypeHintingFactoryAccessor() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/interfaces.py0000664000175000017500000000132300000000000022116 0ustar00lieryanlieryanclass ITypeHintingFactory(object): def make_param_provider(self): """ :rtype: rope.base.oi.type_hinting.providers.interfaces.IParamProvider """ raise NotImplementedError def make_return_provider(self): """ :rtype: rope.base.oi.type_hinting.providers.interfaces.IReturnProvider """ raise NotImplementedError def make_assignment_provider(self): """ :rtype: rope.base.oi.type_hinting.providers.interfaces.IAssignmentProvider """ raise NotImplementedError def make_resolver(self): """ :rtype: rope.base.oi.type_hinting.resolvers.interfaces.IResolver """ raise NotImplementedError ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1637593692.35939 rope-0.22.0/rope/base/oi/type_hinting/providers/0000775000175000017500000000000000000000000021437 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631106611.0 rope-0.22.0/rope/base/oi/type_hinting/providers/__init__.py0000664000175000017500000000000000000000000023536 0ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/providers/composite.py0000664000175000017500000000350000000000000024011 0ustar00lieryanlieryanfrom rope.base.oi.type_hinting.providers import interfaces class ParamProvider(interfaces.IParamProvider): def __init__(self, *delegates): """ :type delegates: list[rope.base.oi.type_hinting.providers.interfaces.IParamProvider] """ self._delegates = delegates def __call__(self, pyfunc, param_name): """ :type pyfunc: rope.base.pyobjectsdef.PyFunction :type param_name: str :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ for delegate in self._delegates: result = delegate(pyfunc, param_name) if result: return result class ReturnProvider(interfaces.IReturnProvider): def __init__(self, *delegates): """ :type delegates: list[rope.base.oi.type_hinting.providers.interfaces.IReturnProvider] """ self._delegates = delegates def __call__(self, pyfunc): """ :type pyfunc: rope.base.pyobjectsdef.PyFunction :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ for delegate in self._delegates: result = delegate(pyfunc) if result: return result class AssignmentProvider(interfaces.IAssignmentProvider): def __init__(self, *delegates): """ :type delegates: list[rope.base.oi.type_hinting.providers.interfaces.IAssignmentProvider] """ self._delegates = delegates def __call__(self, pyname): """ :type pyname: rope.base.pynamesdef.AssignedName :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ for delegate in self._delegates: result = delegate(pyname) if result: return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/providers/docstrings.py0000664000175000017500000001405700000000000024177 0ustar00lieryanlieryan""" Hinting the type using docstring of class/function. It's an irreplaceable thing if you are using Dependency Injection with passive class: http://www.martinfowler.com/articles/injection.html Some code extracted (or based on code) from: https://github.com/davidhalter/jedi/blob/b489019f5bd5750051122b94cc767df47751ecb7/jedi/evaluate/docstrings.py Thanks to @davidhalter for this utils under MIT License. Similar solutions: - https://www.jetbrains.com/pycharm/help/type-hinting-in-pycharm.html - https://www.python.org/dev/peps/pep-0484/#type-comments - http://www.pydev.org/manual_adv_type_hints.html - https://jedi.readthedocs.org/en/latest/docs/features.html#type-hinting Discussions: - https://groups.google.com/d/topic/rope-dev/JlAzmZ83K1M/discussion - https://groups.google.com/d/topic/rope-dev/LCFNN98vckI/discussion """ import re from rope.base.oi.type_hinting import utils from rope.base.oi.type_hinting.providers import interfaces class ParamProvider(interfaces.IParamProvider): def __init__(self, docstring_parser, resolver): """ :type docstring_parser: rope.base.oi.type_hinting.providers.docstrings.IParamParser :type resolver: rope.base.oi.type_hinting.resolvers.interfaces.IResolver """ self._parse_docstring = docstring_parser self._resolve = resolver def __call__(self, pyfunc, param_name): """ :type pyfunc: rope.base.pyobjectsdef.PyFunction :type param_name: str :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ type_strs = self._parse_docstring(pyfunc.get_doc(), param_name) if type_strs: return self._resolve(type_strs[0], pyfunc) class ReturnProvider(interfaces.IReturnProvider): def __init__(self, docstring_parser, resolver): """ :type docstring_parser: rope.base.oi.type_hinting.providers.docstrings.IReturnParser :type resolver: rope.base.oi.type_hinting.resolvers.interfaces.IResolver """ self._parse_docstring = docstring_parser self._resolve = resolver def __call__(self, pyfunc): """ :type pyfunc: rope.base.pyobjectsdef.PyFunction :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ type_strs = self._parse_docstring(pyfunc.get_doc()) if type_strs: return self._resolve(type_strs[0], pyfunc) class AssignmentProvider(interfaces.IAssignmentProvider): def __init__(self, docstring_parser, resolver): """ :type docstring_parser: rope.base.oi.type_hinting.providers.docstrings.IParamParser :type resolver: rope.base.oi.type_hinting.resolvers.interfaces.IResolver """ self._parse_docstring = docstring_parser self._resolve = resolver def __call__(self, pyname): """ :type pyname: rope.base.pynamesdef.AssignedName :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ try: pyclass, attr_name = utils.get_class_with_attr_name(pyname) except TypeError: return else: type_strs = self._parse_docstring(pyclass.get_doc(), attr_name) if type_strs: return self._resolve(type_strs[0], pyclass) class IParamParser(object): def __call__(self, docstring, param_name): """ :type docstring: str :type param_name: str """ class IReturnParser(object): def __call__(self, docstring): """ :type docstring: str """ class DocstringParamParser(IParamParser): DOCSTRING_PARAM_PATTERNS = [ r"\s*:type\s+%s:\s*([^\n]+)", # Sphinx r"\s*:param\s+(\w+)\s+%s:[^\n]+", # Sphinx param with type r"\s*@type\s+%s:\s*([^\n]+)", # Epydoc ] def __init__(self): self._strip_rst_role = RSTRoleStrip() def __call__(self, docstring, param_name): """Search `docstring` for type(-s) of `param_name`. >>> DocstringParamParser()(':type param: int', 'param') ['int'] >>> DocstringParamParser()('@type param: int', 'param') ['int'] >>> DocstringParamParser()(':type param: :class:`threading.Thread`', 'param') ['threading.Thread'] >>> bool(DocstringParamParser()('no document', 'param')) False >>> DocstringParamParser()(':param int param: some description', 'param') ['int'] """ if not docstring: return [] patterns = [ re.compile(p % re.escape(param_name)) for p in self.DOCSTRING_PARAM_PATTERNS ] for pattern in patterns: match = pattern.search(docstring) if match: return [self._strip_rst_role(match.group(1))] return [] class DocstringReturnParser(IReturnParser): DOCSTRING_RETURN_PATTERNS = [ re.compile(r"\s*:rtype:\s*([^\n]+)", re.M), # Sphinx re.compile(r"\s*@rtype:\s*([^\n]+)", re.M), # Epydoc ] def __init__(self): self._strip_rst_role = RSTRoleStrip() def __call__(self, docstring): if not docstring: return [] for p in self.DOCSTRING_RETURN_PATTERNS: match = p.search(docstring) if match: return [self._strip_rst_role(match.group(1))] return [] class RSTRoleStrip(object): RST_ROLE_PATTERN = re.compile(r":[^`]+:`([^`]+)`") def __call__(self, type_str): """ Strip off the part looks like a ReST role in `type_str`. >>> RSTRoleStrip()(':class:`ClassName`') # strip off :class: 'ClassName' >>> RSTRoleStrip()(':py:obj:`module.Object`') # works with domain 'module.Object' >>> RSTRoleStrip()('ClassName') # do nothing when not ReST role 'ClassName' See also: http://sphinx-doc.org/domains.html#cross-referencing-python-objects """ match = self.RST_ROLE_PATTERN.match(type_str) if match: return match.group(1) else: return type_str ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/providers/inheritance.py0000664000175000017500000000410400000000000024301 0ustar00lieryanlieryanfrom rope.base.oi.type_hinting import utils from rope.base.oi.type_hinting.providers import interfaces class ParamProvider(interfaces.IParamProvider): def __init__(self, delegate): """ :type delegate: rope.base.oi.type_hinting.providers.interfaces.IParamProvider """ self._delegate = delegate def __call__(self, pyfunc, param_name): """ :type pyfunc: rope.base.pyobjectsdef.PyFunction :type param_name: str :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ superfunc = pyfunc while superfunc: result = self._delegate(superfunc, param_name) if result: return result superfunc = utils.get_super_func(superfunc) class ReturnProvider(interfaces.IReturnProvider): def __init__(self, delegate): """ :type delegate: rope.base.oi.type_hinting.providers.interfaces.IReturnProvider """ self._delegate = delegate def __call__(self, pyfunc): """ :type pyfunc: rope.base.pyobjectsdef.PyFunction :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ superfunc = pyfunc while superfunc: result = self._delegate(superfunc) if result: return result superfunc = utils.get_super_func(superfunc) class AssignmentProvider(interfaces.IAssignmentProvider): def __init__(self, delegate): """ :type delegate: rope.base.oi.type_hinting.providers.interfaces.IAssignmentProvider """ self._delegate = delegate def __call__(self, pyname): """ :type pyname: rope.base.pynamesdef.AssignedName :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ super_pyname = pyname while super_pyname: result = self._delegate(super_pyname) if result: return result super_pyname = utils.get_super_assignment(super_pyname) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/providers/interfaces.py0000664000175000017500000000206700000000000024141 0ustar00lieryanlieryanclass IParamProvider(object): def __call__(self, pyfunc, param_name): """ :type pyfunc: rope.base.pyobjectsdef.PyFunction :type param_name: str :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ raise NotImplementedError class IReturnProvider(object): """ :type resolve: rope.base.oi.type_hinting.resolvers.interfaces.IResolver """ resolve = None def __call__(self, pyfunc): """ :type pyfunc: rope.base.pyobjectsdef.PyFunction :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ raise NotImplementedError class IAssignmentProvider(object): """ :type resolve: rope.base.oi.type_hinting.resolvers.interfaces.IResolver """ resolve = None def __call__(self, pyname): """ :type pyname: rope.base.pynamesdef.AssignedName :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ raise NotImplementedError ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/providers/numpydocstrings.py0000664000175000017500000000254500000000000025267 0ustar00lieryanlieryan""" Some code extracted (or based on code) from: https://github.com/davidhalter/jedi/blob/b489019f5bd5750051122b94cc767df47751ecb7/jedi/evaluate/docstrings.py Thanks to @davidhalter for this utils under MIT License. """ import re from ast import literal_eval from rope.base.oi.type_hinting.providers import docstrings try: from numpydoc.docscrape import NumpyDocString except ImportError: NumpyDocString = None class NumPyDocstringParamParser(docstrings.IParamParser): def __call__(self, docstring, param_name): """Search `docstring` (in numpydoc format) for type(-s) of `param_name`.""" if not docstring: return [] params = NumpyDocString(docstring)._parsed_data["Parameters"] for p_name, p_type, p_descr in params: if p_name == param_name: m = re.match("([^,]+(,[^,]+)*?)(,[ ]*optional)?$", p_type) if m: p_type = m.group(1) if p_type.startswith("{"): types = set(type(x).__name__ for x in literal_eval(p_type)) return list(types) else: return [p_type] return [] class _DummyParamParser(docstrings.IParamParser): def __call__(self, docstring, param_name): return [] if not NumpyDocString: NumPyDocstringParamParser = _DummyParamParser ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/providers/pep0484_type_comments.py0000664000175000017500000000276400000000000026074 0ustar00lieryanlieryanimport re from rope.base.oi.type_hinting import utils from rope.base.oi.type_hinting.providers import interfaces class AssignmentProvider(interfaces.IAssignmentProvider): def __init__(self, resolver): """ :type resolver: rope.base.oi.type_hinting.resolvers.interfaces.IResolver """ self._resolve = resolver PEP0484_TYPE_COMMENT_PATTERNS = (re.compile(r"type:\s*([^\n]+)"),) def __call__(self, pyname): """ :type pyname: rope.base.pynamesdef.AssignedName :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ from rope.base.oi.soi import _get_lineno_for_node lineno = _get_lineno_for_node(pyname.assignments[0].ast_node) holding_scope = pyname.module.get_scope().get_inner_scope_for_line(lineno) line = holding_scope._get_global_scope()._scope_finder.lines.get_line(lineno) if "#" in line: type_strs = self._search_type_in_type_comment(line.split("#", 1)[1]) if type_strs: return self._resolve(type_strs[0], holding_scope.pyobject) def _search_type_in_type_comment(self, code): """For more info see: https://www.python.org/dev/peps/pep-0484/#type-comments >>> AssignmentProvider()._search_type_in_type_comment('type: int') ['int'] """ for p in self.PEP0484_TYPE_COMMENT_PATTERNS: match = p.search(code) if match: return [match.group(1)] ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1637593692.35939 rope-0.22.0/rope/base/oi/type_hinting/resolvers/0000775000175000017500000000000000000000000021446 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631106611.0 rope-0.22.0/rope/base/oi/type_hinting/resolvers/__init__.py0000664000175000017500000000000000000000000023545 0ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/resolvers/composite.py0000664000175000017500000000141200000000000024020 0ustar00lieryanlieryanfrom rope.base.oi.type_hinting.resolvers import interfaces class Resolver(interfaces.IResolver): def __init__(self, *delegates): """ :type delegates: list[rope.base.oi.type_hinting.resolvers.interfaces.IResolver] """ self._delegates = delegates def __call__(self, hint, pyobject): """ :param hint: For example "List[int]" or "(Foo, Bar) -> Baz" or simple "Foo" :type hint: str :type pyobject: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ for delegate in self._delegates: result = delegate(hint, pyobject) if result: return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/resolvers/interfaces.py0000664000175000017500000000063500000000000024147 0ustar00lieryanlieryanclass IResolver(object): def __call__(self, hint, pyobject): """ :param hint: For example "List[int]" or "(Foo, Bar) -> Baz" or simple "Foo" :type hint: str :type pyobject: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ raise NotImplementedError ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631106611.0 rope-0.22.0/rope/base/oi/type_hinting/resolvers/types.py0000664000175000017500000000114300000000000023163 0ustar00lieryanlieryanfrom rope.base.oi.type_hinting import evaluate from rope.base.oi.type_hinting.resolvers import interfaces class Resolver(interfaces.IResolver): def __call__(self, hint, pyobject): """ :param hint: For example "List[int]" or "(Foo, Bar) -> Baz" or simple "Foo" :type hint: str :type pyobject: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ try: return evaluate.evaluate(hint, pyobject) except (Exception): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/oi/type_hinting/utils.py0000664000175000017500000001275100000000000021142 0ustar00lieryanlieryanimport logging try: from typing import Union, Optional except ImportError: pass import rope.base.utils as base_utils from rope.base.evaluate import ScopeNameFinder from rope.base.exceptions import AttributeNotFoundError from rope.base.pyobjects import PyClass, PyDefinedObject, PyFunction, PyObject from rope.base.utils import pycompat def get_super_func(pyfunc): if not isinstance(pyfunc.parent, PyClass): return for cls in get_mro(pyfunc.parent)[1:]: try: superfunc = cls.get_attribute(pyfunc.get_name()).get_object() except AttributeNotFoundError: pass else: if isinstance(superfunc, PyFunction): return superfunc def get_super_assignment(pyname): """ :type pyname: rope.base.pynamesdef.AssignedName :type: rope.base.pynamesdef.AssignedName """ try: pyclass, attr_name = get_class_with_attr_name(pyname) except TypeError: return else: for super_pyclass in get_mro(pyclass)[1:]: if attr_name in super_pyclass: return super_pyclass[attr_name] def get_class_with_attr_name(pyname): """ :type pyname: rope.base.pynamesdef.AssignedName :return: rope.base.pyobjectsdef.PyClass, str :rtype: tuple """ lineno = get_lineno_for_node(pyname.assignments[0].ast_node) holding_scope = pyname.module.get_scope().get_inner_scope_for_line(lineno) pyobject = holding_scope.pyobject if isinstance(pyobject, PyClass): pyclass = pyobject elif isinstance(pyobject, PyFunction) and isinstance(pyobject.parent, PyClass): pyclass = pyobject.parent else: return for name, attr in pyclass.get_attributes().items(): if attr is pyname: return (pyclass, name) def get_lineno_for_node(assign_node): if hasattr(assign_node, "lineno") and assign_node.lineno is not None: return assign_node.lineno return 1 def get_mro(pyclass): # FIXME: to use real mro() result class_list = [pyclass] for cls in class_list: for super_cls in cls.get_superclasses(): if isinstance(super_cls, PyClass) and super_cls not in class_list: class_list.append(super_cls) return class_list def resolve_type(type_name, pyobject): # type: (str, Union[PyDefinedObject, PyObject]) -> Optional[PyDefinedObject, PyObject] """ Find proper type object from its name. """ deprecated_aliases = {"collections": "collections.abc"} ret_type = None logging.debug("Looking for %s", type_name) if "." not in type_name: try: ret_type = ( pyobject.get_module().get_scope().get_name(type_name).get_object() ) except AttributeNotFoundError: logging.exception("Cannot resolve type %s", type_name) else: mod_name, attr_name = type_name.rsplit(".", 1) try: mod_finder = ScopeNameFinder(pyobject.get_module()) mod = mod_finder._find_module(mod_name).get_object() ret_type = mod.get_attribute(attr_name).get_object() except AttributeNotFoundError: if mod_name in deprecated_aliases: try: logging.debug( "Looking for %s in %s", attr_name, deprecated_aliases[mod_name] ) mod = mod_finder._find_module( deprecated_aliases[mod_name] ).get_object() ret_type = mod.get_attribute(attr_name).get_object() except AttributeNotFoundError: logging.exception( "Cannot resolve type %s in %s", attr_name, dir(mod) ) logging.debug("ret_type = %s", ret_type) return ret_type class ParametrizeType(object): _supported_mapping = { "builtins.list": "rope.base.builtins.get_list", "builtins.tuple": "rope.base.builtins.get_tuple", "builtins.set": "rope.base.builtins.get_set", "builtins.dict": "rope.base.builtins.get_dict", "_collections_abc.Iterable": "rope.base.builtins.get_iterator", "_collections_abc.Iterator": "rope.base.builtins.get_iterator", "collections.abc.Iterable": "rope.base.builtins.get_iterator", # Python3.3 "collections.abc.Iterator": "rope.base.builtins.get_iterator", # Python3.3 } if pycompat.PY2: _supported_mapping = dict( ( ( k.replace("builtins.", "__builtin__.").replace( "_collections_abc.", "_abcoll." ), v, ) for k, v in _supported_mapping.items() ) ) def __call__(self, pyobject, *args, **kwargs): """ :type pyobject: rope.base.pyobjects.PyObject :rtype: rope.base.pyobjects.PyDefinedObject | rope.base.pyobjects.PyObject or None """ type_factory = self._get_type_factory(pyobject) if type_factory: parametrized_type = type_factory(*args, **kwargs) if parametrized_type: return parametrized_type return pyobject def _get_type_factory(self, pyobject): type_str = "{0}.{1}".format( pyobject.get_module().get_name(), pyobject.get_name(), ) if type_str in self._supported_mapping: return base_utils.resolve(self._supported_mapping[type_str]) parametrize_type = ParametrizeType() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/prefs.py0000664000175000017500000000210500000000000016001 0ustar00lieryanlieryanclass Prefs(object): def __init__(self): self.prefs = {} self.callbacks = {} def set(self, key, value): """Set the value of `key` preference to `value`.""" if key in self.callbacks: self.callbacks[key](value) else: self.prefs[key] = value def add(self, key, value): """Add an entry to a list preference Add `value` to the list of entries for the `key` preference. """ if not key in self.prefs: self.prefs[key] = [] self.prefs[key].append(value) def get(self, key, default=None): """Get the value of the key preference""" return self.prefs.get(key, default) def add_callback(self, key, callback): """Add `key` preference with `callback` function Whenever `key` is set the callback is called with the given `value` as parameter. """ self.callbacks[key] = callback def __setitem__(self, key, value): self.set(key, value) def __getitem__(self, key): return self.get(key) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633867163.0 rope-0.22.0/rope/base/project.py0000664000175000017500000004012500000000000016334 0ustar00lieryanlieryanimport os import shutil import sys import warnings import rope.base.fscommands import rope.base.resourceobserver as resourceobserver import rope.base.utils.pycompat as pycompat from rope.base import exceptions, taskhandle, prefs, history, pycore, utils from rope.base.exceptions import ModuleNotFoundError from rope.base.resources import File, Folder, _ResourceMatcher try: import cPickle as pickle except ImportError: import pickle class _Project(object): def __init__(self, fscommands): self.observers = [] self.fscommands = fscommands self.prefs = prefs.Prefs() self.data_files = _DataFiles(self) self._custom_source_folders = [] def get_resource(self, resource_name): """Get a resource in a project. `resource_name` is the path of a resource in a project. It is the path of a resource relative to project root. Project root folder address is an empty string. If the resource does not exist a `exceptions.ResourceNotFound` exception would be raised. Use `get_file()` and `get_folder()` when you need to get nonexistent `Resource`. """ path = self._get_resource_path(resource_name) if not os.path.exists(path): raise exceptions.ResourceNotFoundError( "Resource <%s> does not exist" % resource_name ) elif os.path.isfile(path): return File(self, resource_name) elif os.path.isdir(path): return Folder(self, resource_name) else: raise exceptions.ResourceNotFoundError("Unknown resource " + resource_name) def get_module(self, name, folder=None): """Returns a `PyObject` if the module was found.""" # check if this is a builtin module pymod = self.pycore.builtin_module(name) if pymod is not None: return pymod module = self.find_module(name, folder) if module is None: raise ModuleNotFoundError("Module %s not found" % name) return self.pycore.resource_to_pyobject(module) def get_python_path_folders(self): result = [] for src in self.prefs.get("python_path", []) + sys.path: try: src_folder = get_no_project().get_resource(src) result.append(src_folder) except exceptions.ResourceNotFoundError: pass return result # INFO: It was decided not to cache source folders, since: # - Does not take much time when the root folder contains # packages, that is most of the time # - We need a separate resource observer; `self.observer` # does not get notified about module and folder creations def get_source_folders(self): """Returns project source folders""" if self.root is None: return [] result = list(self._custom_source_folders) result.extend(self.pycore._find_source_folders(self.root)) return result def validate(self, folder): """Validate files and folders contained in this folder It validates all of the files and folders contained in this folder if some observers are interested in them. """ for observer in list(self.observers): observer.validate(folder) def add_observer(self, observer): """Register a `ResourceObserver` See `FilteredResourceObserver`. """ self.observers.append(observer) def remove_observer(self, observer): """Remove a registered `ResourceObserver`""" if observer in self.observers: self.observers.remove(observer) def do(self, changes, task_handle=taskhandle.NullTaskHandle()): """Apply the changes in a `ChangeSet` Most of the time you call this function for committing the changes for a refactoring. """ self.history.do(changes, task_handle=task_handle) def get_pymodule(self, resource, force_errors=False): return self.pycore.resource_to_pyobject(resource, force_errors) def get_pycore(self): return self.pycore def get_file(self, path): """Get the file with `path` (it may not exist)""" return File(self, path) def get_folder(self, path): """Get the folder with `path` (it may not exist)""" return Folder(self, path) def get_prefs(self): return self.prefs def get_relative_module(self, name, folder, level): module = self.find_relative_module(name, folder, level) if module is None: raise ModuleNotFoundError("Module %s not found" % name) return self.pycore.resource_to_pyobject(module) def find_module(self, modname, folder=None): """Returns a resource corresponding to the given module returns None if it can not be found """ for src in self.get_source_folders(): module = _find_module_in_folder(src, modname) if module is not None: return module for src in self.get_python_path_folders(): module = _find_module_in_folder(src, modname) if module is not None: return module if folder is not None: module = _find_module_in_folder(folder, modname) if module is not None: return module return None def find_relative_module(self, modname, folder, level): for i in range(level - 1): folder = folder.parent if modname == "": return folder else: return _find_module_in_folder(folder, modname) def is_ignored(self, resource): return False def _get_resource_path(self, name): pass @property @utils.saveit def history(self): return history.History(self) @property @utils.saveit def pycore(self): return pycore.PyCore(self) def close(self): warnings.warn("Cannot close a NoProject", DeprecationWarning, stacklevel=2) ropefolder = None class Project(_Project): """A Project containing files and folders""" def __init__( self, projectroot, fscommands=None, ropefolder=".ropeproject", **prefs ): """A rope project :parameters: - `projectroot`: The address of the root folder of the project - `fscommands`: Implements the file system operations used by rope; have a look at `rope.base.fscommands` - `ropefolder`: The name of the folder in which rope stores project configurations and data. Pass `None` for not using such a folder at all. - `prefs`: Specify project preferences. These values overwrite config file preferences. """ if projectroot != "/": projectroot = _realpath(projectroot).rstrip("/\\") self._address = projectroot self._ropefolder_name = ropefolder if not os.path.exists(self._address): os.mkdir(self._address) elif not os.path.isdir(self._address): raise exceptions.RopeError("Project root exists and" " is not a directory") if fscommands is None: fscommands = rope.base.fscommands.create_fscommands(self._address) super(Project, self).__init__(fscommands) self.ignored = _ResourceMatcher() self.file_list = _FileListCacher(self) self.prefs.add_callback("ignored_resources", self.ignored.set_patterns) if ropefolder is not None: self.prefs["ignored_resources"] = [ropefolder] self._init_prefs(prefs) self._init_source_folders() @utils.deprecated("Delete once deprecated functions are gone") def _init_source_folders(self): for path in self.prefs.get("source_folders", []): folder = self.get_resource(path) self._custom_source_folders.append(folder) def get_files(self): return self.file_list.get_files() def get_python_files(self): """Returns all python files available in the project""" return [ resource for resource in self.get_files() if self.pycore.is_python_file(resource) ] def _get_resource_path(self, name): return os.path.join(self._address, *name.split("/")) def _init_ropefolder(self): if self.ropefolder is not None: if not self.ropefolder.exists(): self._create_recursively(self.ropefolder) if not self.ropefolder.has_child("config.py"): config = self.ropefolder.create_file("config.py") config.write(self._default_config()) def _create_recursively(self, folder): if folder.parent != self.root and not folder.parent.exists(): self._create_recursively(folder.parent) folder.create() def _init_prefs(self, prefs): run_globals = {} if self.ropefolder is not None: config = self.get_file(self.ropefolder.path + "/config.py") run_globals.update( { "__name__": "__main__", "__builtins__": __builtins__, "__file__": config.real_path, } ) if config.exists(): config = self.ropefolder.get_child("config.py") pycompat.execfile(config.real_path, run_globals) else: exec(self._default_config(), run_globals) if "set_prefs" in run_globals: run_globals["set_prefs"](self.prefs) for key, value in prefs.items(): self.prefs[key] = value self._init_other_parts() self._init_ropefolder() if "project_opened" in run_globals: run_globals["project_opened"](self) def _default_config(self): import rope.base.default_config import inspect return inspect.getsource(rope.base.default_config) def _init_other_parts(self): # Forcing the creation of `self.pycore` to register observers self.pycore def is_ignored(self, resource): return self.ignored.does_match(resource) def sync(self): """Closes project open resources""" self.close() def close(self): """Closes project open resources""" self.data_files.write() def set(self, key, value): """Set the `key` preference to `value`""" self.prefs.set(key, value) @property def ropefolder(self): if self._ropefolder_name is not None: return self.get_folder(self._ropefolder_name) def validate(self, folder=None): if folder is None: folder = self.root super(Project, self).validate(folder) root = property(lambda self: self.get_resource("")) address = property(lambda self: self._address) class NoProject(_Project): """A null object for holding out of project files. This class is singleton use `get_no_project` global function """ def __init__(self): fscommands = rope.base.fscommands.FileSystemCommands() super(NoProject, self).__init__(fscommands) def _get_resource_path(self, name): real_name = name.replace("/", os.path.sep) return _realpath(real_name) def get_resource(self, name): universal_name = _realpath(name).replace(os.path.sep, "/") return super(NoProject, self).get_resource(universal_name) def get_files(self): return [] def get_python_files(self): return [] _no_project = None def get_no_project(): if NoProject._no_project is None: NoProject._no_project = NoProject() return NoProject._no_project class _FileListCacher(object): def __init__(self, project): self.project = project self.files = None rawobserver = resourceobserver.ResourceObserver( self._changed, self._invalid, self._invalid, self._invalid, self._invalid ) self.project.add_observer(rawobserver) def get_files(self): if self.files is None: self.files = set() self._add_files(self.project.root) return self.files def _add_files(self, folder): for child in folder.get_children(): if child.is_folder(): self._add_files(child) elif not self.project.is_ignored(child): self.files.add(child) def _changed(self, resource): if resource.is_folder(): self.files = None def _invalid(self, resource, new_resource=None): self.files = None class _DataFiles(object): def __init__(self, project): self.project = project self.hooks = [] def read_data(self, name, compress=False, import_=False): if self.project.ropefolder is None: return None compress = compress and self._can_compress() opener = self._get_opener(compress) file = self._get_file(name, compress) if not compress and import_: self._import_old_files(name) if file.exists(): input = opener(file.real_path, "rb") try: result = [] try: while True: result.append(pickle.load(input)) except EOFError: pass if len(result) == 1: return result[0] if len(result) > 1: return result finally: input.close() def write_data(self, name, data, compress=False): if self.project.ropefolder is not None: compress = compress and self._can_compress() file = self._get_file(name, compress) opener = self._get_opener(compress) output = opener(file.real_path, "wb") try: pickle.dump(data, output, 2) finally: output.close() def add_write_hook(self, hook): self.hooks.append(hook) def write(self): for hook in self.hooks: hook() def _can_compress(self): try: import gzip # noqa return True except ImportError: return False def _import_old_files(self, name): old = self._get_file(name + ".pickle", False) new = self._get_file(name, False) if old.exists() and not new.exists(): shutil.move(old.real_path, new.real_path) def _get_opener(self, compress): if compress: try: import gzip return gzip.open except ImportError: pass return open def _get_file(self, name, compress): path = self.project.ropefolder.path + "/" + name if compress: path += ".gz" return self.project.get_file(path) def _realpath(path): """Return the real path of `path` Is equivalent to ``realpath(abspath(expanduser(path)))``. Of the particular notice is the hack dealing with the unfortunate situation of running native-Windows python (os.name == 'nt') inside of Cygwin (abspath starts with '/'), which apparently normal os.path.realpath completely messes up. """ # there is a bug in cygwin for os.path.abspath() for abs paths if sys.platform == "cygwin": if path[1:3] == ":\\": return path elif path[1:3] == ":/": path = "/cygdrive/" + path[0] + path[2:] return os.path.abspath(os.path.expanduser(path)) return os.path.realpath(os.path.abspath(os.path.expanduser(path))) def _find_module_in_folder(folder, modname): module = folder packages = modname.split(".") for pkg in packages[:-1]: if module.is_folder() and module.has_child(pkg): module = module.get_child(pkg) else: return None if module.is_folder(): if ( module.has_child(packages[-1]) and module.get_child(packages[-1]).is_folder() ): return module.get_child(packages[-1]) elif ( module.has_child(packages[-1] + ".py") and not module.get_child(packages[-1] + ".py").is_folder() ): return module.get_child(packages[-1] + ".py") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/pycore.py0000664000175000017500000003123000000000000016164 0ustar00lieryanlieryanimport bisect import difflib import sys import warnings import rope.base.libutils import rope.base.resourceobserver import rope.base.resources import rope.base.oi.doa import rope.base.oi.objectinfo import rope.base.oi.soa from rope.base import builtins from rope.base import exceptions from rope.base import stdmods from rope.base import taskhandle from rope.base import utils from rope.base.exceptions import ModuleNotFoundError from rope.base.pyobjectsdef import PyModule, PyPackage class PyCore(object): def __init__(self, project): self.project = project self._init_resource_observer() self.cache_observers = [] self.module_cache = _ModuleCache(self) self.extension_cache = _ExtensionCache(self) self.object_info = rope.base.oi.objectinfo.ObjectInfoManager(project) self._init_python_files() self._init_automatic_soa() def _init_python_files(self): self.python_matcher = None patterns = self.project.prefs.get("python_files", None) if patterns is not None: self.python_matcher = rope.base.resources._ResourceMatcher() self.python_matcher.set_patterns(patterns) def _init_resource_observer(self): callback = self._invalidate_resource_cache observer = rope.base.resourceobserver.ResourceObserver( changed=callback, moved=callback, removed=callback ) self.observer = rope.base.resourceobserver.FilteredResourceObserver(observer) self.project.add_observer(self.observer) def _init_automatic_soa(self): if not self.automatic_soa: return callback = self._file_changed_for_soa observer = rope.base.resourceobserver.ResourceObserver( changed=callback, moved=callback, removed=callback ) self.project.add_observer(observer) @property def automatic_soa(self): auto_soa = self.project.prefs.get("automatic_soi", None) return self.project.prefs.get("automatic_soa", auto_soa) def _file_changed_for_soa(self, resource, new_resource=None): old_contents = self.project.history.contents_before_current_change(resource) if old_contents is not None: perform_soa_on_changed_scopes(self.project, resource, old_contents) def is_python_file(self, resource): if resource.is_folder(): return False if self.python_matcher is None: return resource.name.endswith(".py") return self.python_matcher.does_match(resource) @utils.deprecated("Use `project.get_module` instead") def get_module(self, name, folder=None): """Returns a `PyObject` if the module was found.""" return self.project.get_module(name, folder) def _builtin_submodules(self, modname): result = {} for extension in self.extension_modules: if extension.startswith(modname + "."): name = extension[len(modname) + 1 :] if "." not in name: result[name] = self.builtin_module(extension) return result def builtin_module(self, name): return self.extension_cache.get_pymodule(name) @utils.deprecated("Use `project.get_relative_module` instead") def get_relative_module(self, name, folder, level): return self.project.get_relative_module(name, folder, level) @utils.deprecated("Use `libutils.get_string_module` instead") def get_string_module(self, code, resource=None, force_errors=False): """Returns a `PyObject` object for the given code If `force_errors` is `True`, `exceptions.ModuleSyntaxError` is raised if module has syntax errors. This overrides ``ignore_syntax_errors`` project config. """ return PyModule(self, code, resource, force_errors=force_errors) @utils.deprecated("Use `libutils.get_string_scope` instead") def get_string_scope(self, code, resource=None): """Returns a `Scope` object for the given code""" return rope.base.libutils.get_string_scope(code, resource) def _invalidate_resource_cache(self, resource, new_resource=None): for observer in self.cache_observers: observer(resource) @utils.deprecated("Use `project.get_python_path_folders` instead") def get_python_path_folders(self): return self.project.get_python_path_folders() @utils.deprecated("Use `project.find_module` instead") def find_module(self, modname, folder=None): """Returns a resource corresponding to the given module returns None if it can not be found """ return self.project.find_module(modname, folder) @utils.deprecated("Use `project.find_relative_module` instead") def find_relative_module(self, modname, folder, level): return self.project.find_relative_module(modname, folder, level) # INFO: It was decided not to cache source folders, since: # - Does not take much time when the root folder contains # packages, that is most of the time # - We need a separate resource observer; `self.observer` # does not get notified about module and folder creations @utils.deprecated("Use `project.get_source_folders` instead") def get_source_folders(self): """Returns project source folders""" return self.project.get_source_folders() def resource_to_pyobject(self, resource, force_errors=False): return self.module_cache.get_pymodule(resource, force_errors) @utils.deprecated("Use `project.get_python_files` instead") def get_python_files(self): """Returns all python files available in the project""" return self.project.get_python_files() def _is_package(self, folder): if ( folder.has_child("__init__.py") and not folder.get_child("__init__.py").is_folder() ): return True else: return False def _find_source_folders(self, folder): for resource in folder.get_folders(): if self._is_package(resource): return [folder] result = [] for resource in folder.get_files(): if resource.name.endswith(".py"): result.append(folder) break for resource in folder.get_folders(): result.extend(self._find_source_folders(resource)) return result def run_module(self, resource, args=None, stdin=None, stdout=None): """Run `resource` module Returns a `rope.base.oi.doa.PythonFileRunner` object for controlling the process. """ perform_doa = self.project.prefs.get("perform_doi", True) perform_doa = self.project.prefs.get("perform_doa", perform_doa) receiver = self.object_info.doa_data_received if not perform_doa: receiver = None runner = rope.base.oi.doa.PythonFileRunner( self, resource, args, stdin, stdout, receiver ) runner.add_finishing_observer(self.module_cache.forget_all_data) runner.run() return runner def analyze_module( self, resource, should_analyze=lambda py: True, search_subscopes=lambda py: True, followed_calls=None, ): """Analyze `resource` module for static object inference This function forces rope to analyze this module to collect information about function calls. `should_analyze` is a function that is called with a `PyDefinedObject` argument. If it returns `True` the element is analyzed. If it is `None` or returns `False` the element is not analyzed. `search_subscopes` is like `should_analyze`; The difference is that if it returns `False` the sub-scopes are all ignored. That is it is assumed that `should_analyze` returns `False` for all of its subscopes. `followed_calls` override the value of ``soa_followed_calls`` project config. """ if followed_calls is None: followed_calls = self.project.prefs.get("soa_followed_calls", 0) pymodule = self.resource_to_pyobject(resource) self.module_cache.forget_all_data() rope.base.oi.soa.analyze_module( self, pymodule, should_analyze, search_subscopes, followed_calls ) def get_classes(self, task_handle=taskhandle.NullTaskHandle()): warnings.warn( "`PyCore.get_classes()` is deprecated", DeprecationWarning, stacklevel=2 ) return [] def __str__(self): return str(self.module_cache) + str(self.object_info) @utils.deprecated("Use `libutils.modname` instead") def modname(self, resource): return rope.base.libutils.modname(resource) @property @utils.cacheit def extension_modules(self): result = set(self.project.prefs.get("extension_modules", [])) if self.project.prefs.get("import_dynload_stdmods", False): result.update(stdmods.dynload_modules()) return result class _ModuleCache(object): def __init__(self, pycore): self.pycore = pycore self.module_map = {} self.pycore.cache_observers.append(self._invalidate_resource) self.observer = self.pycore.observer def _invalidate_resource(self, resource): if resource in self.module_map: self.forget_all_data() self.observer.remove_resource(resource) del self.module_map[resource] def get_pymodule(self, resource, force_errors=False): if resource in self.module_map: return self.module_map[resource] if resource.is_folder(): result = PyPackage(self.pycore, resource, force_errors=force_errors) else: result = PyModule(self.pycore, resource=resource, force_errors=force_errors) if result.has_errors: return result self.module_map[resource] = result self.observer.add_resource(resource) return result def forget_all_data(self): for pymodule in self.module_map.values(): pymodule._forget_concluded_data() def __str__(self): return "PyCore caches %d PyModules\n" % len(self.module_map) class _ExtensionCache(object): def __init__(self, pycore): self.pycore = pycore self.extensions = {} def get_pymodule(self, name): if name == "__builtin__": return builtins.builtins allowed = self.pycore.extension_modules if name not in self.extensions and name in allowed: self.extensions[name] = builtins.BuiltinModule(name, self.pycore) return self.extensions.get(name) def perform_soa_on_changed_scopes(project, resource, old_contents): pycore = project.pycore if resource.exists() and pycore.is_python_file(resource): try: new_contents = resource.read() # detecting changes in new_contents relative to old_contents detector = _TextChangeDetector(new_contents, old_contents) def search_subscopes(pydefined): scope = pydefined.get_scope() return detector.is_changed(scope.get_start(), scope.get_end()) def should_analyze(pydefined): scope = pydefined.get_scope() start = scope.get_start() end = scope.get_end() return detector.consume_changes(start, end) pycore.analyze_module(resource, should_analyze, search_subscopes) except exceptions.ModuleSyntaxError: pass class _TextChangeDetector(object): def __init__(self, old, new): self.old = old self.new = new self._set_diffs() def _set_diffs(self): differ = difflib.Differ() self.lines = [] lineno = 0 for line in differ.compare( self.old.splitlines(True), self.new.splitlines(True) ): if line.startswith(" "): lineno += 1 elif line.startswith("-"): lineno += 1 self.lines.append(lineno) def is_changed(self, start, end): """Tell whether any of start till end lines have changed The end points are inclusive and indices start from 1. """ left, right = self._get_changed(start, end) if left < right: return True return False def consume_changes(self, start, end): """Clear the changed status of lines from start till end""" left, right = self._get_changed(start, end) if left < right: del self.lines[left:right] return left < right def _get_changed(self, start, end): left = bisect.bisect_left(self.lines, start) right = bisect.bisect_right(self.lines, end) return left, right ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/pynames.py0000664000175000017500000001365300000000000016350 0ustar00lieryanlieryanimport rope.base.pyobjects from rope.base import exceptions, utils class PyName(object): """References to `PyObject` inside python programs""" def get_object(self): """Return the `PyObject` object referenced by this `PyName`""" def get_definition_location(self): """Return a (module, lineno) tuple""" class DefinedName(PyName): def __init__(self, pyobject): self.pyobject = pyobject def get_object(self): return self.pyobject def get_definition_location(self): lineno = utils.guess_def_lineno( self.pyobject.get_module(), self.pyobject.get_ast() ) return (self.pyobject.get_module(), lineno) class AssignedName(PyName): """Only a placeholder""" class UnboundName(PyName): def __init__(self, pyobject=None): self.pyobject = pyobject if self.pyobject is None: self.pyobject = rope.base.pyobjects.get_unknown() def get_object(self): return self.pyobject def get_definition_location(self): return (None, None) class AssignmentValue(object): """An assigned expression""" def __init__( self, ast_node, levels=None, evaluation="", assign_type=False, type_hint=None ): """The `level` is `None` for simple assignments and is a list of numbers for tuple assignments for example in:: a, (b, c) = x The levels for for `a` is ``[0]``, for `b` is ``[1, 0]`` and for `c` is ``[1, 1]``. """ self.ast_node = ast_node if levels is None: self.levels = [] else: self.levels = levels self.evaluation = evaluation self.assign_type = assign_type self.type_hint = type_hint def get_lineno(self): return self.ast_node.lineno class EvaluatedName(PyName): """A name whose object will be evaluated later""" def __init__(self, callback, module=None, lineno=None): self.module = module self.lineno = lineno self.callback = callback self.pyobject = _Inferred(callback, _get_concluded_data(module)) def get_object(self): return self.pyobject.get() def get_definition_location(self): return (self.module, self.lineno) def invalidate(self): """Forget the `PyObject` this `PyName` holds""" self.pyobject.set(None) class ParameterName(PyName): """Only a placeholder""" class ImportedModule(PyName): def __init__(self, importing_module, module_name=None, level=0, resource=None): self.importing_module = importing_module self.module_name = module_name self.level = level self.resource = resource self.pymodule = _get_concluded_data(self.importing_module) def _current_folder(self): resource = self.importing_module.get_module().get_resource() if resource is None: return None return resource.parent def _get_pymodule(self): if self.pymodule.get() is None: pycore = self.importing_module.pycore if self.resource is not None: self.pymodule.set(pycore.project.get_pymodule(self.resource)) elif self.module_name is not None: try: if self.level == 0: pymodule = pycore.project.get_module( self.module_name, self._current_folder() ) else: pymodule = pycore.project.get_relative_module( self.module_name, self._current_folder(), self.level ) self.pymodule.set(pymodule) except exceptions.ModuleNotFoundError: pass return self.pymodule.get() def get_object(self): if self._get_pymodule() is None: return rope.base.pyobjects.get_unknown() return self._get_pymodule() def get_definition_location(self): pymodule = self._get_pymodule() if not isinstance(pymodule, rope.base.pyobjects.PyDefinedObject): return (None, None) return (pymodule.get_module(), 1) class ImportedName(PyName): def __init__(self, imported_module, imported_name): self.imported_module = imported_module self.imported_name = imported_name def _get_imported_pyname(self): try: result = self.imported_module.get_object()[self.imported_name] if result != self: return result except exceptions.AttributeNotFoundError: pass return UnboundName() @utils.prevent_recursion(rope.base.pyobjects.get_unknown) def get_object(self): return self._get_imported_pyname().get_object() @utils.prevent_recursion(lambda: (None, None)) def get_definition_location(self): return self._get_imported_pyname().get_definition_location() def _get_concluded_data(module): if module is None: return rope.base.pyobjects._ConcludedData() return module._get_concluded_data() def _circular_inference(): raise rope.base.pyobjects.IsBeingInferredError("Circular Object Inference") class _Inferred(object): def __init__(self, get_inferred, concluded=None): self.get_inferred = get_inferred self.concluded = concluded if self.concluded is None: self.temp = None @utils.prevent_recursion(_circular_inference) def get(self, *args, **kwds): if self.concluded is None or self.concluded.get() is None: self.set(self.get_inferred(*args, **kwds)) if self._get() is None: self.set(rope.base.pyobjects.get_unknown()) return self._get() def set(self, pyobject): if self.concluded is not None: self.concluded.set(pyobject) self.temp = pyobject def _get(self): if self.concluded is not None: return self.concluded.get() return self.temp ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/pynamesdef.py0000664000175000017500000000335200000000000017022 0ustar00lieryanlieryanimport rope.base.oi.soi from rope.base import pynames from rope.base.pynames import * class AssignedName(pynames.AssignedName): def __init__(self, lineno=None, module=None, pyobject=None): self.lineno = lineno self.module = module self.assignments = [] self.pyobject = _Inferred( self._get_inferred, pynames._get_concluded_data(module) ) self.pyobject.set(pyobject) @utils.prevent_recursion(lambda: None) def _get_inferred(self): if self.module is not None: return rope.base.oi.soi.infer_assigned_object(self) def get_object(self): return self.pyobject.get() def get_definition_location(self): """Returns a (module, lineno) tuple""" if self.lineno is None and self.assignments: try: self.lineno = self.assignments[0].get_lineno() except AttributeError: pass return (self.module, self.lineno) def invalidate(self): """Forget the `PyObject` this `PyName` holds""" self.pyobject.set(None) class ParameterName(pynames.ParameterName): def __init__(self, pyfunction, index): self.pyfunction = pyfunction self.index = index def get_object(self): result = self.pyfunction.get_parameter(self.index) if result is None: result = rope.base.pyobjects.get_unknown() return result def get_objects(self): """Returns the list of objects passed as this parameter""" return rope.base.oi.soi.get_passed_objects(self.pyfunction, self.index) def get_definition_location(self): return (self.pyfunction.get_module(), self.pyfunction.get_ast().lineno) _Inferred = pynames._Inferred ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/base/pyobjects.py0000664000175000017500000002124200000000000016667 0ustar00lieryanlieryanfrom rope.base.fscommands import _decode_data from rope.base import ast, exceptions, utils class PyObject(object): def __init__(self, type_): if type_ is None: type_ = self self.type = type_ def get_attributes(self): if self.type is self: return {} return self.type.get_attributes() def get_attribute(self, name): if name not in self.get_attributes(): raise exceptions.AttributeNotFoundError("Attribute %s not found" % name) return self.get_attributes()[name] def get_type(self): return self.type def __getitem__(self, key): """The same as ``get_attribute(key)``""" return self.get_attribute(key) def __contains__(self, key): """The same as ``key in self.get_attributes()``""" return key in self.get_attributes() def __eq__(self, obj): """Check the equality of two `PyObject` Currently it is assumed that instances (the direct instances of `PyObject`, not the instances of its subclasses) are equal if their types are equal. For every other object like defineds or builtins rope assumes objects are reference objects and their identities should match. """ if self.__class__ != obj.__class__: return False if type(self) == PyObject: if self is not self.type: return self.type == obj.type else: return self.type is obj.type return self is obj def __ne__(self, obj): return not self.__eq__(obj) def __hash__(self): """See docs for `__eq__()` method""" if type(self) == PyObject and self != self.type: return hash(self.type) + 1 else: return super(PyObject, self).__hash__() def __iter__(self): """The same as ``iter(self.get_attributes())``""" return iter(self.get_attributes()) _types = None _unknown = None @staticmethod def _get_base_type(name): if PyObject._types is None: PyObject._types = {} base_type = PyObject(None) PyObject._types["Type"] = base_type PyObject._types["Module"] = PyObject(base_type) PyObject._types["Function"] = PyObject(base_type) PyObject._types["Unknown"] = PyObject(base_type) return PyObject._types[name] def get_base_type(name): """Return the base type with name `name`. The base types are 'Type', 'Function', 'Module' and 'Unknown'. It was used to check the type of a `PyObject` but currently its use is discouraged. Use classes defined in this module instead. For example instead of ``pyobject.get_type() == get_base_type('Function')`` use ``isinstance(pyobject, AbstractFunction)``. You can use `AbstractClass` for classes, `AbstractFunction` for functions, and `AbstractModule` for modules. You can also use `PyFunction` and `PyClass` for testing if an object is defined somewhere and rope can access its source. These classes provide more methods. """ return PyObject._get_base_type(name) def get_unknown(): """Return a pyobject whose type is unknown Note that two unknown objects are equal. So for example you can write:: if pyname.get_object() == get_unknown(): print('cannot determine what this pyname holds') Rope could have used `None` for indicating unknown objects but we had to check that in many places. So actually this method returns a null object. """ if PyObject._unknown is None: PyObject._unknown = PyObject(get_base_type("Unknown")) return PyObject._unknown class AbstractClass(PyObject): def __init__(self): super(AbstractClass, self).__init__(get_base_type("Type")) def get_name(self): pass def get_doc(self): pass def get_superclasses(self): return [] class AbstractFunction(PyObject): def __init__(self): super(AbstractFunction, self).__init__(get_base_type("Function")) def get_name(self): pass def get_doc(self): pass def get_param_names(self, special_args=True): return [] def get_returned_object(self, args): return get_unknown() class AbstractModule(PyObject): def __init__(self, doc=None): super(AbstractModule, self).__init__(get_base_type("Module")) def get_doc(self): pass def get_resource(self): pass class PyDefinedObject(object): """Python defined names that rope can access their sources""" def __init__(self, pycore, ast_node, parent): self.pycore = pycore self.ast_node = ast_node self.scope = None self.parent = parent self.structural_attributes = None self.concluded_attributes = self.get_module()._get_concluded_data() self.attributes = self.get_module()._get_concluded_data() self.defineds = None visitor_class = None @utils.prevent_recursion(lambda: {}) def _get_structural_attributes(self): if self.structural_attributes is None: self.structural_attributes = self._create_structural_attributes() return self.structural_attributes @utils.prevent_recursion(lambda: {}) def _get_concluded_attributes(self): if self.concluded_attributes.get() is None: self._get_structural_attributes() self.concluded_attributes.set(self._create_concluded_attributes()) return self.concluded_attributes.get() def get_attributes(self): if self.attributes.get() is None: result = dict(self._get_concluded_attributes()) result.update(self._get_structural_attributes()) self.attributes.set(result) return self.attributes.get() def get_attribute(self, name): if name in self._get_structural_attributes(): return self._get_structural_attributes()[name] if name in self._get_concluded_attributes(): return self._get_concluded_attributes()[name] raise exceptions.AttributeNotFoundError("Attribute %s not found" % name) def get_scope(self): if self.scope is None: self.scope = self._create_scope() return self.scope def get_module(self): current_object = self while current_object.parent is not None: current_object = current_object.parent return current_object def get_doc(self): if len(self.get_ast().body) > 0: expr = self.get_ast().body[0] if isinstance(expr, ast.Expr) and isinstance(expr.value, ast.Str): docstring = expr.value.s coding = self.get_module().coding return _decode_data(docstring, coding) def _get_defined_objects(self): if self.defineds is None: self._get_structural_attributes() return self.defineds def _create_structural_attributes(self): if self.visitor_class is None: return {} new_visitor = self.visitor_class(self.pycore, self) for child in ast.get_child_nodes(self.ast_node): ast.walk(child, new_visitor) self.defineds = new_visitor.defineds return new_visitor.names def _create_concluded_attributes(self): return {} def get_ast(self): return self.ast_node def _create_scope(self): pass class PyFunction(PyDefinedObject, AbstractFunction): """Only a placeholder""" class PyComprehension(PyDefinedObject, PyObject): """Only a placeholder""" class PyClass(PyDefinedObject, AbstractClass): """Only a placeholder""" class _ConcludedData(object): def __init__(self): self.data_ = None def set(self, data): self.data_ = data def get(self): return self.data_ data = property(get, set) def _invalidate(self): self.data = None def __str__(self): return "<" + str(self.data) + ">" class _PyModule(PyDefinedObject, AbstractModule): def __init__(self, pycore, ast_node, resource): self.resource = resource self.concluded_data = [] AbstractModule.__init__(self) PyDefinedObject.__init__(self, pycore, ast_node, None) def _get_concluded_data(self): new_data = _ConcludedData() self.concluded_data.append(new_data) return new_data def _forget_concluded_data(self): for data in self.concluded_data: data._invalidate() def get_resource(self): return self.resource class PyModule(_PyModule): """Only a placeholder""" class PyPackage(_PyModule): """Only a placeholder""" class IsBeingInferredError(exceptions.RopeError): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636636936.0 rope-0.22.0/rope/base/pyobjectsdef.py0000664000175000017500000005465400000000000017363 0ustar00lieryanlieryanfrom rope.base.pynames import DefinedName import rope.base.builtins import rope.base.codeanalyze import rope.base.evaluate import rope.base.libutils import rope.base.oi.soi import rope.base.pyscopes from rope.base import ( pynamesdef as pynames, exceptions, ast, astutils, pyobjects, fscommands, arguments, utils, ) from rope.base.utils import pycompat try: unicode except NameError: unicode = str class PyFunction(pyobjects.PyFunction): def __init__(self, pycore, ast_node, parent): rope.base.pyobjects.AbstractFunction.__init__(self) rope.base.pyobjects.PyDefinedObject.__init__(self, pycore, ast_node, parent) self.arguments = self.ast_node.args self.parameter_pyobjects = pynames._Inferred( self._infer_parameters, self.get_module()._get_concluded_data() ) self.returned = pynames._Inferred(self._infer_returned) self.parameter_pynames = None def _create_structural_attributes(self): return {} def _create_concluded_attributes(self): return {} def _create_scope(self): return rope.base.pyscopes.FunctionScope(self.pycore, self, _FunctionVisitor) def _infer_parameters(self): pyobjects = rope.base.oi.soi.infer_parameter_objects(self) self._handle_special_args(pyobjects) return pyobjects def _infer_returned(self, args=None): return rope.base.oi.soi.infer_returned_object(self, args) def _handle_special_args(self, pyobjects): if len(pyobjects) == len(self.arguments.args): if self.arguments.vararg: pyobjects.append(rope.base.builtins.get_list()) if self.arguments.kwarg: pyobjects.append(rope.base.builtins.get_dict()) def _set_parameter_pyobjects(self, pyobjects): if pyobjects is not None: self._handle_special_args(pyobjects) self.parameter_pyobjects.set(pyobjects) def get_parameters(self): if self.parameter_pynames is None: result = {} for index, name in enumerate(self.get_param_names()): # TODO: handle tuple parameters result[name] = pynames.ParameterName(self, index) self.parameter_pynames = result return self.parameter_pynames def get_parameter(self, index): if index < len(self.parameter_pyobjects.get()): return self.parameter_pyobjects.get()[index] def get_returned_object(self, args): return self.returned.get(args) def get_name(self): return self.get_ast().name def get_param_names(self, special_args=True): # TODO: handle tuple parameters result = [ pycompat.get_ast_arg_arg(node) for node in self.arguments.args if isinstance(node, pycompat.ast_arg_type) ] if special_args: if self.arguments.vararg: result.append(pycompat.get_ast_arg_arg(self.arguments.vararg)) if self.arguments.kwarg: result.append(pycompat.get_ast_arg_arg(self.arguments.kwarg)) return result def get_kind(self): """Get function type It returns one of 'function', 'method', 'staticmethod' or 'classmethod' strs. """ scope = self.parent.get_scope() if isinstance(self.parent, PyClass): for decorator in self.decorators: pyname = rope.base.evaluate.eval_node(scope, decorator) if pyname == rope.base.builtins.builtins["staticmethod"]: return "staticmethod" if pyname == rope.base.builtins.builtins["classmethod"]: return "classmethod" return "method" return "function" @property def decorators(self): try: return getattr(self.ast_node, "decorator_list") except AttributeError: return getattr(self.ast_node, "decorators", None) class PyComprehension(pyobjects.PyComprehension): def __init__(self, pycore, ast_node, parent): self.visitor_class = _ComprehensionVisitor rope.base.pyobjects.PyObject.__init__(self, type_="Comp") rope.base.pyobjects.PyDefinedObject.__init__(self, pycore, ast_node, parent) def _create_scope(self): return rope.base.pyscopes.ComprehensionScope( self.pycore, self, _ComprehensionVisitor ) class PyClass(pyobjects.PyClass): def __init__(self, pycore, ast_node, parent): self.visitor_class = _ClassVisitor rope.base.pyobjects.AbstractClass.__init__(self) rope.base.pyobjects.PyDefinedObject.__init__(self, pycore, ast_node, parent) self.parent = parent self._superclasses = self.get_module()._get_concluded_data() def get_superclasses(self): if self._superclasses.get() is None: self._superclasses.set(self._get_bases()) return self._superclasses.get() def get_name(self): return self.get_ast().name def _create_concluded_attributes(self): result = {} for base in reversed(self.get_superclasses()): result.update(base.get_attributes()) return result def _get_bases(self): result = [] for base_name in self.ast_node.bases: base = rope.base.evaluate.eval_node(self.parent.get_scope(), base_name) if ( base is not None and base.get_object().get_type() == rope.base.pyobjects.get_base_type("Type") ): result.append(base.get_object()) return result def _create_scope(self): return rope.base.pyscopes.ClassScope(self.pycore, self) class PyModule(pyobjects.PyModule): def __init__(self, pycore, source=None, resource=None, force_errors=False): ignore = pycore.project.prefs.get("ignore_syntax_errors", False) syntax_errors = force_errors or not ignore self.has_errors = False try: source, node = self._init_source(pycore, source, resource) except exceptions.ModuleSyntaxError: self.has_errors = True if syntax_errors: raise else: source = "\n" node = ast.parse("\n") self.source_code = source self.star_imports = [] self.visitor_class = _GlobalVisitor self.coding = fscommands.read_str_coding(self.source_code) super(PyModule, self).__init__(pycore, node, resource) def _init_source(self, pycore, source_code, resource): filename = "string" if resource: filename = resource.path try: if source_code is None: source_bytes = resource.read_bytes() source_code = fscommands.file_data_to_unicode(source_bytes) else: if isinstance(source_code, unicode): source_bytes = fscommands.unicode_to_file_data(source_code) else: source_bytes = source_code ast_node = ast.parse(source_bytes, filename=filename) except SyntaxError as e: raise exceptions.ModuleSyntaxError(filename, e.lineno, e.msg) except UnicodeDecodeError as e: raise exceptions.ModuleSyntaxError(filename, 1, "%s" % (e.reason)) return source_code, ast_node @utils.prevent_recursion(lambda: {}) def _create_concluded_attributes(self): result = {} for star_import in self.star_imports: result.update(star_import.get_names()) return result def _create_scope(self): return rope.base.pyscopes.GlobalScope(self.pycore, self) @property @utils.saveit def lines(self): """A `SourceLinesAdapter`""" return rope.base.codeanalyze.SourceLinesAdapter(self.source_code) @property @utils.saveit def logical_lines(self): """A `LogicalLinesFinder`""" return rope.base.codeanalyze.CachingLogicalLineFinder(self.lines) def get_name(self): return rope.base.libutils.modname(self.get_resource()) class PyPackage(pyobjects.PyPackage): def __init__(self, pycore, resource=None, force_errors=False): self.resource = resource init_dot_py = self._get_init_dot_py() if init_dot_py is not None: ast_node = pycore.project.get_pymodule( init_dot_py, force_errors=force_errors ).get_ast() else: ast_node = ast.parse("\n") super(PyPackage, self).__init__(pycore, ast_node, resource) def _create_structural_attributes(self): result = {} modname = rope.base.libutils.modname(self.resource) extension_submodules = self.pycore._builtin_submodules(modname) for name, module in extension_submodules.items(): result[name] = rope.base.builtins.BuiltinName(module) if self.resource is None: return result for name, resource in self._get_child_resources().items(): result[name] = pynames.ImportedModule(self, resource=resource) return result def _create_concluded_attributes(self): result = {} init_dot_py = self._get_init_dot_py() if init_dot_py: init_object = self.pycore.project.get_pymodule(init_dot_py) result.update(init_object.get_attributes()) return result def _get_child_resources(self): result = {} for child in self.resource.get_children(): if child.is_folder(): result[child.name] = child elif child.name.endswith(".py") and child.name != "__init__.py": name = child.name[:-3] result[name] = child return result def _get_init_dot_py(self): if self.resource is not None and self.resource.has_child("__init__.py"): return self.resource.get_child("__init__.py") else: return None def _create_scope(self): return self.get_module().get_scope() def get_module(self): init_dot_py = self._get_init_dot_py() if init_dot_py: return self.pycore.project.get_pymodule(init_dot_py) return self class _AnnAssignVisitor(object): def __init__(self, scope_visitor): self.scope_visitor = scope_visitor self.assigned_ast = None self.type_hint = None def _AnnAssign(self, node): self.assigned_ast = node.value self.type_hint = node.annotation ast.walk(node.target, self) def _assigned(self, name, assignment=None): self.scope_visitor._assigned(name, assignment) def _Name(self, node): assignment = pynames.AssignmentValue( self.assigned_ast, assign_type=True, type_hint=self.type_hint ) self._assigned(node.id, assignment) def _Tuple(self, node): names = astutils.get_name_levels(node) for name, levels in names: assignment = None if self.assigned_ast is not None: assignment = pynames.AssignmentValue(self.assigned_ast, levels) self._assigned(name, assignment) def _Annotation(self, node): pass def _Attribute(self, node): pass def _Subscript(self, node): pass def _Slice(self, node): pass class _ExpressionVisitor(object): def __init__(self, scope_visitor): self.scope_visitor = scope_visitor def _assigned(self, name, assignment=None): self.scope_visitor._assigned(name, assignment) def _GeneratorExp(self, node): list_comp = PyComprehension( self.scope_visitor.pycore, node, self.scope_visitor.owner_object ) self.scope_visitor.defineds.append(list_comp) def _SetComp(self, node): self._GeneratorExp(node) def _ListComp(self, node): self._GeneratorExp(node) def _DictComp(self, node): self._GeneratorExp(node) def _NamedExpr(self, node): ast.walk(node.target, _AssignVisitor(self)) ast.walk(node.value, self) class _AssignVisitor(object): def __init__(self, scope_visitor): self.scope_visitor = scope_visitor self.assigned_ast = None def _Assign(self, node): self.assigned_ast = node.value for child_node in node.targets: ast.walk(child_node, self) ast.walk(node.value, _ExpressionVisitor(self.scope_visitor)) def _assigned(self, name, assignment=None): self.scope_visitor._assigned(name, assignment) def _Name(self, node): assignment = None if self.assigned_ast is not None: assignment = pynames.AssignmentValue(self.assigned_ast) self._assigned(node.id, assignment) def _Tuple(self, node): names = astutils.get_name_levels(node) for name, levels in names: assignment = None if self.assigned_ast is not None: assignment = pynames.AssignmentValue(self.assigned_ast, levels) self._assigned(name, assignment) def _Attribute(self, node): pass def _Subscript(self, node): pass def _Slice(self, node): pass class _ScopeVisitor(_ExpressionVisitor): def __init__(self, pycore, owner_object): _ExpressionVisitor.__init__(self, scope_visitor=self) self.pycore = pycore self.owner_object = owner_object self.names = {} self.defineds = [] def get_module(self): if self.owner_object is not None: return self.owner_object.get_module() else: return None def _ClassDef(self, node): pyclass = PyClass(self.pycore, node, self.owner_object) self.names[node.name] = pynames.DefinedName(pyclass) self.defineds.append(pyclass) def _FunctionDef(self, node): pyfunction = PyFunction(self.pycore, node, self.owner_object) for decorator in pyfunction.decorators: if isinstance(decorator, ast.Name) and decorator.id == "property": if isinstance(self, _ClassVisitor): type_ = rope.base.builtins.Property(pyfunction) arg = pynames.UnboundName( rope.base.pyobjects.PyObject(self.owner_object) ) def _eval(type_=type_, arg=arg): return type_.get_property_object( arguments.ObjectArguments([arg]) ) lineno = utils.guess_def_lineno(self.get_module(), node) self.names[node.name] = pynames.EvaluatedName( _eval, module=self.get_module(), lineno=lineno ) break else: self.names[node.name] = pynames.DefinedName(pyfunction) self.defineds.append(pyfunction) def _AsyncFunctionDef(self, node): return self._FunctionDef(node) def _Assign(self, node): ast.walk(node, _AssignVisitor(self)) def _AnnAssign(self, node): ast.walk(node, _AnnAssignVisitor(self)) def _AugAssign(self, node): pass def _For(self, node): names = self._update_evaluated( node.target, node.iter, ".__iter__().next()" # noqa ) for child in node.body + node.orelse: ast.walk(child, self) def _AsyncFor(self, node): return self._For(node) def _assigned(self, name, assignment): pyname = self.names.get(name, None) if pyname is None: pyname = pynames.AssignedName(module=self.get_module()) if isinstance(pyname, pynames.AssignedName): if assignment is not None: pyname.assignments.append(assignment) self.names[name] = pyname def _update_evaluated( self, targets, assigned, evaluation="", eval_type=False, type_hint=None ): result = {} if isinstance(targets, str): assignment = pynames.AssignmentValue(assigned, [], evaluation, eval_type) self._assigned(targets, assignment) else: names = astutils.get_name_levels(targets) for name, levels in names: assignment = pynames.AssignmentValue( assigned, levels, evaluation, eval_type ) self._assigned(name, assignment) return result def _With(self, node): for item in pycompat.get_ast_with_items(node): if item.optional_vars: self._update_evaluated( item.optional_vars, item.context_expr, ".__enter__()" ) for child in node.body: ast.walk(child, self) def _AsyncWith(self, node): return self._With(node) def _excepthandler(self, node): node_name_type = str if pycompat.PY3 else ast.Name if node.name is not None and isinstance(node.name, node_name_type): type_node = node.type if isinstance(node.type, ast.Tuple) and type_node.elts: type_node = type_node.elts[0] self._update_evaluated(node.name, type_node, eval_type=True) for child in node.body: ast.walk(child, self) def _ExceptHandler(self, node): self._excepthandler(node) def _Import(self, node): for import_pair in node.names: module_name = import_pair.name alias = import_pair.asname first_package = module_name.split(".")[0] if alias is not None: imported = pynames.ImportedModule(self.get_module(), module_name) if not self._is_ignored_import(imported): self.names[alias] = imported else: imported = pynames.ImportedModule(self.get_module(), first_package) if not self._is_ignored_import(imported): self.names[first_package] = imported def _ImportFrom(self, node): level = 0 if node.level: level = node.level imported_module = pynames.ImportedModule(self.get_module(), node.module, level) if self._is_ignored_import(imported_module): return if len(node.names) == 1 and node.names[0].name == "*": if isinstance(self.owner_object, PyModule): self.owner_object.star_imports.append(StarImport(imported_module)) else: for imported_name in node.names: imported = imported_name.name alias = imported_name.asname if alias is not None: imported = alias self.names[imported] = pynames.ImportedName( imported_module, imported_name.name ) def _is_ignored_import(self, imported_module): if not self.pycore.project.prefs.get("ignore_bad_imports", False): return False return not isinstance( imported_module.get_object(), rope.base.pyobjects.AbstractModule ) def _Global(self, node): module = self.get_module() for name in node.names: if module is not None: try: pyname = module[name] except exceptions.AttributeNotFoundError: pyname = pynames.AssignedName(node.lineno) self.names[name] = pyname class _ComprehensionVisitor(_ScopeVisitor): def _comprehension(self, node): ast.walk(node.target, self) ast.walk(node.iter, self) def _Name(self, node): if isinstance(node.ctx, ast.Store): self.names[node.id] = self._get_pyobject(node) def _get_pyobject(self, node): return pynames.AssignedName(lineno=node.lineno, module=self.get_module()) class _GlobalVisitor(_ScopeVisitor): def __init__(self, pycore, owner_object): super(_GlobalVisitor, self).__init__(pycore, owner_object) class _ClassVisitor(_ScopeVisitor): def __init__(self, pycore, owner_object): super(_ClassVisitor, self).__init__(pycore, owner_object) def _FunctionDef(self, node): _ScopeVisitor._FunctionDef(self, node) if len(node.args.args) > 0: first = node.args.args[0] new_visitor = None if isinstance(first, pycompat.ast_arg_type): new_visitor = _ClassInitVisitor(self, pycompat.get_ast_arg_arg(first)) if new_visitor is not None: for child in ast.get_child_nodes(node): ast.walk(child, new_visitor) class _FunctionVisitor(_ScopeVisitor): def __init__(self, pycore, owner_object): super(_FunctionVisitor, self).__init__(pycore, owner_object) self.returned_asts = [] self.generator = False def _Return(self, node): if node.value is not None: self.returned_asts.append(node.value) def _Yield(self, node): if node.value is not None: self.returned_asts.append(node.value) self.generator = True class _ClassInitVisitor(_AssignVisitor): def __init__(self, scope_visitor, self_name): super(_ClassInitVisitor, self).__init__(scope_visitor) self.self_name = self_name def _Attribute(self, node): if not isinstance(node.ctx, ast.Store): return if isinstance(node.value, ast.Name) and node.value.id == self.self_name: if node.attr not in self.scope_visitor.names: self.scope_visitor.names[node.attr] = pynames.AssignedName( lineno=node.lineno, module=self.scope_visitor.get_module() ) if self.assigned_ast is not None: pyname = self.scope_visitor.names[node.attr] if isinstance(pyname, pynames.AssignedName): pyname.assignments.append( pynames.AssignmentValue(self.assigned_ast) ) def _Tuple(self, node): if not isinstance(node.ctx, ast.Store): return for child in ast.get_child_nodes(node): ast.walk(child, self) def _Name(self, node): pass def _FunctionDef(self, node): pass def _ClassDef(self, node): pass def _For(self, node): pass def _With(self, node): pass class StarImport(object): def __init__(self, imported_module): self.imported_module = imported_module def get_names(self): result = {} imported = self.imported_module.get_object() for name in imported: if not name.startswith("_"): result[name] = pynames.ImportedName(self.imported_module, name) return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633802493.0 rope-0.22.0/rope/base/pyscopes.py0000664000175000017500000002666700000000000016552 0ustar00lieryanlieryanimport rope.base.builtins import rope.base.codeanalyze import rope.base.pynames from rope.base import ast, exceptions, utils from rope.refactor import patchedast class Scope(object): def __init__(self, pycore, pyobject, parent_scope): self.pycore = pycore self.pyobject = pyobject self.parent = parent_scope def get_names(self): """Return the names defined or imported in this scope""" return self.pyobject.get_attributes() def get_defined_names(self): """Return the names defined in this scope""" return self.pyobject._get_structural_attributes() def get_name(self, name): """Return name `PyName` defined in this scope""" if name not in self.get_names(): raise exceptions.NameNotFoundError("name %s not found" % name) return self.get_names()[name] def __getitem__(self, key): """The same as ``get_name(key)``""" return self.get_name(key) def __contains__(self, key): """The same as ``key in self.get_names()``""" return key in self.get_names() @utils.saveit def get_scopes(self): """Return the subscopes of this scope The returned scopes should be sorted by the order they appear. """ return self._create_scopes() def lookup(self, name): if name in self.get_names(): return self.get_names()[name] if self.parent is not None: return self.parent._propagated_lookup(name) return None def get_propagated_names(self): """Return the visible names of this scope Return the names defined in this scope that are visible from scopes containing this scope. This method returns the same dictionary returned by `get_names()` except for `ClassScope` which returns an empty dict. """ return self.get_names() def _propagated_lookup(self, name): if name in self.get_propagated_names(): return self.get_propagated_names()[name] if self.parent is not None: return self.parent._propagated_lookup(name) return None def _create_scopes(self): return [ pydefined.get_scope() for pydefined in self.pyobject._get_defined_objects() ] def _get_global_scope(self): current = self while current.parent is not None: current = current.parent return current def get_start(self): return self.pyobject.get_ast().lineno def get_body_start(self): body = self.pyobject.get_ast().body if body: return body[0].lineno return self.get_start() def get_end(self): pymodule = self._get_global_scope().pyobject return pymodule.logical_lines.logical_line_in(self.logical_end)[1] @utils.saveit def get_logical_end(self): global_scope = self._get_global_scope() return global_scope._scope_finder.find_scope_end(self) start = property(get_start) end = property(get_end) logical_end = property(get_logical_end) def get_kind(self): pass def get_region(self): self._calculate_scope_regions_for_module() node = self.pyobject.get_ast() region = patchedast.node_region(node) return region def _calculate_scope_regions_for_module(self): self._get_global_scope()._calculate_scope_regions() def in_region(self, offset): """Checks if offset is in scope region""" region = self.get_region() return region[0] < offset < region[1] class GlobalScope(Scope): def __init__(self, pycore, module): super(GlobalScope, self).__init__(pycore, module, None) self.names = module._get_concluded_data() def get_start(self): return 1 def get_kind(self): return "Module" def get_name(self, name): try: return self.pyobject[name] except exceptions.AttributeNotFoundError: if name in self.builtin_names: return self.builtin_names[name] raise exceptions.NameNotFoundError("name %s not found" % name) @utils.saveit def _calculate_scope_regions(self): source = self._get_source() patchedast.patch_ast(self.pyobject.get_ast(), source) def _get_source(self): return self.pyobject.source_code def get_names(self): if self.names.get() is None: result = dict(self.builtin_names) result.update(super(GlobalScope, self).get_names()) self.names.set(result) return self.names.get() def get_inner_scope_for_line(self, lineno, indents=None): return self._scope_finder.get_holding_scope(self, lineno, indents) def get_inner_scope_for_offset(self, offset): return self._scope_finder.get_holding_scope_for_offset(self, offset) @property @utils.saveit def _scope_finder(self): return _HoldingScopeFinder(self.pyobject) @property def builtin_names(self): return rope.base.builtins.builtins.get_attributes() class ComprehensionScope(Scope): def __init__(self, pycore, pyobject, visitor): super(ComprehensionScope, self).__init__( pycore, pyobject, pyobject.parent.get_scope() ) self.names = None self.returned_asts = None self.defineds = None self.visitor = visitor def _get_names(self): if self.names is None: self._visit_comprehension() return self.names def get_names(self): return self._get_names() def _visit_comprehension(self): if self.names is None: new_visitor = self.visitor(self.pycore, self.pyobject) for node in ast.get_child_nodes(self.pyobject.get_ast()): ast.walk(node, new_visitor) self.names = dict(self.parent.get_names()) self.names.update(new_visitor.names) self.defineds = new_visitor.defineds def get_logical_end(self): return self.get_start() logical_end = property(get_logical_end) def get_body_start(self): return self.get_start() class FunctionScope(Scope): def __init__(self, pycore, pyobject, visitor): super(FunctionScope, self).__init__( pycore, pyobject, pyobject.parent.get_scope() ) self.names = None self.returned_asts = None self.is_generator = None self.defineds = None self.visitor = visitor def _get_names(self): if self.names is None: self._visit_function() return self.names def _visit_function(self): if self.names is None: new_visitor = self.visitor(self.pycore, self.pyobject) for n in ast.get_child_nodes(self.pyobject.get_ast()): ast.walk(n, new_visitor) self.names = new_visitor.names self.names.update(self.pyobject.get_parameters()) self.returned_asts = new_visitor.returned_asts self.is_generator = new_visitor.generator self.defineds = new_visitor.defineds def _get_returned_asts(self): if self.names is None: self._visit_function() return self.returned_asts def _is_generator(self): if self.is_generator is None: self._get_returned_asts() return self.is_generator def get_names(self): return self._get_names() def _create_scopes(self): if self.defineds is None: self._visit_function() return [pydefined.get_scope() for pydefined in self.defineds] def get_kind(self): return "Function" def invalidate_data(self): for pyname in self.get_names().values(): if isinstance( pyname, (rope.base.pynames.AssignedName, rope.base.pynames.EvaluatedName), ): pyname.invalidate() class ClassScope(Scope): def __init__(self, pycore, pyobject): super(ClassScope, self).__init__(pycore, pyobject, pyobject.parent.get_scope()) def get_kind(self): return "Class" def get_propagated_names(self): return {} class _HoldingScopeFinder(object): def __init__(self, pymodule): self.pymodule = pymodule def get_indents(self, lineno): return rope.base.codeanalyze.count_line_indents(self.lines.get_line(lineno)) def _get_scope_indents(self, scope): return self.get_indents(scope.get_start()) def get_holding_scope(self, module_scope, lineno, line_indents=None): if line_indents is None: line_indents = self.get_indents(lineno) current_scope = module_scope new_scope = current_scope while new_scope is not None and ( new_scope.get_kind() == "Module" or self._get_scope_indents(new_scope) <= line_indents ): current_scope = new_scope if ( current_scope.get_start() == lineno and current_scope.get_kind() != "Module" ): return current_scope new_scope = None for scope in current_scope.get_scopes(): if scope.get_start() <= lineno: if lineno <= scope.get_end(): new_scope = scope break else: break return current_scope def _is_empty_line(self, lineno): line = self.lines.get_line(lineno) return line.strip() == "" or line.lstrip().startswith("#") def _get_body_indents(self, scope): return self.get_indents(scope.get_body_start()) @staticmethod def get_holding_scope_for_offset(scope, offset): for inner_scope in scope.get_scopes(): if inner_scope.in_region(offset): return _HoldingScopeFinder.get_holding_scope_for_offset( inner_scope, offset ) return scope def find_scope_end(self, scope): if not scope.parent: return self.lines.length() end = scope.pyobject.get_ast().body[-1].lineno scope_start = self.pymodule.logical_lines.logical_line_in(scope.start) if scope_start[1] >= end: # handling one-liners body_indents = self._get_scope_indents(scope) + 4 else: body_indents = self._get_body_indents(scope) for l in self.logical_lines.generate_starts( min(end + 1, self.lines.length()), self.lines.length() + 1 ): if not self._is_empty_line(l): if self.get_indents(l) < body_indents: return end else: end = l return end @property def lines(self): return self.pymodule.lines @property def code(self): return self.pymodule.source_code @property def logical_lines(self): return self.pymodule.logical_lines class TemporaryScope(Scope): """Currently used for list comprehensions and generator expressions These scopes do not appear in the `get_scopes()` method of their parent scopes. """ def __init__(self, pycore, parent_scope, names): super(TemporaryScope, self).__init__( pycore, parent_scope.pyobject, parent_scope ) self.names = names def get_names(self): return self.names def get_defined_names(self): return self.names def _create_scopes(self): return [] def get_kind(self): return "Temporary" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/resourceobserver.py0000664000175000017500000002412300000000000020265 0ustar00lieryanlieryanimport os class ResourceObserver(object): """Provides the interface for observing resources `ResourceObserver` can be registered using `Project. add_observer()`. But most of the time `FilteredResourceObserver` should be used. `ResourceObserver` report all changes passed to them and they don't report changes to all resources. For example if a folder is removed, it only calls `removed()` for that folder and not its contents. You can use `FilteredResourceObserver` if you are interested in changes only to a list of resources. And you want changes to be reported on individual resources. """ def __init__( self, changed=None, moved=None, created=None, removed=None, validate=None ): self.changed = changed self.moved = moved self.created = created self.removed = removed self._validate = validate def resource_changed(self, resource): """It is called when the resource changes""" if self.changed is not None: self.changed(resource) def resource_moved(self, resource, new_resource): """It is called when a resource is moved""" if self.moved is not None: self.moved(resource, new_resource) def resource_created(self, resource): """Is called when a new resource is created""" if self.created is not None: self.created(resource) def resource_removed(self, resource): """Is called when a new resource is removed""" if self.removed is not None: self.removed(resource) def validate(self, resource): """Validate the existence of this resource and its children. This function is called when rope need to update its resource cache about the files that might have been changed or removed by other processes. """ if self._validate is not None: self._validate(resource) class FilteredResourceObserver(object): """A useful decorator for `ResourceObserver` Most resource observers have a list of resources and are interested only in changes to those files. This class satisfies this need. It dispatches resource changed and removed messages. It performs these tasks: * Changes to files and folders are analyzed to check whether any of the interesting resources are changed or not. If they are, it reports these changes to `resource_observer` passed to the constructor. * When a resource is removed it checks whether any of the interesting resources are contained in that folder and reports them to `resource_observer`. * When validating a folder it validates all of the interesting files in that folder. Since most resource observers are interested in a list of resources that change over time, `add_resource` and `remove_resource` might be useful. """ def __init__(self, resource_observer, initial_resources=None, timekeeper=None): self.observer = resource_observer self.resources = {} if timekeeper is not None: self.timekeeper = timekeeper else: self.timekeeper = ChangeIndicator() if initial_resources is not None: for resource in initial_resources: self.add_resource(resource) def add_resource(self, resource): """Add a resource to the list of interesting resources""" if resource.exists(): self.resources[resource] = self.timekeeper.get_indicator(resource) else: self.resources[resource] = None def remove_resource(self, resource): """Add a resource to the list of interesting resources""" if resource in self.resources: del self.resources[resource] def clear_resources(self): """Removes all registered resources""" self.resources.clear() def resource_changed(self, resource): changes = _Changes() self._update_changes_caused_by_changed(changes, resource) self._perform_changes(changes) def _update_changes_caused_by_changed(self, changes, changed): if changed in self.resources: changes.add_changed(changed) if self._is_parent_changed(changed): changes.add_changed(changed.parent) def _update_changes_caused_by_moved(self, changes, resource, new_resource=None): if resource in self.resources: changes.add_removed(resource, new_resource) if new_resource in self.resources: changes.add_created(new_resource) if resource.is_folder(): for file in list(self.resources): if resource.contains(file): new_file = self._calculate_new_resource( resource, new_resource, file ) changes.add_removed(file, new_file) if self._is_parent_changed(resource): changes.add_changed(resource.parent) if new_resource is not None: if self._is_parent_changed(new_resource): changes.add_changed(new_resource.parent) def _is_parent_changed(self, child): return child.parent in self.resources def resource_moved(self, resource, new_resource): changes = _Changes() self._update_changes_caused_by_moved(changes, resource, new_resource) self._perform_changes(changes) def resource_created(self, resource): changes = _Changes() self._update_changes_caused_by_created(changes, resource) self._perform_changes(changes) def _update_changes_caused_by_created(self, changes, resource): if resource in self.resources: changes.add_created(resource) if self._is_parent_changed(resource): changes.add_changed(resource.parent) def resource_removed(self, resource): changes = _Changes() self._update_changes_caused_by_moved(changes, resource) self._perform_changes(changes) def _perform_changes(self, changes): for resource in changes.changes: self.observer.resource_changed(resource) self.resources[resource] = self.timekeeper.get_indicator(resource) for resource, new_resource in changes.moves.items(): self.resources[resource] = None if new_resource is not None: self.observer.resource_moved(resource, new_resource) else: self.observer.resource_removed(resource) for resource in changes.creations: self.observer.resource_created(resource) self.resources[resource] = self.timekeeper.get_indicator(resource) def validate(self, resource): changes = _Changes() for file in self._search_resource_moves(resource): if file in self.resources: self._update_changes_caused_by_moved(changes, file) for file in self._search_resource_changes(resource): if file in self.resources: self._update_changes_caused_by_changed(changes, file) for file in self._search_resource_creations(resource): if file in self.resources: changes.add_created(file) self._perform_changes(changes) def _search_resource_creations(self, resource): creations = set() if ( resource in self.resources and resource.exists() and self.resources[resource] is None ): creations.add(resource) if resource.is_folder(): for file in self.resources: if ( file.exists() and resource.contains(file) and self.resources[file] is None ): creations.add(file) return creations def _search_resource_moves(self, resource): all_moved = set() if resource in self.resources and not resource.exists(): all_moved.add(resource) if resource.is_folder(): for file in self.resources: if resource.contains(file): if not file.exists(): all_moved.add(file) moved = set(all_moved) for folder in [file for file in all_moved if file.is_folder()]: if folder in moved: for file in list(moved): if folder.contains(file): moved.remove(file) return moved def _search_resource_changes(self, resource): changed = set() if resource in self.resources and self._is_changed(resource): changed.add(resource) if resource.is_folder(): for file in self.resources: if file.exists() and resource.contains(file): if self._is_changed(file): changed.add(file) return changed def _is_changed(self, resource): if self.resources[resource] is None: return False return self.resources[resource] != self.timekeeper.get_indicator(resource) def _calculate_new_resource(self, main, new_main, resource): if new_main is None: return None diff = resource.path[len(main.path) :] return resource.project.get_resource(new_main.path + diff) class ChangeIndicator(object): def get_indicator(self, resource): """Return the modification time and size of a `Resource`.""" path = resource.real_path # on dos, mtime does not change for a folder when files are added if os.name != "posix" and os.path.isdir(path): return ( os.path.getmtime(path), len(os.listdir(path)), os.path.getsize(path), ) return (os.path.getmtime(path), os.path.getsize(path)) class _Changes(object): def __init__(self): self.changes = set() self.creations = set() self.moves = {} def add_changed(self, resource): self.changes.add(resource) def add_removed(self, resource, new_resource=None): self.moves[resource] = new_resource def add_created(self, resource): self.creations.add(resource) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636636936.0 rope-0.22.0/rope/base/resources.py0000664000175000017500000001677400000000000016715 0ustar00lieryanlieryan"""Files and folders in a project are represented as resource objects. Files and folders are access through `Resource` objects. `Resource` has two subclasses: `File` and `Folder`. What we care about is that refactorings and `rope.base.change.Change`s use resources. There are two options to create a `Resource` for a path in a project. Note that in these examples `path` is the path to a file or folder relative to the project's root. A project's root folder is represented by an empty string. 1) Use the `rope.base.Project.get_resource()` method. E.g.: myresource = myproject.get_resource(path) 2) Use the `rope.base.libutils` module. `libutils` has a function named `path_to_resource()`. It takes a project and a path: from rope.base import libutils myresource = libutils.path_to_resource(myproject, path) Once we have a `Resource`, we can retrieve information from it, like getting the path relative to the project's root (via `path`), reading from and writing to the resource, moving the resource, etc. """ import os import re import warnings from rope.base import change from rope.base import exceptions from rope.base import fscommands class Resource(object): """Represents files and folders in a project""" def __init__(self, project, path): self.project = project self._path = path def move(self, new_location): """Move resource to `new_location`""" self._perform_change( change.MoveResource(self, new_location), "Moving <%s> to <%s>" % (self.path, new_location), ) def remove(self): """Remove resource from the project""" self._perform_change(change.RemoveResource(self), "Removing <%s>" % self.path) def is_folder(self): """Return true if the resource is a folder""" def create(self): """Create this resource""" def exists(self): return os.path.exists(self.real_path) @property def parent(self): parent = "/".join(self.path.split("/")[0:-1]) return self.project.get_folder(parent) @property def path(self): """Return the path of this resource relative to the project root The path is the list of parent directories separated by '/' followed by the resource name. """ return self._path @property def name(self): """Return the name of this resource""" return self.path.split("/")[-1] @property def real_path(self): """Return the file system path of this resource""" return self.project._get_resource_path(self.path) def __eq__(self, obj): return self.__class__ == obj.__class__ and self.path == obj.path def __ne__(self, obj): return not self.__eq__(obj) def __hash__(self): return hash(self.path) def _perform_change(self, change_, description): changes = change.ChangeSet(description) changes.add_change(change_) self.project.do(changes) class File(Resource): """Represents a file""" def __init__(self, project, name): # from rope.base.project import Project # self.project = Project() super(File, self).__init__(project, name) def read(self): data = self.read_bytes() try: return fscommands.file_data_to_unicode(data) except UnicodeDecodeError as e: raise exceptions.ModuleDecodeError(self.path, e.reason) def read_bytes(self): if not hasattr(self.project.fscommands, "read"): warnings.warn( "FileSystemCommands should implement read() method", DeprecationWarning, stacklevel=2, ) with open(self.real_path, "rb") as handle: return handle.read() return self.project.fscommands.read(self.real_path) def write(self, contents): try: if contents == self.read(): return except IOError: pass self._perform_change( change.ChangeContents(self, contents), "Writing file <%s>" % self.path ) def is_folder(self): return False def create(self): self.parent.create_file(self.name) class Folder(Resource): """Represents a folder""" def __init__(self, project, name): super(Folder, self).__init__(project, name) def is_folder(self): return True def get_children(self): """Return the children of this folder""" try: children = os.listdir(self.real_path) except OSError: return [] result = [] for name in children: try: child = self.get_child(name) except exceptions.ResourceNotFoundError: continue if not self.project.is_ignored(child): result.append(self.get_child(name)) return result def create_file(self, file_name): self._perform_change( change.CreateFile(self, file_name), "Creating file <%s>" % self._get_child_path(file_name), ) return self.get_child(file_name) def create_folder(self, folder_name): self._perform_change( change.CreateFolder(self, folder_name), "Creating folder <%s>" % self._get_child_path(folder_name), ) return self.get_child(folder_name) def _get_child_path(self, name): if self.path: return self.path + "/" + name else: return name def get_child(self, name): return self.project.get_resource(self._get_child_path(name)) def has_child(self, name): try: self.get_child(name) return True except exceptions.ResourceNotFoundError: return False def get_files(self): return [ resource for resource in self.get_children() if not resource.is_folder() ] def get_folders(self): return [resource for resource in self.get_children() if resource.is_folder()] def contains(self, resource): if self == resource: return False return self.path == "" or resource.path.startswith(self.path + "/") def create(self): self.parent.create_folder(self.name) class _ResourceMatcher(object): def __init__(self): self.patterns = [] self._compiled_patterns = [] def set_patterns(self, patterns): """Specify which resources to match `patterns` is a `list` of `str` that can contain ``*`` and ``?`` signs for matching resource names. """ self._compiled_patterns = None self.patterns = patterns def _add_pattern(self, pattern): re_pattern = ( pattern.replace(".", "\\.") .replace("*", "[^/]*") .replace("?", "[^/]") .replace("//", "/(.*/)?") ) re_pattern = "^(.*/)?" + re_pattern + "(/.*)?$" self.compiled_patterns.append(re.compile(re_pattern)) def does_match(self, resource): for pattern in self.compiled_patterns: if pattern.match(resource.path): return True path = os.path.join(resource.project.address, *resource.path.split("/")) if os.path.islink(path): return True return False @property def compiled_patterns(self): if self._compiled_patterns is None: self._compiled_patterns = [] for pattern in self.patterns: self._add_pattern(pattern) return self._compiled_patterns ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632705896.0 rope-0.22.0/rope/base/simplify.py0000664000175000017500000000356100000000000016525 0ustar00lieryanlieryan"""A module to ease code analysis This module is here to help source code analysis. """ import re from rope.base import codeanalyze, utils @utils.cached(7) def real_code(source): """Simplify `source` for analysis It replaces: * comments with spaces * strs with a new str filled with spaces * implicit and explicit continuations with spaces * tabs and semicolons with spaces The resulting code is a lot easier to analyze if we are interested only in offsets. """ collector = codeanalyze.ChangeCollector(source) for start, end, matchgroups in ignored_regions(source): if source[start] == "#": replacement = " " * (end - start) elif "f" in matchgroups.get("prefix", "").lower(): replacement = None else: replacement = '"%s"' % (" " * (end - start - 2)) if replacement is not None: collector.add_change(start, end, replacement) source = collector.get_changed() or source collector = codeanalyze.ChangeCollector(source) parens = 0 for match in _parens.finditer(source): i = match.start() c = match.group() if c in "({[": parens += 1 if c in ")}]": parens -= 1 if c == "\n" and parens > 0: collector.add_change(i, i + 1, " ") source = collector.get_changed() or source return source.replace("\\\n", " ").replace("\t", " ").replace(";", "\n") @utils.cached(7) def ignored_regions(source): """Return ignored regions like strings and comments in `source`""" return [ (match.start(), match.end(), match.groupdict()) for match in _str.finditer(source) ] _str = re.compile( "|".join( [ codeanalyze.get_comment_pattern(), codeanalyze.get_any_string_pattern(), ] ) ) _parens = re.compile(r"[\({\[\]}\)\n]") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/stdmods.py0000664000175000017500000000344600000000000016350 0ustar00lieryanlieryanimport os import re import sys from rope.base import utils from rope.base.utils import pycompat def _stdlib_path(): if pycompat.PY2: from distutils import sysconfig return sysconfig.get_python_lib(standard_lib=True, plat_specific=True) elif pycompat.PY3: import inspect return os.path.dirname(inspect.getsourcefile(inspect)) @utils.cached(1) def standard_modules(): return python_modules() | dynload_modules() @utils.cached(1) def python_modules(): result = set() lib_path = _stdlib_path() if os.path.exists(lib_path): for name in os.listdir(lib_path): path = os.path.join(lib_path, name) if os.path.isdir(path): if "-" not in name: result.add(name) else: if name.endswith(".py"): result.add(name[:-3]) return result def normalize_so_name(name): """ Handle different types of python installations """ if "cpython" in name: return os.path.splitext(os.path.splitext(name)[0])[0] # XXX: Special handling for Fedora python2 distribution # See: https://github.com/python-rope/rope/issues/211 if name == "timemodule.so": return "time" return os.path.splitext(name)[0] @utils.cached(1) def dynload_modules(): result = set(sys.builtin_module_names) dynload_path = os.path.join(_stdlib_path(), "lib-dynload") if os.path.exists(dynload_path): for name in os.listdir(dynload_path): path = os.path.join(dynload_path, name) if os.path.isfile(path): if name.endswith(".dll"): result.add(normalize_so_name(name)) if name.endswith(".so"): result.add(normalize_so_name(name)) return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/taskhandle.py0000664000175000017500000000546600000000000017015 0ustar00lieryanlieryanfrom rope.base import exceptions class TaskHandle(object): def __init__(self, name="Task", interrupts=True): """Construct a TaskHandle If `interrupts` is `False` the task won't be interrupted by calling `TaskHandle.stop()`. """ self.name = name self.interrupts = interrupts self.stopped = False self.job_sets = [] self.observers = [] def stop(self): """Interrupts the refactoring""" if self.interrupts: self.stopped = True self._inform_observers() def current_jobset(self): """Return the current `JobSet`""" if self.job_sets: return self.job_sets[-1] def add_observer(self, observer): """Register an observer for this task handle The observer is notified whenever the task is stopped or a job gets finished. """ self.observers.append(observer) def is_stopped(self): return self.stopped def get_jobsets(self): return self.job_sets def create_jobset(self, name="JobSet", count=None): result = JobSet(self, name=name, count=count) self.job_sets.append(result) self._inform_observers() return result def _inform_observers(self): for observer in list(self.observers): observer() class JobSet(object): def __init__(self, handle, name, count): self.handle = handle self.name = name self.count = count self.done = 0 self.job_name = None def started_job(self, name): self.check_status() self.job_name = name self.handle._inform_observers() def finished_job(self): self.check_status() self.done += 1 self.handle._inform_observers() self.job_name = None def check_status(self): if self.handle.is_stopped(): raise exceptions.InterruptedTaskError() def get_active_job_name(self): return self.job_name def get_percent_done(self): if self.count is not None and self.count > 0: percent = self.done * 100 // self.count return min(percent, 100) def get_name(self): return self.name class NullTaskHandle(object): def __init__(self): pass def is_stopped(self): return False def stop(self): pass def create_jobset(self, *args, **kwds): return NullJobSet() def get_jobsets(self): return [] def add_observer(self, observer): pass class NullJobSet(object): def started_job(self, name): pass def finished_job(self): pass def check_status(self): pass def get_active_job_name(self): pass def get_percent_done(self): pass def get_name(self): pass ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1637593692.35939 rope-0.22.0/rope/base/utils/0000775000175000017500000000000000000000000015452 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632877237.0 rope-0.22.0/rope/base/utils/__init__.py0000664000175000017500000000766500000000000017601 0ustar00lieryanlieryanimport sys import warnings def saveit(func): """A decorator that caches the return value of a function""" name = "_" + func.__name__ def _wrapper(self, *args, **kwds): if not hasattr(self, name): setattr(self, name, func(self, *args, **kwds)) return getattr(self, name) return _wrapper cacheit = saveit def prevent_recursion(default): """A decorator that returns the return value of `default` in recursions""" def decorator(func): name = "_calling_%s_" % func.__name__ def newfunc(self, *args, **kwds): if getattr(self, name, False): return default() setattr(self, name, True) try: return func(self, *args, **kwds) finally: setattr(self, name, False) return newfunc return decorator def ignore_exception(exception_class): """A decorator that ignores `exception_class` exceptions""" def _decorator(func): def newfunc(*args, **kwds): try: return func(*args, **kwds) except exception_class: pass return newfunc return _decorator def deprecated(message=None): """A decorator for deprecated functions""" def _decorator(func, message=message): if message is None: message = "%s is deprecated" % func.__name__ def newfunc(*args, **kwds): warnings.warn(message, DeprecationWarning, stacklevel=2) return func(*args, **kwds) return newfunc return _decorator def cached(size): """A caching decorator based on parameter objects""" def decorator(func): cached_func = _Cached(func, size) return lambda *a, **kw: cached_func(*a, **kw) return decorator class _Cached(object): def __init__(self, func, count): self.func = func self.cache = [] self.count = count def __call__(self, *args, **kwds): key = (args, kwds) for cached_key, cached_result in self.cache: if cached_key == key: return cached_result result = self.func(*args, **kwds) self.cache.append((key, result)) if len(self.cache) > self.count: del self.cache[0] return result def resolve(str_or_obj): """Returns object from string""" from rope.base.utils.pycompat import string_types if not isinstance(str_or_obj, string_types): return str_or_obj if "." not in str_or_obj: str_or_obj += "." mod_name, obj_name = str_or_obj.rsplit(".", 1) __import__(mod_name) mod = sys.modules[mod_name] return getattr(mod, obj_name) if obj_name else mod def guess_def_lineno(module, node): """Find the line number for a function or class definition. `node` may be either an ast.FunctionDef, ast.AsyncFunctionDef, or ast.ClassDef Python 3.8 simply provides this to us, but in earlier versions the ast node.lineno points to the first decorator rather than the actual definition, so we try our best to find where the definitions are. This is to workaround bpo-33211 (https://bugs.python.org/issue33211) """ def is_inline_body(): # class Foo(object): # def inline_body(): pass # ^ ^--- body_col_offset # `--- indent_col_offset # def not_inline_body(): # pass # ^--- body_col_offset == indent_col_offset line = module.lines.get_line(node.body[0].lineno) indent_col_offset = len(line) - len(line.lstrip()) body_col_offset = node.body[0].col_offset return indent_col_offset < body_col_offset if sys.version_info >= (3, 8) or not hasattr(node, "body"): return node.lineno possible_def_line = ( node.body[0].lineno if is_inline_body() else node.body[0].lineno - 1 ) return module.logical_lines.logical_line_in(possible_def_line)[0] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/utils/datastructures.py0000664000175000017500000000355500000000000021111 0ustar00lieryanlieryan# this snippet was taken from this link # http://code.activestate.com/recipes/576694/ try: from collections.abc import MutableSet except ImportError: from collections import MutableSet class OrderedSet(MutableSet): def __init__(self, iterable=None): self.end = end = [] end += [None, end, end] # sentinel # node for doubly linked list self.map = {} # key --> [key, prev, next] if iterable is not None: self |= iterable def __len__(self): return len(self.map) def __contains__(self, key): return key in self.map def add(self, key): if key not in self.map: end = self.end curr = end[1] curr[2] = end[1] = self.map[key] = [key, curr, end] def intersection(self, set_b): return OrderedSet([item for item in self if item in set_b]) def discard(self, key): if key in self.map: key, prev, next = self.map.pop(key) prev[2] = next next[1] = prev def __iter__(self): end = self.end curr = end[2] while curr is not end: yield curr[0] curr = curr[2] def __reversed__(self): end = self.end curr = end[1] while curr is not end: yield curr[0] curr = curr[1] def pop(self, last=True): if not self: raise KeyError("set is empty") key = self.end[1][0] if last else self.end[2][0] self.discard(key) return key def __repr__(self): if not self: return "%s()" % (self.__class__.__name__,) return "%s(%r)" % (self.__class__.__name__, list(self)) def __eq__(self, other): if isinstance(other, OrderedSet): return len(self) == len(other) and list(self) == list(other) return set(self) == set(other) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/base/utils/pycompat.py0000664000175000017500000000224400000000000017662 0ustar00lieryanlieryanimport sys import ast # from rope.base import ast PY2 = sys.version_info[0] == 2 PY27 = sys.version_info[0:2] >= (2, 7) PY3 = sys.version_info[0] == 3 PY34 = sys.version_info[0:2] >= (3, 4) PY36 = sys.version_info[0:2] >= (3, 6) try: str = unicode except NameError: # PY3 str = str string_types = (str,) import builtins ast_arg_type = ast.arg def execfile(fn, global_vars=None, local_vars=None): with open(fn) as f: code = compile(f.read(), fn, "exec") exec(code, global_vars or {}, local_vars) def get_ast_arg_arg(node): if isinstance( node, string_types ): # TODO: G21: Understand the Algorithm (Where it's used?) return node return node.arg def get_ast_with_items(node): return node.items else: # PY2 string_types = (basestring,) builtins = __import__("__builtin__") ast_arg_type = ast.Name execfile = execfile def get_ast_arg_arg(node): if isinstance(node, string_types): # Python2 arguments.vararg, arguments.kwarg return node return node.id def get_ast_with_items(node): return [node] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637501104.0 rope-0.22.0/rope/base/worder.py0000664000175000017500000005344300000000000016177 0ustar00lieryanlieryanimport bisect import keyword import rope.base.simplify MINIMAL_LEN_FOR_AS = 5 def get_name_at(resource, offset): source_code = resource.read() word_finder = Worder(source_code) return word_finder.get_word_at(offset) class Worder(object): """A class for finding boundaries of words and expressions 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): simplified = rope.base.simplify.real_code(code) self.code_finder = _RealFinder(simplified, code) self.handle_ignores = handle_ignores self.code = code def _init_ignores(self): ignores = rope.base.simplify.ignored_regions(self.code) self.dumb_finder = _RealFinder(self.code, self.code) self.starts = [ignored[0] for ignored in ignores] self.ends = [ignored[1] for ignored in ignores] def _context_call(self, name, offset): if self.handle_ignores: if not hasattr(self, "starts"): self._init_ignores() start = bisect.bisect(self.starts, offset) if start > 0 and offset < self.ends[start - 1]: return getattr(self.dumb_finder, name)(offset) return getattr(self.code_finder, name)(offset) def get_primary_at(self, offset): return self._context_call("get_primary_at", offset) def get_word_at(self, offset): return self._context_call("get_word_at", offset) def get_primary_range(self, offset): return self._context_call("get_primary_range", offset) def get_splitted_primary_before(self, offset): return self._context_call("get_splitted_primary_before", offset) def get_word_range(self, offset): return self._context_call("get_word_range", offset) def is_function_keyword_parameter(self, offset): return self.code_finder.is_function_keyword_parameter(offset) def is_a_class_or_function_name_in_header(self, offset): return self.code_finder.is_a_class_or_function_name_in_header(offset) def is_from_statement_module(self, offset): return self.code_finder.is_from_statement_module(offset) def is_from_aliased(self, offset): return self.code_finder.is_from_aliased(offset) def is_import_statement_aliased_module(self, offset): return self.code_finder.is_import_statement_aliased_module(offset) def find_parens_start_from_inside(self, offset): return self.code_finder.find_parens_start_from_inside(offset) def is_a_name_after_from_import(self, offset): return self.code_finder.is_a_name_after_from_import(offset) def is_from_statement(self, offset): return self.code_finder.is_from_statement(offset) def get_from_aliased(self, offset): return self.code_finder.get_from_aliased(offset) def is_import_statement(self, offset): return self.code_finder.is_import_statement(offset) def is_assigned_here(self, offset): return self.code_finder.is_assigned_here(offset) def is_a_function_being_called(self, offset): return self.code_finder.is_a_function_being_called(offset) def get_word_parens_range(self, offset): return self.code_finder.get_word_parens_range(offset) def is_name_assigned_in_class_body(self, offset): return self.code_finder.is_name_assigned_in_class_body(offset) def is_on_function_call_keyword(self, offset): return self.code_finder.is_on_function_call_keyword(offset) def _find_parens_start(self, offset): return self.code_finder._find_parens_start(offset) def get_parameters(self, first, last): return self.code_finder.get_parameters(first, last) def get_from_module(self, offset): return self.code_finder.get_from_module(offset) def is_assigned_in_a_tuple_assignment(self, offset): return self.code_finder.is_assigned_in_a_tuple_assignment(offset) def get_assignment_type(self, offset): return self.code_finder.get_assignment_type(offset) def get_function_and_args_in_header(self, offset): return self.code_finder.get_function_and_args_in_header(offset) def get_lambda_and_args(self, offset): return self.code_finder.get_lambda_and_args(offset) def find_function_offset(self, offset): return self.code_finder.find_function_offset(offset) class _RealFinder(object): def __init__(self, code, raw): self.code = code self.raw = raw def _find_word_start(self, offset): current_offset = offset while current_offset >= 0 and self._is_id_char(current_offset): current_offset -= 1 return current_offset + 1 def _find_word_end(self, offset): while offset + 1 < len(self.code) and self._is_id_char(offset + 1): offset += 1 return offset def _find_last_non_space_char(self, offset): while offset >= 0 and self.code[offset].isspace(): if self.code[offset] == "\n": return offset offset -= 1 return max(-1, offset) def get_word_at(self, offset): offset = self._get_fixed_offset(offset) return self.raw[self._find_word_start(offset) : self._find_word_end(offset) + 1] def _get_fixed_offset(self, offset): if offset >= len(self.code): return offset - 1 if not self._is_id_char(offset): if offset > 0 and self._is_id_char(offset - 1): return offset - 1 if offset < len(self.code) - 1 and self._is_id_char(offset + 1): return offset + 1 return offset def _is_id_char(self, offset): return self.code[offset].isalnum() or self.code[offset] == "_" def _find_string_start(self, offset): kind = self.code[offset] try: return self.code.rindex(kind, 0, offset) except ValueError: return 0 def _find_parens_start(self, offset): offset = self._find_last_non_space_char(offset - 1) while offset >= 0 and self.code[offset] not in "[({": if self.code[offset] not in ":,": offset = self._find_primary_start(offset) offset = self._find_last_non_space_char(offset - 1) return offset def _find_atom_start(self, offset): old_offset = offset if self.code[offset] == "\n": return offset + 1 if self.code[offset].isspace(): offset = self._find_last_non_space_char(offset) if self.code[offset] in "'\"": return self._find_string_start(offset) if self.code[offset] in ")]}": return self._find_parens_start(offset) if self._is_id_char(offset): return self._find_word_start(offset) return old_offset def _find_primary_without_dot_start(self, offset): """It tries to find the undotted primary start It is different from `self._get_atom_start()` in that it follows function calls, too; such as in ``f(x)``. """ last_atom = offset offset = self._find_last_non_space_char(last_atom) while offset > 0 and self.code[offset] in ")]": last_atom = self._find_parens_start(offset) offset = self._find_last_non_space_char(last_atom - 1) if offset >= 0 and (self.code[offset] in "\"'})]" or self._is_id_char(offset)): atom_start = self._find_atom_start(offset) if not keyword.iskeyword(self.code[atom_start : offset + 1]) or ( offset + 1 < len(self.code) and self._is_id_char(offset + 1) ): return atom_start return last_atom def _find_primary_start(self, offset): if offset >= len(self.code): offset = len(self.code) - 1 if self.code[offset] != ".": offset = self._find_primary_without_dot_start(offset) else: offset = offset + 1 while offset > 0: prev = self._find_last_non_space_char(offset - 1) if offset <= 0 or self.code[prev] != ".": break # Check if relative import # XXX: Looks like a hack... prev_word_end = self._find_last_non_space_char(prev - 1) if self.code[prev_word_end - 3 : prev_word_end + 1] == "from": offset = prev break offset = self._find_primary_without_dot_start(prev - 1) if not self._is_id_char(offset): break return offset def get_primary_at(self, offset): offset = self._get_fixed_offset(offset) start, end = self.get_primary_range(offset) return self.raw[start:end].strip() def get_splitted_primary_before(self, offset): """returns expression, starting, starting_offset This function is used in `rope.codeassist.assist` function. """ if offset == 0: return ("", "", 0) end = offset - 1 word_start = self._find_atom_start(end) real_start = self._find_primary_start(end) if self.code[word_start:offset].strip() == "": word_start = end if self.code[end].isspace(): word_start = end if self.code[real_start:word_start].strip() == "": real_start = word_start if real_start == word_start == end and not self._is_id_char(end): return ("", "", offset) if real_start == word_start: return ("", self.raw[word_start:offset], word_start) else: if self.code[end] == ".": return (self.raw[real_start:end], "", offset) last_dot_position = word_start if self.code[word_start] != ".": last_dot_position = self._find_last_non_space_char(word_start - 1) last_char_position = self._find_last_non_space_char(last_dot_position - 1) if self.code[word_start].isspace(): word_start = offset return ( self.raw[real_start : last_char_position + 1], self.raw[word_start:offset], word_start, ) def _get_line_start(self, offset): try: return self.code.rindex("\n", 0, offset + 1) except ValueError: return 0 def _get_line_end(self, offset): try: return self.code.index("\n", offset) except ValueError: return len(self.code) def is_name_assigned_in_class_body(self, offset): word_start = self._find_word_start(offset - 1) word_end = self._find_word_end(offset) + 1 if "." in self.code[word_start:word_end]: return False line_start = self._get_line_start(word_start) line = self.code[line_start:word_start].strip() return not line and self.get_assignment_type(offset) == "=" def is_a_class_or_function_name_in_header(self, offset): word_start = self._find_word_start(offset - 1) line_start = self._get_line_start(word_start) prev_word = self.code[line_start:word_start].strip() return prev_word in ["def", "class"] def _find_first_non_space_char(self, offset): if offset >= len(self.code): return len(self.code) while offset < len(self.code) and self.code[offset].isspace(): if self.code[offset] == "\n": return offset offset += 1 return offset def is_a_function_being_called(self, offset): word_end = self._find_word_end(offset) + 1 next_char = self._find_first_non_space_char(word_end) return ( next_char < len(self.code) and self.code[next_char] == "(" and not self.is_a_class_or_function_name_in_header(offset) ) def _find_import_end(self, start): return self._get_line_end(start) def is_import_statement(self, offset): try: last_import = self.code.rindex("import ", 0, offset) except ValueError: return False line_start = self._get_line_start(last_import) return ( self._find_import_end(last_import + 7) >= offset and self._find_word_start(line_start) == last_import ) def is_from_statement(self, offset): try: last_from = self.code.rindex("from ", 0, offset) from_import = self.code.index(" import ", last_from) from_names = from_import + 8 except ValueError: return False from_names = self._find_first_non_space_char(from_names) return self._find_import_end(from_names) >= offset def is_from_statement_module(self, offset): if offset >= len(self.code) - 1: return False stmt_start = self._find_primary_start(offset) line_start = self._get_line_start(stmt_start) prev_word = self.code[line_start:stmt_start].strip() return prev_word == "from" def is_import_statement_aliased_module(self, offset): if not self.is_import_statement(offset): return False try: line_start = self._get_line_start(offset) import_idx = self.code.rindex("import", line_start, offset) imported_names = import_idx + 7 except ValueError: return False # Check if the offset is within the imported names if ( imported_names - 1 > offset or self._find_import_end(imported_names) < offset ): return False try: 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": line_start = self._get_line_start(offset - 1) else: line_start = self._get_line_start(offset) last_from = self.code.rindex("from ", line_start, offset) from_import = self.code.index(" import ", last_from) from_names = from_import + 8 except ValueError: return False if from_names - 1 > offset: return False return self._find_import_end(from_names) >= offset def get_from_module(self, offset): try: last_from = self.code.rindex("from ", 0, offset) import_offset = self.code.index(" import ", last_from) end = self._find_last_non_space_char(import_offset) return self.get_primary_at(end) except ValueError: pass def is_from_aliased(self, offset): if not self.is_a_name_after_from_import(offset): return False try: end = self._find_word_end(offset) 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 get_from_aliased(self, offset): try: end = self._find_word_end(offset) as_ = self._find_word_end(end + 1) alias = self._find_word_end(as_ + 1) start = self._find_word_start(alias) return self.raw[start : alias + 1] except ValueError: pass def is_function_keyword_parameter(self, offset): word_end = self._find_word_end(offset) if word_end + 1 == len(self.code): return False next_char = self._find_first_non_space_char(word_end + 1) equals = self.code[next_char : next_char + 2] if equals == "==" or not equals.startswith("="): return False word_start = self._find_word_start(offset) prev_char = self._find_last_non_space_char(word_start - 1) return prev_char - 1 >= 0 and self.code[prev_char] in ",(" def is_on_function_call_keyword(self, offset): stop = self._get_line_start(offset) if self._is_id_char(offset): offset = self._find_word_start(offset) - 1 offset = self._find_last_non_space_char(offset) if offset <= stop or self.code[offset] not in "(,": return False parens_start = self.find_parens_start_from_inside(offset) return stop < parens_start def find_parens_start_from_inside(self, offset): stop = self._get_line_start(offset) while offset > stop: if self.code[offset] == "(": break if self.code[offset] != ",": offset = self._find_primary_start(offset) offset -= 1 return max(stop, offset) def is_assigned_here(self, offset): return self.get_assignment_type(offset) is not None def get_assignment_type(self, offset): # XXX: does not handle tuple assignments word_end = self._find_word_end(offset) next_char = self._find_first_non_space_char(word_end + 1) single = self.code[next_char : next_char + 1] double = self.code[next_char : next_char + 2] triple = self.code[next_char : next_char + 3] if double not in ("==", "<=", ">=", "!="): for op in [single, double, triple]: if op.endswith("="): return op def get_primary_range(self, offset): start = self._find_primary_start(offset) end = self._find_word_end(offset) + 1 return (start, end) def get_word_range(self, offset): offset = max(0, offset) start = self._find_word_start(offset) end = self._find_word_end(offset) + 1 return (start, end) def get_word_parens_range(self, offset, opening="(", closing=")"): end = self._find_word_end(offset) start_parens = self.code.index(opening, end) index = start_parens open_count = 0 while index < len(self.code): if self.code[index] == opening: open_count += 1 if self.code[index] == closing: open_count -= 1 if open_count == 0: return (start_parens, index + 1) index += 1 return (start_parens, index) def get_parameters(self, first, last): keywords = [] args = [] current = self._find_last_non_space_char(last - 1) while current > first: primary_start = current current = self._find_primary_start(current) while current != first and ( self.code[current] not in "=," or self.code[current - 1] in "=!<>" ): current = self._find_last_non_space_char(current - 1) primary = self.raw[current + 1 : primary_start + 1].strip() if self.code[current] == "=": primary_start = current - 1 current -= 1 while current != first and self.code[current] not in ",": current = self._find_last_non_space_char(current - 1) param_name = self.raw[current + 1 : primary_start + 1].strip() keywords.append((param_name, primary)) else: args.append(primary) current = self._find_last_non_space_char(current - 1) args.reverse() keywords.reverse() return args, keywords def is_assigned_in_a_tuple_assignment(self, offset): start = self._get_line_start(offset) end = self._get_line_end(offset) primary_start = self._find_primary_start(offset) primary_end = self._find_word_end(offset) prev_char_offset = self._find_last_non_space_char(primary_start - 1) next_char_offset = self._find_first_non_space_char(primary_end + 1) next_char = prev_char = "" if prev_char_offset >= start: prev_char = self.code[prev_char_offset] if next_char_offset < end: next_char = self.code[next_char_offset] try: equals_offset = self.code.index("=", start, end) except ValueError: return False if prev_char not in "(," and next_char not in ",)": return False parens_start = self.find_parens_start_from_inside(offset) # XXX: only handling (x, y) = value return offset < equals_offset and self.code[start:parens_start].strip() == "" def get_function_and_args_in_header(self, offset): offset = self.find_function_offset(offset) lparens, rparens = self.get_word_parens_range(offset) return self.raw[offset : rparens + 1] def find_function_offset(self, offset, definition="def "): while True: offset = self.code.index(definition, offset) if offset == 0 or not self._is_id_char(offset - 1): break offset += 1 def_ = offset + 4 return self._find_first_non_space_char(def_) def get_lambda_and_args(self, offset): offset = self.find_function_offset(offset, definition="lambda ") lparens, rparens = self.get_word_parens_range(offset, opening=" ", closing=":") return self.raw[offset : rparens + 1] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3633902 rope-0.22.0/rope/contrib/0000775000175000017500000000000000000000000015040 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1599360855.0 rope-0.22.0/rope/contrib/__init__.py0000664000175000017500000000025100000000000017147 0ustar00lieryanlieryan"""rope IDE tools package This package contains modules that can be used in IDEs but do not depend on the UI. So these modules will be used by `rope.ui` modules. """ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632885830.0 rope-0.22.0/rope/contrib/autoimport.py0000664000175000017500000001771600000000000017631 0ustar00lieryanlieryanimport re from rope.base import builtins from rope.base import exceptions from rope.base import libutils from rope.base import pynames from rope.base import pyobjects from rope.base import resources from rope.base import resourceobserver from rope.base import taskhandle from rope.refactor import importutils class AutoImport(object): """A class for finding the module that provides a name This class maintains a cache of global names in python modules. Note that this cache is not accurate and might be out of date. """ def __init__(self, project, observe=True, underlined=False): """Construct an AutoImport object If `observe` is `True`, listen for project changes and update the cache. If `underlined` is `True`, underlined names are cached, too. """ self.project = project self.underlined = underlined self.names = project.data_files.read_data("globalnames") if self.names is None: self.names = {} project.data_files.add_write_hook(self._write) # XXX: using a filtered observer observer = resourceobserver.ResourceObserver( changed=self._changed, moved=self._moved, removed=self._removed ) if observe: project.add_observer(observer) def import_assist(self, starting): """Return a list of ``(name, module)`` tuples This function tries to find modules that have a global name that starts with `starting`. """ # XXX: breaking if gave up! use generators result = [] for module in self.names: for global_name in self.names[module]: if global_name.startswith(starting): result.append((global_name, module)) return result def get_modules(self, name): """Return the list of modules that have global `name`""" result = [] for module in self.names: if name in self.names[module]: result.append(module) return result def get_all_names(self): """Return the list of all cached global names""" result = set() for module in self.names: result.update(set(self.names[module])) return result def get_name_locations(self, name): """Return a list of ``(resource, lineno)`` tuples""" result = [] for module in self.names: if name in self.names[module]: try: pymodule = self.project.get_module(module) if name in pymodule: pyname = pymodule[name] module, lineno = pyname.get_definition_location() if module is not None: resource = module.get_module().get_resource() if resource is not None and lineno is not None: result.append((resource, lineno)) except exceptions.ModuleNotFoundError: pass return result def generate_cache( self, resources=None, underlined=None, task_handle=taskhandle.NullTaskHandle() ): """Generate global name cache for project files If `resources` is a list of `rope.base.resource.File`, only those files are searched; otherwise all python modules in the project are cached. """ if resources is None: resources = self.project.get_python_files() job_set = task_handle.create_jobset( "Generating autoimport cache", len(resources) ) for file in resources: job_set.started_job("Working on <%s>" % file.path) self.update_resource(file, underlined) job_set.finished_job() def generate_modules_cache( self, modules, underlined=None, task_handle=taskhandle.NullTaskHandle() ): """Generate global name cache for modules listed in `modules`""" job_set = task_handle.create_jobset( "Generating autoimport cache for modules", len(modules) ) for modname in modules: job_set.started_job("Working on <%s>" % modname) if modname.endswith(".*"): mod = self.project.find_module(modname[:-2]) if mod: for sub in submodules(mod): self.update_resource(sub, underlined) else: self.update_module(modname, underlined) job_set.finished_job() def clear_cache(self): """Clear all entries in global-name cache It might be a good idea to use this function before regenerating global names. """ self.names.clear() def find_insertion_line(self, code): """Guess at what line the new import should be inserted""" match = re.search(r"^(def|class)\s+", code) if match is not None: code = code[: match.start()] try: pymodule = libutils.get_string_module(self.project, code) except exceptions.ModuleSyntaxError: return 1 testmodname = "__rope_testmodule_rope" importinfo = importutils.NormalImport(((testmodname, None),)) module_imports = importutils.get_module_imports(self.project, pymodule) module_imports.add_import(importinfo) code = module_imports.get_changed_source() offset = code.index(testmodname) lineno = code.count("\n", 0, offset) + 1 return lineno def update_resource(self, resource, underlined=None): """Update the cache for global names in `resource`""" try: pymodule = self.project.get_pymodule(resource) modname = self._module_name(resource) self._add_names(pymodule, modname, underlined) except exceptions.ModuleSyntaxError: pass def update_module(self, modname, underlined=None): """Update the cache for global names in `modname` module `modname` is the name of a module. """ try: pymodule = self.project.get_module(modname) self._add_names(pymodule, modname, underlined) except exceptions.ModuleNotFoundError: pass def _module_name(self, resource): return libutils.modname(resource) def _add_names(self, pymodule, modname, underlined): if underlined is None: underlined = self.underlined globals = [] if isinstance(pymodule, pyobjects.PyDefinedObject): attributes = pymodule._get_structural_attributes() else: attributes = pymodule.get_attributes() for name, pyname in attributes.items(): if not underlined and name.startswith("_"): continue if isinstance(pyname, (pynames.AssignedName, pynames.DefinedName)): globals.append(name) if isinstance(pymodule, builtins.BuiltinModule): globals.append(name) self.names[modname] = globals def _write(self): self.project.data_files.write_data("globalnames", self.names) def _changed(self, resource): if not resource.is_folder(): self.update_resource(resource) def _moved(self, resource, newresource): if not resource.is_folder(): modname = self._module_name(resource) if modname in self.names: del self.names[modname] self.update_resource(newresource) def _removed(self, resource): if not resource.is_folder(): modname = self._module_name(resource) if modname in self.names: del self.names[modname] def submodules(mod): if isinstance(mod, resources.File): if mod.name.endswith(".py") and mod.name != "__init__.py": return set([mod]) return set() if not mod.has_child("__init__.py"): return set() result = set([mod]) for child in mod.get_children(): result |= submodules(child) return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/contrib/changestack.py0000664000175000017500000000255700000000000017676 0ustar00lieryanlieryan"""For performing many refactorings as a single command `changestack` module can be used to perform many refactorings on top of each other as one bigger command. It can be used like:: stack = ChangeStack(project, 'my big command') #.. stack.push(refactoring1.get_changes()) #.. stack.push(refactoring2.get_changes()) #.. stack.push(refactoringX.get_changes()) stack.pop_all() changes = stack.merged() Now `changes` can be previewed or performed as before. """ from rope.base import change class ChangeStack(object): def __init__(self, project, description="merged changes"): self.project = project self.description = description self.stack = [] def push(self, changes): self.stack.append(changes) self.project.do(changes) def pop_all(self): for i in range(len(self.stack)): self.project.history.undo(drop=True) def merged(self): result = change.ChangeSet(self.description) for changes in self.stack: for c in self._basic_changes(changes): result.add_change(c) return result def _basic_changes(self, changes): if isinstance(changes, change.ChangeSet): for child in changes.changes: for atom in self._basic_changes(child): yield atom else: yield changes ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/contrib/codeassist.py0000664000175000017500000006377700000000000017577 0ustar00lieryanlieryanimport keyword import sys import warnings import rope.base.codeanalyze import rope.base.evaluate from rope.base import builtins from rope.base import exceptions from rope.base import libutils from rope.base import pynames from rope.base import pynamesdef from rope.base import pyobjects from rope.base import pyobjectsdef from rope.base import pyscopes from rope.base import worder from rope.contrib import fixsyntax from rope.refactor import functionutils def code_assist( project, source_code, offset, resource=None, templates=None, maxfixes=1, later_locals=True, ): """Return python code completions as a list of `CodeAssistProposal` `resource` is a `rope.base.resources.Resource` object. If provided, relative imports are handled. `maxfixes` is the maximum number of errors to fix if the code has errors in it. If `later_locals` is `False` names defined in this scope and after this line is ignored. """ if templates is not None: warnings.warn( "Codeassist no longer supports templates", DeprecationWarning, stacklevel=2 ) assist = _PythonCodeAssist( project, source_code, offset, resource=resource, maxfixes=maxfixes, later_locals=later_locals, ) return assist() def starting_offset(source_code, offset): """Return the offset in which the completion should be inserted Usually code assist proposals should be inserted like:: completion = proposal.name result = (source_code[:starting_offset] + completion + source_code[offset:]) Where starting_offset is the offset returned by this function. """ word_finder = worder.Worder(source_code, True) expression, starting, starting_offset = word_finder.get_splitted_primary_before( offset ) return starting_offset def get_doc(project, source_code, offset, resource=None, maxfixes=1): """Get the pydoc""" fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes) pyname = fixer.pyname_at(offset) if pyname is None: return None pyobject = pyname.get_object() return PyDocExtractor().get_doc(pyobject) def get_calltip( project, source_code, offset, resource=None, maxfixes=1, ignore_unknown=False, remove_self=False, ): """Get the calltip of a function The format of the returned string is ``module_name.holding_scope_names.function_name(arguments)``. For classes `__init__()` and for normal objects `__call__()` function is used. Note that the offset is on the function itself *not* after the its open parenthesis. (Actually it used to be the other way but it was easily confused when string literals were involved. So I decided it is better for it not to try to be too clever when it cannot be clever enough). You can use a simple search like:: offset = source_code.rindex('(', 0, offset) - 1 to handle simple situations. If `ignore_unknown` is `True`, `None` is returned for functions without source-code like builtins and extensions. If `remove_self` is `True`, the first parameter whose name is self will be removed for methods. """ fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes) pyname = fixer.pyname_at(offset) if pyname is None: return None pyobject = pyname.get_object() return PyDocExtractor().get_calltip(pyobject, ignore_unknown, remove_self) def get_definition_location(project, source_code, offset, resource=None, maxfixes=1): """Return the definition location of the python name at `offset` Return a (`rope.base.resources.Resource`, lineno) tuple. If no `resource` is given and the definition is inside the same module, the first element of the returned tuple would be `None`. If the location cannot be determined ``(None, None)`` is returned. """ fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes) pyname = fixer.pyname_at(offset) if pyname is not None: module, lineno = pyname.get_definition_location() if module is not None: return module.get_module().get_resource(), lineno return (None, None) def find_occurrences(*args, **kwds): import rope.contrib.findit warnings.warn( "Use `rope.contrib.findit.find_occurrences()` instead", DeprecationWarning, stacklevel=2, ) return rope.contrib.findit.find_occurrences(*args, **kwds) def get_canonical_path(project, resource, offset): """Get the canonical path to an object. Given the offset of the object, this returns a list of (name, name_type) tuples representing the canonical path to the object. For example, the 'x' in the following code: class Foo(object): def bar(self): class Qux(object): def mux(self, x): pass we will return: [('Foo', 'CLASS'), ('bar', 'FUNCTION'), ('Qux', 'CLASS'), ('mux', 'FUNCTION'), ('x', 'PARAMETER')] `resource` is a `rope.base.resources.Resource` object. `offset` is the offset of the pyname you want the path to. """ # Retrieve the PyName. pymod = project.get_pymodule(resource) pyname = rope.base.evaluate.eval_location(pymod, offset) # Now get the location of the definition and its containing scope. defmod, lineno = pyname.get_definition_location() if not defmod: return None scope = defmod.get_scope().get_inner_scope_for_line(lineno) # Start with the name of the object we're interested in. names = [] if isinstance(pyname, pynamesdef.ParameterName): names = [(worder.get_name_at(pymod.get_resource(), offset), "PARAMETER")] elif isinstance(pyname, pynamesdef.AssignedName): names = [(worder.get_name_at(pymod.get_resource(), offset), "VARIABLE")] # Collect scope names. while scope.parent: if isinstance(scope, pyscopes.FunctionScope): scope_type = "FUNCTION" elif isinstance(scope, pyscopes.ClassScope): scope_type = "CLASS" else: scope_type = None names.append((scope.pyobject.get_name(), scope_type)) scope = scope.parent names.append((defmod.get_resource().real_path, "MODULE")) names.reverse() return names class CompletionProposal(object): """A completion proposal The `scope` instance variable shows where proposed name came from and can be 'global', 'local', 'builtin', 'attribute', 'keyword', 'imported', 'parameter_keyword'. The `type` instance variable shows the approximate type of the proposed object and can be 'instance', 'class', 'function', 'module', and `None`. All possible relations between proposal's `scope` and `type` are shown in the table below (different scopes in rows and types in columns): | instance | class | function | module | None local | + | + | + | + | global | + | + | + | + | builtin | + | + | + | | attribute | + | + | + | + | imported | + | + | + | + | keyword | | | | | + parameter_keyword | | | | | + """ def __init__(self, name, scope, pyname=None): self.name = name self.pyname = pyname self.scope = self._get_scope(scope) def __str__(self): return "%s (%s, %s)" % (self.name, self.scope, self.type) def __repr__(self): return str(self) @property def parameters(self): """The names of the parameters the function takes. Returns None if this completion is not a function. """ pyname = self.pyname if isinstance(pyname, pynames.ImportedName): pyname = pyname._get_imported_pyname() if isinstance(pyname, pynames.DefinedName): pyobject = pyname.get_object() if isinstance(pyobject, pyobjects.AbstractFunction): return pyobject.get_param_names() @property def type(self): pyname = self.pyname if isinstance(pyname, builtins.BuiltinName): pyobject = pyname.get_object() if isinstance(pyobject, builtins.BuiltinFunction): return "function" elif isinstance(pyobject, builtins.BuiltinClass): return "class" elif isinstance(pyobject, builtins.BuiltinObject) or isinstance( pyobject, builtins.BuiltinName ): return "instance" elif isinstance(pyname, pynames.ImportedModule): return "module" elif isinstance(pyname, pynames.ImportedName) or isinstance( pyname, pynames.DefinedName ): pyobject = pyname.get_object() if isinstance(pyobject, pyobjects.AbstractFunction): return "function" if isinstance(pyobject, pyobjects.AbstractClass): return "class" return "instance" def _get_scope(self, scope): if isinstance(self.pyname, builtins.BuiltinName): return "builtin" if isinstance(self.pyname, pynames.ImportedModule) or isinstance( self.pyname, pynames.ImportedName ): return "imported" return scope def get_doc(self): """Get the proposed object's docstring. Returns None if it can not be get. """ if not self.pyname: return None pyobject = self.pyname.get_object() if not hasattr(pyobject, "get_doc"): return None return self.pyname.get_object().get_doc() @property def kind(self): warnings.warn( "the proposal's `kind` property is deprecated, " "use `scope` instead" ) return self.scope # leaved for backward compatibility CodeAssistProposal = CompletionProposal class NamedParamProposal(CompletionProposal): """A parameter keyword completion proposal Holds reference to ``_function`` -- the function which parameter ``name`` belongs to. This allows to determine default value for this parameter. """ def __init__(self, name, function): self.argname = name name = "%s=" % name super(NamedParamProposal, self).__init__(name, "parameter_keyword") self._function = function def get_default(self): """Get a string representation of a param's default value. Returns None if there is no default value for this param. """ definfo = functionutils.DefinitionInfo.read(self._function) for arg, default in definfo.args_with_defaults: if self.argname == arg: return default return None def sorted_proposals(proposals, scopepref=None, typepref=None): """Sort a list of proposals Return a sorted list of the given `CodeAssistProposal`. `scopepref` can be a list of proposal scopes. Defaults to ``['parameter_keyword', 'local', 'global', 'imported', 'attribute', 'builtin', 'keyword']``. `typepref` can be a list of proposal types. Defaults to ``['class', 'function', 'instance', 'module', None]``. (`None` stands for completions with no type like keywords.) """ sorter = _ProposalSorter(proposals, scopepref, typepref) return sorter.get_sorted_proposal_list() def starting_expression(source_code, offset): """Return the expression to complete""" word_finder = worder.Worder(source_code, True) expression, starting, starting_offset = word_finder.get_splitted_primary_before( offset ) if expression: return expression + "." + starting return starting def default_templates(): warnings.warn( "default_templates() is deprecated.", DeprecationWarning, stacklevel=2 ) return {} class _PythonCodeAssist(object): def __init__( self, project, source_code, offset, resource=None, maxfixes=1, later_locals=True ): self.project = project self.code = source_code self.resource = resource self.maxfixes = maxfixes self.later_locals = later_locals self.word_finder = worder.Worder(source_code, True) ( self.expression, self.starting, self.offset, ) = self.word_finder.get_splitted_primary_before(offset) keywords = keyword.kwlist def _find_starting_offset(self, source_code, offset): current_offset = offset - 1 while current_offset >= 0 and ( source_code[current_offset].isalnum() or source_code[current_offset] in "_" ): current_offset -= 1 return current_offset + 1 def _matching_keywords(self, starting): result = [] for kw in self.keywords: if kw.startswith(starting): result.append(CompletionProposal(kw, "keyword")) return result def __call__(self): if self.offset > len(self.code): return [] completions = list(self._code_completions().values()) if self.expression.strip() == "" and self.starting.strip() != "": completions.extend(self._matching_keywords(self.starting)) return completions def _dotted_completions(self, module_scope, holding_scope): result = {} found_pyname = rope.base.evaluate.eval_str(holding_scope, self.expression) if found_pyname is not None: element = found_pyname.get_object() compl_scope = "attribute" if isinstance(element, (pyobjectsdef.PyModule, pyobjectsdef.PyPackage)): compl_scope = "imported" for name, pyname in element.get_attributes().items(): if name.startswith(self.starting): result[name] = CompletionProposal(name, compl_scope, pyname) return result def _undotted_completions(self, scope, result, lineno=None): if scope.parent is not None: self._undotted_completions(scope.parent, result) if lineno is None: names = scope.get_propagated_names() else: names = scope.get_names() for name, pyname in names.items(): if name.startswith(self.starting): compl_scope = "local" if scope.get_kind() == "Module": compl_scope = "global" if ( lineno is None or self.later_locals or not self._is_defined_after(scope, pyname, lineno) ): result[name] = CompletionProposal(name, compl_scope, pyname) def _from_import_completions(self, pymodule): module_name = self.word_finder.get_from_module(self.offset) if module_name is None: return {} pymodule = self._find_module(pymodule, module_name) result = {} for name in pymodule: if name.startswith(self.starting): result[name] = CompletionProposal( name, scope="global", pyname=pymodule[name] ) return result def _find_module(self, pymodule, module_name): dots = 0 while module_name[dots] == ".": dots += 1 pyname = pynames.ImportedModule(pymodule, module_name[dots:], dots) return pyname.get_object() def _is_defined_after(self, scope, pyname, lineno): location = pyname.get_definition_location() if location is not None and location[1] is not None: if ( location[0] == scope.pyobject.get_module() and lineno <= location[1] <= scope.get_end() ): return True def _code_completions(self): lineno = self.code.count("\n", 0, self.offset) + 1 fixer = fixsyntax.FixSyntax( self.project, self.code, self.resource, self.maxfixes ) pymodule = fixer.get_pymodule() module_scope = pymodule.get_scope() code = pymodule.source_code lines = code.split("\n") result = {} start = fixsyntax._logical_start(lines, lineno) indents = fixsyntax._get_line_indents(lines[start - 1]) inner_scope = module_scope.get_inner_scope_for_line(start, indents) if self.word_finder.is_a_name_after_from_import(self.offset): return self._from_import_completions(pymodule) if self.expression.strip() != "": result.update(self._dotted_completions(module_scope, inner_scope)) else: result.update(self._keyword_parameters(module_scope.pyobject, inner_scope)) self._undotted_completions(inner_scope, result, lineno=lineno) return result def _keyword_parameters(self, pymodule, scope): offset = self.offset if offset == 0: return {} word_finder = worder.Worder(self.code, True) if word_finder.is_on_function_call_keyword(offset - 1): function_parens = word_finder.find_parens_start_from_inside(offset - 1) primary = word_finder.get_primary_at(function_parens - 1) try: function_pyname = rope.base.evaluate.eval_str(scope, primary) except exceptions.BadIdentifierError: return {} if function_pyname is not None: pyobject = function_pyname.get_object() if isinstance(pyobject, pyobjects.AbstractFunction): pass elif ( isinstance(pyobject, pyobjects.AbstractClass) and "__init__" in pyobject ): pyobject = pyobject["__init__"].get_object() elif "__call__" in pyobject: pyobject = pyobject["__call__"].get_object() if isinstance(pyobject, pyobjects.AbstractFunction): param_names = [] param_names.extend(pyobject.get_param_names(special_args=False)) result = {} for name in param_names: if name.startswith(self.starting): result[name + "="] = NamedParamProposal(name, pyobject) return result return {} class _ProposalSorter(object): """Sort a list of code assist proposals""" def __init__(self, code_assist_proposals, scopepref=None, typepref=None): self.proposals = code_assist_proposals if scopepref is None: scopepref = [ "parameter_keyword", "local", "global", "imported", "attribute", "builtin", "keyword", ] self.scopepref = scopepref if typepref is None: typepref = ["class", "function", "instance", "module", None] self.typerank = dict((type, index) for index, type in enumerate(typepref)) def get_sorted_proposal_list(self): """Return a list of `CodeAssistProposal`""" proposals = {} for proposal in self.proposals: proposals.setdefault(proposal.scope, []).append(proposal) result = [] for scope in self.scopepref: scope_proposals = proposals.get(scope, []) scope_proposals = [ proposal for proposal in scope_proposals if proposal.type in self.typerank ] scope_proposals.sort(key=self._proposal_key) result.extend(scope_proposals) return result def _proposal_key(self, proposal1): def _underline_count(name): return sum(1 for c in name if c == "_") return ( self.typerank.get(proposal1.type, 100), _underline_count(proposal1.name), proposal1.name, ) # if proposal1.type != proposal2.type: # return cmp(self.typerank.get(proposal1.type, 100), # self.typerank.get(proposal2.type, 100)) # return self._compare_underlined_names(proposal1.name, # proposal2.name) class PyDocExtractor(object): def get_doc(self, pyobject): if isinstance(pyobject, pyobjects.AbstractFunction): return self._get_function_docstring(pyobject) elif isinstance(pyobject, pyobjects.AbstractClass): return self._get_class_docstring(pyobject) elif isinstance(pyobject, pyobjects.AbstractModule): return self._trim_docstring(pyobject.get_doc()) return None def get_calltip(self, pyobject, ignore_unknown=False, remove_self=False): try: if isinstance(pyobject, pyobjects.AbstractClass): pyobject = pyobject["__init__"].get_object() if not isinstance(pyobject, pyobjects.AbstractFunction): pyobject = pyobject["__call__"].get_object() except exceptions.AttributeNotFoundError: return None if ignore_unknown and not isinstance(pyobject, pyobjects.PyFunction): return if isinstance(pyobject, pyobjects.AbstractFunction): result = self._get_function_signature(pyobject, add_module=True) if remove_self and self._is_method(pyobject): return result.replace("(self)", "()").replace("(self, ", "(") return result def _get_class_docstring(self, pyclass): contents = self._trim_docstring(pyclass.get_doc(), 2) supers = [super.get_name() for super in pyclass.get_superclasses()] doc = "class %s(%s):\n\n" % (pyclass.get_name(), ", ".join(supers)) + contents if "__init__" in pyclass: init = pyclass["__init__"].get_object() if isinstance(init, pyobjects.AbstractFunction): doc += "\n\n" + self._get_single_function_docstring(init) return doc def _get_function_docstring(self, pyfunction): functions = [pyfunction] if self._is_method(pyfunction): functions.extend( self._get_super_methods(pyfunction.parent, pyfunction.get_name()) ) return "\n\n".join( [self._get_single_function_docstring(function) for function in functions] ) def _is_method(self, pyfunction): return isinstance(pyfunction, pyobjects.PyFunction) and isinstance( pyfunction.parent, pyobjects.PyClass ) def _get_single_function_docstring(self, pyfunction): signature = self._get_function_signature(pyfunction) docs = self._trim_docstring(pyfunction.get_doc(), indents=2) return signature + ":\n\n" + docs def _get_super_methods(self, pyclass, name): result = [] for super_class in pyclass.get_superclasses(): if name in super_class: function = super_class[name].get_object() if isinstance(function, pyobjects.AbstractFunction): result.append(function) result.extend(self._get_super_methods(super_class, name)) return result def _get_function_signature(self, pyfunction, add_module=False): location = self._location(pyfunction, add_module) if isinstance(pyfunction, pyobjects.PyFunction): info = functionutils.DefinitionInfo.read(pyfunction) return location + info.to_string() else: return "%s(%s)" % ( location + pyfunction.get_name(), ", ".join(pyfunction.get_param_names()), ) def _location(self, pyobject, add_module=False): location = [] parent = pyobject.parent while parent and not isinstance(parent, pyobjects.AbstractModule): location.append(parent.get_name()) location.append(".") parent = parent.parent if add_module: if isinstance(pyobject, pyobjects.PyFunction): location.insert(0, self._get_module(pyobject)) if isinstance(parent, builtins.BuiltinModule): location.insert(0, parent.get_name() + ".") return "".join(location) def _get_module(self, pyfunction): module = pyfunction.get_module() if module is not None: resource = module.get_resource() if resource is not None: return libutils.modname(resource) + "." return "" def _trim_docstring(self, docstring, indents=0): """The sample code from :PEP:`257`""" if not docstring: return "" # Convert tabs to spaces (following normal Python rules) # and split into a list of lines: lines = docstring.expandtabs().splitlines() # Determine minimum indentation (first line doesn't count): indent = sys.maxsize for line in lines[1:]: stripped = line.lstrip() if stripped: indent = min(indent, len(line) - len(stripped)) # Remove indentation (first line is special): trimmed = [lines[0].strip()] if indent < sys.maxsize: for line in lines[1:]: trimmed.append(line[indent:].rstrip()) # Strip off trailing and leading blank lines: while trimmed and not trimmed[-1]: trimmed.pop() while trimmed and not trimmed[0]: trimmed.pop(0) # Return a single string: return "\n".join((" " * indents + line for line in trimmed)) # Deprecated classes class TemplateProposal(CodeAssistProposal): def __init__(self, name, template): warnings.warn( "TemplateProposal is deprecated.", DeprecationWarning, stacklevel=2 ) super(TemplateProposal, self).__init__(name, "template") self.template = template class Template(object): def __init__(self, template): self.template = template warnings.warn("Template is deprecated.", DeprecationWarning, stacklevel=2) def variables(self): return [] def substitute(self, mapping): return self.template def get_cursor_location(self, mapping): return len(self.template) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/contrib/finderrors.py0000664000175000017500000000560000000000000017570 0ustar00lieryanlieryan"""Finding bad name and attribute accesses `find_errors` function can be used to find possible bad name and attribute accesses. As an example:: errors = find_errors(project, project.get_resource('mod.py')) for error in errors: print('%s: %s' % (error.lineno, error.error)) prints possible errors for ``mod.py`` file. TODO: * use task handles * reporting names at most once * attributes of extension modules that don't appear in extension_modules project config can be ignored * not calling `PyScope.get_inner_scope_for_line()` if it is a bottleneck; needs profiling * not reporting occurrences where rope cannot infer the object * rope saves multiple objects for some of the names in its objectdb use all of them not to give false positives * ... ;-) """ from rope.base import ast, evaluate, pyobjects def find_errors(project, resource): """Find possible bad name and attribute accesses It returns a list of `Error`. """ pymodule = project.get_pymodule(resource) finder = _BadAccessFinder(pymodule) ast.walk(pymodule.get_ast(), finder) return finder.errors class _BadAccessFinder(object): def __init__(self, pymodule): self.pymodule = pymodule self.scope = pymodule.get_scope() self.errors = [] def _Name(self, node): if isinstance(node.ctx, (ast.Store, ast.Param)): return scope = self.scope.get_inner_scope_for_line(node.lineno) pyname = scope.lookup(node.id) if pyname is None: self._add_error(node, "Unresolved variable") elif self._is_defined_after(scope, pyname, node.lineno): self._add_error(node, "Defined later") def _Attribute(self, node): if not isinstance(node.ctx, ast.Store): scope = self.scope.get_inner_scope_for_line(node.lineno) pyname = evaluate.eval_node(scope, node.value) if pyname is not None and pyname.get_object() != pyobjects.get_unknown(): if node.attr not in pyname.get_object(): self._add_error(node, "Unresolved attribute") ast.walk(node.value, self) def _add_error(self, node, msg): if isinstance(node, ast.Attribute): name = node.attr else: name = node.id if name != "None": error = Error(node.lineno, msg + " " + name) self.errors.append(error) def _is_defined_after(self, scope, pyname, lineno): location = pyname.get_definition_location() if location is not None and location[1] is not None: if ( location[0] == self.pymodule and lineno <= location[1] <= scope.get_end() ): return True class Error(object): def __init__(self, lineno, error): self.lineno = lineno self.error = error def __str__(self): return "%s: %s" % (self.lineno, self.error) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/contrib/findit.py0000664000175000017500000001031400000000000016666 0ustar00lieryanlieryanimport rope.base.codeanalyze import rope.base.evaluate import rope.base.pyobjects from rope.base import taskhandle, exceptions, worder from rope.contrib import fixsyntax from rope.refactor import occurrences def find_occurrences( project, resource, offset, unsure=False, resources=None, in_hierarchy=False, task_handle=taskhandle.NullTaskHandle(), ): """Return a list of `Location` If `unsure` is `True`, possible matches are returned, too. You can use `Location.unsure` to see which are unsure occurrences. `resources` can be a list of `rope.base.resource.File` that should be searched for occurrences; if `None` all python files in the project are searched. """ name = worder.get_name_at(resource, offset) this_pymodule = project.get_pymodule(resource) primary, pyname = rope.base.evaluate.eval_location2(this_pymodule, offset) def is_match(occurrence): return unsure finder = occurrences.create_finder( project, name, pyname, unsure=is_match, in_hierarchy=in_hierarchy, instance=primary, ) if resources is None: resources = project.get_python_files() job_set = task_handle.create_jobset("Finding Occurrences", count=len(resources)) return _find_locations(finder, resources, job_set) def find_implementations( project, resource, offset, resources=None, task_handle=taskhandle.NullTaskHandle() ): """Find the places a given method is overridden. Finds the places a method is implemented. Returns a list of `Location`. """ name = worder.get_name_at(resource, offset) this_pymodule = project.get_pymodule(resource) pyname = rope.base.evaluate.eval_location(this_pymodule, offset) if pyname is not None: pyobject = pyname.get_object() if ( not isinstance(pyobject, rope.base.pyobjects.PyFunction) or pyobject.get_kind() != "method" ): raise exceptions.BadIdentifierError("Not a method!") else: raise exceptions.BadIdentifierError("Cannot resolve the identifier!") def is_defined(occurrence): if not occurrence.is_defined(): return False def not_self(occurrence): if occurrence.get_pyname().get_object() == pyname.get_object(): return False filters = [is_defined, not_self, occurrences.InHierarchyFilter(pyname, True)] finder = occurrences.Finder(project, name, filters=filters) if resources is None: resources = project.get_python_files() job_set = task_handle.create_jobset("Finding Implementations", count=len(resources)) return _find_locations(finder, resources, job_set) def find_definition(project, code, offset, resource=None, maxfixes=1): """Return the definition location of the python name at `offset` A `Location` object is returned if the definition location can be determined, otherwise ``None`` is returned. """ fixer = fixsyntax.FixSyntax(project, code, resource, maxfixes) pyname = fixer.pyname_at(offset) if pyname is not None: module, lineno = pyname.get_definition_location() name = rope.base.worder.Worder(code).get_word_at(offset) if lineno is not None: start = module.lines.get_line_start(lineno) def check_offset(occurrence): if occurrence.offset < start: return False pyname_filter = occurrences.PyNameFilter(pyname) finder = occurrences.Finder(project, name, [check_offset, pyname_filter]) for occurrence in finder.find_occurrences(pymodule=module): return Location(occurrence) class Location(object): def __init__(self, occurrence): self.resource = occurrence.resource self.region = occurrence.get_word_range() self.offset = self.region[0] self.unsure = occurrence.is_unsure() self.lineno = occurrence.lineno def _find_locations(finder, resources, job_set): result = [] for resource in resources: job_set.started_job(resource.path) for occurrence in finder.find_occurrences(resource): result.append(Location(occurrence)) job_set.finished_job() return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/contrib/fixmodnames.py0000664000175000017500000000422700000000000017731 0ustar00lieryanlieryan"""Fix the name of modules This module is useful when you want to rename many of the modules in your project. That can happen specially when you want to change their naming style. For instance:: fixer = FixModuleNames(project) changes = fixer.get_changes(fixer=str.lower) project.do(changes) Here it renames all modules and packages to use lower-cased chars. You can tell it to use any other style by using the ``fixer`` argument. """ from rope.base import taskhandle from rope.contrib import changestack from rope.refactor import rename class FixModuleNames(object): def __init__(self, project): self.project = project def get_changes(self, fixer=str.lower, task_handle=taskhandle.NullTaskHandle()): """Fix module names `fixer` is a function that takes and returns a `str`. Given the name of a module, it should return the fixed name. """ stack = changestack.ChangeStack(self.project, "Fixing module names") jobset = task_handle.create_jobset( "Fixing module names", self._count_fixes(fixer) + 1 ) try: while True: for resource in self._tobe_fixed(fixer): jobset.started_job(resource.path) renamer = rename.Rename(self.project, resource) changes = renamer.get_changes(fixer(self._name(resource))) stack.push(changes) jobset.finished_job() break else: break finally: jobset.started_job("Reverting to original state") stack.pop_all() jobset.finished_job() return stack.merged() def _count_fixes(self, fixer): return len(list(self._tobe_fixed(fixer))) def _tobe_fixed(self, fixer): for resource in self.project.get_python_files(): modname = self._name(resource) if modname != fixer(modname): yield resource def _name(self, resource): modname = resource.name.rsplit(".", 1)[0] if modname == "__init__": modname = resource.parent.name return modname ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/contrib/fixsyntax.py0000664000175000017500000001550500000000000017455 0ustar00lieryanlieryanimport rope.base.codeanalyze import rope.base.evaluate from rope.base import exceptions from rope.base import libutils from rope.base import utils from rope.base import worder from rope.base.codeanalyze import ArrayLinesAdapter, LogicalLineFinder class FixSyntax(object): def __init__(self, project, code, resource, maxfixes=1): self.project = project self.code = code self.resource = resource self.maxfixes = maxfixes @utils.saveit def get_pymodule(self): """Get a `PyModule`""" msg = None code = self.code tries = 0 while True: try: if ( tries == 0 and self.resource is not None and self.resource.read() == code ): return self.project.get_pymodule(self.resource, force_errors=True) return libutils.get_string_module( self.project, code, resource=self.resource, force_errors=True ) except exceptions.ModuleSyntaxError as e: if msg is None: msg = "%s:%s %s" % (e.filename, e.lineno, e.message_) if tries < self.maxfixes: tries += 1 self.commenter.comment(e.lineno) code = "\n".join(self.commenter.lines) else: raise exceptions.ModuleSyntaxError( e.filename, e.lineno, "Failed to fix error: {0}".format(msg) ) @property @utils.saveit def commenter(self): return _Commenter(self.code) def pyname_at(self, offset): pymodule = self.get_pymodule() def old_pyname(): word_finder = worder.Worder(self.code, True) expression = word_finder.get_primary_at(offset) expression = expression.replace("\\\n", " ").replace("\n", " ") lineno = self.code.count("\n", 0, offset) scope = pymodule.get_scope().get_inner_scope_for_line(lineno) return rope.base.evaluate.eval_str(scope, expression) new_code = pymodule.source_code def new_pyname(): newoffset = self.commenter.transfered_offset(offset) return rope.base.evaluate.eval_location(pymodule, newoffset) if new_code.startswith(self.code[: offset + 1]): return new_pyname() result = old_pyname() if result is None: return new_pyname() return result class _Commenter(object): def __init__(self, code): self.code = code self.lines = self.code.split("\n") self.lines.append("\n") self.origs = list(range(len(self.lines) + 1)) self.diffs = [0] * (len(self.lines) + 1) def comment(self, lineno): start = _logical_start(self.lines, lineno, check_prev=True) - 1 # using self._get_stmt_end() instead of self._get_block_end() # to lower commented lines end = self._get_stmt_end(start) indents = _get_line_indents(self.lines[start]) if 0 < start: last_lineno = self._last_non_blank(start - 1) last_line = self.lines[last_lineno] if last_line.rstrip().endswith(":"): indents = _get_line_indents(last_line) + 4 self._set(start, " " * indents + "pass") for line in range(start + 1, end + 1): self._set(line, self.lines[start]) self._fix_incomplete_try_blocks(lineno, indents) def transfered_offset(self, offset): lineno = self.code.count("\n", 0, offset) diff = sum(self.diffs[:lineno]) return offset + diff def _last_non_blank(self, start): while start > 0 and self.lines[start].strip() == "": start -= 1 return start def _get_block_end(self, lineno): end_line = lineno base_indents = _get_line_indents(self.lines[lineno]) for i in range(lineno + 1, len(self.lines)): if _get_line_indents(self.lines[i]) >= base_indents: end_line = i else: break return end_line def _get_stmt_end(self, lineno): base_indents = _get_line_indents(self.lines[lineno]) for i in range(lineno + 1, len(self.lines)): if _get_line_indents(self.lines[i]) <= base_indents: return i - 1 return lineno def _fix_incomplete_try_blocks(self, lineno, indents): block_start = lineno last_indents = indents while block_start > 0: block_start = ( rope.base.codeanalyze.get_block_start( ArrayLinesAdapter(self.lines), block_start ) - 1 ) if self.lines[block_start].strip().startswith("try:"): indents = _get_line_indents(self.lines[block_start]) if indents > last_indents: continue last_indents = indents block_end = self._find_matching_deindent(block_start) line = self.lines[block_end].strip() if not ( line.startswith("finally:") or line.startswith("except ") or line.startswith("except:") ): self._insert(block_end, " " * indents + "finally:") self._insert(block_end + 1, " " * indents + " pass") def _find_matching_deindent(self, line_number): indents = _get_line_indents(self.lines[line_number]) current_line = line_number + 1 while current_line < len(self.lines): line = self.lines[current_line] if not line.strip().startswith("#") and not line.strip() == "": # HACK: We should have used logical lines here if _get_line_indents(self.lines[current_line]) <= indents: return current_line current_line += 1 return len(self.lines) - 1 def _set(self, lineno, line): self.diffs[self.origs[lineno]] += len(line) - len(self.lines[lineno]) self.lines[lineno] = line def _insert(self, lineno, line): self.diffs[self.origs[lineno]] += len(line) + 1 self.origs.insert(lineno, self.origs[lineno]) self.lines.insert(lineno, line) def _logical_start(lines, lineno, check_prev=False): logical_finder = LogicalLineFinder(ArrayLinesAdapter(lines)) if check_prev: prev = lineno - 1 while prev > 0: start, end = logical_finder.logical_line_in(prev) if end is None or start <= lineno < end: return start if start <= prev: break prev -= 1 return logical_finder.logical_line_in(lineno)[0] def _get_line_indents(line): return rope.base.codeanalyze.count_line_indents(line) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/contrib/generate.py0000664000175000017500000003374600000000000017221 0ustar00lieryanlieryanimport rope.base.evaluate from rope.base import libutils from rope.base import change, pyobjects, exceptions, pynames, worder, codeanalyze from rope.refactor import sourceutils, importutils, functionutils, suites def create_generate(kind, project, resource, offset, goal_resource=None): """A factory for creating `Generate` objects `kind` can be 'variable', 'function', 'class', 'module' or 'package'. """ generate = eval("Generate" + kind.title()) return generate(project, resource, offset, goal_resource=goal_resource) def create_module(project, name, sourcefolder=None): """Creates a module and returns a `rope.base.resources.File`""" if sourcefolder is None: sourcefolder = project.root packages = name.split(".") parent = sourcefolder for package in packages[:-1]: parent = parent.get_child(package) return parent.create_file(packages[-1] + ".py") def create_package(project, name, sourcefolder=None): """Creates a package and returns a `rope.base.resources.Folder`""" if sourcefolder is None: sourcefolder = project.root packages = name.split(".") parent = sourcefolder for package in packages[:-1]: parent = parent.get_child(package) made_packages = parent.create_folder(packages[-1]) made_packages.create_file("__init__.py") return made_packages class _Generate(object): def __init__(self, project, resource, offset, goal_resource=None): self.project = project self.resource = resource self.goal_resource = goal_resource self.info = self._generate_info(project, resource, offset) self.name = self.info.get_name() self._check_exceptional_conditions() def _generate_info(self, project, resource, offset): return _GenerationInfo(project.pycore, resource, offset, self.goal_resource) def _check_exceptional_conditions(self): if self.info.element_already_exists(): raise exceptions.RefactoringError( "Element <%s> already exists." % self.name ) if not self.info.primary_is_found(): raise exceptions.RefactoringError( "Cannot determine the scope <%s> should be defined in." % self.name ) def get_changes(self): changes = change.ChangeSet( "Generate %s <%s>" % (self._get_element_kind(), self.name) ) indents = self.info.get_scope_indents() blanks = self.info.get_blank_lines() base_definition = sourceutils.fix_indentation(self._get_element(), indents) definition = "\n" * blanks[0] + base_definition + "\n" * blanks[1] resource = self.info.get_insertion_resource() start, end = self.info.get_insertion_offsets() collector = codeanalyze.ChangeCollector(resource.read()) collector.add_change(start, end, definition) changes.add_change(change.ChangeContents(resource, collector.get_changed())) if self.goal_resource: relative_import = _add_relative_import_to_module( self.project, self.resource, self.goal_resource, self.name ) changes.add_change(relative_import) return changes def get_location(self): return (self.info.get_insertion_resource(), self.info.get_insertion_lineno()) def _get_element_kind(self): raise NotImplementedError() def _get_element(self): raise NotImplementedError() class GenerateFunction(_Generate): def _generate_info(self, project, resource, offset): return _FunctionGenerationInfo(project.pycore, resource, offset) def _get_element(self): decorator = "" args = [] if self.info.is_static_method(): decorator = "@staticmethod\n" if ( self.info.is_method() or self.info.is_constructor() or self.info.is_instance() ): args.append("self") args.extend(self.info.get_passed_args()) definition = "%sdef %s(%s):\n pass\n" % ( decorator, self.name, ", ".join(args), ) return definition def _get_element_kind(self): return "Function" class GenerateVariable(_Generate): def _get_element(self): return "%s = None\n" % self.name def _get_element_kind(self): return "Variable" class GenerateClass(_Generate): def _get_element(self): return "class %s(object):\n pass\n" % self.name def _get_element_kind(self): return "Class" class GenerateModule(_Generate): def get_changes(self): package = self.info.get_package() changes = change.ChangeSet("Generate Module <%s>" % self.name) new_resource = self.project.get_file("%s/%s.py" % (package.path, self.name)) if new_resource.exists(): raise exceptions.RefactoringError( "Module <%s> already exists" % new_resource.path ) changes.add_change(change.CreateResource(new_resource)) changes.add_change( _add_import_to_module(self.project, self.resource, new_resource) ) return changes def get_location(self): package = self.info.get_package() return (package.get_child("%s.py" % self.name), 1) class GeneratePackage(_Generate): def get_changes(self): package = self.info.get_package() changes = change.ChangeSet("Generate Package <%s>" % self.name) new_resource = self.project.get_folder("%s/%s" % (package.path, self.name)) if new_resource.exists(): raise exceptions.RefactoringError( "Package <%s> already exists" % new_resource.path ) changes.add_change(change.CreateResource(new_resource)) changes.add_change( _add_import_to_module(self.project, self.resource, new_resource) ) child = self.project.get_folder(package.path + "/" + self.name) changes.add_change(change.CreateFile(child, "__init__.py")) return changes def get_location(self): package = self.info.get_package() child = package.get_child(self.name) return (child.get_child("__init__.py"), 1) def _add_import_to_module(project, resource, imported): pymodule = project.get_pymodule(resource) import_tools = importutils.ImportTools(project) module_imports = import_tools.module_imports(pymodule) module_name = libutils.modname(imported) new_import = importutils.NormalImport(((module_name, None),)) module_imports.add_import(new_import) return change.ChangeContents(resource, module_imports.get_changed_source()) def _add_relative_import_to_module(project, resource, imported, name): pymodule = project.get_pymodule(resource) import_tools = importutils.ImportTools(project) module_imports = import_tools.module_imports(pymodule) new_import = import_tools.get_from_import(imported, name) module_imports.add_import(new_import) return change.ChangeContents(resource, module_imports.get_changed_source()) class _GenerationInfo(object): def __init__(self, pycore, resource, offset, goal_resource=None): self.pycore = pycore self.resource = resource self.offset = offset self.goal_resource = goal_resource self.source_pymodule = self.pycore.project.get_pymodule(resource) finder = rope.base.evaluate.ScopeNameFinder(self.source_pymodule) self.primary, self.pyname = finder.get_primary_and_pyname_at(offset) self._init_fields() def _init_fields(self): self.source_scope = self._get_source_scope() self.goal_scope = self._get_goal_scope() self.goal_pymodule = self._get_goal_module(self.goal_scope) def _get_goal_scope(self): if self.primary is None: if self.goal_resource: return self.pycore.project.get_pymodule(self.goal_resource).get_scope() else: return self._get_source_scope() pyobject = self.primary.get_object() if isinstance(pyobject, pyobjects.PyDefinedObject): return pyobject.get_scope() elif isinstance(pyobject.get_type(), pyobjects.PyClass): return pyobject.get_type().get_scope() def _get_goal_module(self, scope): if scope is None: return while scope.parent is not None: scope = scope.parent return scope.pyobject def _get_source_scope(self): module_scope = self.source_pymodule.get_scope() lineno = self.source_pymodule.lines.get_line_number(self.offset) return module_scope.get_inner_scope_for_line(lineno) def get_insertion_lineno(self): lines = self.goal_pymodule.lines if self.goal_scope == self.source_scope: line_finder = self.goal_pymodule.logical_lines lineno = lines.get_line_number(self.offset) lineno = line_finder.logical_line_in(lineno)[0] root = suites.ast_suite_tree(self.goal_scope.pyobject.get_ast()) suite = root.find_suite(lineno) indents = sourceutils.get_indents(lines, lineno) while self.get_scope_indents() < indents: lineno = suite.get_start() indents = sourceutils.get_indents(lines, lineno) suite = suite.parent return lineno else: return min(self.goal_scope.get_end() + 1, lines.length()) def get_insertion_resource(self): return self.goal_pymodule.get_resource() def get_insertion_offsets(self): if self.goal_scope.get_kind() == "Class": start, end = sourceutils.get_body_region(self.goal_scope.pyobject) if self.goal_pymodule.source_code[start:end].strip() == "pass": return start, end lines = self.goal_pymodule.lines start = lines.get_line_start(self.get_insertion_lineno()) return (start, start) def get_scope_indents(self): if self.goal_scope.get_kind() == "Module": return 0 return ( sourceutils.get_indents( self.goal_pymodule.lines, self.goal_scope.get_start() ) + 4 ) def get_blank_lines(self): if self.goal_scope.get_kind() == "Module": base_blanks = 2 if self.goal_pymodule.source_code.strip() == "": base_blanks = 0 if self.goal_scope.get_kind() == "Class": base_blanks = 1 if self.goal_scope.get_kind() == "Function": base_blanks = 0 if self.goal_scope == self.source_scope: return (0, base_blanks) return (base_blanks, 0) def get_package(self): primary = self.primary if self.primary is None: return self.pycore.project.get_source_folders()[0] if isinstance(primary.get_object(), pyobjects.PyPackage): return primary.get_object().get_resource() raise exceptions.RefactoringError( "A module/package can be only created in a package." ) def primary_is_found(self): return self.goal_scope is not None def element_already_exists(self): if self.pyname is None or isinstance(self.pyname, pynames.UnboundName): return False return self.get_name() in self.goal_scope.get_defined_names() def get_name(self): return worder.get_name_at(self.resource, self.offset) class _FunctionGenerationInfo(_GenerationInfo): def _get_goal_scope(self): if self.is_constructor(): return self.pyname.get_object().get_scope() if self.is_instance(): return self.pyname.get_object().get_type().get_scope() if self.primary is None: return self._get_source_scope() pyobject = self.primary.get_object() if isinstance(pyobject, pyobjects.PyDefinedObject): return pyobject.get_scope() elif isinstance(pyobject.get_type(), pyobjects.PyClass): return pyobject.get_type().get_scope() def element_already_exists(self): if self.pyname is None or isinstance(self.pyname, pynames.UnboundName): return False return self.get_name() in self.goal_scope.get_defined_names() def is_static_method(self): return self.primary is not None and isinstance( self.primary.get_object(), pyobjects.PyClass ) def is_method(self): return self.primary is not None and isinstance( self.primary.get_object().get_type(), pyobjects.PyClass ) def is_constructor(self): return self.pyname is not None and isinstance( self.pyname.get_object(), pyobjects.PyClass ) def is_instance(self): if self.pyname is None: return False pyobject = self.pyname.get_object() return isinstance(pyobject.get_type(), pyobjects.PyClass) def get_name(self): if self.is_constructor(): return "__init__" if self.is_instance(): return "__call__" return worder.get_name_at(self.resource, self.offset) def get_passed_args(self): result = [] source = self.source_pymodule.source_code finder = worder.Worder(source) if finder.is_a_function_being_called(self.offset): start, end = finder.get_primary_range(self.offset) parens_start, parens_end = finder.get_word_parens_range(end - 1) call = source[start:parens_end] parser = functionutils._FunctionParser(call, False) args, keywords = parser.get_parameters() for arg in args: if self._is_id(arg): result.append(arg) else: result.append("arg%d" % len(result)) for name, value in keywords: result.append(name) return result def _is_id(self, arg): def id_or_underline(c): return c.isalpha() or c == "_" for c in arg: if not id_or_underline(c) and not c.isdigit(): return False return id_or_underline(arg[0]) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3673904 rope-0.22.0/rope/refactor/0000775000175000017500000000000000000000000015205 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/__init__.py0000664000175000017500000000411700000000000017321 0ustar00lieryanlieryan"""rope refactor package This package contains modules that perform python refactorings. Refactoring classes perform refactorings in 4 steps: 1. Collect some data for performing the refactoring and use them to construct a refactoring class. Like:: renamer = Rename(project, resource, offset) 2. Some refactorings give you useful information about the refactoring after their construction. Like:: print(renamer.get_old_name()) 3. Give the refactoring class more information about how to perform the refactoring and get the changes this refactoring is going to make. This is done by calling `get_changes` method of the refactoring class. Like:: changes = renamer.get_changes(new_name) 4. You can commit the changes. Like:: project.do(changes) These steps are like the steps IDEs usually do for performing a refactoring. These are the things an IDE does in each step: 1. Construct a refactoring object by giving it information like resource, offset and ... . Some of the refactoring problems (like performing rename refactoring on language keywords) can be reported here. 2. Print some information about the refactoring and ask the user about the information that are necessary for completing the refactoring (like new name). 3. Call the `get_changes` by passing it information asked from the user (if necessary) and get and preview the changes returned by it. 4. perform the refactoring. From ``0.5m5`` release the `get_changes()` method of some time- consuming refactorings take an optional `rope.base.taskhandle. TaskHandle` parameter. You can use this object for stopping or monitoring the progress of refactorings. """ from rope.refactor.importutils import ImportOrganizer # noqa from rope.refactor.topackage import ModuleToPackage # noqa __all__ = [ "rename", "move", "inline", "extract", "restructure", "topackage", "importutils", "usefunction", "change_signature", "encapsulate_field", "introduce_factory", "introduce_parameter", "localtofield", "method_object", "multiproject", ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/change_signature.py0000664000175000017500000003243300000000000021072 0ustar00lieryanlieryanimport copy import rope.base.exceptions from rope.base import codeanalyze from rope.base import evaluate from rope.base import pyobjects from rope.base import taskhandle from rope.base import utils from rope.base import worder from rope.base.change import ChangeContents, ChangeSet from rope.refactor import occurrences, functionutils class ChangeSignature(object): def __init__(self, project, resource, offset): self.project = project self.resource = resource self.offset = offset self._set_name_and_pyname() if ( self.pyname is None or self.pyname.get_object() is None or not isinstance(self.pyname.get_object(), pyobjects.PyFunction) ): raise rope.base.exceptions.RefactoringError( "Change method signature should be performed on functions" ) def _set_name_and_pyname(self): self.name = worder.get_name_at(self.resource, self.offset) this_pymodule = self.project.get_pymodule(self.resource) self.primary, self.pyname = evaluate.eval_location2(this_pymodule, self.offset) if self.pyname is None: return pyobject = self.pyname.get_object() if isinstance(pyobject, pyobjects.PyClass) and "__init__" in pyobject: self.pyname = pyobject["__init__"] self.name = "__init__" pyobject = self.pyname.get_object() self.others = None if ( self.name == "__init__" and isinstance(pyobject, pyobjects.PyFunction) and isinstance(pyobject.parent, pyobjects.PyClass) ): pyclass = pyobject.parent self.others = (pyclass.get_name(), pyclass.parent[pyclass.get_name()]) def _change_calls( self, call_changer, in_hierarchy=None, resources=None, handle=taskhandle.NullTaskHandle(), ): if resources is None: resources = self.project.get_python_files() changes = ChangeSet("Changing signature of <%s>" % self.name) job_set = handle.create_jobset("Collecting Changes", len(resources)) finder = occurrences.create_finder( self.project, self.name, self.pyname, instance=self.primary, in_hierarchy=in_hierarchy and self.is_method(), ) if self.others: name, pyname = self.others constructor_finder = occurrences.create_finder( self.project, name, pyname, only_calls=True ) finder = _MultipleFinders([finder, constructor_finder]) for file in resources: job_set.started_job(file.path) change_calls = _ChangeCallsInModule( self.project, finder, file, call_changer ) changed_file = change_calls.get_changed_module() if changed_file is not None: changes.add_change(ChangeContents(file, changed_file)) job_set.finished_job() return changes def get_args(self): """Get function arguments. Return a list of ``(name, default)`` tuples for all but star and double star arguments. For arguments that don't have a default, `None` will be used. """ return self._definfo().args_with_defaults def is_method(self): pyfunction = self.pyname.get_object() return isinstance(pyfunction.parent, pyobjects.PyClass) @utils.deprecated("Use `ChangeSignature.get_args()` instead") def get_definition_info(self): return self._definfo() def _definfo(self): return functionutils.DefinitionInfo.read(self.pyname.get_object()) @utils.deprecated() def normalize(self): changer = _FunctionChangers( self.pyname.get_object(), self.get_definition_info(), [ArgumentNormalizer()] ) return self._change_calls(changer) @utils.deprecated() def remove(self, index): changer = _FunctionChangers( self.pyname.get_object(), self.get_definition_info(), [ArgumentRemover(index)], ) return self._change_calls(changer) @utils.deprecated() def add(self, index, name, default=None, value=None): changer = _FunctionChangers( self.pyname.get_object(), self.get_definition_info(), [ArgumentAdder(index, name, default, value)], ) return self._change_calls(changer) @utils.deprecated() def inline_default(self, index): changer = _FunctionChangers( self.pyname.get_object(), self.get_definition_info(), [ArgumentDefaultInliner(index)], ) return self._change_calls(changer) @utils.deprecated() def reorder(self, new_ordering): changer = _FunctionChangers( self.pyname.get_object(), self.get_definition_info(), [ArgumentReorderer(new_ordering)], ) return self._change_calls(changer) def get_changes( self, changers, in_hierarchy=False, resources=None, task_handle=taskhandle.NullTaskHandle(), ): """Get changes caused by this refactoring `changers` is a list of `_ArgumentChanger`. If `in_hierarchy` is `True` the changers are applyed to all matching methods in the class hierarchy. `resources` can be a list of `rope.base.resource.File` that should be searched for occurrences; if `None` all python files in the project are searched. """ function_changer = _FunctionChangers( self.pyname.get_object(), self._definfo(), changers ) return self._change_calls( function_changer, in_hierarchy, resources, task_handle ) class _FunctionChangers(object): def __init__(self, pyfunction, definition_info, changers=None): self.pyfunction = pyfunction self.definition_info = definition_info self.changers = changers self.changed_definition_infos = self._get_changed_definition_infos() def _get_changed_definition_infos(self): result = [] definition_info = self.definition_info result.append(definition_info) for changer in self.changers: definition_info = copy.deepcopy(definition_info) changer.change_definition_info(definition_info) result.append(definition_info) return result def change_definition(self, call): return self.changed_definition_infos[-1].to_string() def change_call(self, primary, pyname, call): call_info = functionutils.CallInfo.read( primary, pyname, self.definition_info, call ) mapping = functionutils.ArgumentMapping(self.definition_info, call_info) for definition_info, changer in zip( self.changed_definition_infos, self.changers ): changer.change_argument_mapping(definition_info, mapping) return mapping.to_call_info(self.changed_definition_infos[-1]).to_string() class _ArgumentChanger(object): def change_definition_info(self, definition_info): pass def change_argument_mapping(self, definition_info, argument_mapping): pass class ArgumentNormalizer(_ArgumentChanger): pass class ArgumentRemover(_ArgumentChanger): def __init__(self, index): self.index = index def change_definition_info(self, call_info): if self.index < len(call_info.args_with_defaults): del call_info.args_with_defaults[self.index] elif ( self.index == len(call_info.args_with_defaults) and call_info.args_arg is not None ): call_info.args_arg = None elif ( self.index == len(call_info.args_with_defaults) and call_info.args_arg is None and call_info.keywords_arg is not None ) or ( self.index == len(call_info.args_with_defaults) + 1 and call_info.args_arg is not None and call_info.keywords_arg is not None ): call_info.keywords_arg = None def change_argument_mapping(self, definition_info, mapping): if self.index < len(definition_info.args_with_defaults): name = definition_info.args_with_defaults[0] if name in mapping.param_dict: del mapping.param_dict[name] class ArgumentAdder(_ArgumentChanger): def __init__(self, index, name, default=None, value=None): self.index = index self.name = name self.default = default self.value = value def change_definition_info(self, definition_info): for pair in definition_info.args_with_defaults: if pair[0] == self.name: raise rope.base.exceptions.RefactoringError( "Adding duplicate parameter: <%s>." % self.name ) definition_info.args_with_defaults.insert(self.index, (self.name, self.default)) def change_argument_mapping(self, definition_info, mapping): if self.value is not None: mapping.param_dict[self.name] = self.value class ArgumentDefaultInliner(_ArgumentChanger): def __init__(self, index): self.index = index self.remove = False def change_definition_info(self, definition_info): if self.remove: definition_info.args_with_defaults[self.index] = ( definition_info.args_with_defaults[self.index][0], None, ) def change_argument_mapping(self, definition_info, mapping): default = definition_info.args_with_defaults[self.index][1] name = definition_info.args_with_defaults[self.index][0] if default is not None and name not in mapping.param_dict: mapping.param_dict[name] = default class ArgumentReorderer(_ArgumentChanger): def __init__(self, new_order, autodef=None): """Construct an `ArgumentReorderer` Note that the `new_order` is a list containing the new position of parameters; not the position each parameter is going to be moved to. (changed in ``0.5m4``) For example changing ``f(a, b, c)`` to ``f(c, a, b)`` requires passing ``[2, 0, 1]`` and *not* ``[1, 2, 0]``. The `autodef` (automatic default) argument, forces rope to use it as a default if a default is needed after the change. That happens when an argument without default is moved after another that has a default value. Note that `autodef` should be a string or `None`; the latter disables adding automatic default. """ self.new_order = new_order self.autodef = autodef def change_definition_info(self, definition_info): new_args = list(definition_info.args_with_defaults) for new_index, index in enumerate(self.new_order): new_args[new_index] = definition_info.args_with_defaults[index] seen_default = False for index, (arg, default) in enumerate(list(new_args)): if default is not None: seen_default = True if seen_default and default is None and self.autodef is not None: new_args[index] = (arg, self.autodef) definition_info.args_with_defaults = new_args class _ChangeCallsInModule(object): def __init__(self, project, occurrence_finder, resource, call_changer): self.project = project self.occurrence_finder = occurrence_finder self.resource = resource self.call_changer = call_changer def get_changed_module(self): word_finder = worder.Worder(self.source) change_collector = codeanalyze.ChangeCollector(self.source) for occurrence in self.occurrence_finder.find_occurrences(self.resource): if not occurrence.is_called() and not occurrence.is_defined(): continue start, end = occurrence.get_primary_range() begin_parens, end_parens = word_finder.get_word_parens_range(end - 1) if occurrence.is_called(): primary, pyname = occurrence.get_primary_and_pyname() changed_call = self.call_changer.change_call( primary, pyname, self.source[start:end_parens] ) else: changed_call = self.call_changer.change_definition( self.source[start:end_parens] ) if changed_call is not None: change_collector.add_change(start, end_parens, changed_call) return change_collector.get_changed() @property @utils.saveit def pymodule(self): return self.project.get_pymodule(self.resource) @property @utils.saveit def source(self): if self.resource is not None: return self.resource.read() else: return self.pymodule.source_code @property @utils.saveit def lines(self): return self.pymodule.lines class _MultipleFinders(object): def __init__(self, finders): self.finders = finders def find_occurrences(self, resource=None, pymodule=None): all_occurrences = [] for finder in self.finders: all_occurrences.extend(finder.find_occurrences(resource, pymodule)) all_occurrences.sort(key=lambda x: x.get_primary_range()) return all_occurrences ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633441152.0 rope-0.22.0/rope/refactor/encapsulate_field.py0000664000175000017500000002073500000000000021235 0ustar00lieryanlieryanfrom rope.base import evaluate from rope.base import exceptions from rope.base import libutils from rope.base import pynames from rope.base import taskhandle from rope.base import utils from rope.base import worder from rope.base.change import ChangeSet, ChangeContents from rope.refactor import sourceutils, occurrences class EncapsulateField(object): def __init__(self, project, resource, offset): self.project = project self.name = worder.get_name_at(resource, offset) this_pymodule = self.project.get_pymodule(resource) self.pyname = evaluate.eval_location(this_pymodule, offset) if not self._is_an_attribute(self.pyname): raise exceptions.RefactoringError( "Encapsulate field should be performed on class attributes." ) self.resource = self.pyname.get_definition_location()[0].get_resource() def get_changes( self, getter=None, setter=None, resources=None, task_handle=taskhandle.NullTaskHandle(), ): """Get the changes this refactoring makes If `getter` is not `None`, that will be the name of the getter, otherwise ``get_${field_name}`` will be used. The same is true for `setter` and if it is None set_${field_name} is used. `resources` can be a list of `rope.base.resource.File` that the refactoring should be applied on; if `None` all python files in the project are searched. """ if resources is None: resources = self.project.get_python_files() changes = ChangeSet("Encapsulate field <%s>" % self.name) job_set = task_handle.create_jobset("Collecting Changes", len(resources)) if getter is None: getter = "get_" + self.name if setter is None: setter = "set_" + self.name renamer = GetterSetterRenameInModule( self.project, self.name, self.pyname, getter, setter ) for file in resources: job_set.started_job(file.path) if file == self.resource: result = self._change_holding_module(changes, renamer, getter, setter) changes.add_change(ChangeContents(self.resource, result)) else: result = renamer.get_changed_module(file) if result is not None: changes.add_change(ChangeContents(file, result)) job_set.finished_job() return changes def get_field_name(self): """Get the name of the field to be encapsulated""" return self.name def _is_an_attribute(self, pyname): if pyname is not None and isinstance(pyname, pynames.AssignedName): pymodule, lineno = self.pyname.get_definition_location() scope = pymodule.get_scope().get_inner_scope_for_line(lineno) if scope.get_kind() == "Class": return pyname in scope.get_names().values() parent = scope.parent if parent is not None and parent.get_kind() == "Class": return pyname in parent.get_names().values() return False def _get_defining_class_scope(self): defining_scope = self._get_defining_scope() if defining_scope.get_kind() == "Function": defining_scope = defining_scope.parent return defining_scope def _get_defining_scope(self): pymodule, line = self.pyname.get_definition_location() return pymodule.get_scope().get_inner_scope_for_line(line) def _change_holding_module(self, changes, renamer, getter, setter): pymodule = self.project.get_pymodule(self.resource) class_scope = self._get_defining_class_scope() defining_object = self._get_defining_scope().pyobject start, end = sourceutils.get_body_region(defining_object) new_source = renamer.get_changed_module( pymodule=pymodule, skip_start=start, skip_end=end ) if new_source is not None: pymodule = libutils.get_string_module( self.project, new_source, self.resource ) class_scope = pymodule.get_scope().get_inner_scope_for_line( class_scope.get_start() ) indents = sourceutils.get_indent(self.project) * " " getter = "def %s(self):\n%sreturn self.%s" % (getter, indents, self.name) setter = "def %s(self, value):\n%sself.%s = value" % ( setter, indents, self.name, ) new_source = sourceutils.add_methods(pymodule, class_scope, [getter, setter]) return new_source class GetterSetterRenameInModule(object): def __init__(self, project, name, pyname, getter, setter): self.project = project self.name = name self.finder = occurrences.create_finder(project, name, pyname) self.getter = getter self.setter = setter def get_changed_module( self, resource=None, pymodule=None, skip_start=0, skip_end=0 ): change_finder = _FindChangesForModule( self, resource, pymodule, skip_start, skip_end ) return change_finder.get_changed_module() class _FindChangesForModule(object): def __init__(self, finder, resource, pymodule, skip_start, skip_end): self.project = finder.project self.finder = finder.finder self.getter = finder.getter self.setter = finder.setter self.resource = resource self.pymodule = pymodule self.last_modified = 0 self.last_set = None self.set_index = None self.skip_start = skip_start self.skip_end = skip_end def get_changed_module(self): result = [] for occurrence in self.finder.find_occurrences(self.resource, self.pymodule): start, end = occurrence.get_word_range() if self.skip_start <= start < self.skip_end: continue self._manage_writes(start, result) result.append(self.source[self.last_modified : start]) if self._is_assigned_in_a_tuple_assignment(occurrence): raise exceptions.RefactoringError( "Cannot handle tuple assignments in encapsulate field." ) if occurrence.is_written(): assignment_type = self.worder.get_assignment_type(start) if assignment_type == "=": result.append(self.setter + "(") else: var_name = ( self.source[occurrence.get_primary_range()[0] : start] + self.getter + "()" ) result.append( self.setter + "(" + var_name + " %s " % assignment_type[:-1] ) current_line = self.lines.get_line_number(start) start_line, end_line = self.pymodule.logical_lines.logical_line_in( current_line ) self.last_set = self.lines.get_line_end(end_line) end = self.source.index("=", end) + 1 self.set_index = len(result) else: result.append(self.getter + "()") self.last_modified = end if self.last_modified != 0: self._manage_writes(len(self.source), result) result.append(self.source[self.last_modified :]) return "".join(result) return None def _manage_writes(self, offset, result): if self.last_set is not None and self.last_set <= offset: result.append(self.source[self.last_modified : self.last_set]) set_value = "".join(result[self.set_index :]).strip() del result[self.set_index :] result.append(set_value + ")") self.last_modified = self.last_set self.last_set = None def _is_assigned_in_a_tuple_assignment(self, occurrence): offset = occurrence.get_word_range()[0] return self.worder.is_assigned_in_a_tuple_assignment(offset) @property @utils.saveit def source(self): if self.resource is not None: return self.resource.read() else: return self.pymodule.source_code @property @utils.saveit def lines(self): if self.pymodule is None: self.pymodule = self.project.get_pymodule(self.resource) return self.pymodule.lines @property @utils.saveit def worder(self): return worder.Worder(self.source) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636636936.0 rope-0.22.0/rope/refactor/extract.py0000664000175000017500000011072500000000000017237 0ustar00lieryanlieryanimport re from contextlib import contextmanager from rope.base import ast, codeanalyze from rope.base.change import ChangeSet, ChangeContents from rope.base.exceptions import RefactoringError from rope.base.utils import pycompat from rope.base.utils.datastructures import OrderedSet from rope.refactor import sourceutils, similarfinder, patchedast, suites, usefunction # Extract refactoring has lots of special cases. I tried to split it # to smaller parts to make it more manageable: # # _ExtractInfo: holds information about the refactoring; it is passed # to the parts that need to have information about the refactoring # # _ExtractCollector: merely saves all of the information necessary for # performing the refactoring. # # _DefinitionLocationFinder: finds where to insert the definition. # # _ExceptionalConditionChecker: checks for exceptional conditions in # which the refactoring cannot be applied. # # _ExtractMethodParts: generates the pieces of code (like definition) # needed for performing extract method. # # _ExtractVariableParts: like _ExtractMethodParts for variables. # # _ExtractPerformer: Uses above classes to collect refactoring # changes. # # There are a few more helper functions and classes used by above # classes. class _ExtractRefactoring(object): kind_prefixes = {} def __init__(self, project, resource, start_offset, end_offset, variable=False): self.project = project self.resource = resource self.start_offset = self._fix_start(resource.read(), start_offset) self.end_offset = self._fix_end(resource.read(), end_offset) def _fix_start(self, source, offset): while offset < len(source) and source[offset].isspace(): offset += 1 return offset def _fix_end(self, source, offset): while offset > 0 and source[offset - 1].isspace(): offset -= 1 return offset def get_changes(self, extracted_name, similar=False, global_=False, kind=None): """Get the changes this refactoring makes :parameters: - `extracted_name`: target name, when starts with @ - set kind to classmethod, $ - staticmethod - `similar`: if `True`, similar expressions/statements are also replaced. - `global_`: if `True`, the extracted method/variable will be global. - `kind`: kind of target refactoring to (staticmethod, classmethod) """ extracted_name, kind = self._get_kind_from_name(extracted_name, kind) info = _ExtractInfo( self.project, self.resource, self.start_offset, self.end_offset, extracted_name, variable=self._get_kind(kind) == "variable", similar=similar, make_global=global_, ) info.kind = self._get_kind(kind) new_contents = _ExtractPerformer(info).extract() changes = ChangeSet("Extract %s <%s>" % (info.kind, extracted_name)) changes.add_change(ChangeContents(self.resource, new_contents)) return changes def _get_kind_from_name(self, extracted_name, kind): for sign, selected_kind in self.kind_prefixes.items(): if extracted_name.startswith(sign): self._validate_kind_prefix(kind, selected_kind) return extracted_name[1:], selected_kind return extracted_name, kind @staticmethod def _validate_kind_prefix(kind, selected_kind): if kind and kind != selected_kind: raise RefactoringError("Kind and shortcut in name mismatch") @classmethod def _get_kind(cls, kind): raise NotImplementedError("You have to sublass {}".format(cls)) class ExtractMethod(_ExtractRefactoring): kind = "method" allowed_kinds = ("function", "method", "staticmethod", "classmethod") kind_prefixes = {"@": "classmethod", "$": "staticmethod"} @classmethod def _get_kind(cls, kind): return kind if kind in cls.allowed_kinds else cls.kind class ExtractVariable(_ExtractRefactoring): def __init__(self, *args, **kwds): kwds = dict(kwds) kwds["variable"] = True super(ExtractVariable, self).__init__(*args, **kwds) kind = "variable" def _get_kind(cls, kind): return cls.kind class _ExtractInfo(object): """Holds information about the extract to be performed""" def __init__( self, project, resource, start, end, new_name, variable, similar, make_global ): self.project = project self.resource = resource self.pymodule = project.get_pymodule(resource) self.global_scope = self.pymodule.get_scope() self.source = self.pymodule.source_code self.lines = self.pymodule.lines self.new_name = new_name self.variable = variable self.similar = similar self._init_parts(start, end) self.kind = None self._init_scope() self.make_global = make_global def _init_parts(self, start, end): self.region = ( self._choose_closest_line_end(start), self._choose_closest_line_end(end, end=True), ) start = self.logical_lines.logical_line_in( self.lines.get_line_number(self.region[0]) )[0] end = self.logical_lines.logical_line_in( self.lines.get_line_number(self.region[1]) )[1] self.region_lines = (start, end) self.lines_region = ( self.lines.get_line_start(self.region_lines[0]), self.lines.get_line_end(self.region_lines[1]), ) @property def logical_lines(self): return self.pymodule.logical_lines def _init_scope(self): start_line = self.region_lines[0] scope = self.global_scope.get_inner_scope_for_line(start_line) if scope.get_kind() != "Module" and scope.get_start() == start_line: scope = scope.parent self.scope = scope self.scope_region = self._get_scope_region(self.scope) def _get_scope_region(self, scope): return ( self.lines.get_line_start(scope.get_start()), self.lines.get_line_end(scope.get_end()) + 1, ) def _choose_closest_line_end(self, offset, end=False): lineno = self.lines.get_line_number(offset) line_start = self.lines.get_line_start(lineno) line_end = self.lines.get_line_end(lineno) if self.source[line_start:offset].strip() == "": if end: return line_start - 1 else: return line_start elif self.source[offset:line_end].strip() == "": return min(line_end, len(self.source)) return offset @property def one_line(self): return self.region != self.lines_region and ( self.logical_lines.logical_line_in(self.region_lines[0]) == self.logical_lines.logical_line_in(self.region_lines[1]) ) @property def global_(self): return self.scope.parent is None @property def method(self): return self.scope.parent is not None and self.scope.parent.get_kind() == "Class" @property def indents(self): return sourceutils.get_indents(self.pymodule.lines, self.region_lines[0]) @property def scope_indents(self): if self.global_: return 0 return sourceutils.get_indents(self.pymodule.lines, self.scope.get_start()) @property def extracted(self): return self.source[self.region[0] : self.region[1]] _cached_parsed_extraced = None @property def _parsed_extracted(self): if self._cached_parsed_extraced is None: self._cached_parsed_extraced = _parse_text(self.extracted) return self._cached_parsed_extraced _returned = None @property def returned(self): """Does the extracted piece contain return statement""" if self._returned is None: self._returned = usefunction._returns_last(self._parsed_extracted) return self._returned _returning_named_expr = None @property def returning_named_expr(self): """Does the extracted piece contains named expression/:= operator)""" if self._returning_named_expr is None: self._returning_named_expr = usefunction._namedexpr_last( self._parsed_extracted ) return self._returning_named_expr class _ExtractCollector(object): """Collects information needed for performing the extract""" def __init__(self, info): self.definition = None self.body_pattern = None self.checks = {} self.replacement_pattern = None self.matches = None self.replacements = None self.definition_location = None class _ExtractPerformer(object): def __init__(self, info): self.info = info _ExceptionalConditionChecker()(self.info) def extract(self): extract_info = self._collect_info() content = codeanalyze.ChangeCollector(self.info.source) definition = extract_info.definition lineno, indents = extract_info.definition_location offset = self.info.lines.get_line_start(lineno) indented = sourceutils.fix_indentation(definition, indents) content.add_change(offset, offset, indented) self._replace_occurrences(content, extract_info) return content.get_changed() def _replace_occurrences(self, content, extract_info): for match in extract_info.matches: replacement = similarfinder.CodeTemplate(extract_info.replacement_pattern) mapping = {} for name in replacement.get_names(): node = match.get_ast(name) if node: start, end = patchedast.node_region(match.get_ast(name)) mapping[name] = self.info.source[start:end] else: mapping[name] = name region = match.get_region() content.add_change(region[0], region[1], replacement.substitute(mapping)) def _collect_info(self): extract_collector = _ExtractCollector(self.info) self._find_definition(extract_collector) self._find_matches(extract_collector) self._find_definition_location(extract_collector) return extract_collector def _find_matches(self, collector): regions = self._where_to_search() finder = similarfinder.SimilarFinder(self.info.pymodule) matches = [] for start, end in regions: region_matches = finder.get_matches( collector.body_pattern, collector.checks, start, end ) # Don't extract overlapping regions last_match_end = -1 for region_match in region_matches: if self.info.one_line and self._is_assignment(region_match): continue start, end = region_match.get_region() if last_match_end < start: matches.append(region_match) last_match_end = end collector.matches = matches @staticmethod def _is_assignment(region_match): return isinstance( region_match.ast, (ast.Attribute, ast.Subscript) ) and isinstance(region_match.ast.ctx, ast.Store) def _where_to_search(self): if self.info.similar: if self.info.make_global or self.info.global_: return [(0, len(self.info.pymodule.source_code))] if self.info.method and not self.info.variable: class_scope = self.info.scope.parent regions = [] method_kind = _get_function_kind(self.info.scope) for scope in class_scope.get_scopes(): if ( method_kind == "method" and _get_function_kind(scope) != "method" ): continue start = self.info.lines.get_line_start(scope.get_start()) end = self.info.lines.get_line_end(scope.get_end()) regions.append((start, end)) return regions else: if self.info.variable: return [self.info.scope_region] else: return [self.info._get_scope_region(self.info.scope.parent)] else: return [self.info.region] def _find_definition_location(self, collector): matched_lines = [] for match in collector.matches: start = self.info.lines.get_line_number(match.get_region()[0]) start_line = self.info.logical_lines.logical_line_in(start)[0] matched_lines.append(start_line) location_finder = _DefinitionLocationFinder(self.info, matched_lines) collector.definition_location = ( location_finder.find_lineno(), location_finder.find_indents(), ) def _find_definition(self, collector): if self.info.variable: parts = _ExtractVariableParts(self.info) else: parts = _ExtractMethodParts(self.info) collector.definition = parts.get_definition() collector.body_pattern = parts.get_body_pattern() collector.replacement_pattern = parts.get_replacement_pattern() collector.checks = parts.get_checks() class _DefinitionLocationFinder(object): def __init__(self, info, matched_lines): self.info = info self.matched_lines = matched_lines # This only happens when subexpressions cannot be matched if not matched_lines: self.matched_lines.append(self.info.region_lines[0]) def find_lineno(self): if self.info.variable and not self.info.make_global: return self._get_before_line() if self.info.global_: toplevel = self._find_toplevel(self.info.scope) ast = self.info.pymodule.get_ast() newlines = sorted(self.matched_lines + [toplevel.get_end() + 1]) return suites.find_visible(ast, newlines) if self.info.make_global: toplevel = self._find_toplevel(self.info.scope) return toplevel.get_end() + 1 return self._get_after_scope() def _find_toplevel(self, scope): toplevel = scope if toplevel.parent is not None: while toplevel.parent.parent is not None: toplevel = toplevel.parent return toplevel def find_indents(self): if self.info.variable and not self.info.make_global: return sourceutils.get_indents(self.info.lines, self._get_before_line()) else: if self.info.global_ or self.info.make_global: return 0 return self.info.scope_indents def _get_before_line(self): ast = self.info.scope.pyobject.get_ast() return suites.find_visible(ast, self.matched_lines) def _get_after_scope(self): return self.info.scope.get_end() + 1 class _ExceptionalConditionChecker(object): def __call__(self, info): self.base_conditions(info) if info.one_line: self.one_line_conditions(info) else: self.multi_line_conditions(info) def base_conditions(self, info): if info.region[1] > info.scope_region[1]: raise RefactoringError("Bad region selected for extract method") end_line = info.region_lines[1] end_scope = info.global_scope.get_inner_scope_for_line(end_line) if end_scope != info.scope and end_scope.get_end() != end_line: raise RefactoringError("Bad region selected for extract method") try: extracted = info.extracted if info.one_line: extracted = "(%s)" % extracted if _UnmatchedBreakOrContinueFinder.has_errors(extracted): raise RefactoringError( "A break/continue without having a matching for/while loop." ) except SyntaxError: raise RefactoringError( "Extracted piece should contain complete statements." ) def one_line_conditions(self, info): if self._is_region_on_a_word(info): raise RefactoringError("Should extract complete statements.") if info.variable and not info.one_line: raise RefactoringError("Extract variable should not 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 (:= 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: raise RefactoringError( "Extracted piece should contain complete statements." ) def _is_region_on_a_word(self, info): if ( info.region[0] > 0 and self._is_on_a_word(info, info.region[0] - 1) or self._is_on_a_word(info, info.region[1] - 1) ): return True def _is_on_a_word(self, info, offset): prev = info.source[offset] if not (prev.isalnum() or prev == "_") or offset + 1 == len(info.source): return False next = info.source[offset + 1] return next.isalnum() or next == "_" class _ExtractMethodParts(object): def __init__(self, info): self.info = info self.info_collector = self._create_info_collector() self.info.kind = self._get_kind_by_scope() self._check_constraints() def _get_kind_by_scope(self): if self._extacting_from_staticmethod(): return "staticmethod" elif self._extracting_from_classmethod(): return "classmethod" return self.info.kind def _check_constraints(self): if self._extracting_staticmethod() or self._extracting_classmethod(): if not self.info.method: raise RefactoringError( "Cannot extract to staticmethod/classmethod outside class" ) def _extacting_from_staticmethod(self): return ( self.info.method and _get_function_kind(self.info.scope) == "staticmethod" ) def _extracting_from_classmethod(self): return self.info.method and _get_function_kind(self.info.scope) == "classmethod" def get_definition(self): if self.info.global_: return "\n%s\n" % self._get_function_definition() else: return "\n%s" % self._get_function_definition() def get_replacement_pattern(self): variables = [] variables.extend(self._find_function_arguments()) variables.extend(self._find_function_returns()) return similarfinder.make_pattern(self._get_call(), variables) def get_body_pattern(self): variables = [] variables.extend(self._find_function_arguments()) variables.extend(self._find_function_returns()) variables.extend(self._find_temps()) return similarfinder.make_pattern(self._get_body(), variables) def _get_body(self): result = sourceutils.fix_indentation(self.info.extracted, 0) if self.info.one_line: result = "(%s)" % result return result def _find_temps(self): return usefunction.find_temps(self.info.project, self._get_body()) def get_checks(self): if self.info.method and not self.info.make_global: if _get_function_kind(self.info.scope) == "method": class_name = similarfinder._pydefined_to_str( self.info.scope.parent.pyobject ) return {self._get_self_name(): "type=" + class_name} return {} def _create_info_collector(self): zero = self.info.scope.get_start() - 1 start_line = self.info.region_lines[0] - zero end_line = self.info.region_lines[1] - zero info_collector = _FunctionInformationCollector( start_line, end_line, self.info.global_ ) body = self.info.source[self.info.scope_region[0] : self.info.scope_region[1]] node = _parse_text(body) ast.walk(node, info_collector) return info_collector def _get_function_definition(self): args = self._find_function_arguments() returns = self._find_function_returns() result = [] self._append_decorators(result) result.append("def %s:\n" % self._get_function_signature(args)) unindented_body = self._get_unindented_function_body(returns) indents = sourceutils.get_indent(self.info.project) function_body = sourceutils.indent_lines(unindented_body, indents) result.append(function_body) definition = "".join(result) return definition + "\n" def _append_decorators(self, result): if self._extracting_staticmethod(): result.append("@staticmethod\n") elif self._extracting_classmethod(): result.append("@classmethod\n") def _extracting_classmethod(self): return self.info.kind == "classmethod" def _extracting_staticmethod(self): return self.info.kind == "staticmethod" def _get_function_signature(self, args): args = list(args) prefix = "" if self._extracting_method() or self._extracting_classmethod(): self_name = self._get_self_name() if self_name is None: raise RefactoringError( "Extracting a method from a function with no self argument." ) if self_name in args: args.remove(self_name) args.insert(0, self_name) return prefix + self.info.new_name + "(%s)" % self._get_comma_form(args) def _extracting_method(self): return not self._extracting_staticmethod() and ( self.info.method and not self.info.make_global and _get_function_kind(self.info.scope) == "method" ) def _get_self_name(self): if self._extracting_classmethod(): return "cls" return self._get_scope_self_name() def _get_scope_self_name(self): if self.info.scope.pyobject.get_kind() == "staticmethod": return param_names = self.info.scope.pyobject.get_param_names() if param_names: return param_names[0] def _get_function_call(self, args): return "{prefix}{name}({args})".format( prefix=self._get_function_call_prefix(args), name=self.info.new_name, args=self._get_comma_form(args), ) def _get_function_call_prefix(self, args): prefix = "" if self.info.method and not self.info.make_global: if self._extracting_staticmethod() or self._extracting_classmethod(): prefix = self.info.scope.parent.pyobject.get_name() + "." else: self_name = self._get_self_name() if self_name in args: args.remove(self_name) prefix = self_name + "." return prefix def _get_comma_form(self, names): return ", ".join(names) def _get_call(self): args = self._find_function_arguments() returns = self._find_function_returns() call_prefix = "" if returns and (not self.info.one_line or self.info.returning_named_expr): assignment_operator = " := " if self.info.one_line else " = " call_prefix = self._get_comma_form(returns) + assignment_operator if self.info.returned: call_prefix = "return " return call_prefix + self._get_function_call(args) def _find_function_arguments(self): # if not make_global, do not pass any global names; they are # all visible. if self.info.global_ and not self.info.make_global: return list( self.info_collector.read & self.info_collector.postread & self.info_collector.written ) if not self.info.one_line: result = self.info_collector.prewritten & self.info_collector.read result |= ( self.info_collector.prewritten & self.info_collector.postread & (self.info_collector.maybe_written - self.info_collector.written) ) return list(result) start = self.info.region[0] if start == self.info.lines_region[0]: start = start + re.search("\\S", self.info.extracted).start() function_definition = self.info.source[start : self.info.region[1]] read = _VariableReadsAndWritesFinder.find_reads_for_one_liners( function_definition ) return list(self.info_collector.prewritten.intersection(read)) def _find_function_returns(self): if self.info.one_line: written = self.info_collector.written | self.info_collector.maybe_written return list(written & self.info_collector.postread) if self.info.returned: return [] written = self.info_collector.written | self.info_collector.maybe_written return list(written & self.info_collector.postread) def _get_unindented_function_body(self, returns): if self.info.one_line: return self._get_one_line_function_body() return self._get_multiline_function_body(returns) def _get_multiline_function_body(self, returns): unindented_body = sourceutils.fix_indentation(self.info.extracted, 0) unindented_body = self._insert_globals(unindented_body) if returns: unindented_body += "\nreturn %s" % self._get_comma_form(returns) return unindented_body def _get_one_line_function_body(self): if self.info.returning_named_expr: body = "return " + "(" + _join_lines(self.info.extracted) + ")" else: body = "return " + _join_lines(self.info.extracted) return self._insert_globals(body) def _insert_globals(self, unindented_body): globals_in_body = self._get_globals_in_body(unindented_body) globals_ = self.info_collector.globals_ & ( self.info_collector.written | self.info_collector.maybe_written ) globals_ = globals_ - globals_in_body if globals_: unindented_body = "global {}\n{}".format( ", ".join(globals_), unindented_body ) return unindented_body @staticmethod def _get_globals_in_body(unindented_body): node = _parse_text(unindented_body) visitor = _GlobalFinder() ast.walk(node, visitor) return visitor.globals_ class _ExtractVariableParts(object): def __init__(self, info): self.info = info def get_definition(self): result = self.info.new_name + " = " + _join_lines(self.info.extracted) + "\n" return result def get_body_pattern(self): return "(%s)" % self.info.extracted.strip() def get_replacement_pattern(self): return self.info.new_name def get_checks(self): return {} class _FunctionInformationCollector(object): def __init__(self, start, end, is_global): self.start = start self.end = end self.is_global = is_global self.prewritten = OrderedSet() self.maybe_written = OrderedSet() self.written = OrderedSet() self.read = OrderedSet() self.postread = OrderedSet() self.postwritten = OrderedSet() self.host_function = True self.conditional = False self.globals_ = OrderedSet() self.surrounded_by_loop = 0 self.loop_depth = 0 def _read_variable(self, name, lineno): if self.start <= lineno <= self.end: if name not in self.written: if not self.conditional or name not in self.maybe_written: self.read.add(name) if self.end < lineno: if name not in self.postwritten: self.postread.add(name) def _written_variable(self, name, lineno): if self.start <= lineno <= self.end: if self.conditional: self.maybe_written.add(name) else: self.written.add(name) if self.loop_depth > 0 and name in self.read: self.postread.add(name) if self.start > lineno: self.prewritten.add(name) if self.end < lineno: self.postwritten.add(name) def _FunctionDef(self, node): if not self.is_global and self.host_function: self.host_function = False for name in _get_argnames(node.args): self._written_variable(name, node.lineno) for child in node.body: ast.walk(child, self) else: self._written_variable(node.name, node.lineno) visitor = _VariableReadsAndWritesFinder() for child in node.body: ast.walk(child, visitor) for name in visitor.read - visitor.written: self._read_variable(name, node.lineno) 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) if not isinstance(node.ctx, ast.Store): self._read_variable(node.id, node.lineno) def _Assign(self, node): ast.walk(node.value, self) for child in node.targets: ast.walk(child, self) def _AugAssign(self, node): ast.walk(node.value, self) self._read_variable(node.target.id, node.target.lineno) self._written_variable(node.target.id, node.target.lineno) def _ClassDef(self, node): self._written_variable(node.name, node.lineno) def _ListComp(self, node): self._comp_exp(node) def _GeneratorExp(self, node): self._comp_exp(node) def _SetComp(self, node): self._comp_exp(node) def _DictComp(self, node): self._comp_exp(node) def _comp_exp(self, node): read = OrderedSet(self.read) written = OrderedSet(self.written) maybe_written = OrderedSet(self.maybe_written) for child in ast.get_child_nodes(node): ast.walk(child, self) comp_names = set([name.target.id for name in node.generators]) self.read = self.read - comp_names | read self.written = self.written - comp_names | written self.maybe_written = self.maybe_written - comp_names | maybe_written def _If(self, node): self._handle_conditional_node(node) def _While(self, node): with self._handle_loop_context(node): self._handle_conditional_node(node) def _For(self, node): with self._handle_loop_context(node), self._handle_conditional_context(node): # iter has to be checked before the target variables ast.walk(node.iter, self) ast.walk(node.target, self) for child in node.body: ast.walk(child, self) for child in node.orelse: ast.walk(child, self) def _handle_conditional_node(self, node): with self._handle_conditional_context(node): for child in ast.get_child_nodes(node): ast.walk(child, self) @contextmanager def _handle_conditional_context(self, node): if self.start <= node.lineno <= self.end: self.conditional = True try: yield finally: self.conditional = False @contextmanager def _handle_loop_context(self, node): if node.lineno < self.start: self.loop_depth += 1 try: yield finally: self.loop_depth -= 1 def _get_argnames(arguments): result = [ pycompat.get_ast_arg_arg(node) for node in arguments.args if isinstance(node, pycompat.ast_arg_type) ] if arguments.vararg: result.append(pycompat.get_ast_arg_arg(arguments.vararg)) if arguments.kwarg: result.append(pycompat.get_ast_arg_arg(arguments.kwarg)) return result class _VariableReadsAndWritesFinder(object): def __init__(self): self.written = set() self.read = set() def _Name(self, node): if isinstance(node.ctx, (ast.Store, ast.AugStore)): self.written.add(node.id) if not isinstance(node, ast.Store): self.read.add(node.id) def _FunctionDef(self, node): self.written.add(node.name) visitor = _VariableReadsAndWritesFinder() for child in ast.get_child_nodes(node): ast.walk(child, visitor) self.read.update(visitor.read - visitor.written) def _Class(self, node): self.written.add(node.name) @staticmethod def find_reads_and_writes(code): if code.strip() == "": return set(), set() node = _parse_text(code) visitor = _VariableReadsAndWritesFinder() ast.walk(node, visitor) return visitor.read, visitor.written @staticmethod def find_reads_for_one_liners(code): if code.strip() == "": return set(), set() node = _parse_text(code) visitor = _VariableReadsAndWritesFinder() ast.walk(node, visitor) return visitor.read 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 self.loop_count = 0 def _For(self, node): self.loop_encountered(node) def _While(self, node): self.loop_encountered(node) def loop_encountered(self, node): self.loop_count += 1 for child in node.body: ast.walk(child, self) self.loop_count -= 1 if node.orelse: if isinstance(node.orelse, (list, tuple)): for node_ in node.orelse: ast.walk(node_, self) else: ast.walk(node.orelse, self) def _Break(self, node): self.check_loop() def _Continue(self, node): self.check_loop() def check_loop(self): if self.loop_count < 1: self.error = True def _FunctionDef(self, node): pass def _ClassDef(self, node): pass 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): def __init__(self): self.globals_ = OrderedSet() def _Global(self, node): self.globals_.add(*node.names) def _get_function_kind(scope): return scope.pyobject.get_kind() def _parse_text(body): body = sourceutils.fix_indentation(body, 0) try: node = ast.parse(body) except SyntaxError: # needed to parse expression containing := operator 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 def _join_lines(code): lines = [] for line in code.splitlines(): if line.endswith("\\"): lines.append(line[:-1].strip()) else: lines.append(line.strip()) return " ".join(lines) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/functionutils.py0000664000175000017500000002050000000000000020462 0ustar00lieryanlieryanimport rope.base.exceptions import rope.base.pyobjects from rope.base.builtins import Lambda from rope.base import worder class DefinitionInfo(object): def __init__( self, function_name, is_method, args_with_defaults, args_arg, keywords_arg ): self.function_name = function_name self.is_method = is_method self.args_with_defaults = args_with_defaults self.args_arg = args_arg self.keywords_arg = keywords_arg def to_string(self): return "%s(%s)" % (self.function_name, self.arguments_to_string()) def arguments_to_string(self, from_index=0): params = [] for arg, default in self.args_with_defaults: if default is not None: params.append("%s=%s" % (arg, default)) else: params.append(arg) if self.args_arg is not None: params.append("*" + self.args_arg) if self.keywords_arg: params.append("**" + self.keywords_arg) return ", ".join(params[from_index:]) @staticmethod def _read(pyfunction, code): kind = pyfunction.get_kind() is_method = kind == "method" is_lambda = kind == "lambda" info = _FunctionParser(code, is_method, is_lambda) args, keywords = info.get_parameters() args_arg = None keywords_arg = None if args and args[-1].startswith("**"): keywords_arg = args[-1][2:] del args[-1] if args and args[-1].startswith("*"): args_arg = args[-1][1:] del args[-1] args_with_defaults = [(name, None) for name in args] args_with_defaults.extend(keywords) return DefinitionInfo( info.get_function_name(), is_method, args_with_defaults, args_arg, keywords_arg, ) @staticmethod def read(pyfunction): pymodule = pyfunction.get_module() word_finder = worder.Worder(pymodule.source_code) lineno = pyfunction.get_ast().lineno start = pymodule.lines.get_line_start(lineno) if isinstance(pyfunction, Lambda): call = word_finder.get_lambda_and_args(start) else: call = word_finder.get_function_and_args_in_header(start) return DefinitionInfo._read(pyfunction, call) class CallInfo(object): def __init__( self, function_name, args, keywords, args_arg, 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.implicit_arg = implicit_arg self.constructor = constructor def to_string(self): function = self.function_name if self.implicit_arg: function = self.args[0] + "." + self.function_name params = [] start = 0 if self.implicit_arg or self.constructor: start = 1 if self.args[start:]: params.extend(self.args[start:]) if self.keywords: params.extend(["%s=%s" % (name, value) for name, value in self.keywords]) if self.args_arg is not None: params.append("*" + self.args_arg) if self.keywords_arg: params.append("**" + self.keywords_arg) return "%s(%s)" % (function, ", ".join(params)) @staticmethod def read(primary, pyname, definition_info, code): 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 args and args[-1].startswith("**"): keywords_arg = args[-1][2:] del args[-1] if args and args[-1].startswith("*"): args_arg = args[-1][1:] del args[-1] 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 or is_classmethod, is_constructor, ) @staticmethod 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): return pyname is not None and 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 @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): def __init__(self, definition_info, call_info): self.call_info = call_info self.param_dict = {} self.keyword_args = [] self.args_arg = [] for index, value in enumerate(call_info.args): if index < len(definition_info.args_with_defaults): name = definition_info.args_with_defaults[index][0] self.param_dict[name] = value else: self.args_arg.append(value) for name, value in call_info.keywords: index = -1 for pair in definition_info.args_with_defaults: if pair[0] == name: self.param_dict[name] = value break else: self.keyword_args.append((name, value)) def to_call_info(self, definition_info): args = [] keywords = [] for index in range(len(definition_info.args_with_defaults)): name = definition_info.args_with_defaults[index][0] if name in self.param_dict: args.append(self.param_dict[name]) else: for i in range(index, len(definition_info.args_with_defaults)): name = definition_info.args_with_defaults[i][0] if name in self.param_dict: keywords.append((name, self.param_dict[name])) break args.extend(self.args_arg) 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.implicit_arg, self.call_info.constructor, ) class _FunctionParser(object): def __init__(self, call, implicit_arg, is_lambda=False): self.call = call self.implicit_arg = implicit_arg self.word_finder = worder.Worder(self.call) if is_lambda: self.last_parens = self.call.rindex(":") else: self.last_parens = self.call.rindex(")") self.first_parens = self.word_finder._find_parens_start(self.last_parens) def get_parameters(self): args, keywords = self.word_finder.get_parameters( self.first_parens, self.last_parens ) if self.is_called_as_a_method(): instance = self.call[: self.call.rindex(".", 0, self.first_parens)] args.insert(0, instance.strip()) return args, keywords def get_instance(self): if self.is_called_as_a_method(): return self.word_finder.get_primary_at( self.call.rindex(".", 0, self.first_parens) - 1 ) def get_function_name(self): if self.is_called_as_a_method(): return self.word_finder.get_word_at(self.first_parens - 1) else: return self.word_finder.get_primary_at(self.first_parens - 1) def is_called_as_a_method(self): return self.implicit_arg and "." in self.call[: self.first_parens] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3673904 rope-0.22.0/rope/refactor/importutils/0000775000175000017500000000000000000000000017600 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/importutils/__init__.py0000664000175000017500000003202700000000000021715 0ustar00lieryanlieryan"""A package for handling imports This package provides tools for modifying module imports after refactorings or as a separate task. """ import rope.base.evaluate from rope.base import libutils from rope.base.change import ChangeSet, ChangeContents from rope.refactor import occurrences, rename from rope.refactor.importutils import module_imports, actions from rope.refactor.importutils.importinfo import NormalImport, FromImport import rope.base.codeanalyze class ImportOrganizer(object): """Perform some import-related commands Each method returns a `rope.base.change.Change` object. """ def __init__(self, project): self.project = project self.import_tools = ImportTools(self.project) def organize_imports(self, resource, offset=None): return self._perform_command_on_import_tools( self.import_tools.organize_imports, resource, offset ) def expand_star_imports(self, resource, offset=None): return self._perform_command_on_import_tools( self.import_tools.expand_stars, resource, offset ) def froms_to_imports(self, resource, offset=None): return self._perform_command_on_import_tools( self.import_tools.froms_to_imports, resource, offset ) def relatives_to_absolutes(self, resource, offset=None): return self._perform_command_on_import_tools( self.import_tools.relatives_to_absolutes, resource, offset ) def handle_long_imports(self, resource, offset=None): return self._perform_command_on_import_tools( self.import_tools.handle_long_imports, resource, offset ) def _perform_command_on_import_tools(self, method, resource, offset): pymodule = self.project.get_pymodule(resource) before_performing = pymodule.source_code import_filter = None if offset is not None: import_filter = self._line_filter(pymodule.lines.get_line_number(offset)) result = method(pymodule, import_filter=import_filter) if result is not None and result != before_performing: changes = ChangeSet( method.__name__.replace("_", " ") + " in <%s>" % resource.path ) changes.add_change(ChangeContents(resource, result)) return changes def _line_filter(self, lineno): def import_filter(import_stmt): return import_stmt.start_line <= lineno < import_stmt.end_line return import_filter class ImportTools(object): def __init__(self, project): self.project = project def get_import(self, resource): """The import statement for `resource`""" module_name = libutils.modname(resource) return NormalImport(((module_name, None),)) def get_from_import(self, resource, name): """The from import statement for `name` in `resource`""" module_name = libutils.modname(resource) names = [] if isinstance(name, list): names = [(imported, None) for imported in name] else: names = [ (name, None), ] return FromImport(module_name, 0, tuple(names)) def module_imports(self, module, imports_filter=None): return module_imports.ModuleImports(self.project, module, imports_filter) def froms_to_imports(self, pymodule, import_filter=None): pymodule = self._clean_up_imports(pymodule, import_filter) module_imports = self.module_imports(pymodule, import_filter) for import_stmt in module_imports.imports: if import_stmt.readonly or not self._is_transformable_to_normal( import_stmt.import_info ): continue pymodule = self._from_to_normal(pymodule, import_stmt) # Adding normal imports in place of froms module_imports = self.module_imports(pymodule, import_filter) for import_stmt in module_imports.imports: if not import_stmt.readonly and self._is_transformable_to_normal( import_stmt.import_info ): import_stmt.import_info = NormalImport( ((import_stmt.import_info.module_name, None),) ) module_imports.remove_duplicates() return module_imports.get_changed_source() def expand_stars(self, pymodule, import_filter=None): module_imports = self.module_imports(pymodule, import_filter) module_imports.expand_stars() return module_imports.get_changed_source() def _from_to_normal(self, pymodule, import_stmt): resource = pymodule.get_resource() from_import = import_stmt.import_info module_name = from_import.module_name for name, alias in from_import.names_and_aliases: imported = name if alias is not None: imported = alias occurrence_finder = occurrences.create_finder( self.project, imported, pymodule[imported], imports=False ) source = rename.rename_in_module( occurrence_finder, module_name + "." + name, pymodule=pymodule, replace_primary=True, ) if source is not None: pymodule = libutils.get_string_module(self.project, source, resource) return pymodule def _clean_up_imports(self, pymodule, import_filter): resource = pymodule.get_resource() module_with_imports = self.module_imports(pymodule, import_filter) module_with_imports.expand_stars() source = module_with_imports.get_changed_source() if source is not None: pymodule = libutils.get_string_module(self.project, source, resource) source = self.relatives_to_absolutes(pymodule) if source is not None: pymodule = libutils.get_string_module(self.project, source, resource) module_with_imports = self.module_imports(pymodule, import_filter) module_with_imports.remove_duplicates() module_with_imports.remove_unused_imports() source = module_with_imports.get_changed_source() if source is not None: pymodule = libutils.get_string_module(self.project, source, resource) return pymodule def relatives_to_absolutes(self, pymodule, import_filter=None): module_imports = self.module_imports(pymodule, import_filter) to_be_absolute_list = module_imports.get_relative_to_absolute_list() for name, absolute_name in to_be_absolute_list: pymodule = self._rename_in_module(pymodule, name, absolute_name) module_imports = self.module_imports(pymodule, import_filter) module_imports.get_relative_to_absolute_list() source = module_imports.get_changed_source() if source is None: source = pymodule.source_code return source def _is_transformable_to_normal(self, import_info): if not isinstance(import_info, FromImport): return False return True def organize_imports( self, pymodule, unused=True, duplicates=True, selfs=True, sort=True, import_filter=None, ): if unused or duplicates: module_imports = self.module_imports(pymodule, import_filter) if unused: module_imports.remove_unused_imports() if self.project.prefs.get("split_imports"): module_imports.force_single_imports() if duplicates: module_imports.remove_duplicates() source = module_imports.get_changed_source() if source is not None: pymodule = libutils.get_string_module( self.project, source, pymodule.get_resource() ) if selfs: pymodule = self._remove_self_imports(pymodule, import_filter) if sort: return self.sort_imports(pymodule, import_filter) else: return pymodule.source_code def _remove_self_imports(self, pymodule, import_filter=None): module_imports = self.module_imports(pymodule, import_filter) ( to_be_fixed, to_be_renamed, ) = module_imports.get_self_import_fix_and_rename_list() for name in to_be_fixed: try: pymodule = self._rename_in_module(pymodule, name, "", till_dot=True) except ValueError: # There is a self import with direct access to it return pymodule for name, new_name in to_be_renamed: pymodule = self._rename_in_module(pymodule, name, new_name) module_imports = self.module_imports(pymodule, import_filter) module_imports.get_self_import_fix_and_rename_list() source = module_imports.get_changed_source() if source is not None: pymodule = libutils.get_string_module( self.project, source, pymodule.get_resource() ) return pymodule def _rename_in_module(self, pymodule, name, new_name, till_dot=False): old_name = name.split(".")[-1] old_pyname = rope.base.evaluate.eval_str(pymodule.get_scope(), name) occurrence_finder = occurrences.create_finder( self.project, old_name, old_pyname, imports=False ) changes = rope.base.codeanalyze.ChangeCollector(pymodule.source_code) for occurrence in occurrence_finder.find_occurrences(pymodule=pymodule): start, end = occurrence.get_primary_range() if till_dot: new_end = pymodule.source_code.index(".", end) + 1 space = pymodule.source_code[end : new_end - 1].strip() if not space == "": for c in space: if not c.isspace() and c not in "\\": raise ValueError() end = new_end changes.add_change(start, end, new_name) source = changes.get_changed() if source is not None: pymodule = libutils.get_string_module( self.project, source, pymodule.get_resource() ) return pymodule def sort_imports(self, pymodule, import_filter=None): module_imports = self.module_imports(pymodule, import_filter) module_imports.sort_imports() return module_imports.get_changed_source() def handle_long_imports( self, pymodule, maxdots=2, maxlength=27, import_filter=None ): # IDEA: `maxdots` and `maxlength` can be specified in project config # adding new from imports module_imports = self.module_imports(pymodule, import_filter) to_be_fixed = module_imports.handle_long_imports(maxdots, maxlength) # performing the renaming pymodule = libutils.get_string_module( self.project, module_imports.get_changed_source(), resource=pymodule.get_resource(), ) for name in to_be_fixed: pymodule = self._rename_in_module(pymodule, name, name.split(".")[-1]) # organizing imports return self.organize_imports( pymodule, selfs=False, sort=False, import_filter=import_filter ) def get_imports(project, pydefined): """A shortcut for getting the `ImportInfo` used in a scope""" pymodule = pydefined.get_module() module = module_imports.ModuleImports(project, pymodule) if pymodule == pydefined: return [stmt.import_info for stmt in module.imports] return module.get_used_imports(pydefined) def get_module_imports(project, pymodule): """A shortcut for creating a `module_imports.ModuleImports` object""" return module_imports.ModuleImports(project, pymodule) def add_import(project, pymodule, module_name, name=None): imports = get_module_imports(project, pymodule) candidates = [] names = [] selected_import = None # from mod import name if name is not None: from_import = FromImport(module_name, 0, [(name, None)]) names.append(name) candidates.append(from_import) # from pkg import mod if "." in module_name: pkg, mod = module_name.rsplit(".", 1) from_import = FromImport(pkg, 0, [(mod, None)]) if project.prefs.get("prefer_module_from_imports"): selected_import = from_import candidates.append(from_import) if name: names.append(mod + "." + name) else: names.append(mod) # import mod normal_import = NormalImport([(module_name, None)]) if name: names.append(module_name + "." + name) else: names.append(module_name) candidates.append(normal_import) visitor = actions.AddingVisitor(project, candidates) if selected_import is None: selected_import = normal_import for import_statement in imports.imports: if import_statement.accept(visitor): selected_import = visitor.import_info break imports.add_import(selected_import) imported_name = names[candidates.index(selected_import)] return imports.get_changed_source(), imported_name ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/importutils/actions.py0000664000175000017500000003375100000000000021623 0ustar00lieryanlieryanfrom rope.base import libutils from rope.base import pyobjects, exceptions, stdmods from rope.refactor import occurrences from rope.refactor.importutils import importinfo class ImportInfoVisitor(object): def dispatch(self, import_): try: method_name = "visit" + import_.import_info.__class__.__name__ method = getattr(self, method_name) return method(import_, import_.import_info) except exceptions.ModuleNotFoundError: pass def visitEmptyImport(self, import_stmt, import_info): pass def visitNormalImport(self, import_stmt, import_info): pass def visitFromImport(self, import_stmt, import_info): pass class RelativeToAbsoluteVisitor(ImportInfoVisitor): def __init__(self, project, current_folder): self.to_be_absolute = [] self.project = project self.folder = current_folder self.context = importinfo.ImportContext(project, current_folder) def visitNormalImport(self, import_stmt, import_info): self.to_be_absolute.extend(self._get_relative_to_absolute_list(import_info)) new_pairs = [] for name, alias in import_info.names_and_aliases: resource = self.project.find_module(name, folder=self.folder) if resource is None: new_pairs.append((name, alias)) continue absolute_name = libutils.modname(resource) new_pairs.append((absolute_name, alias)) if not import_info._are_name_and_alias_lists_equal( new_pairs, import_info.names_and_aliases ): import_stmt.import_info = importinfo.NormalImport(new_pairs) def _get_relative_to_absolute_list(self, import_info): result = [] for name, alias in import_info.names_and_aliases: if alias is not None: continue resource = self.project.find_module(name, folder=self.folder) if resource is None: continue absolute_name = libutils.modname(resource) if absolute_name != name: result.append((name, absolute_name)) return result def visitFromImport(self, import_stmt, import_info): resource = import_info.get_imported_resource(self.context) if resource is None: return None absolute_name = libutils.modname(resource) if import_info.module_name != absolute_name: import_stmt.import_info = importinfo.FromImport( absolute_name, 0, import_info.names_and_aliases ) class FilteringVisitor(ImportInfoVisitor): def __init__(self, project, folder, can_select): self.to_be_absolute = [] self.project = project self.can_select = self._transform_can_select(can_select) self.context = importinfo.ImportContext(project, folder) def _transform_can_select(self, can_select): def can_select_name_and_alias(name, alias): imported = name if alias is not None: imported = alias return can_select(imported) return can_select_name_and_alias def visitNormalImport(self, import_stmt, import_info): new_pairs = [] for name, alias in import_info.names_and_aliases: if self.can_select(name, alias): new_pairs.append((name, alias)) return importinfo.NormalImport(new_pairs) def visitFromImport(self, import_stmt, import_info): if _is_future(import_info): return import_info new_pairs = [] if import_info.is_star_import(): for name in import_info.get_imported_names(self.context): if self.can_select(name, None): new_pairs.append(import_info.names_and_aliases[0]) break else: for name, alias in import_info.names_and_aliases: if self.can_select(name, alias): new_pairs.append((name, alias)) return importinfo.FromImport( import_info.module_name, import_info.level, new_pairs ) class RemovingVisitor(ImportInfoVisitor): def __init__(self, project, folder, can_select): self.to_be_absolute = [] self.project = project self.filtering = FilteringVisitor(project, folder, can_select) def dispatch(self, import_): result = self.filtering.dispatch(import_) if result is not None: import_.import_info = result class AddingVisitor(ImportInfoVisitor): """A class for adding imports Given a list of `ImportInfo`, it tries to add each import to the module and returns `True` and gives up when an import can be added to older ones. """ def __init__(self, project, import_list): self.project = project self.import_list = import_list self.import_info = None def dispatch(self, import_): for import_info in self.import_list: self.import_info = import_info if ImportInfoVisitor.dispatch(self, import_): return True # TODO: Handle adding relative and absolute imports def visitNormalImport(self, import_stmt, import_info): if not isinstance(self.import_info, import_info.__class__): return False # Adding ``import x`` and ``import x.y`` that results ``import x.y`` if ( len(import_info.names_and_aliases) == len(self.import_info.names_and_aliases) == 1 ): imported1 = import_info.names_and_aliases[0] imported2 = self.import_info.names_and_aliases[0] if imported1[1] == imported2[1] is None: if imported1[0].startswith(imported2[0] + "."): return True if imported2[0].startswith(imported1[0] + "."): import_stmt.import_info = self.import_info return True # Multiple imports using a single import statement is discouraged # so we won't bother adding them. if self.import_info._are_name_and_alias_lists_equal( import_info.names_and_aliases, self.import_info.names_and_aliases ): return True def visitFromImport(self, import_stmt, import_info): if ( isinstance(self.import_info, import_info.__class__) and import_info.module_name == self.import_info.module_name and import_info.level == self.import_info.level ): if import_info.is_star_import(): return True if self.import_info.is_star_import(): import_stmt.import_info = self.import_info return True if self.project.prefs.get("split_imports"): return ( self.import_info.names_and_aliases == import_info.names_and_aliases ) new_pairs = list(import_info.names_and_aliases) for pair in self.import_info.names_and_aliases: if pair not in new_pairs: new_pairs.append(pair) import_stmt.import_info = importinfo.FromImport( import_info.module_name, import_info.level, new_pairs ) return True class ExpandStarsVisitor(ImportInfoVisitor): def __init__(self, project, folder, can_select): self.project = project self.filtering = FilteringVisitor(project, folder, can_select) self.context = importinfo.ImportContext(project, folder) def visitNormalImport(self, import_stmt, import_info): self.filtering.dispatch(import_stmt) def visitFromImport(self, import_stmt, import_info): if import_info.is_star_import(): new_pairs = [] for name in import_info.get_imported_names(self.context): new_pairs.append((name, None)) new_import = importinfo.FromImport( import_info.module_name, import_info.level, new_pairs ) import_stmt.import_info = self.filtering.visitFromImport(None, new_import) else: self.filtering.dispatch(import_stmt) class SelfImportVisitor(ImportInfoVisitor): def __init__(self, project, current_folder, resource): self.project = project self.folder = current_folder self.resource = resource self.to_be_fixed = set() self.to_be_renamed = set() self.context = importinfo.ImportContext(project, current_folder) def visitNormalImport(self, import_stmt, import_info): new_pairs = [] for name, alias in import_info.names_and_aliases: resource = self.project.find_module(name, folder=self.folder) if resource is not None and resource == self.resource: imported = name if alias is not None: imported = alias self.to_be_fixed.add(imported) else: new_pairs.append((name, alias)) if not import_info._are_name_and_alias_lists_equal( new_pairs, import_info.names_and_aliases ): import_stmt.import_info = importinfo.NormalImport(new_pairs) def visitFromImport(self, import_stmt, import_info): resource = import_info.get_imported_resource(self.context) if resource is None: return if resource == self.resource: self._importing_names_from_self(import_info, import_stmt) return pymodule = self.project.get_pymodule(resource) new_pairs = [] for name, alias in import_info.names_and_aliases: try: result = pymodule[name].get_object() if ( isinstance(result, pyobjects.PyModule) and result.get_resource() == self.resource ): imported = name if alias is not None: imported = alias self.to_be_fixed.add(imported) else: new_pairs.append((name, alias)) except exceptions.AttributeNotFoundError: new_pairs.append((name, alias)) if not import_info._are_name_and_alias_lists_equal( new_pairs, import_info.names_and_aliases ): import_stmt.import_info = importinfo.FromImport( import_info.module_name, import_info.level, new_pairs ) def _importing_names_from_self(self, import_info, import_stmt): if not import_info.is_star_import(): for name, alias in import_info.names_and_aliases: if alias is not None: self.to_be_renamed.add((alias, name)) import_stmt.empty_import() class SortingVisitor(ImportInfoVisitor): def __init__(self, project, current_folder): self.project = project self.folder = current_folder self.standard = set() self.third_party = set() self.in_project = set() self.future = set() self.context = importinfo.ImportContext(project, current_folder) def visitNormalImport(self, import_stmt, import_info): if import_info.names_and_aliases: name, alias = import_info.names_and_aliases[0] resource = self.project.find_module(name, folder=self.folder) self._check_imported_resource(import_stmt, resource, name) def visitFromImport(self, import_stmt, import_info): resource = import_info.get_imported_resource(self.context) self._check_imported_resource(import_stmt, resource, import_info.module_name) def _check_imported_resource(self, import_stmt, resource, imported_name): info = import_stmt.import_info if resource is not None and resource.project == self.project: self.in_project.add(import_stmt) elif _is_future(info): self.future.add(import_stmt) elif imported_name.split(".")[0] in stdmods.standard_modules(): self.standard.add(import_stmt) else: self.third_party.add(import_stmt) class LongImportVisitor(ImportInfoVisitor): def __init__(self, current_folder, project, maxdots, maxlength): self.maxdots = maxdots self.maxlength = maxlength self.to_be_renamed = set() self.current_folder = current_folder self.project = project self.new_imports = [] def visitNormalImport(self, import_stmt, import_info): for name, alias in import_info.names_and_aliases: if alias is None and self._is_long(name): self.to_be_renamed.add(name) last_dot = name.rindex(".") from_ = name[:last_dot] imported = name[last_dot + 1 :] self.new_imports.append( importinfo.FromImport(from_, 0, ((imported, None),)) ) def _is_long(self, name): return name.count(".") > self.maxdots or ( "." in name and len(name) > self.maxlength ) class RemovePyNameVisitor(ImportInfoVisitor): def __init__(self, project, pymodule, pyname, folder): self.pymodule = pymodule self.pyname = pyname self.context = importinfo.ImportContext(project, folder) def visitFromImport(self, import_stmt, import_info): new_pairs = [] if not import_info.is_star_import(): for name, alias in import_info.names_and_aliases: try: pyname = self.pymodule[alias or name] if occurrences.same_pyname(self.pyname, pyname): continue except exceptions.AttributeNotFoundError: pass new_pairs.append((name, alias)) return importinfo.FromImport( import_info.module_name, import_info.level, new_pairs ) def dispatch(self, import_): result = ImportInfoVisitor.dispatch(self, import_) if result is not None: import_.import_info = result def _is_future(info): return isinstance(info, importinfo.FromImport) and info.module_name == "__future__" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/importutils/importinfo.py0000664000175000017500000001317700000000000022351 0ustar00lieryanlieryanclass ImportStatement(object): """Represent an import in a module `readonly` attribute controls whether this import can be changed by import actions or not. """ def __init__( self, import_info, start_line, end_line, main_statement=None, blank_lines=0 ): self.start_line = start_line self.end_line = end_line self.readonly = False self.main_statement = main_statement self._import_info = None self.import_info = import_info self._is_changed = False self.new_start = None self.blank_lines = blank_lines def _get_import_info(self): return self._import_info def _set_import_info(self, new_import): if ( not self.readonly and new_import is not None and not new_import == self._import_info ): self._is_changed = True self._import_info = new_import import_info = property(_get_import_info, _set_import_info) def get_import_statement(self): if self._is_changed or self.main_statement is None: return self.import_info.get_import_statement() else: return self.main_statement def empty_import(self): self.import_info = ImportInfo.get_empty_import() def move(self, lineno, blank_lines=0): self.new_start = lineno self.blank_lines = blank_lines def get_old_location(self): return self.start_line, self.end_line def get_new_start(self): return self.new_start def is_changed(self): return self._is_changed or ( self.new_start is not None or self.new_start != self.start_line ) def accept(self, visitor): return visitor.dispatch(self) class ImportInfo(object): def get_imported_primaries(self, context): pass def get_imported_names(self, context): return [ primary.split(".")[0] for primary in self.get_imported_primaries(context) ] def get_import_statement(self): pass def is_empty(self): pass def __hash__(self): return hash(self.get_import_statement()) def _are_name_and_alias_lists_equal(self, list1, list2): if len(list1) != len(list2): return False for pair1, pair2 in zip(list1, list2): if pair1 != pair2: return False return True def __eq__(self, obj): return ( isinstance(obj, self.__class__) and self.get_import_statement() == obj.get_import_statement() ) def __ne__(self, obj): return not self.__eq__(obj) @staticmethod def get_empty_import(): return EmptyImport() class NormalImport(ImportInfo): def __init__(self, names_and_aliases): self.names_and_aliases = names_and_aliases def get_imported_primaries(self, context): result = [] for name, alias in self.names_and_aliases: if alias: result.append(alias) else: result.append(name) return result def get_import_statement(self): result = "import " for name, alias in self.names_and_aliases: result += name if alias: result += " as " + alias result += ", " return result[:-2] def is_empty(self): return len(self.names_and_aliases) == 0 class FromImport(ImportInfo): def __init__(self, module_name, level, names_and_aliases): self.module_name = module_name self.level = level self.names_and_aliases = names_and_aliases def get_imported_primaries(self, context): if self.names_and_aliases[0][0] == "*": module = self.get_imported_module(context) return [name for name in module if not name.startswith("_")] result = [] for name, alias in self.names_and_aliases: if alias: result.append(alias) else: result.append(name) return result def get_imported_resource(self, context): """Get the imported resource Returns `None` if module was not found. """ if self.level == 0: return context.project.find_module(self.module_name, folder=context.folder) else: return context.project.find_relative_module( self.module_name, context.folder, self.level ) def get_imported_module(self, context): """Get the imported `PyModule` Raises `rope.base.exceptions.ModuleNotFoundError` if module could not be found. """ if self.level == 0: return context.project.get_module(self.module_name, context.folder) else: return context.project.get_relative_module( self.module_name, context.folder, self.level ) def get_import_statement(self): result = "from " + "." * self.level + self.module_name + " import " for name, alias in self.names_and_aliases: result += name if alias: result += " as " + alias result += ", " return result[:-2] def is_empty(self): return len(self.names_and_aliases) == 0 def is_star_import(self): return len(self.names_and_aliases) > 0 and self.names_and_aliases[0][0] == "*" class EmptyImport(ImportInfo): names_and_aliases = [] def is_empty(self): return True def get_imported_primaries(self, context): return [] class ImportContext(object): def __init__(self, project, folder): self.project = project self.folder = folder ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/refactor/importutils/module_imports.py0000664000175000017500000004743600000000000023232 0ustar00lieryanlieryanfrom rope.base import ast from rope.base import exceptions from rope.base import pynames from rope.base import utils from rope.refactor.importutils import actions from rope.refactor.importutils import importinfo class ModuleImports(object): def __init__(self, project, pymodule, import_filter=None): self.project = project self.pymodule = pymodule self.separating_lines = 0 self.filter = import_filter self.sorted = False @property @utils.saveit def imports(self): finder = _GlobalImportFinder(self.pymodule) result = finder.find_import_statements() self.separating_lines = finder.get_separating_line_count() if self.filter is not None: for import_stmt in result: if not self.filter(import_stmt): import_stmt.readonly = True return result def _get_unbound_names(self, defined_pyobject): visitor = _GlobalUnboundNameFinder(self.pymodule, defined_pyobject) ast.walk(self.pymodule.get_ast(), visitor) return visitor.unbound def _get_all_star_list(self, pymodule): result = set() try: all_star_list = pymodule.get_attribute("__all__") except exceptions.AttributeNotFoundError: return result # FIXME: Need a better way to recursively infer possible values. # Currently pyobjects can recursively infer type, but not values. # Do a very basic 1-level value inference for assignment in all_star_list.assignments: if isinstance(assignment.ast_node, ast.List): stack = list(assignment.ast_node.elts) while stack: el = stack.pop() if isinstance(el, ast.Str): result.add(el.s) elif isinstance(el, ast.Name): name = pymodule.get_attribute(el.id) if isinstance(name, pynames.AssignedName): for av in name.assignments: if isinstance(av.ast_node, ast.Str): result.add(av.ast_node.s) elif isinstance(el, ast.IfExp): stack.append(el.body) stack.append(el.orelse) return result def remove_unused_imports(self): can_select = _OneTimeSelector( self._get_unbound_names(self.pymodule) | self._get_all_star_list(self.pymodule) ) visitor = actions.RemovingVisitor( self.project, self._current_folder(), can_select ) for import_statement in self.imports: import_statement.accept(visitor) def get_used_imports(self, defined_pyobject): result = [] can_select = _OneTimeSelector(self._get_unbound_names(defined_pyobject)) visitor = actions.FilteringVisitor( self.project, self._current_folder(), can_select ) for import_statement in self.imports: new_import = import_statement.accept(visitor) if new_import is not None and not new_import.is_empty(): result.append(new_import) return result def get_changed_source(self): if not self.project.prefs.get("pull_imports_to_top") and not self.sorted: return "".join(self._rewrite_imports(self.imports)) # Make sure we forward a removed import's preceding blank # lines count to the following import statement. prev_stmt = None for stmt in self.imports: if prev_stmt is not None and prev_stmt.import_info.is_empty(): stmt.blank_lines = max(prev_stmt.blank_lines, stmt.blank_lines) prev_stmt = stmt # The new list of imports. imports = [stmt for stmt in self.imports if not stmt.import_info.is_empty()] after_removing = self._remove_imports(self.imports) first_non_blank = self._first_non_blank_line(after_removing, 0) first_import = self._first_import_line() - 1 result = [] # Writing module docs result.extend(after_removing[first_non_blank:first_import]) # Writing imports sorted_imports = sorted(imports, key=self._get_location) for stmt in sorted_imports: if stmt != sorted_imports[0]: result.append("\n" * stmt.blank_lines) result.append(stmt.get_import_statement() + "\n") if sorted_imports and first_non_blank < len(after_removing): result.append("\n" * self.separating_lines) # Writing the body first_after_imports = self._first_non_blank_line(after_removing, first_import) result.extend(after_removing[first_after_imports:]) return "".join(result) def _get_import_location(self, stmt): start = stmt.get_new_start() if start is None: start = stmt.get_old_location()[0] return start def _get_location(self, stmt): if stmt.get_new_start() is not None: return stmt.get_new_start() else: return stmt.get_old_location()[0] def _remove_imports(self, imports): lines = self.pymodule.source_code.splitlines(True) after_removing = [] first_import_line = self._first_import_line() last_index = 0 for stmt in imports: start, end = stmt.get_old_location() blank_lines = 0 if start != first_import_line: blank_lines = _count_blank_lines( lines.__getitem__, start - 2, last_index - 1, -1 ) after_removing.extend(lines[last_index : start - 1 - blank_lines]) last_index = end - 1 after_removing.extend(lines[last_index:]) return after_removing def _rewrite_imports(self, imports): lines = self.pymodule.source_code.splitlines(True) after_rewriting = [] last_index = 0 for stmt in imports: start, end = stmt.get_old_location() after_rewriting.extend(lines[last_index : start - 1]) if not stmt.import_info.is_empty(): after_rewriting.append(stmt.get_import_statement() + "\n") last_index = end - 1 after_rewriting.extend(lines[last_index:]) return after_rewriting def _first_non_blank_line(self, lines, lineno): return lineno + _count_blank_lines(lines.__getitem__, lineno, len(lines)) def add_import(self, import_info): visitor = actions.AddingVisitor(self.project, [import_info]) for import_statement in self.imports: if import_statement.accept(visitor): break else: lineno = self._get_new_import_lineno() blanks = self._get_new_import_blanks() self.imports.append( importinfo.ImportStatement( import_info, lineno, lineno, blank_lines=blanks ) ) def _get_new_import_blanks(self): return 0 def _get_new_import_lineno(self): if self.imports: return self.imports[-1].end_line return 1 def filter_names(self, can_select): visitor = actions.RemovingVisitor( self.project, self._current_folder(), can_select ) for import_statement in self.imports: import_statement.accept(visitor) def expand_stars(self): can_select = _OneTimeSelector(self._get_unbound_names(self.pymodule)) visitor = actions.ExpandStarsVisitor( self.project, self._current_folder(), can_select ) for import_statement in self.imports: import_statement.accept(visitor) def remove_duplicates(self): added_imports = [] for import_stmt in self.imports: visitor = actions.AddingVisitor(self.project, [import_stmt.import_info]) for added_import in added_imports: if added_import.accept(visitor): import_stmt.empty_import() else: added_imports.append(import_stmt) def force_single_imports(self): """force a single import per statement""" for import_stmt in self.imports[:]: import_info = import_stmt.import_info if import_info.is_empty() or import_stmt.readonly: continue if len(import_info.names_and_aliases) > 1: for name_and_alias in import_info.names_and_aliases: if hasattr(import_info, "module_name"): new_import = importinfo.FromImport( import_info.module_name, import_info.level, [name_and_alias] ) else: new_import = importinfo.NormalImport([name_and_alias]) self.add_import(new_import) import_stmt.empty_import() def get_relative_to_absolute_list(self): visitor = actions.RelativeToAbsoluteVisitor( self.project, self._current_folder() ) for import_stmt in self.imports: if not import_stmt.readonly: import_stmt.accept(visitor) return visitor.to_be_absolute def get_self_import_fix_and_rename_list(self): visitor = actions.SelfImportVisitor( self.project, self._current_folder(), self.pymodule.get_resource() ) for import_stmt in self.imports: if not import_stmt.readonly: import_stmt.accept(visitor) return visitor.to_be_fixed, visitor.to_be_renamed def _current_folder(self): return self.pymodule.get_resource().parent def sort_imports(self): if self.project.prefs.get("sort_imports_alphabetically"): sort_kwargs = dict(key=self._get_import_name) else: sort_kwargs = dict(key=self._key_imports) # IDEA: Sort from import list visitor = actions.SortingVisitor(self.project, self._current_folder()) for import_statement in self.imports: import_statement.accept(visitor) in_projects = sorted(visitor.in_project, **sort_kwargs) third_party = sorted(visitor.third_party, **sort_kwargs) standards = sorted(visitor.standard, **sort_kwargs) future = sorted(visitor.future, **sort_kwargs) last_index = self._first_import_line() last_index = self._move_imports(future, last_index, 0) last_index = self._move_imports(standards, last_index, 1) last_index = self._move_imports(third_party, last_index, 1) last_index = self._move_imports(in_projects, last_index, 1) self.separating_lines = 2 self.sorted = True def _first_import_line(self): nodes = self.pymodule.get_ast().body lineno = 0 if self.pymodule.get_doc() is not None: lineno = 1 if len(nodes) > lineno: if isinstance(nodes[lineno], ast.Import) or isinstance( nodes[lineno], ast.ImportFrom ): return nodes[lineno].lineno lineno = self.pymodule.logical_lines.logical_line_in(nodes[lineno].lineno)[ 0 ] else: lineno = self.pymodule.lines.length() return lineno - _count_blank_lines( self.pymodule.lines.get_line, lineno - 1, 1, -1 ) def _get_import_name(self, import_stmt): import_info = import_stmt.import_info if hasattr(import_info, "module_name"): return "%s.%s" % ( import_info.module_name, import_info.names_and_aliases[0][0], ) else: return import_info.names_and_aliases[0][0] def _key_imports(self, stm1): str1 = stm1.get_import_statement() return str1.startswith("from "), str1 # str1 = stmt1.get_import_statement() # str2 = stmt2.get_import_statement() # if str1.startswith('from ') and not str2.startswith('from '): # return 1 # if not str1.startswith('from ') and str2.startswith('from '): # return -1 # return cmp(str1, str2) def _move_imports(self, imports, index, blank_lines): if imports: imports[0].move(index, blank_lines) index += 1 if len(imports) > 1: for stmt in imports[1:]: stmt.move(index) index += 1 return index def handle_long_imports(self, maxdots, maxlength): visitor = actions.LongImportVisitor( self._current_folder(), self.project, maxdots, maxlength ) for import_statement in self.imports: if not import_statement.readonly: import_statement.accept(visitor) for import_info in visitor.new_imports: self.add_import(import_info) return visitor.to_be_renamed def remove_pyname(self, pyname): """Removes pyname when imported in ``from mod import x``""" visitor = actions.RemovePyNameVisitor( self.project, self.pymodule, pyname, self._current_folder() ) for import_stmt in self.imports: import_stmt.accept(visitor) def _count_blank_lines(get_line, start, end, step=1): count = 0 for idx in range(start, end, step): if get_line(idx).strip() == "": count += 1 else: break return count class _OneTimeSelector(object): def __init__(self, names): self.names = names self.selected_names = set() def __call__(self, imported_primary): if self._can_name_be_added(imported_primary): for name in self._get_dotted_tokens(imported_primary): self.selected_names.add(name) return True return False def _get_dotted_tokens(self, imported_primary): tokens = imported_primary.split(".") for i in range(len(tokens)): yield ".".join(tokens[: i + 1]) def _can_name_be_added(self, imported_primary): for name in self._get_dotted_tokens(imported_primary): if name in self.names and name not in self.selected_names: return True return False class _UnboundNameFinder(object): def __init__(self, pyobject): self.pyobject = pyobject def _visit_child_scope(self, node): pyobject = ( self.pyobject.get_module() .get_scope() .get_inner_scope_for_line(node.lineno) .pyobject ) visitor = _LocalUnboundNameFinder(pyobject, self) for child in ast.get_child_nodes(node): ast.walk(child, visitor) def _FunctionDef(self, node): self._visit_child_scope(node) def _ClassDef(self, node): self._visit_child_scope(node) def _Name(self, node): if self._get_root()._is_node_interesting(node) and not self.is_bound(node.id): self.add_unbound(node.id) def _Attribute(self, node): result = [] while isinstance(node, ast.Attribute): result.append(node.attr) node = node.value if isinstance(node, ast.Name): result.append(node.id) primary = ".".join(reversed(result)) if self._get_root()._is_node_interesting(node) and not self.is_bound( primary ): self.add_unbound(primary) else: ast.walk(node, self) def _get_root(self): pass def is_bound(self, name, propagated=False): pass def add_unbound(self, name): pass class _GlobalUnboundNameFinder(_UnboundNameFinder): def __init__(self, pymodule, wanted_pyobject): super(_GlobalUnboundNameFinder, self).__init__(pymodule) self.unbound = set() self.names = set() for name, pyname in pymodule._get_structural_attributes().items(): if not isinstance(pyname, (pynames.ImportedName, pynames.ImportedModule)): self.names.add(name) wanted_scope = wanted_pyobject.get_scope() self.start = wanted_scope.get_start() self.end = wanted_scope.get_end() + 1 def _get_root(self): return self def is_bound(self, primary, propagated=False): name = primary.split(".")[0] return name in self.names def add_unbound(self, name): names = name.split(".") for i in range(len(names)): self.unbound.add(".".join(names[: i + 1])) def _is_node_interesting(self, node): return self.start <= node.lineno < self.end class _LocalUnboundNameFinder(_UnboundNameFinder): def __init__(self, pyobject, parent): super(_LocalUnboundNameFinder, self).__init__(pyobject) self.parent = parent def _get_root(self): return self.parent._get_root() def is_bound(self, primary, propagated=False): name = primary.split(".")[0] if propagated: names = self.pyobject.get_scope().get_propagated_names() else: names = self.pyobject.get_scope().get_names() if name in names or self.parent.is_bound(name, propagated=True): return True return False def add_unbound(self, name): self.parent.add_unbound(name) class _GlobalImportFinder(object): def __init__(self, pymodule): self.current_folder = None if pymodule.get_resource(): self.current_folder = pymodule.get_resource().parent self.pymodule = pymodule self.imports = [] self.pymodule = pymodule self.lines = self.pymodule.lines def visit_import(self, node, end_line): start_line = node.lineno import_statement = importinfo.ImportStatement( importinfo.NormalImport(self._get_names(node.names)), start_line, end_line, self._get_text(start_line, end_line), blank_lines=self._count_empty_lines_before(start_line), ) self.imports.append(import_statement) def _count_empty_lines_before(self, lineno): return _count_blank_lines(self.lines.get_line, lineno - 1, 0, -1) def _count_empty_lines_after(self, lineno): return _count_blank_lines(self.lines.get_line, lineno + 1, self.lines.length()) def get_separating_line_count(self): if not self.imports: return 0 return self._count_empty_lines_after(self.imports[-1].end_line - 1) def _get_text(self, start_line, end_line): result = [] for index in range(start_line, end_line): result.append(self.lines.get_line(index)) return "\n".join(result) def visit_from(self, node, end_line): level = 0 if node.level: level = node.level import_info = importinfo.FromImport( node.module or "", # see comment at rope.base.ast.walk level, self._get_names(node.names), ) start_line = node.lineno self.imports.append( importinfo.ImportStatement( import_info, node.lineno, end_line, self._get_text(start_line, end_line), blank_lines=self._count_empty_lines_before(start_line), ) ) def _get_names(self, alias_names): result = [] for alias in alias_names: result.append((alias.name, alias.asname)) return result def find_import_statements(self): nodes = self.pymodule.get_ast().body for index, node in enumerate(nodes): if isinstance(node, (ast.Import, ast.ImportFrom)): lines = self.pymodule.logical_lines end_line = lines.logical_line_in(node.lineno)[1] + 1 if isinstance(node, ast.Import): self.visit_import(node, end_line) if isinstance(node, ast.ImportFrom): self.visit_from(node, end_line) return self.imports ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/refactor/inline.py0000664000175000017500000006077400000000000017053 0ustar00lieryanlieryan# Known Bugs when inlining a function/method # The values passed to function are inlined using _inlined_variable. # This may cause two problems, illustrated in the examples below # # def foo(var1): # var1 = var1*10 # return var1 # # If a call to foo(20) is inlined, the result of inlined function is 20, # but it should be 200. # # def foo(var1): # var2 = var1*10 # return var2 # # 2- If a call to foo(10+10) is inlined the result of inlined function is 110 # but it should be 200. import re import rope.base.exceptions import rope.refactor.functionutils from rope.base import ( pynames, pyobjects, codeanalyze, taskhandle, evaluate, worder, utils, libutils, ) from rope.base.change import ChangeSet, ChangeContents from rope.refactor import ( occurrences, rename, sourceutils, importutils, move, change_signature, ) def unique_prefix(): n = 0 while True: yield "__" + str(n) + "__" n += 1 def create_inline(project, resource, offset): """Create a refactoring object for inlining Based on `resource` and `offset` it returns an instance of `InlineMethod`, `InlineVariable` or `InlineParameter`. """ pyname = _get_pyname(project, resource, offset) message = ( "Inline refactoring should be performed on " "a method, local variable or parameter." ) if pyname is None: raise rope.base.exceptions.RefactoringError(message) if isinstance(pyname, pynames.ImportedName): pyname = pyname._get_imported_pyname() if isinstance(pyname, pynames.AssignedName): return InlineVariable(project, resource, offset) if isinstance(pyname, pynames.ParameterName): return InlineParameter(project, resource, offset) if isinstance(pyname.get_object(), pyobjects.PyFunction): return InlineMethod(project, resource, offset) else: raise rope.base.exceptions.RefactoringError(message) class _Inliner(object): def __init__(self, project, resource, offset): self.project = project self.pyname = _get_pyname(project, resource, offset) range_finder = worder.Worder(resource.read(), True) self.region = range_finder.get_primary_range(offset) self.name = range_finder.get_word_at(offset) self.offset = offset self.original = resource def get_changes(self, *args, **kwds): pass def get_kind(self): """Return either 'variable', 'method' or 'parameter'""" class InlineMethod(_Inliner): def __init__(self, *args, **kwds): super(InlineMethod, self).__init__(*args, **kwds) self.pyfunction = self.pyname.get_object() self.pymodule = self.pyfunction.get_module() self.resource = self.pyfunction.get_module().get_resource() self.occurrence_finder = occurrences.create_finder( self.project, self.name, self.pyname ) self.normal_generator = _DefinitionGenerator(self.project, self.pyfunction) self._init_imports() def _init_imports(self): body = sourceutils.get_body(self.pyfunction) body, imports = move.moving_code_with_imports(self.project, self.resource, body) self.imports = imports self.others_generator = _DefinitionGenerator( self.project, self.pyfunction, body=body ) def _get_scope_range(self): scope = self.pyfunction.get_scope() lines = self.pymodule.lines start_line = scope.get_start() if self.pyfunction.decorators: decorators = self.pyfunction.decorators if hasattr(decorators[0], "lineno"): start_line = decorators[0].lineno start_offset = lines.get_line_start(start_line) end_offset = min( lines.get_line_end(scope.end) + 1, len(self.pymodule.source_code) ) return (start_offset, end_offset) def get_changes( self, remove=True, only_current=False, resources=None, task_handle=taskhandle.NullTaskHandle(), ): """Get the changes this refactoring makes If `remove` is `False` the definition will not be removed. If `only_current` is `True`, the the current occurrence will be inlined, only. """ changes = ChangeSet("Inline method <%s>" % self.name) if resources is None: resources = self.project.get_python_files() if only_current: resources = [self.original] if remove: resources.append(self.resource) job_set = task_handle.create_jobset("Collecting Changes", len(resources)) for file in resources: job_set.started_job(file.path) if file == self.resource: changes.add_change( self._defining_file_changes( changes, remove=remove, only_current=only_current ) ) else: aim = None if only_current and self.original == file: aim = self.offset handle = _InlineFunctionCallsForModuleHandle( self.project, file, self.others_generator, aim ) result = move.ModuleSkipRenamer( self.occurrence_finder, file, handle ).get_changed_module() if result is not None: result = _add_imports(self.project, result, file, self.imports) if remove: result = _remove_from(self.project, self.pyname, result, file) changes.add_change(ChangeContents(file, result)) job_set.finished_job() return changes def _get_removed_range(self): scope = self.pyfunction.get_scope() lines = self.pymodule.lines start, end = self._get_scope_range() end_line = scope.get_end() for i in range(end_line + 1, lines.length()): if lines.get_line(i).strip() == "": end_line = i else: break end = min(lines.get_line_end(end_line) + 1, len(self.pymodule.source_code)) return (start, end) def _defining_file_changes(self, changes, remove, only_current): start_offset, end_offset = self._get_removed_range() aim = None if only_current: if self.resource == self.original: aim = self.offset else: # we don't want to change any of them aim = len(self.resource.read()) + 100 handle = _InlineFunctionCallsForModuleHandle( self.project, self.resource, self.normal_generator, aim_offset=aim ) replacement = None if remove: replacement = self._get_method_replacement() result = move.ModuleSkipRenamer( self.occurrence_finder, self.resource, handle, start_offset, end_offset, replacement, ).get_changed_module() return ChangeContents(self.resource, result) def _get_method_replacement(self): if self._is_the_last_method_of_a_class(): indents = sourceutils.get_indents( self.pymodule.lines, self.pyfunction.get_scope().get_start() ) return " " * indents + "pass\n" return "" def _is_the_last_method_of_a_class(self): pyclass = self.pyfunction.parent if not isinstance(pyclass, pyobjects.PyClass): return False class_start, class_end = sourceutils.get_body_region(pyclass) source = self.pymodule.source_code func_start, func_end = self._get_scope_range() if ( source[class_start:func_start].strip() == "" and source[func_end:class_end].strip() == "" ): return True return False def get_kind(self): return "method" class InlineVariable(_Inliner): def __init__(self, *args, **kwds): super(InlineVariable, self).__init__(*args, **kwds) self.pymodule = self.pyname.get_definition_location()[0] self.resource = self.pymodule.get_resource() self._check_exceptional_conditions() self._init_imports() def _check_exceptional_conditions(self): if len(self.pyname.assignments) != 1: raise rope.base.exceptions.RefactoringError( "Local variable should be assigned once for inlining." ) def get_changes( self, remove=True, only_current=False, resources=None, docs=False, task_handle=taskhandle.NullTaskHandle(), ): if resources is None: if rename._is_local(self.pyname): resources = [self.resource] else: resources = self.project.get_python_files() if only_current: resources = [self.original] if remove and self.original != self.resource: resources.append(self.resource) changes = ChangeSet("Inline variable <%s>" % self.name) jobset = task_handle.create_jobset("Calculating changes", len(resources)) for resource in resources: jobset.started_job(resource.path) if resource == self.resource: source = self._change_main_module(remove, only_current, docs) changes.add_change(ChangeContents(self.resource, source)) else: result = self._change_module(resource, remove, only_current) if result is not None: result = _add_imports(self.project, result, resource, self.imports) changes.add_change(ChangeContents(resource, result)) jobset.finished_job() return changes def _change_main_module(self, remove, only_current, docs): region = None if only_current and self.original == self.resource: region = self.region return _inline_variable( self.project, self.pymodule, self.pyname, self.name, remove=remove, region=region, docs=docs, ) def _init_imports(self): vardef = _getvardef(self.pymodule, self.pyname) self.imported, self.imports = move.moving_code_with_imports( self.project, self.resource, vardef ) def _change_module(self, resource, remove, only_current): filters = [occurrences.NoImportsFilter(), occurrences.PyNameFilter(self.pyname)] if only_current and resource == self.original: def check_aim(occurrence): start, end = occurrence.get_primary_range() if self.offset < start or end < self.offset: return False filters.insert(0, check_aim) finder = occurrences.Finder(self.project, self.name, filters=filters) changed = rename.rename_in_module( finder, self.imported, resource=resource, replace_primary=True ) if changed and remove: changed = _remove_from(self.project, self.pyname, changed, resource) return changed def get_kind(self): return "variable" class InlineParameter(_Inliner): def __init__(self, *args, **kwds): super(InlineParameter, self).__init__(*args, **kwds) resource, offset = self._function_location() index = self.pyname.index self.changers = [change_signature.ArgumentDefaultInliner(index)] self.signature = change_signature.ChangeSignature( self.project, resource, offset ) def _function_location(self): pymodule, lineno = self.pyname.get_definition_location() resource = pymodule.get_resource() start = pymodule.lines.get_line_start(lineno) word_finder = worder.Worder(pymodule.source_code) offset = word_finder.find_function_offset(start) return resource, offset def get_changes(self, **kwds): """Get the changes needed by this refactoring See `rope.refactor.change_signature.ChangeSignature.get_changes()` for arguments. """ return self.signature.get_changes(self.changers, **kwds) def get_kind(self): return "parameter" def _join_lines(lines): definition_lines = [] for unchanged_line in lines: line = unchanged_line.strip() if line.endswith("\\"): line = line[:-1].strip() definition_lines.append(line) joined = " ".join(definition_lines) return joined class _DefinitionGenerator(object): unique_prefix = unique_prefix() def __init__(self, project, pyfunction, body=None): self.project = project self.pyfunction = pyfunction self.pymodule = pyfunction.get_module() self.resource = self.pymodule.get_resource() self.definition_info = self._get_definition_info() self.definition_params = self._get_definition_params() self._calculated_definitions = {} if body is not None: self.body = body else: self.body = sourceutils.get_body(self.pyfunction) def _get_definition_info(self): return rope.refactor.functionutils.DefinitionInfo.read(self.pyfunction) def _get_definition_params(self): definition_info = self.definition_info paramdict = dict([pair for pair in definition_info.args_with_defaults]) if ( definition_info.args_arg is not None or 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): return self.pyfunction.get_name() def get_definition(self, primary, pyname, call, host_vars=[], returns=False): # caching already calculated definitions return self._calculate_definition(primary, pyname, call, host_vars, returns) def _calculate_header(self, primary, pyname, call): # A header is created which initializes parameters # to the values passed to the function. call_info = rope.refactor.functionutils.CallInfo.read( primary, pyname, self.definition_info, call ) paramdict = self.definition_params mapping = rope.refactor.functionutils.ArgumentMapping( self.definition_info, call_info ) for param_name, value in mapping.param_dict.items(): paramdict[param_name] = value header = "" to_be_inlined = [] for name, value in paramdict.items(): if name != value and value is not None: header += name + " = " + value.replace("\n", " ") + "\n" to_be_inlined.append(name) return header, to_be_inlined def _calculate_definition(self, primary, pyname, call, host_vars, returns): header, to_be_inlined = self._calculate_header(primary, pyname, call) source = header + self.body mod = libutils.get_string_module(self.project, source) name_dict = mod.get_scope().get_names() all_names = [ x for x in name_dict if not isinstance(name_dict[x], rope.base.builtins.BuiltinName) ] # If there is a name conflict, all variable names # inside the inlined function are renamed if len(set(all_names).intersection(set(host_vars))) > 0: prefix = next(_DefinitionGenerator.unique_prefix) guest = libutils.get_string_module(self.project, source, self.resource) to_be_inlined = [prefix + item for item in to_be_inlined] for item in all_names: pyname = guest[item] occurrence_finder = occurrences.create_finder( self.project, item, pyname ) source = rename.rename_in_module( occurrence_finder, prefix + item, pymodule=guest ) guest = libutils.get_string_module(self.project, source, self.resource) # parameters not reassigned inside the functions are now inlined. for name in to_be_inlined: pymodule = libutils.get_string_module(self.project, source, self.resource) pyname = pymodule[name] source = _inline_variable(self.project, pymodule, pyname, name) return self._replace_returns_with(source, returns) def _replace_returns_with(self, source, returns): result = [] returned = None last_changed = 0 for match in _DefinitionGenerator._get_return_pattern().finditer(source): for key, value in match.groupdict().items(): if value and key == "return": result.append(source[last_changed : match.start("return")]) if returns: self._check_nothing_after_return(source, match.end("return")) beg_idx = match.end("return") returned = _join_lines( source[beg_idx : len(source)].splitlines() ) last_changed = len(source) else: current = match.end("return") while current < len(source) and source[current] in " \t": current += 1 last_changed = current if current == len(source) or source[current] == "\n": result.append("pass") result.append(source[last_changed:]) return "".join(result), returned def _check_nothing_after_return(self, source, offset): lines = codeanalyze.SourceLinesAdapter(source) lineno = lines.get_line_number(offset) logical_lines = codeanalyze.LogicalLineFinder(lines) lineno = logical_lines.logical_line_in(lineno)[1] if source[lines.get_line_end(lineno) : len(source)].strip() != "": raise rope.base.exceptions.RefactoringError( "Cannot inline functions with statements " + "after return statement." ) @classmethod def _get_return_pattern(cls): if not hasattr(cls, "_return_pattern"): def named_pattern(name, list_): return "(?P<%s>" % name + "|".join(list_) + ")" comment_pattern = named_pattern("comment", [r"#[^\n]*"]) string_pattern = named_pattern("string", [codeanalyze.get_string_pattern()]) return_pattern = r"\b(?Preturn)\b" cls._return_pattern = re.compile( comment_pattern + "|" + string_pattern + "|" + return_pattern ) return cls._return_pattern class _InlineFunctionCallsForModuleHandle(object): def __init__(self, project, resource, definition_generator, aim_offset=None): """Inlines occurrences If `aim` is not `None` only the occurrences that intersect `aim` offset will be inlined. """ self.project = project self.generator = definition_generator self.resource = resource self.aim = aim_offset def occurred_inside_skip(self, change_collector, occurrence): if not occurrence.is_defined(): raise rope.base.exceptions.RefactoringError( "Cannot inline functions that reference themselves" ) def occurred_outside_skip(self, change_collector, occurrence): start, end = occurrence.get_primary_range() # we remove out of date imports later if occurrence.is_in_import_statement(): return # the function is referenced outside an import statement if not occurrence.is_called(): raise rope.base.exceptions.RefactoringError( "Reference to inlining function other than function call" " in " % (self.resource.path, start) ) if self.aim is not None and (self.aim < start or self.aim > end): return end_parens = self._find_end_parens(self.source, end - 1) lineno = self.lines.get_line_number(start) start_line, end_line = self.pymodule.logical_lines.logical_line_in(lineno) line_start = self.lines.get_line_start(start_line) line_end = self.lines.get_line_end(end_line) returns = ( self.source[line_start:start].strip() != "" or self.source[end_parens:line_end].strip() != "" ) indents = sourceutils.get_indents(self.lines, start_line) primary, pyname = occurrence.get_primary_and_pyname() host = self.pymodule scope = host.scope.get_inner_scope_for_line(lineno) definition, returned = self.generator.get_definition( primary, pyname, self.source[start:end_parens], scope.get_names(), returns=returns, ) end = min(line_end + 1, len(self.source)) change_collector.add_change( line_start, end, sourceutils.fix_indentation(definition, indents) ) if returns: name = returned if name is None: name = "None" change_collector.add_change( line_end, end, self.source[line_start:start] + name + self.source[end_parens:end], ) def _find_end_parens(self, source, offset): finder = worder.Worder(source) return finder.get_word_parens_range(offset)[1] @property @utils.saveit def pymodule(self): return self.project.get_pymodule(self.resource) @property @utils.saveit def source(self): if self.resource is not None: return self.resource.read() else: return self.pymodule.source_code @property @utils.saveit def lines(self): return self.pymodule.lines def _inline_variable( project, pymodule, pyname, name, remove=True, region=None, docs=False ): definition = _getvardef(pymodule, pyname) start, end = _assigned_lineno(pymodule, pyname) occurrence_finder = occurrences.create_finder(project, name, pyname, docs=docs) changed_source = rename.rename_in_module( occurrence_finder, definition, pymodule=pymodule, replace_primary=True, writes=False, region=region, ) if changed_source is None: changed_source = pymodule.source_code if remove: lines = codeanalyze.SourceLinesAdapter(changed_source) source = ( changed_source[: lines.get_line_start(start)] + changed_source[lines.get_line_end(end) + 1 :] ) else: source = changed_source return source def _getvardef(pymodule, pyname): assignment = pyname.assignments[0] lines = pymodule.lines start, end = _assigned_lineno(pymodule, pyname) definition_with_assignment = _join_lines( [lines.get_line(n) for n in range(start, end + 1)] ) if assignment.levels: raise rope.base.exceptions.RefactoringError("Cannot inline tuple assignments.") definition = definition_with_assignment[ definition_with_assignment.index("=") + 1 : ].strip() return definition def _assigned_lineno(pymodule, pyname): definition_line = pyname.assignments[0].ast_node.lineno return pymodule.logical_lines.logical_line_in(definition_line) def _add_imports(project, source, resource, imports): if not imports: return source pymodule = libutils.get_string_module(project, source, resource) module_import = importutils.get_module_imports(project, pymodule) for import_info in imports: module_import.add_import(import_info) source = module_import.get_changed_source() pymodule = libutils.get_string_module(project, source, resource) import_tools = importutils.ImportTools(project) return import_tools.organize_imports(pymodule, unused=False, sort=False) def _get_pyname(project, resource, offset): pymodule = project.get_pymodule(resource) pyname = evaluate.eval_location(pymodule, offset) if isinstance(pyname, pynames.ImportedName): pyname = pyname._get_imported_pyname() return pyname def _remove_from(project, pyname, source, resource): pymodule = libutils.get_string_module(project, source, resource) module_import = importutils.get_module_imports(project, pymodule) module_import.remove_pyname(pyname) return module_import.get_changed_source() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/introduce_factory.py0000664000175000017500000001361300000000000021306 0ustar00lieryanlieryanimport rope.base.exceptions import rope.base.pyobjects from rope.base import libutils from rope.base import taskhandle, evaluate from rope.base.change import ChangeSet, ChangeContents from rope.refactor import rename, occurrences, sourceutils, importutils class IntroduceFactory(object): def __init__(self, project, resource, offset): self.project = project self.offset = offset this_pymodule = self.project.get_pymodule(resource) self.old_pyname = evaluate.eval_location(this_pymodule, offset) if self.old_pyname is None or not isinstance( self.old_pyname.get_object(), rope.base.pyobjects.PyClass ): raise rope.base.exceptions.RefactoringError( "Introduce factory should be performed on a class." ) self.old_name = self.old_pyname.get_object().get_name() self.pymodule = self.old_pyname.get_object().get_module() self.resource = self.pymodule.get_resource() def get_changes( self, factory_name, global_factory=False, resources=None, task_handle=taskhandle.NullTaskHandle(), ): """Get the changes this refactoring makes `factory_name` indicates the name of the factory function to be added. If `global_factory` is `True` the factory will be global otherwise a static method is added to the class. `resources` can be a list of `rope.base.resource.File` that this refactoring should be applied on; if `None` all python files in the project are searched. """ if resources is None: resources = self.project.get_python_files() changes = ChangeSet("Introduce factory method <%s>" % factory_name) job_set = task_handle.create_jobset("Collecting Changes", len(resources)) self._change_module(resources, changes, factory_name, global_factory, job_set) return changes def get_name(self): """Return the name of the class""" return self.old_name def _change_module(self, resources, changes, factory_name, global_, job_set): if global_: replacement = "__rope_factory_%s_" % factory_name else: replacement = self._new_function_name(factory_name, global_) for file_ in resources: job_set.started_job(file_.path) if file_ == self.resource: self._change_resource(changes, factory_name, global_) job_set.finished_job() continue changed_code = self._rename_occurrences(file_, replacement, global_) if changed_code is not None: if global_: new_pymodule = libutils.get_string_module( self.project, changed_code, self.resource ) modname = libutils.modname(self.resource) changed_code, imported = importutils.add_import( self.project, new_pymodule, modname, factory_name ) changed_code = changed_code.replace(replacement, imported) changes.add_change(ChangeContents(file_, changed_code)) job_set.finished_job() def _change_resource(self, changes, factory_name, global_): class_scope = self.old_pyname.get_object().get_scope() source_code = self._rename_occurrences( self.resource, self._new_function_name(factory_name, global_), global_ ) if source_code is None: source_code = self.pymodule.source_code else: self.pymodule = libutils.get_string_module( self.project, source_code, resource=self.resource ) lines = self.pymodule.lines start = self._get_insertion_offset(class_scope, lines) result = source_code[:start] result += self._get_factory_method(lines, class_scope, factory_name, global_) result += source_code[start:] changes.add_change(ChangeContents(self.resource, result)) def _get_insertion_offset(self, class_scope, lines): start_line = class_scope.get_end() if class_scope.get_scopes(): start_line = class_scope.get_scopes()[-1].get_end() start = lines.get_line_end(start_line) + 1 return start def _get_factory_method(self, lines, class_scope, factory_name, global_): unit_indents = " " * sourceutils.get_indent(self.project) if global_: if self._get_scope_indents(lines, class_scope) > 0: raise rope.base.exceptions.RefactoringError( "Cannot make global factory method for nested classes." ) return "\ndef %s(*args, **kwds):\n%sreturn %s(*args, **kwds)\n" % ( factory_name, unit_indents, self.old_name, ) unindented_factory = ( "@staticmethod\ndef %s(*args, **kwds):\n" % factory_name + "%sreturn %s(*args, **kwds)\n" % (unit_indents, self.old_name) ) indents = self._get_scope_indents(lines, class_scope) + sourceutils.get_indent( self.project ) return "\n" + sourceutils.indent_lines(unindented_factory, indents) def _get_scope_indents(self, lines, scope): return sourceutils.get_indents(lines, scope.get_start()) def _new_function_name(self, factory_name, global_): if global_: return factory_name else: return self.old_name + "." + factory_name def _rename_occurrences(self, file_, changed_name, global_factory): finder = occurrences.create_finder( self.project, self.old_name, self.old_pyname, only_calls=True ) result = rename.rename_in_module( finder, changed_name, resource=file_, replace_primary=global_factory ) return result IntroduceFactoryRefactoring = IntroduceFactory ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633441152.0 rope-0.22.0/rope/refactor/introduce_parameter.py0000664000175000017500000000714300000000000021620 0ustar00lieryanlieryanimport rope.base.change from rope.base import exceptions, evaluate, worder, codeanalyze from rope.refactor import functionutils, sourceutils, occurrences class IntroduceParameter(object): """Introduce parameter refactoring This refactoring adds a new parameter to a function and replaces references to an expression in it with the new parameter. The parameter finding part is different from finding similar pieces in extract refactorings. In this refactoring parameters are found based on the object they reference to. For instance in:: class A(object): var = None class B(object): a = A() b = B() a = b.a def f(a): x = b.a.var + a.var using this refactoring on ``a.var`` with ``p`` as the new parameter name, will result in:: def f(p=a.var): x = p + p """ def __init__(self, project, resource, offset): self.project = project self.resource = resource self.offset = offset self.pymodule = self.project.get_pymodule(self.resource) scope = self.pymodule.get_scope().get_inner_scope_for_offset(offset) if scope.get_kind() != "Function": raise exceptions.RefactoringError( "Introduce parameter should be performed inside functions" ) self.pyfunction = scope.pyobject self.name, self.pyname = self._get_name_and_pyname() if self.pyname is None: raise exceptions.RefactoringError( "Cannot find the definition of <%s>" % self.name ) def _get_primary(self): word_finder = worder.Worder(self.resource.read()) return word_finder.get_primary_at(self.offset) def _get_name_and_pyname(self): return ( worder.get_name_at(self.resource, self.offset), evaluate.eval_location(self.pymodule, self.offset), ) def get_changes(self, new_parameter): definition_info = functionutils.DefinitionInfo.read(self.pyfunction) definition_info.args_with_defaults.append((new_parameter, self._get_primary())) collector = codeanalyze.ChangeCollector(self.resource.read()) header_start, header_end = self._get_header_offsets() body_start, body_end = sourceutils.get_body_region(self.pyfunction) collector.add_change(header_start, header_end, definition_info.to_string()) self._change_function_occurrences( collector, body_start, body_end, new_parameter ) changes = rope.base.change.ChangeSet("Introduce parameter <%s>" % new_parameter) change = rope.base.change.ChangeContents(self.resource, collector.get_changed()) changes.add_change(change) return changes def _get_header_offsets(self): lines = self.pymodule.lines start_line = self.pyfunction.get_scope().get_start() end_line = self.pymodule.logical_lines.logical_line_in(start_line)[1] start = lines.get_line_start(start_line) end = lines.get_line_end(end_line) start = self.pymodule.source_code.find("def", start) + 4 end = self.pymodule.source_code.rfind(":", start, end) return start, end def _change_function_occurrences( self, collector, function_start, function_end, new_name ): finder = occurrences.create_finder(self.project, self.name, self.pyname) for occurrence in finder.find_occurrences(resource=self.resource): start, end = occurrence.get_primary_range() if function_start <= start < function_end: collector.add_change(start, end, new_name) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/localtofield.py0000664000175000017500000000404600000000000020224 0ustar00lieryanlieryanfrom rope.base import pynames, evaluate, exceptions, worder from rope.refactor.rename import Rename class LocalToField(object): def __init__(self, project, resource, offset): self.project = project self.resource = resource self.offset = offset def get_changes(self): name = worder.get_name_at(self.resource, self.offset) this_pymodule = self.project.get_pymodule(self.resource) pyname = evaluate.eval_location(this_pymodule, self.offset) if not self._is_a_method_local(pyname): raise exceptions.RefactoringError( "Convert local variable to field should be performed on \n" "a local variable of a method." ) pymodule, lineno = pyname.get_definition_location() function_scope = pymodule.get_scope().get_inner_scope_for_line(lineno) # Not checking redefinition # self._check_redefinition(name, function_scope) new_name = self._get_field_name(function_scope.pyobject, name) changes = Rename(self.project, self.resource, self.offset).get_changes( new_name, resources=[self.resource] ) return changes def _check_redefinition(self, name, function_scope): class_scope = function_scope.parent if name in class_scope.pyobject: raise exceptions.RefactoringError("The field %s already exists" % name) def _get_field_name(self, pyfunction, name): self_name = pyfunction.get_param_names()[0] new_name = self_name + "." + name return new_name def _is_a_method_local(self, pyname): pymodule, lineno = pyname.get_definition_location() holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno) parent = holding_scope.parent return ( isinstance(pyname, pynames.AssignedName) and pyname in holding_scope.get_names().values() and holding_scope.get_kind() == "Function" and parent is not None and parent.get_kind() == "Class" ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/method_object.py0000664000175000017500000000742600000000000020376 0ustar00lieryanlieryanimport warnings from rope.base import libutils from rope.base import pyobjects, exceptions, change, evaluate, codeanalyze from rope.refactor import sourceutils, occurrences, rename class MethodObject(object): def __init__(self, project, resource, offset): self.project = project this_pymodule = self.project.get_pymodule(resource) pyname = evaluate.eval_location(this_pymodule, offset) if pyname is None or not isinstance(pyname.get_object(), pyobjects.PyFunction): raise exceptions.RefactoringError( "Replace method with method object refactoring should be " "performed on a function." ) self.pyfunction = pyname.get_object() self.pymodule = self.pyfunction.get_module() self.resource = self.pymodule.get_resource() def get_new_class(self, name): body = sourceutils.fix_indentation( self._get_body(), sourceutils.get_indent(self.project) * 2 ) return "class %s(object):\n\n%s%sdef __call__(self):\n%s" % ( name, self._get_init(), " " * sourceutils.get_indent(self.project), body, ) def get_changes(self, classname=None, new_class_name=None): if new_class_name is not None: warnings.warn( "new_class_name parameter is deprecated; use classname", DeprecationWarning, stacklevel=2, ) classname = new_class_name collector = codeanalyze.ChangeCollector(self.pymodule.source_code) start, end = sourceutils.get_body_region(self.pyfunction) indents = sourceutils.get_indents( self.pymodule.lines, self.pyfunction.get_scope().get_start() ) + sourceutils.get_indent(self.project) new_contents = " " * indents + "return %s(%s)()\n" % ( classname, ", ".join(self._get_parameter_names()), ) collector.add_change(start, end, new_contents) insertion = self._get_class_insertion_point() collector.add_change( insertion, insertion, "\n\n" + self.get_new_class(classname) ) changes = change.ChangeSet("Replace method with method object refactoring") changes.add_change( change.ChangeContents(self.resource, collector.get_changed()) ) return changes def _get_class_insertion_point(self): current = self.pyfunction while current.parent != self.pymodule: current = current.parent end = self.pymodule.lines.get_line_end(current.get_scope().get_end()) return min(end + 1, len(self.pymodule.source_code)) def _get_body(self): body = sourceutils.get_body(self.pyfunction) for param in self._get_parameter_names(): body = param + " = None\n" + body pymod = libutils.get_string_module(self.project, body, self.resource) pyname = pymod[param] finder = occurrences.create_finder(self.project, param, pyname) result = rename.rename_in_module(finder, "self." + param, pymodule=pymod) body = result[result.index("\n") + 1 :] return body def _get_init(self): params = self._get_parameter_names() indents = " " * sourceutils.get_indent(self.project) if not params: return "" header = indents + "def __init__(self" body = "" for arg in params: new_name = arg if arg == "self": new_name = "host" header += ", %s" % new_name body += indents * 2 + "self.%s = %s\n" % (arg, new_name) header += "):" return "%s\n%s\n" % (header, body) def _get_parameter_names(self): return self.pyfunction.get_param_names() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637501104.0 rope-0.22.0/rope/refactor/move.py0000664000175000017500000010057100000000000016531 0ustar00lieryanlieryan"""A module containing classes for move refactoring `create_move()` is a factory for creating move refactoring objects based on inputs. """ from rope.base import ( pyobjects, codeanalyze, exceptions, pynames, taskhandle, evaluate, worder, libutils, ) from rope.base.change import ChangeSet, ChangeContents, MoveResource from rope.refactor import importutils, rename, occurrences, sourceutils, functionutils def create_move(project, resource, offset=None): """A factory for creating Move objects Based on `resource` and `offset`, return one of `MoveModule`, `MoveGlobal` or `MoveMethod` for performing move refactoring. """ if offset is None: return MoveModule(project, resource) this_pymodule = project.get_pymodule(resource) pyname = evaluate.eval_location(this_pymodule, offset) if pyname is not None: pyobject = pyname.get_object() if isinstance(pyobject, pyobjects.PyModule) or isinstance( pyobject, pyobjects.PyPackage ): return MoveModule(project, pyobject.get_resource()) if isinstance(pyobject, pyobjects.PyFunction) and isinstance( pyobject.parent, pyobjects.PyClass ): return MoveMethod(project, resource, offset) if ( isinstance(pyobject, pyobjects.PyDefinedObject) and isinstance(pyobject.parent, pyobjects.PyModule) or isinstance(pyname, pynames.AssignedName) ): return MoveGlobal(project, resource, offset) raise exceptions.RefactoringError( "Move only works on global classes/functions/variables, modules and " "methods." ) class MoveMethod(object): """For moving methods It makes a new method in the destination class and changes the body of the old method to call the new method. You can inline the old method to change all of its occurrences. """ def __init__(self, project, resource, offset): self.project = project this_pymodule = self.project.get_pymodule(resource) pyname = evaluate.eval_location(this_pymodule, offset) self.method_name = worder.get_name_at(resource, offset) self.pyfunction = pyname.get_object() if self.pyfunction.get_kind() != "method": raise exceptions.RefactoringError("Only normal methods" " can be moved.") def get_changes( self, dest_attr, new_name=None, resources=None, task_handle=taskhandle.NullTaskHandle(), ): """Return the changes needed for this refactoring Parameters: - `dest_attr`: the name of the destination attribute - `new_name`: the name of the new method; if `None` uses the old name - `resources` can be a list of `rope.base.resources.File` to apply this refactoring on. If `None`, the restructuring will be applied to all python files. """ changes = ChangeSet("Moving method <%s>" % self.method_name) if resources is None: resources = self.project.get_python_files() if new_name is None: new_name = self.get_method_name() resource1, start1, end1, new_content1 = self._get_changes_made_by_old_class( dest_attr, new_name ) collector1 = codeanalyze.ChangeCollector(resource1.read()) collector1.add_change(start1, end1, new_content1) resource2, start2, end2, new_content2 = self._get_changes_made_by_new_class( dest_attr, new_name ) if resource1 == resource2: collector1.add_change(start2, end2, new_content2) else: collector2 = codeanalyze.ChangeCollector(resource2.read()) collector2.add_change(start2, end2, new_content2) result = collector2.get_changed() import_tools = importutils.ImportTools(self.project) new_imports = self._get_used_imports(import_tools) if new_imports: goal_pymodule = libutils.get_string_module( self.project, result, resource2 ) result = _add_imports_to_module( import_tools, goal_pymodule, new_imports ) if resource2 in resources: changes.add_change(ChangeContents(resource2, result)) if resource1 in resources: changes.add_change(ChangeContents(resource1, collector1.get_changed())) return changes def get_method_name(self): return self.method_name def _get_used_imports(self, import_tools): return importutils.get_imports(self.project, self.pyfunction) def _get_changes_made_by_old_class(self, dest_attr, new_name): pymodule = self.pyfunction.get_module() indents = self._get_scope_indents(self.pyfunction) body = "return self.%s.%s(%s)\n" % ( dest_attr, new_name, self._get_passed_arguments_string(), ) region = sourceutils.get_body_region(self.pyfunction) return ( pymodule.get_resource(), region[0], region[1], sourceutils.fix_indentation(body, indents), ) def _get_scope_indents(self, pyobject): pymodule = pyobject.get_module() return sourceutils.get_indents( pymodule.lines, pyobject.get_scope().get_start() ) + sourceutils.get_indent(self.project) def _get_changes_made_by_new_class(self, dest_attr, new_name): old_pyclass = self.pyfunction.parent if dest_attr not in old_pyclass: raise exceptions.RefactoringError( "Destination attribute <%s> not found" % dest_attr ) pyclass = old_pyclass[dest_attr].get_object().get_type() if not isinstance(pyclass, pyobjects.PyClass): raise exceptions.RefactoringError( "Unknown class type for attribute <%s>" % dest_attr ) pymodule = pyclass.get_module() resource = pyclass.get_module().get_resource() start, end = sourceutils.get_body_region(pyclass) pre_blanks = "\n" if pymodule.source_code[start:end].strip() != "pass": pre_blanks = "\n\n" start = end indents = self._get_scope_indents(pyclass) body = pre_blanks + sourceutils.fix_indentation( self.get_new_method(new_name), indents ) return resource, start, end, body def get_new_method(self, name): return "%s\n%s" % ( self._get_new_header(name), sourceutils.fix_indentation( self._get_body(), sourceutils.get_indent(self.project) ), ) def _get_unchanged_body(self): return sourceutils.get_body(self.pyfunction) def _get_body(self, host="host"): self_name = self._get_self_name() body = self_name + " = None\n" + self._get_unchanged_body() pymodule = libutils.get_string_module(self.project, body) finder = occurrences.create_finder(self.project, self_name, pymodule[self_name]) result = rename.rename_in_module(finder, host, pymodule=pymodule) if result is None: result = body return result[result.index("\n") + 1 :] def _get_self_name(self): return self.pyfunction.get_param_names()[0] def _get_new_header(self, name): header = "def %s(self" % name if self._is_host_used(): header += ", host" definition_info = functionutils.DefinitionInfo.read(self.pyfunction) others = definition_info.arguments_to_string(1) if others: header += ", " + others return header + "):" def _get_passed_arguments_string(self): result = "" if self._is_host_used(): result = "self" definition_info = functionutils.DefinitionInfo.read(self.pyfunction) others = definition_info.arguments_to_string(1) if others: if result: result += ", " result += others return result def _is_host_used(self): return self._get_body("__old_self") != self._get_unchanged_body() class MoveGlobal(object): """For moving global function and classes""" def __init__(self, project, resource, offset): self.project = project this_pymodule = self.project.get_pymodule(resource) 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." ) if self._is_variable(self.old_pyname): self.old_name = worder.get_name_at(resource, offset) pymodule = this_pymodule else: self.old_name = self.old_pyname.get_object().get_name() pymodule = self.old_pyname.get_object().get_module() self._check_exceptional_conditions() self.source = pymodule.get_resource() self.tools = _MoveTools( self.project, self.source, self.old_pyname, self.old_name ) self.import_tools = self.tools.import_tools def _import_filter(self, stmt): module_name = libutils.modname(self.source) if isinstance(stmt.import_info, importutils.NormalImport): # Affect any statement that imports the source module return any( module_name == name for name, alias in stmt.import_info.names_and_aliases ) elif isinstance(stmt.import_info, importutils.FromImport): # Affect statements importing from the source package if "." in module_name: package_name, basename = module_name.rsplit(".", 1) if stmt.import_info.module_name == package_name and any( basename == name for name, alias in stmt.import_info.names_and_aliases ): return True return stmt.import_info.module_name == module_name return False def _check_exceptional_conditions(self): if self._is_variable(self.old_pyname): pymodule = self.old_pyname.get_definition_location()[0] try: pymodule.get_scope().get_name(self.old_name) except exceptions.NameNotFoundError: self._raise_refactoring_error() elif not ( isinstance(self.old_pyname.get_object(), pyobjects.PyDefinedObject) and self._is_global(self.old_pyname.get_object()) ): self._raise_refactoring_error() def _raise_refactoring_error(self): raise exceptions.RefactoringError( "Move refactoring should be performed on a global class, function " "or variable." ) def _is_global(self, pyobject): return pyobject.get_scope().parent == pyobject.get_module().get_scope() def _is_variable(self, pyname): return isinstance(pyname, pynames.AssignedName) def get_changes( self, dest, resources=None, task_handle=taskhandle.NullTaskHandle() ): if resources is None: resources = self.project.get_python_files() if dest is None or not dest.exists(): raise exceptions.RefactoringError("Move destination does not exist.") if dest.is_folder() and dest.has_child("__init__.py"): dest = dest.get_child("__init__.py") if dest.is_folder(): raise exceptions.RefactoringError( "Move destination for non-modules should not be folders." ) if self.source == dest: raise exceptions.RefactoringError( "Moving global elements to the same module." ) return self._calculate_changes(dest, resources, task_handle) def _calculate_changes(self, dest, resources, task_handle): changes = ChangeSet("Moving global <%s>" % self.old_name) job_set = task_handle.create_jobset("Collecting Changes", len(resources)) for file_ in resources: job_set.started_job(file_.path) if file_ == self.source: changes.add_change(self._source_module_changes(dest)) elif file_ == dest: changes.add_change(self._dest_module_changes(dest)) elif self.tools.occurs_in_module(resource=file_): pymodule = self.project.get_pymodule(file_) # Changing occurrences placeholder = "__rope_renaming_%s_" % self.old_name source = self.tools.rename_in_module(placeholder, resource=file_) should_import = source is not None # Removing out of date imports pymodule = self.tools.new_pymodule(pymodule, source) source = self.import_tools.organize_imports( pymodule, sort=False, import_filter=self._import_filter ) # Adding new import if should_import: pymodule = self.tools.new_pymodule(pymodule, source) source, imported = importutils.add_import( self.project, pymodule, self._new_modname(dest), self.old_name ) source = source.replace(placeholder, imported) source = self.tools.new_source(pymodule, source) if source != file_.read(): changes.add_change(ChangeContents(file_, source)) job_set.finished_job() return changes def _source_module_changes(self, dest): placeholder = "__rope_moving_%s_" % self.old_name handle = _ChangeMoveOccurrencesHandle(placeholder) occurrence_finder = occurrences.create_finder( self.project, self.old_name, self.old_pyname ) start, end = self._get_moving_region() renamer = ModuleSkipRenamer(occurrence_finder, self.source, handle, start, end) source = renamer.get_changed_module() pymodule = libutils.get_string_module(self.project, source, self.source) source = self.import_tools.organize_imports(pymodule, sort=False) if handle.occurred: pymodule = libutils.get_string_module(self.project, source, self.source) # Adding new import source, imported = importutils.add_import( self.project, pymodule, self._new_modname(dest), self.old_name ) source = source.replace(placeholder, imported) return ChangeContents(self.source, source) def _new_modname(self, dest): return libutils.modname(dest) def _dest_module_changes(self, dest): # Changing occurrences pymodule = self.project.get_pymodule(dest) source = self.tools.rename_in_module(self.old_name, pymodule) pymodule = self.tools.new_pymodule(pymodule, source) moving, imports = self._get_moving_element_with_imports() pymodule, has_changed = self._add_imports2(pymodule, imports) module_with_imports = self.import_tools.module_imports(pymodule) source = pymodule.source_code lineno = 0 if module_with_imports.imports: lineno = module_with_imports.imports[-1].end_line - 1 else: while lineno < pymodule.lines.length() and pymodule.lines.get_line( lineno + 1 ).lstrip().startswith("#"): lineno += 1 if lineno > 0: cut = pymodule.lines.get_line_end(lineno) + 1 result = source[:cut] + "\n\n" + moving + source[cut:] else: result = moving + source # Organizing imports source = result pymodule = libutils.get_string_module(self.project, source, dest) source = self.import_tools.organize_imports(pymodule, sort=False, unused=False) # Remove unused imports of the old module pymodule = libutils.get_string_module(self.project, source, dest) source = self.import_tools.organize_imports( pymodule, sort=False, selfs=False, unused=True, import_filter=self._import_filter, ) return ChangeContents(dest, source) def _get_moving_element_with_imports(self): return moving_code_with_imports( self.project, self.source, self._get_moving_element() ) def _get_module_with_imports(self, source_code, resource): pymodule = libutils.get_string_module(self.project, source_code, resource) return self.import_tools.module_imports(pymodule) def _get_moving_element(self): start, end = self._get_moving_region() moving = self.source.read()[start:end] return moving.rstrip() + "\n" def _get_moving_region(self): pymodule = self.project.get_pymodule(self.source) lines = pymodule.lines if self._is_variable(self.old_pyname): logical_lines = pymodule.logical_lines lineno = logical_lines.logical_line_in( self.old_pyname.get_definition_location()[1] )[0] start = lines.get_line_start(lineno) end_line = logical_lines.logical_line_in(lineno)[1] else: scope = self.old_pyname.get_object().get_scope() start = lines.get_line_start(scope.get_start()) end_line = scope.get_end() # Include comment lines before the definition start_line = lines.get_line_number(start) while start_line > 1 and lines.get_line(start_line - 1).startswith("#"): start_line -= 1 start = lines.get_line_start(start_line) while end_line < lines.length() and lines.get_line(end_line + 1).strip() == "": end_line += 1 end = min(lines.get_line_end(end_line) + 1, len(pymodule.source_code)) return start, end def _add_imports2(self, pymodule, new_imports): source = self.tools.add_imports(pymodule, new_imports) if source is None: return pymodule, False else: resource = pymodule.get_resource() pymodule = libutils.get_string_module(self.project, source, resource) return pymodule, True class MoveModule(object): """For moving modules and packages""" def __init__(self, project, resource): self.project = project if not resource.is_folder() and resource.name == "__init__.py": resource = resource.parent if resource.is_folder() and not resource.has_child("__init__.py"): raise exceptions.RefactoringError("Cannot move non-package folder.") dummy_pymodule = libutils.get_string_module(self.project, "") self.old_pyname = pynames.ImportedModule(dummy_pymodule, resource=resource) self.source = self.old_pyname.get_object().get_resource() if self.source.is_folder(): self.old_name = self.source.name else: self.old_name = self.source.name[:-3] self.tools = _MoveTools( self.project, self.source, self.old_pyname, self.old_name ) self.import_tools = self.tools.import_tools def get_changes( self, dest, resources=None, task_handle=taskhandle.NullTaskHandle() ): if resources is None: resources = self.project.get_python_files() if dest is None or not dest.is_folder(): raise exceptions.RefactoringError( "Move destination for modules should be packages." ) return self._calculate_changes(dest, resources, task_handle) def _calculate_changes(self, dest, resources, task_handle): changes = ChangeSet("Moving module <%s>" % self.old_name) job_set = task_handle.create_jobset("Collecting changes", len(resources)) for module in resources: job_set.started_job(module.path) if module == self.source: self._change_moving_module(changes, dest) else: source = self._change_occurrences_in_module(dest, resource=module) if source is not None: changes.add_change(ChangeContents(module, source)) job_set.finished_job() if self.project == self.source.project: changes.add_change(MoveResource(self.source, dest.path)) return changes def _new_modname(self, dest): destname = libutils.modname(dest) if destname: return destname + "." + self.old_name return self.old_name def _new_import(self, dest): return importutils.NormalImport([(self._new_modname(dest), None)]) def _change_moving_module(self, changes, dest): if not self.source.is_folder(): pymodule = self.project.get_pymodule(self.source) source = self.import_tools.relatives_to_absolutes(pymodule) pymodule = self.tools.new_pymodule(pymodule, source) source = self._change_occurrences_in_module(dest, pymodule) source = self.tools.new_source(pymodule, source) if source != self.source.read(): changes.add_change(ChangeContents(self.source, source)) def _change_occurrences_in_module(self, dest, pymodule=None, resource=None): if not self.tools.occurs_in_module(pymodule=pymodule, resource=resource): return if pymodule is None: pymodule = self.project.get_pymodule(resource) new_name = self._new_modname(dest) module_imports = importutils.get_module_imports(self.project, pymodule) changed = False source = None if libutils.modname(dest): changed = self._change_import_statements(dest, new_name, module_imports) if changed: source = module_imports.get_changed_source() source = self.tools.new_source(pymodule, source) pymodule = self.tools.new_pymodule(pymodule, source) new_import = self._new_import(dest) source = self.tools.rename_in_module( new_name, imports=True, pymodule=pymodule, resource=resource if not changed else None, ) should_import = self.tools.occurs_in_module( pymodule=pymodule, resource=resource, imports=False ) pymodule = self.tools.new_pymodule(pymodule, source) source = self.tools.remove_old_imports(pymodule) if should_import: pymodule = self.tools.new_pymodule(pymodule, source) source = self.tools.add_imports(pymodule, [new_import]) source = self.tools.new_source(pymodule, source) if source is not None and source != pymodule.resource.read(): return source return None def _change_import_statements(self, dest, new_name, module_imports): moving_module = self.source parent_module = moving_module.parent changed = False for import_stmt in module_imports.imports: if not any( name_and_alias[0] == self.old_name for name_and_alias in import_stmt.import_info.names_and_aliases ) and not any( name_and_alias[0] == libutils.modname(self.source) for name_and_alias in import_stmt.import_info.names_and_aliases ): continue # Case 1: Look for normal imports of the moving module. if isinstance(import_stmt.import_info, importutils.NormalImport): continue # Case 2: The moving module is from-imported. changed = ( self._handle_moving_in_from_import_stmt( dest, import_stmt, module_imports, parent_module ) or changed ) # Case 3: Names are imported from the moving module. context = importutils.importinfo.ImportContext(self.project, None) if ( not import_stmt.import_info.is_empty() and import_stmt.import_info.get_imported_resource(context) == moving_module ): import_stmt.import_info = importutils.FromImport( new_name, import_stmt.import_info.level, import_stmt.import_info.names_and_aliases, ) changed = True return changed def _handle_moving_in_from_import_stmt( self, dest, import_stmt, module_imports, parent_module ): changed = False context = importutils.importinfo.ImportContext(self.project, None) if import_stmt.import_info.get_imported_resource(context) == parent_module: imports = import_stmt.import_info.names_and_aliases new_imports = [] for name, alias in imports: # The moving module was imported. if name == self.old_name: changed = True new_import = importutils.FromImport( libutils.modname(dest), 0, [(self.old_name, alias)] ) module_imports.add_import(new_import) else: new_imports.append((name, alias)) # Update the imports if the imported names were changed. if new_imports != imports: changed = True if new_imports: import_stmt.import_info = importutils.FromImport( import_stmt.import_info.module_name, import_stmt.import_info.level, new_imports, ) else: import_stmt.empty_import() return changed class _ChangeMoveOccurrencesHandle(object): def __init__(self, new_name): self.new_name = new_name self.occurred = False def occurred_inside_skip(self, change_collector, occurrence): pass def occurred_outside_skip(self, change_collector, occurrence): start, end = occurrence.get_primary_range() change_collector.add_change(start, end, self.new_name) self.occurred = True class _MoveTools(object): def __init__(self, project, source, pyname, old_name): self.project = project self.source = source self.old_pyname = pyname self.old_name = old_name self.import_tools = importutils.ImportTools(self.project) def remove_old_imports(self, pymodule): old_source = pymodule.source_code module_with_imports = self.import_tools.module_imports(pymodule) class CanSelect(object): changed = False old_name = self.old_name old_pyname = self.old_pyname def __call__(self, name): try: if ( name == self.old_name and pymodule[name].get_object() == self.old_pyname.get_object() ): self.changed = True return False except exceptions.AttributeNotFoundError: pass return True can_select = CanSelect() module_with_imports.filter_names(can_select) new_source = module_with_imports.get_changed_source() if old_source != new_source: return new_source def rename_in_module(self, new_name, pymodule=None, imports=False, resource=None): occurrence_finder = self._create_finder(imports) source = rename.rename_in_module( occurrence_finder, new_name, replace_primary=True, pymodule=pymodule, resource=resource, ) return source def occurs_in_module(self, pymodule=None, resource=None, imports=True): finder = self._create_finder(imports) for occurrence in finder.find_occurrences(pymodule=pymodule, resource=resource): return True return False def _create_finder(self, imports): return occurrences.create_finder( self.project, self.old_name, self.old_pyname, imports=imports, keywords=False, ) def new_pymodule(self, pymodule, source): if source is not None: return libutils.get_string_module( self.project, source, pymodule.get_resource() ) return pymodule def new_source(self, pymodule, source): if source is None: return pymodule.source_code return source def add_imports(self, pymodule, new_imports): return _add_imports_to_module(self.import_tools, pymodule, new_imports) def _add_imports_to_module(import_tools, pymodule, new_imports): module_with_imports = import_tools.module_imports(pymodule) for new_import in new_imports: module_with_imports.add_import(new_import) return module_with_imports.get_changed_source() def moving_code_with_imports(project, resource, source): import_tools = importutils.ImportTools(project) pymodule = libutils.get_string_module(project, source, resource) # Strip comment prefix, if any. These need to stay before the moving # section, but imports would be added between them. lines = codeanalyze.SourceLinesAdapter(source) start = 1 while start < lines.length() and lines.get_line(start).startswith("#"): start += 1 moving_prefix = source[: lines.get_line_start(start)] pymodule = libutils.get_string_module( project, source[lines.get_line_start(start) :], resource ) origin = project.get_pymodule(resource) imports = [] for stmt in import_tools.module_imports(origin).imports: imports.append(stmt.import_info) back_names = [] for name in origin: if name not in pymodule: back_names.append(name) imports.append(import_tools.get_from_import(resource, back_names)) source = _add_imports_to_module(import_tools, pymodule, imports) pymodule = libutils.get_string_module(project, source, resource) source = import_tools.relatives_to_absolutes(pymodule) pymodule = libutils.get_string_module(project, source, resource) source = import_tools.organize_imports(pymodule, selfs=False) pymodule = libutils.get_string_module(project, source, resource) # extracting imports after changes module_imports = import_tools.module_imports(pymodule) imports = [import_stmt.import_info for import_stmt in module_imports.imports] start = 1 if module_imports.imports: start = module_imports.imports[-1].end_line lines = codeanalyze.SourceLinesAdapter(source) while start < lines.length() and not lines.get_line(start).strip(): start += 1 # Reinsert the prefix which was removed at the beginning moving = moving_prefix + source[lines.get_line_start(start) :] return moving, imports class ModuleSkipRenamerHandle(object): def occurred_outside_skip(self, change_collector, occurrence): pass def occurred_inside_skip(self, change_collector, occurrence): pass class ModuleSkipRenamer(object): """Rename occurrences in a module This class can be used when you want to treat a region in a file separately from other parts when renaming. """ def __init__( self, occurrence_finder, resource, handle=None, skip_start=0, skip_end=0, replacement="", ): """Constructor if replacement is `None` the region is not changed. Otherwise it is replaced with `replacement`. """ self.occurrence_finder = occurrence_finder self.resource = resource self.skip_start = skip_start self.skip_end = skip_end self.replacement = replacement self.handle = handle if self.handle is None: self.handle = ModuleSkipRenamerHandle() def get_changed_module(self): source = self.resource.read() change_collector = codeanalyze.ChangeCollector(source) if self.replacement is not None: change_collector.add_change( self.skip_start, self.skip_end, self.replacement ) for occurrence in self.occurrence_finder.find_occurrences(self.resource): start, end = occurrence.get_primary_range() if self.skip_start <= start < self.skip_end: self.handle.occurred_inside_skip(change_collector, occurrence) else: self.handle.occurred_outside_skip(change_collector, occurrence) result = change_collector.get_changed() if result is not None and result != source: return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/multiproject.py0000664000175000017500000000500600000000000020301 0ustar00lieryanlieryan"""This module can be used for performing cross-project refactorings See the "cross-project refactorings" section of ``docs/library.rst`` file. """ from rope.base import resources, libutils class MultiProjectRefactoring(object): def __init__(self, refactoring, projects, addpath=True): """Create a multiproject proxy for the main refactoring `projects` are other project. """ self.refactoring = refactoring self.projects = projects self.addpath = addpath def __call__(self, project, *args, **kwds): """Create the refactoring""" return _MultiRefactoring( self.refactoring, self.projects, self.addpath, project, *args, **kwds ) class _MultiRefactoring(object): def __init__(self, refactoring, other_projects, addpath, project, *args, **kwds): self.refactoring = refactoring self.projects = [project] + other_projects for other_project in other_projects: for folder in self.project.get_source_folders(): other_project.get_prefs().add("python_path", folder.real_path) self.refactorings = [] for other in self.projects: args, kwds = self._resources_for_args(other, args, kwds) self.refactorings.append(self.refactoring(other, *args, **kwds)) def get_all_changes(self, *args, **kwds): """Get a project to changes dict""" result = [] for project, refactoring in zip(self.projects, self.refactorings): args, kwds = self._resources_for_args(project, args, kwds) result.append((project, refactoring.get_changes(*args, **kwds))) return result def __getattr__(self, name): return getattr(self.main_refactoring, name) def _resources_for_args(self, project, args, kwds): newargs = [self._change_project_resource(project, arg) for arg in args] newkwds = dict( (name, self._change_project_resource(project, value)) for name, value in kwds.items() ) return newargs, newkwds def _change_project_resource(self, project, obj): if isinstance(obj, resources.Resource) and obj.project != project: return libutils.path_to_resource(project, obj.real_path) return obj @property def project(self): return self.projects[0] @property def main_refactoring(self): return self.refactorings[0] def perform(project_changes): for project, changes in project_changes: project.do(changes) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/occurrences.py0000664000175000017500000003174200000000000020101 0ustar00lieryanlieryan"""Find occurrences of a name in a project. This module consists of a `Finder` that finds all occurrences of a name in a project. The `Finder.find_occurrences()` method is a generator that yields `Occurrence` instances for each occurrence of the name. To create a `Finder` object, use the `create_finder()` function: finder = occurrences.create_finder(project, 'foo', pyname) for occurrence in finder.find_occurrences(): pass It's possible to filter the occurrences. They can be specified when calling the `create_finder()` function. * `only_calls`: If True, return only those instances where the name is a function that's being called. * `imports`: If False, don't return instances that are in import statements. * `unsure`: If a prediate function, return instances where we don't know what the name references. It also filters based on the predicate function. * `docs`: If True, it will search for occurrences in regions normally ignored. E.g., strings and comments. * `in_hierarchy`: If True, it will find occurrences if the name is in the class's hierarchy. * `instance`: Used only when you want implicit interfaces to be considered. * `keywords`: If False, don't return instances that are the names of keyword arguments """ import ast import re from rope.base import codeanalyze from rope.base import evaluate from rope.base import exceptions from rope.base import pynames from rope.base import pyobjects from rope.base import utils from rope.base import worder class Finder(object): """For finding occurrences of a name The constructor takes a `filters` argument. It should be a list of functions that take a single argument. For each possible occurrence, these functions are called in order with the an instance of `Occurrence`: * If it returns `None` other filters are tried. * If it returns `True`, the occurrence will be a match. * If it returns `False`, the occurrence will be skipped. * If all of the filters return `None`, it is skipped also. """ def __init__(self, project, name, filters=[lambda o: True], docs=False): self.project = project self.name = name self.docs = docs self.filters = filters self._textual_finder = _TextualFinder(name, docs=docs) def find_occurrences(self, resource=None, pymodule=None): """Generate `Occurrence` instances""" tools = _OccurrenceToolsCreator( self.project, resource=resource, pymodule=pymodule, docs=self.docs ) for offset in self._textual_finder.find_offsets(tools.source_code): occurrence = Occurrence(tools, offset) for filter in self.filters: result = filter(occurrence) if result is None: continue if result: yield occurrence break def create_finder( project, name, pyname, only_calls=False, imports=True, unsure=None, docs=False, instance=None, in_hierarchy=False, keywords=True, ): """A factory for `Finder` Based on the arguments it creates a list of filters. `instance` argument is needed only when you want implicit interfaces to be considered. """ pynames_ = set([pyname]) filters = [] if only_calls: filters.append(CallsFilter()) if not imports: filters.append(NoImportsFilter()) if not keywords: filters.append(NoKeywordsFilter()) if isinstance(instance, pynames.ParameterName): for pyobject in instance.get_objects(): try: pynames_.add(pyobject[name]) except exceptions.AttributeNotFoundError: pass for pyname in pynames_: filters.append(PyNameFilter(pyname)) if in_hierarchy: filters.append(InHierarchyFilter(pyname)) if unsure: filters.append(UnsureFilter(unsure)) return Finder(project, name, filters=filters, docs=docs) class Occurrence(object): def __init__(self, tools, offset): self.tools = tools self.offset = offset self.resource = tools.resource @utils.saveit def get_word_range(self): return self.tools.word_finder.get_word_range(self.offset) @utils.saveit def get_primary_range(self): return self.tools.word_finder.get_primary_range(self.offset) @utils.saveit def get_pyname(self): try: return self.tools.name_finder.get_pyname_at(self.offset) except exceptions.BadIdentifierError: pass @utils.saveit def get_primary_and_pyname(self): try: return self.tools.name_finder.get_primary_and_pyname_at(self.offset) except exceptions.BadIdentifierError: pass @utils.saveit def is_in_import_statement(self): return self.tools.word_finder.is_from_statement( self.offset ) or self.tools.word_finder.is_import_statement(self.offset) def is_called(self): return self.tools.word_finder.is_a_function_being_called(self.offset) def is_defined(self): return self.tools.word_finder.is_a_class_or_function_name_in_header(self.offset) def is_a_fixed_primary(self): return self.tools.word_finder.is_a_class_or_function_name_in_header( self.offset ) or self.tools.word_finder.is_a_name_after_from_import(self.offset) def is_written(self): return self.tools.word_finder.is_assigned_here(self.offset) def is_unsure(self): return unsure_pyname(self.get_pyname()) def is_function_keyword_parameter(self): return self.tools.word_finder.is_function_keyword_parameter(self.offset) @property @utils.saveit def lineno(self): offset = self.get_word_range()[0] return self.tools.pymodule.lines.get_line_number(offset) def same_pyname(expected, pyname): """Check whether `expected` and `pyname` are the same""" if expected is None or pyname is None: return False if expected == pyname: return True if type(expected) not in (pynames.ImportedModule, pynames.ImportedName) and type( pyname ) not in (pynames.ImportedModule, pynames.ImportedName): return False return ( expected.get_definition_location() == pyname.get_definition_location() and expected.get_object() == pyname.get_object() ) def unsure_pyname(pyname, unbound=True): """Return `True` if we don't know what this name references""" if pyname is None: return True if unbound and not isinstance(pyname, pynames.UnboundName): return False if pyname.get_object() == pyobjects.get_unknown(): return True class PyNameFilter(object): """For finding occurrences of a name.""" def __init__(self, pyname): self.pyname = pyname def __call__(self, occurrence): if same_pyname(self.pyname, occurrence.get_pyname()): return True class InHierarchyFilter(object): """Finds the occurrence if the name is in the class's hierarchy.""" def __init__(self, pyname, implementations_only=False): self.pyname = pyname self.impl_only = implementations_only self.pyclass = self._get_containing_class(pyname) if self.pyclass is not None: self.name = pyname.get_object().get_name() self.roots = self._get_root_classes(self.pyclass, self.name) else: self.roots = None def __call__(self, occurrence): if self.roots is None: return pyclass = self._get_containing_class(occurrence.get_pyname()) if pyclass is not None: roots = self._get_root_classes(pyclass, self.name) if self.roots.intersection(roots): return True def _get_containing_class(self, pyname): if isinstance(pyname, pynames.DefinedName): scope = pyname.get_object().get_scope() parent = scope.parent if parent is not None and parent.get_kind() == "Class": return parent.pyobject def _get_root_classes(self, pyclass, name): if self.impl_only and pyclass == self.pyclass: return set([pyclass]) result = set() for superclass in pyclass.get_superclasses(): if name in superclass: result.update(self._get_root_classes(superclass, name)) if not result: return set([pyclass]) return result class UnsureFilter(object): """Occurrences where we don't knoow what the name references.""" def __init__(self, unsure): self.unsure = unsure def __call__(self, occurrence): if occurrence.is_unsure() and self.unsure(occurrence): return True class NoImportsFilter(object): """Don't include import statements as occurrences.""" def __call__(self, occurrence): if occurrence.is_in_import_statement(): return False class CallsFilter(object): """Filter out non-call occurrences.""" def __call__(self, occurrence): if not occurrence.is_called(): return False class NoKeywordsFilter(object): """Filter out keyword parameters.""" def __call__(self, occurrence): if occurrence.is_function_keyword_parameter(): return False class _TextualFinder(object): def __init__(self, name, docs=False): self.name = name self.docs = docs self.comment_pattern = _TextualFinder.any("comment", [r"#[^\n]*"]) self.string_pattern = _TextualFinder.any( "string", [codeanalyze.get_string_pattern()] ) self.f_string_pattern = _TextualFinder.any( "fstring", [codeanalyze.get_formatted_string_pattern()] ) self.pattern = self._get_occurrence_pattern(self.name) def find_offsets(self, source): if not self._fast_file_query(source): return if self.docs: searcher = self._normal_search else: searcher = self._re_search for matched in searcher(source): yield matched def _re_search(self, source): for match in self.pattern.finditer(source): if match.groupdict()["occurrence"]: yield match.start("occurrence") elif utils.pycompat.PY36 and 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 def _search_in_f_string(self, f_string): tree = ast.parse(f_string) for node in ast.walk(tree): if isinstance(node, ast.Name) and node.id == self.name: yield node def _normal_search(self, source): current = 0 while True: try: found = source.index(self.name, current) current = found + len(self.name) if (found == 0 or not self._is_id_char(source[found - 1])) and ( current == len(source) or not self._is_id_char(source[current]) ): yield found except ValueError: break def _is_id_char(self, c): return c.isalnum() or c == "_" def _fast_file_query(self, source): try: source.index(self.name) return True except ValueError: return False def _get_source(self, resource, pymodule): if resource is not None: return resource.read() else: return pymodule.source_code def _get_occurrence_pattern(self, name): occurrence_pattern = _TextualFinder.any("occurrence", ["\\b" + name + "\\b"]) pattern = re.compile( occurrence_pattern + "|" + self.comment_pattern + "|" + self.string_pattern + "|" + self.f_string_pattern ) return pattern @staticmethod def any(name, list_): return "(?P<%s>" % name + "|".join(list_) + ")" class _OccurrenceToolsCreator(object): def __init__(self, project, resource=None, pymodule=None, docs=False): self.project = project self.__resource = resource self.__pymodule = pymodule self.docs = docs @property @utils.saveit def name_finder(self): return evaluate.ScopeNameFinder(self.pymodule) @property @utils.saveit def source_code(self): return self.pymodule.source_code @property @utils.saveit def word_finder(self): return worder.Worder(self.source_code, self.docs) @property @utils.saveit def resource(self): if self.__resource is not None: return self.__resource if self.__pymodule is not None: return self.__pymodule.resource @property @utils.saveit def pymodule(self): if self.__pymodule is not None: return self.__pymodule return self.project.get_pymodule(self.resource) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637553386.0 rope-0.22.0/rope/refactor/patchedast.py0000664000175000017500000010543000000000000017702 0ustar00lieryanlieryanimport collections import numbers import re import warnings from rope.base import ast, codeanalyze, exceptions from rope.base.utils import pycompat try: basestring except NameError: basestring = (str, bytes) COMMA_IN_WITH_PATTERN = re.compile(r"\(.*?\)|(,)") def get_patched_ast(source, sorted_children=False): """Adds ``region`` and ``sorted_children`` fields to nodes Adds ``sorted_children`` field only if `sorted_children` is True. """ return patch_ast(ast.parse(source), source, sorted_children) def patch_ast(node, source, sorted_children=False): """Patches the given node After calling, each node in `node` will have a new field named `region` that is a tuple containing the start and end offsets of the code that generated it. If `sorted_children` is true, a `sorted_children` field will be created for each node, too. It is a list containing child nodes as well as whitespaces and comments that occur between them. """ if hasattr(node, "region"): return node walker = _PatchingASTWalker(source, children=sorted_children) ast.call_for_nodes(node, walker) return node def node_region(patched_ast_node): """Get the region of a patched ast node""" return patched_ast_node.region def write_ast(patched_ast_node): """Extract source form a patched AST node with `sorted_children` field If the node is patched with sorted_children turned off you can use `node_region` function for obtaining code using module source code. """ result = [] for child in patched_ast_node.sorted_children: if isinstance(child, ast.AST): result.append(write_ast(child)) else: result.append(child) return "".join(result) class MismatchedTokenError(exceptions.RopeError): pass class _PatchingASTWalker(object): def __init__(self, source, children=False): self.source = _Source(source) self.children = children self.lines = codeanalyze.SourceLinesAdapter(source) self.children_stack = [] Number = object() String = object() semicolon_or_as_in_except = object() exec_open_paren_or_space = object() 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) if method is not None: return method(node) # ???: Unknown node; what should we do here? warnings.warn( "Unknown node type <%s>; please report!" % node.__class__.__name__, RuntimeWarning, ) node.region = (self.source.offset, self.source.offset) if self.children: node.sorted_children = ast.get_children(node) def _handle(self, node, base_children, eat_parens=False, eat_spaces=False): if hasattr(node, "region"): # ???: The same node was seen twice; what should we do? warnings.warn( "Node <%s> has been already patched; please report!" % node.__class__.__name__, RuntimeWarning, ) return base_children = collections.deque(base_children) self.children_stack.append(base_children) children = collections.deque() formats = [] suspected_start = self.source.offset start = suspected_start first_token = True while base_children: child = base_children.popleft() if child is None: continue offset = self.source.offset if isinstance(child, ast.AST): ast.call_for_nodes(child, self) token_start = child.region[0] else: if child is self.String: region = self.source.consume_string( end=self._find_next_statement_start() ) 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() elif child == self.semicolon_or_as_in_except: # INFO: This has been added to handle deprecated # semicolon in except region = self.source.consume_except_as_or_semicolon() elif child == self.exec_open_paren_or_space: # These three cases handle the differences between # the deprecated exec statement and the exec # function. region = self.source.consume_exec_open_paren_or_space() elif child == self.exec_in_or_comma: region = self.source.consume_exec_in_or_comma() elif child == self.exec_close_paren_or_space: region = self.source.consume_exec_close_paren_or_space() elif child == self.with_or_comma_context_manager: region = self.source.consume_with_or_comma_context_manager() else: if hasattr(ast, "JoinedStr") and isinstance( node, (ast.JoinedStr, ast.FormattedValue) ): region = self.source.consume_joined_string(child) else: region = self.source.consume(child) child = self.source[region[0] : region[1]] token_start = region[0] if not first_token: formats.append(self.source[offset:token_start]) if self.children: children.append(self.source[offset:token_start]) else: first_token = False start = token_start if self.children: children.append(child) start = self._handle_parens(children, start, formats) if eat_parens: start = self._eat_surrounding_parens(children, suspected_start, start) if eat_spaces: if self.children: children.appendleft(self.source[0:start]) end_spaces = self.source[self.source.offset :] self.source.consume(end_spaces) if self.children: children.append(end_spaces) start = 0 if self.children: node.sorted_children = children node.region = (start, self.source.offset) self.children_stack.pop() def _handle_parens(self, children, start, formats): """Changes `children` and returns new start""" opens, closes = self._count_needed_parens(formats) old_end = self.source.offset new_end = None for i in range(closes): new_end = self.source.consume(")")[1] if new_end is not None: if self.children: children.append(self.source[old_end:new_end]) new_start = start for i in range(opens): new_start = self.source.rfind_token("(", 0, new_start) if new_start != start: if self.children: children.appendleft(self.source[new_start:start]) start = new_start return start def _eat_surrounding_parens(self, children, suspected_start, start): index = self.source.rfind_token("(", suspected_start, start) if index is not None: old_start = start old_offset = self.source.offset start = index if self.children: children.appendleft(self.source[start + 1 : old_start]) children.appendleft("(") token_start, token_end = self.source.consume(")") if self.children: children.append(self.source[old_offset:token_start]) children.append(")") return start def _count_needed_parens(self, children): start = 0 opens = 0 for child in children: if not isinstance(child, basestring): continue if child == "" or child[0] in "'\"": continue index = 0 while index < len(child): if child[index] == ")": if opens > 0: opens -= 1 else: start += 1 if child[index] == "(": opens += 1 if child[index] == "#": try: index = child.index("\n", index) except ValueError: break index += 1 return start, opens def _find_next_statement_start(self): for children in reversed(self.children_stack): for child in children: if isinstance(child, ast.stmt): return child.col_offset + self.lines.get_line_start(child.lineno) return len(self.source.source) _operators = { "And": "and", "Or": "or", "Add": "+", "Sub": "-", "Mult": "*", "Div": "/", "Mod": "%", "Pow": "**", "LShift": "<<", "RShift": ">>", "BitOr": "|", "BitAnd": "&", "BitXor": "^", "FloorDiv": "//", "Invert": "~", "Not": "not", "UAdd": "+", "USub": "-", "Eq": "==", "NotEq": "!=", "Lt": "<", "LtE": "<=", "Gt": ">", "GtE": ">=", "Is": "is", "IsNot": "is not", "In": "in", "NotIn": "not in", } def _get_op(self, node): return self._operators[node.__class__.__name__].split(" ") def _Attribute(self, node): self._handle(node, [node.value, ".", node.attr]) def _Assert(self, node): children = ["assert", node.test] if node.msg: children.append(",") children.append(node.msg) self._handle(node, children) def _Assign(self, node): children = self._child_nodes(node.targets, "=") children.append("=") children.append(node.value) self._handle(node, children) def _AugAssign(self, node): children = [node.target] children.extend(self._get_op(node.op)) children.extend(["=", node.value]) self._handle(node, children) def _AnnAssign(self, node): children = [node.target, ":", node.annotation] if node.value is not None: children.append("=") children.append(node.value) self._handle(node, children) def _Repr(self, node): self._handle(node, ["`", node.value, "`"]) def _BinOp(self, node): children = [node.left] + self._get_op(node.op) + [node.right] self._handle(node, children) def _BoolOp(self, node): self._handle(node, self._child_nodes(node.values, self._get_op(node.op)[0])) def _Break(self, node): self._handle(node, ["break"]) def _Call(self, node): def _arg_sort_key(node): if isinstance(node, ast.keyword): return (node.value.lineno, node.value.col_offset) return (node.lineno, node.col_offset) children = [node.func, "("] unstarred_args = [] starred_and_keywords = list(node.keywords) for i, arg in enumerate(node.args): if hasattr(ast, "Starred") and isinstance(arg, ast.Starred): starred_and_keywords.append(arg) else: unstarred_args.append(arg) if getattr(node, "starargs", None): starred_and_keywords.append(node.starargs) starred_and_keywords.sort(key=_arg_sort_key) children.extend(self._child_nodes(unstarred_args, ",")) # positional args come before keywords, *args comes after all # positional args, and **kwargs comes last if starred_and_keywords: if len(children) > 2: children.append(",") for i, arg in enumerate(starred_and_keywords): if arg == getattr(node, "starargs", None): children.append("*") children.append(arg) if i + 1 < len(starred_and_keywords): children.append(",") if getattr(node, "kwargs", None): if len(children) > 2: children.append(",") children.extend(["**", node.kwargs]) children.append(")") self._handle(node, children) def _ClassDef(self, node): children = [] if getattr(node, "decorator_list", None): for decorator in node.decorator_list: children.append("@") children.append(decorator) children.extend(["class", node.name]) if node.bases: children.append("(") children.extend(self._child_nodes(node.bases, ",")) children.append(")") children.append(":") children.extend(node.body) self._handle(node, children) def _Compare(self, node): children = [] children.append(node.left) for op, expr in zip(node.ops, node.comparators): children.extend(self._get_op(op)) children.append(expr) self._handle(node, children) def _Delete(self, node): self._handle(node, ["del"] + self._child_nodes(node.targets, ",")) def _Constant(self, node): if isinstance(node.value, basestring): self._handle(node, [self.String]) return if any(node.value is v for v in [True, False, None]): self._handle(node, [str(node.value)]) return if isinstance(node.value, numbers.Number): self._handle(node, [self.Number]) return if node.value is Ellipsis: self._handle(node, ["..."]) return assert False def _Num(self, node): self._handle(node, [self.Number]) def _Str(self, node): self._handle(node, [self.String]) def _Bytes(self, node): self._handle(node, [self.String]) def _JoinedStr(self, node): def start_quote_char(): possible_quotes = [ (self.source.source.find(q, start, end), q) for q in QUOTE_CHARS ] quote_pos, quote_char = min( (pos, q) for pos, q in possible_quotes if pos != -1 ) return self.source[start : quote_pos + len(quote_char)] def end_quote_char(): possible_quotes = [ (self.source.source.rfind(q, start, end), q) for q in reversed(QUOTE_CHARS) ] _, quote_pos, quote_char = max( (len(q), pos, q) for pos, q in possible_quotes if pos != -1 ) return self.source[end - len(quote_char) : end] QUOTE_CHARS = ['"""', "'''", '"', "'"] offset = self.source.offset start, end = self.source.consume_string( end=self._find_next_statement_start(), ) self.source.offset = offset children = [] children.append(start_quote_char()) for part in node.values: if isinstance(part, ast.FormattedValue): children.append(part) children.append(end_quote_char()) self._handle(node, children) def _FormattedValue(self, node): children = [] children.append("{") children.append(node.value) if node.format_spec: children.append(":") for val in node.format_spec.values: if isinstance(val, ast.FormattedValue): children.append(val.value) else: children.append(val.s) children.append("}") self._handle(node, children) def _Continue(self, node): self._handle(node, ["continue"]) def _Dict(self, node): children = [] children.append("{") if node.keys: for index, (key, value) in enumerate(zip(node.keys, node.values)): if key is None: # PEP-448 dict unpacking: {a: b, **unpack} children.extend(["**", value]) else: children.extend([key, ":", value]) if index < len(node.keys) - 1: children.append(",") children.append("}") self._handle(node, children) def _Ellipsis(self, node): self._handle(node, ["..."]) def _Expr(self, node): self._handle(node, [node.value]) def _NamedExpr(self, node): children = [node.target, ":=", node.value] self._handle(node, children) def _Exec(self, node): children = ["exec", self.exec_open_paren_or_space, node.body] if node.globals: children.extend([self.exec_in_or_comma, node.globals]) if node.locals: children.extend([",", node.locals]) children.append(self.exec_close_paren_or_space) self._handle(node, children) def _ExtSlice(self, node): children = [] for index, dim in enumerate(node.dims): if index > 0: children.append(",") children.append(dim) self._handle(node, children) 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: children.append("." * node.level) # see comment at rope.base.ast.walk children.extend([node.module or "", "import"]) children.extend(self._child_nodes(node.names, ",")) self._handle(node, children) def _alias(self, node): children = [node.name] if node.asname: children.extend(["as", node.asname]) self._handle(node, children) def _handle_function_def_node(self, node, is_async): children = [] try: decorators = getattr(node, "decorator_list") except AttributeError: decorators = getattr(node, "decorators", None) if decorators: for decorator in decorators: children.append("@") children.append(decorator) 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) defaults = [None] * (len(args) - len(node.defaults)) + list(node.defaults) for index, (arg, default) in enumerate(zip(args, defaults)): if index > 0: children.append(",") self._add_args_to_children(children, arg, default) if node.vararg is not None: if args: children.append(",") children.extend(["*", pycompat.get_ast_arg_arg(node.vararg)]) if node.kwarg is not None: if args or node.vararg is not None: children.append(",") children.extend(["**", pycompat.get_ast_arg_arg(node.kwarg)]) self._handle(node, children) def _add_args_to_children(self, children, arg, default): if isinstance(arg, (list, tuple)): self._add_tuple_parameter(children, arg) else: children.append(arg) if default is not None: children.append("=") children.append(default) def _add_tuple_parameter(self, children, arg): children.append("(") for index, token in enumerate(arg): if index > 0: children.append(",") if isinstance(token, (list, tuple)): self._add_tuple_parameter(children, token) else: children.append(token) children.append(")") def _GeneratorExp(self, node): children = [node.elt] children.extend(node.generators) self._handle(node, children, eat_parens=True) def _comprehension(self, node): children = ["for", node.target, "in", node.iter] if node.ifs: for if_ in node.ifs: children.append("if") children.append(if_) self._handle(node, children) def _Global(self, node): children = self._child_nodes(node.names, ",") children.insert(0, "global") self._handle(node, children) def _If(self, node): if self._is_elif(node): children = ["elif"] else: children = ["if"] children.extend([node.test, ":"]) children.extend(node.body) if node.orelse: if len(node.orelse) == 1 and self._is_elif(node.orelse[0]): pass else: children.extend(["else", ":"]) children.extend(node.orelse) self._handle(node, children) def _is_elif(self, node): if not isinstance(node, ast.If): return False offset = self.lines.get_line_start(node.lineno) + node.col_offset word = self.source[offset : offset + 4] # XXX: This is a bug; the offset does not point to the first alt_word = self.source[offset - 5 : offset - 1] return "elif" in (word, alt_word) def _IfExp(self, node): return self._handle(node, [node.body, "if", node.test, "else", node.orelse]) def _Import(self, node): children = ["import"] children.extend(self._child_nodes(node.names, ",")) self._handle(node, children) def _keyword(self, node): children = [] if node.arg is None: children.append(node.value) else: children.extend([node.arg, "=", node.value]) self._handle(node, children) def _Lambda(self, node): self._handle(node, ["lambda", node.args, ":", node.body]) def _List(self, node): self._handle(node, ["["] + self._child_nodes(node.elts, ",") + ["]"]) def _ListComp(self, node): children = ["[", node.elt] children.extend(node.generators) children.append("]") self._handle(node, children) def _Set(self, node): if node.elts: self._handle(node, ["{"] + self._child_nodes(node.elts, ",") + ["}"]) return # Python doesn't have empty set literals warnings.warn( "Tried to handle empty literal; please report!", RuntimeWarning ) self._handle(node, ["set(", ")"]) def _SetComp(self, node): children = ["{", node.elt] children.extend(node.generators) children.append("}") self._handle(node, children) def _DictComp(self, node): children = ["{"] children.extend([node.key, ":", node.value]) children.extend(node.generators) children.append("}") self._handle(node, children) def _Module(self, node): self._handle(node, list(node.body), eat_spaces=True) def _Name(self, node): self._handle(node, [node.id]) def _NameConstant(self, node): self._handle(node, [str(node.value)]) def _arg(self, node): self._handle(node, [node.arg]) def _Pass(self, node): self._handle(node, ["pass"]) def _Print(self, node): children = ["print"] if node.dest: children.extend([">>", node.dest]) if node.values: children.append(",") children.extend(self._child_nodes(node.values, ",")) if not node.nl: children.append(",") self._handle(node, children) def _Raise(self, node): def get_python3_raise_children(node): children = ["raise"] if node.exc: children.append(node.exc) if node.cause: children.append(node.cause) return children def get_python2_raise_children(node): children = ["raise"] if node.type: children.append(node.type) if node.inst: children.append(",") children.append(node.inst) if node.tback: children.append(",") children.append(node.tback) return children if pycompat.PY2: children = get_python2_raise_children(node) else: children = get_python3_raise_children(node) self._handle(node, children) def _Return(self, node): children = ["return"] if node.value: children.append(node.value) self._handle(node, children) def _Sliceobj(self, node): children = [] for index, slice in enumerate(node.nodes): if index > 0: children.append(":") if slice: children.append(slice) self._handle(node, children) def _Index(self, node): self._handle(node, [node.value]) def _Subscript(self, node): self._handle(node, [node.value, "[", node.slice, "]"]) def _Slice(self, node): children = [] if node.lower: children.append(node.lower) children.append(":") if node.upper: children.append(node.upper) if node.step: children.append(":") children.append(node.step) self._handle(node, children) def _TryFinally(self, node): # @todo fixme is_there_except_handler = False not_empty_body = True if len(node.finalbody) == 1: if pycompat.PY2: is_there_except_handler = isinstance(node.body[0], ast.TryExcept) not_empty_body = not bool(len(node.body)) elif pycompat.PY3: try: is_there_except_handler = isinstance( node.handlers[0], ast.ExceptHandler ) not_empty_body = True except IndexError: pass children = [] if not_empty_body or not is_there_except_handler: children.extend(["try", ":"]) children.extend(node.body) if pycompat.PY3: children.extend(node.handlers) children.extend(["finally", ":"]) children.extend(node.finalbody) self._handle(node, children) def _TryExcept(self, node): children = ["try", ":"] children.extend(node.body) children.extend(node.handlers) if node.orelse: children.extend(["else", ":"]) children.extend(node.orelse) self._handle(node, children) def _Try(self, node): if len(node.finalbody): self._TryFinally(node) else: self._TryExcept(node) def _ExceptHandler(self, node): self._excepthandler(node) def _excepthandler(self, node): # self._handle(node, [self.semicolon_or_as_in_except]) children = ["except"] if node.type: children.append(node.type) if node.name: children.append(self.semicolon_or_as_in_except) children.append(node.name) children.append(":") children.extend(node.body) self._handle(node, children) def _Tuple(self, node): if node.elts: self._handle(node, self._child_nodes(node.elts, ","), eat_parens=True) else: self._handle(node, [self.empty_tuple]) def _UnaryOp(self, node): children = self._get_op(node.op) 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: 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) if node.orelse: children.extend(["else", ":"]) children.extend(node.orelse) self._handle(node, children) def _handle_with_node(self, node, is_async): children = [] if is_async: children.extend(["async"]) for item in pycompat.get_ast_with_items(node): children.extend([self.with_or_comma_context_manager, item.context_expr]) if item.optional_vars: children.extend(["as", item.optional_vars]) if pycompat.PY2 and COMMA_IN_WITH_PATTERN.search(self.source.source): children.append(node.body[0]) else: children.append(":") children.extend(node.body) self._handle(node, children) def _With(self, node): self._handle_with_node(node, is_async=False) def _AsyncWith(self, node): self._handle_with_node(node, is_async=True) def _child_nodes(self, nodes, separator): children = [] for index, child in enumerate(nodes): children.append(child) if index < len(nodes) - 1: children.append(separator) return children def _Starred(self, node): self._handle(node, [node.value]) class _Source(object): def __init__(self, source): self.source = source self.offset = 0 def consume(self, token, skip_comment=True): try: while True: new_offset = self.source.index(token, self.offset) if self._good_token(token, new_offset) or not skip_comment: break else: self._skip_comment() except (ValueError, TypeError) as e: raise MismatchedTokenError( "Token <%s> at %s cannot be matched" % (token, self._get_location()) ) self.offset = new_offset + len(token) return (new_offset, self.offset) def consume_joined_string(self, token): new_offset = self.source.index(token, self.offset) self.offset = new_offset + len(token) return (new_offset, self.offset) def consume_string(self, end=None): if _Source._string_pattern is None: string_pattern = codeanalyze.get_string_pattern() formatted_string_pattern = codeanalyze.get_formatted_string_pattern() original = r"(?:%s)|(?:%s)" % (string_pattern, formatted_string_pattern) pattern = r"(%s)((\s|\\\n|#[^\n]*\n)*(%s))*" % (original, original) _Source._string_pattern = re.compile(pattern) repattern = _Source._string_pattern return self._consume_pattern(repattern, end) def consume_number(self): if _Source._number_pattern is None: _Source._number_pattern = re.compile(self._get_number_pattern()) 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"<>|!=") repattern = _Source._not_equals_pattern return self._consume_pattern(repattern) def consume_except_as_or_semicolon(self): repattern = re.compile(r"as|,") return self._consume_pattern(repattern) def consume_exec_open_paren_or_space(self): repattern = re.compile(r"\(|") return self._consume_pattern(repattern) def consume_exec_in_or_comma(self): repattern = re.compile(r"in|,") return self._consume_pattern(repattern) def consume_exec_close_paren_or_space(self): repattern = re.compile(r"\)|") return self._consume_pattern(repattern) def consume_with_or_comma_context_manager(self): repattern = re.compile(r"with|,") return self._consume_pattern(repattern) def _good_token(self, token, offset, start=None): """Checks whether consumed token is in comments""" if start is None: start = self.offset try: comment_index = self.source.rindex("#", start, offset) except ValueError: return True try: new_line_index = self.source.rindex("\n", start, offset) except ValueError: return False return comment_index < new_line_index def _skip_comment(self): self.offset = self.source.index("\n", self.offset + 1) def _get_location(self): lines = self.source[: self.offset].split("\n") return (len(lines), len(lines[-1])) def _consume_pattern(self, repattern, end=None): while True: if end is None: end = len(self.source) match = repattern.search(self.source, self.offset, end) if self._good_token(match.group(), match.start()): break else: self._skip_comment() self.offset = match.end() return match.start(), match.end() def till_token(self, token): new_offset = self.source.index(token, self.offset) return self[self.offset : new_offset] def rfind_token(self, token, start, end): index = start while True: try: index = self.source.rindex(token, start, end) if self._good_token(token, index, start=start): return index else: end = index except ValueError: return None def from_offset(self, offset): return self[offset : self.offset] def find_backwards(self, pattern, offset): return self.source.rindex(pattern, 0, offset) def __getitem__(self, index): return self.source[index] def __getslice__(self, i, j): return self.source[i:j] def _get_number_pattern(self): # HACK: It is merely an approaximation and does the job integer = r"\-?(0x[\da-fA-F]+|\d+)[lL]?" return r"(%s(\.\d*)?|(\.\d+))([eE][-+]?\d+)?[jJ]?" % integer _string_pattern = None _number_pattern = None _not_equals_pattern = None ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633802493.0 rope-0.22.0/rope/refactor/rename.py0000664000175000017500000002233500000000000017033 0ustar00lieryanlieryanimport warnings from rope.base import ( exceptions, pyobjects, pynames, taskhandle, evaluate, worder, codeanalyze, libutils, ) from rope.base.change import ChangeSet, ChangeContents, MoveResource from rope.refactor import occurrences class Rename(object): """A class for performing rename refactoring It can rename everything: classes, functions, modules, packages, methods, variables and keyword arguments. """ def __init__(self, project, resource, offset=None): """If `offset` is None, the `resource` itself will be renamed""" self.project = project self.resource = resource if offset is not None: self.old_name = worder.get_name_at(self.resource, offset) this_pymodule = self.project.get_pymodule(self.resource) self.old_instance, self.old_pyname = evaluate.eval_location2( this_pymodule, offset ) if self.old_pyname is None: raise exceptions.RefactoringError( "Rename refactoring should be performed" " on resolvable python identifiers." ) else: if not resource.is_folder() and resource.name == "__init__.py": resource = resource.parent dummy_pymodule = libutils.get_string_module(self.project, "") self.old_instance = None self.old_pyname = pynames.ImportedModule(dummy_pymodule, resource=resource) if resource.is_folder(): self.old_name = resource.name else: self.old_name = resource.name[:-3] def get_old_name(self): return self.old_name def get_changes( self, new_name, in_file=None, in_hierarchy=False, unsure=None, docs=False, resources=None, task_handle=taskhandle.NullTaskHandle(), ): """Get the changes needed for this refactoring Parameters: - `in_hierarchy`: when renaming a method this keyword forces to rename all matching methods in the hierarchy - `docs`: when `True` rename refactoring will rename occurrences in comments and strings where the name is visible. Setting it will make renames faster, too. - `unsure`: decides what to do about unsure occurrences. If `None`, they are ignored. Otherwise `unsure` is called with an instance of `occurrence.Occurrence` as parameter. If it returns `True`, the occurrence is considered to be a match. - `resources` can be a list of `rope.base.resources.File` to apply this refactoring on. If `None`, the restructuring will be applied to all python files. - `in_file`: this argument has been deprecated; use `resources` instead. """ if unsure in (True, False): warnings.warn( "unsure parameter should be a function that returns " "True or False", DeprecationWarning, stacklevel=2, ) def unsure_func(value=unsure): return value unsure = unsure_func if in_file is not None: warnings.warn( "`in_file` argument has been deprecated; use `resources` " "instead. ", DeprecationWarning, stacklevel=2, ) if in_file: resources = [self.resource] if _is_local(self.old_pyname): resources = [self.resource] if resources is None: resources = self.project.get_python_files() changes = ChangeSet("Renaming <%s> to <%s>" % (self.old_name, new_name)) finder = occurrences.create_finder( self.project, self.old_name, self.old_pyname, unsure=unsure, docs=docs, instance=self.old_instance, in_hierarchy=in_hierarchy and self.is_method(), ) job_set = task_handle.create_jobset("Collecting Changes", len(resources)) for file_ in resources: job_set.started_job(file_.path) new_content = rename_in_module(finder, new_name, resource=file_) if new_content is not None: changes.add_change(ChangeContents(file_, new_content)) job_set.finished_job() if self._is_renaming_a_module(): resource = self.old_pyname.get_object().get_resource() if self._is_allowed_to_move(resources, resource): self._rename_module(resource, new_name, changes) return changes def _is_allowed_to_move(self, resources, resource): if resource.is_folder(): try: return resource.get_child("__init__.py") in resources except exceptions.ResourceNotFoundError: return False else: return resource in resources def _is_renaming_a_module(self): if isinstance(self.old_pyname.get_object(), pyobjects.AbstractModule): return True return False def is_method(self): pyname = self.old_pyname return ( isinstance(pyname, pynames.DefinedName) and isinstance(pyname.get_object(), pyobjects.PyFunction) and isinstance(pyname.get_object().parent, pyobjects.PyClass) ) def _rename_module(self, resource, new_name, changes): if not resource.is_folder(): new_name = new_name + ".py" parent_path = resource.parent.path if parent_path == "": new_location = new_name else: new_location = parent_path + "/" + new_name changes.add_change(MoveResource(resource, new_location)) class ChangeOccurrences(object): """A class for changing the occurrences of a name in a scope This class replaces the occurrences of a name. Note that it only changes the scope containing the offset passed to the constructor. What's more it does not have any side-effects. That is for example changing occurrences of a module does not rename the module; it merely replaces the occurrences of that module in a scope with the given expression. This class is useful for performing many custom refactorings. """ def __init__(self, project, resource, offset): self.project = project self.resource = resource self.offset = offset self.old_name = worder.get_name_at(resource, offset) self.pymodule = project.get_pymodule(self.resource) self.old_pyname = evaluate.eval_location(self.pymodule, offset) def get_old_name(self): word_finder = worder.Worder(self.resource.read()) return word_finder.get_primary_at(self.offset) def _get_scope_offset(self): scope = self.pymodule.get_scope().get_inner_scope_for_offset(self.offset) return scope.get_region() def get_changes(self, new_name, only_calls=False, reads=True, writes=True): changes = ChangeSet( "Changing <%s> occurrences to <%s>" % (self.old_name, new_name) ) scope_start, scope_end = self._get_scope_offset() finder = occurrences.create_finder( self.project, self.old_name, self.old_pyname, imports=False, only_calls=only_calls, ) new_contents = rename_in_module( finder, new_name, pymodule=self.pymodule, replace_primary=True, region=(scope_start, scope_end), reads=reads, writes=writes, ) if new_contents is not None: changes.add_change(ChangeContents(self.resource, new_contents)) return changes def rename_in_module( occurrences_finder, new_name, resource=None, pymodule=None, replace_primary=False, region=None, reads=True, writes=True, ): """Returns the changed source or `None` if there is no changes""" if resource is not None: source_code = resource.read() else: source_code = pymodule.source_code change_collector = codeanalyze.ChangeCollector(source_code) for occurrence in occurrences_finder.find_occurrences(resource, pymodule): if replace_primary and occurrence.is_a_fixed_primary(): continue if replace_primary: start, end = occurrence.get_primary_range() else: start, end = occurrence.get_word_range() if (not reads and not occurrence.is_written()) or ( not writes and occurrence.is_written() ): continue if region is None or region[0] <= start < region[1]: change_collector.add_change(start, end, new_name) return change_collector.get_changed() def _is_local(pyname): module, lineno = pyname.get_definition_location() if lineno is None: return False scope = module.get_scope().get_inner_scope_for_line(lineno) if isinstance(pyname, pynames.DefinedName) and scope.get_kind() in ( "Function", "Class", ): scope = scope.parent return ( scope.get_kind() == "Function" and pyname in scope.get_names().values() and isinstance(pyname, pynames.AssignedName) ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/restructure.py0000664000175000017500000002636600000000000020163 0ustar00lieryanlieryanimport warnings from rope.base import change, taskhandle, builtins, ast, codeanalyze from rope.base import libutils from rope.refactor import patchedast, similarfinder, sourceutils from rope.refactor.importutils import module_imports class Restructure(object): """A class to perform python restructurings A restructuring transforms pieces of code matching `pattern` to `goal`. In the `pattern` wildcards can appear. Wildcards match some piece of code based on their kind and arguments that are passed to them through `args`. `args` is a dictionary of wildcard names to wildcard arguments. If the argument is a tuple, the first item of the tuple is considered to be the name of the wildcard to use; otherwise the "default" wildcard is used. For getting the list arguments a wildcard supports, see the pydoc of the wildcard. (see `rope.refactor.wildcard.DefaultWildcard` for the default wildcard.) `wildcards` is the list of wildcard types that can appear in `pattern`. See `rope.refactor.wildcards`. If a wildcard does not specify its kind (by using a tuple in args), the wildcard named "default" is used. So there should be a wildcard with "default" name in `wildcards`. `imports` is the list of imports that changed modules should import. Note that rope handles duplicate imports and does not add the import if it already appears. Example #1:: pattern ${pyobject}.get_attribute(${name}) goal ${pyobject}[${name}] args pyobject: instance=rope.base.pyobjects.PyObject Example #2:: pattern ${name} in ${pyobject}.get_attributes() goal ${name} in {pyobject} args pyobject: instance=rope.base.pyobjects.PyObject Example #3:: pattern ${pycore}.create_module(${project}.root, ${name}) goal generate.create_module(${project}, ${name}) imports from rope.contrib import generate args project: type=rope.base.project.Project Example #4:: pattern ${pow}(${param1}, ${param2}) goal ${param1} ** ${param2} args pow: name=mod.pow, exact Example #5:: pattern ${inst}.longtask(${p1}, ${p2}) goal ${inst}.subtask1(${p1}) ${inst}.subtask2(${p2}) args inst: type=mod.A,unsure """ def __init__(self, project, pattern, goal, args=None, imports=None, wildcards=None): """Construct a restructuring See class pydoc for more info about the arguments. """ self.project = project self.pattern = pattern self.goal = goal self.args = args if self.args is None: self.args = {} self.imports = imports if self.imports is None: self.imports = [] self.wildcards = wildcards self.template = similarfinder.CodeTemplate(self.goal) def get_changes( self, checks=None, imports=None, resources=None, task_handle=taskhandle.NullTaskHandle(), ): """Get the changes needed by this restructuring `resources` can be a list of `rope.base.resources.File` to apply the restructuring on. If `None`, the restructuring will be applied to all python files. `checks` argument has been deprecated. Use the `args` argument of the constructor. The usage of:: strchecks = {'obj1.type': 'mod.A', 'obj2': 'mod.B', 'obj3.object': 'mod.C'} checks = restructuring.make_checks(strchecks) can be replaced with:: args = {'obj1': 'type=mod.A', 'obj2': 'name=mod.B', 'obj3': 'object=mod.C'} where obj1, obj2 and obj3 are wildcard names that appear in restructuring pattern. """ if checks is not None: warnings.warn( "The use of checks parameter is deprecated; " "use the args parameter of the constructor instead.", DeprecationWarning, stacklevel=2, ) for name, value in checks.items(): self.args[name] = similarfinder._pydefined_to_str(value) if imports is not None: warnings.warn( "The use of imports parameter is deprecated; " "use imports parameter of the constructor, instead.", DeprecationWarning, stacklevel=2, ) self.imports = imports changes = change.ChangeSet( "Restructuring <%s> to <%s>" % (self.pattern, self.goal) ) if resources is not None: files = [ resource for resource in resources if libutils.is_python_file(self.project, resource) ] else: files = self.project.get_python_files() job_set = task_handle.create_jobset("Collecting Changes", len(files)) for resource in files: job_set.started_job(resource.path) pymodule = self.project.get_pymodule(resource) finder = similarfinder.SimilarFinder(pymodule, wildcards=self.wildcards) matches = list(finder.get_matches(self.pattern, self.args)) computer = self._compute_changes(matches, pymodule) result = computer.get_changed() if result is not None: imported_source = self._add_imports(resource, result, self.imports) changes.add_change(change.ChangeContents(resource, imported_source)) job_set.finished_job() return changes def _compute_changes(self, matches, pymodule): return _ChangeComputer( pymodule.source_code, pymodule.get_ast(), pymodule.lines, self.template, matches, ) def _add_imports(self, resource, source, imports): if not imports: return source import_infos = self._get_import_infos(resource, imports) pymodule = libutils.get_string_module(self.project, source, resource) imports = module_imports.ModuleImports(self.project, pymodule) for import_info in import_infos: imports.add_import(import_info) return imports.get_changed_source() def _get_import_infos(self, resource, imports): pymodule = libutils.get_string_module( self.project, "\n".join(imports), resource ) imports = module_imports.ModuleImports(self.project, pymodule) return [imports.import_info for imports in imports.imports] def make_checks(self, string_checks): """Convert str to str dicts to str to PyObject dicts This function is here to ease writing a UI. """ checks = {} for key, value in string_checks.items(): is_pyname = not key.endswith(".object") and not key.endswith(".type") evaluated = self._evaluate(value, is_pyname=is_pyname) if evaluated is not None: checks[key] = evaluated return checks def _evaluate(self, code, is_pyname=True): attributes = code.split(".") pyname = None if attributes[0] in ("__builtin__", "__builtins__"): class _BuiltinsStub(object): def get_attribute(self, name): return builtins.builtins[name] pyobject = _BuiltinsStub() else: pyobject = self.project.get_module(attributes[0]) for attribute in attributes[1:]: pyname = pyobject[attribute] if pyname is None: return None pyobject = pyname.get_object() return pyname if is_pyname else pyobject def replace(code, pattern, goal): """used by other refactorings""" finder = similarfinder.RawSimilarFinder(code) matches = list(finder.get_matches(pattern)) ast = patchedast.get_patched_ast(code) lines = codeanalyze.SourceLinesAdapter(code) template = similarfinder.CodeTemplate(goal) computer = _ChangeComputer(code, ast, lines, template, matches) result = computer.get_changed() if result is None: return code return result class _ChangeComputer(object): def __init__(self, code, ast, lines, goal, matches): self.source = code self.goal = goal self.matches = matches self.ast = ast self.lines = lines self.matched_asts = {} self._nearest_roots = {} if self._is_expression(): for match in self.matches: self.matched_asts[match.ast] = match def get_changed(self): if self._is_expression(): result = self._get_node_text(self.ast) if result == self.source: return None return result else: collector = codeanalyze.ChangeCollector(self.source) last_end = -1 for match in self.matches: start, end = match.get_region() if start < last_end: if not self._is_expression(): continue last_end = end replacement = self._get_matched_text(match) collector.add_change(start, end, replacement) return collector.get_changed() def _is_expression(self): return self.matches and isinstance( self.matches[0], similarfinder.ExpressionMatch ) def _get_matched_text(self, match): mapping = {} for name in self.goal.get_names(): node = match.get_ast(name) if node is None: raise similarfinder.BadNameInCheckError("Unknown name <%s>" % name) force = self._is_expression() and match.ast == node mapping[name] = self._get_node_text(node, force) unindented = self.goal.substitute(mapping) return self._auto_indent(match.get_region()[0], unindented) def _get_node_text(self, node, force=False): if not force and node in self.matched_asts: return self._get_matched_text(self.matched_asts[node]) start, end = patchedast.node_region(node) main_text = self.source[start:end] collector = codeanalyze.ChangeCollector(main_text) for node in self._get_nearest_roots(node): sub_start, sub_end = patchedast.node_region(node) collector.add_change( sub_start - start, sub_end - start, self._get_node_text(node) ) result = collector.get_changed() if result is None: return main_text return result def _auto_indent(self, offset, text): lineno = self.lines.get_line_number(offset) indents = sourceutils.get_indents(self.lines, lineno) result = [] for index, line in enumerate(text.splitlines(True)): if index != 0 and line.strip(): result.append(" " * indents) result.append(line) return "".join(result) def _get_nearest_roots(self, node): if node not in self._nearest_roots: result = [] for child in ast.get_child_nodes(node): if child in self.matched_asts: result.append(child) else: result.extend(self._get_nearest_roots(child)) self._nearest_roots[node] = result return self._nearest_roots[node] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/rope/refactor/similarfinder.py0000664000175000017500000003044300000000000020413 0ustar00lieryanlieryan"""This module can be used for finding similar code""" import re import rope.refactor.wildcards from rope.base import libutils from rope.base import codeanalyze, exceptions, ast, builtins from rope.refactor import patchedast, wildcards from rope.refactor.patchedast import MismatchedTokenError class BadNameInCheckError(exceptions.RefactoringError): pass class SimilarFinder(object): """`SimilarFinder` can be used to find similar pieces of code See the notes in the `rope.refactor.restructure` module for more info. """ def __init__(self, pymodule, wildcards=None): """Construct a SimilarFinder""" self.source = pymodule.source_code try: self.raw_finder = RawSimilarFinder( pymodule.source_code, pymodule.get_ast(), self._does_match ) except MismatchedTokenError: print("in file %s" % pymodule.resource.path) raise self.pymodule = pymodule if wildcards is None: self.wildcards = {} for wildcard in [ rope.refactor.wildcards.DefaultWildcard(pymodule.pycore.project) ]: self.wildcards[wildcard.get_name()] = wildcard else: self.wildcards = wildcards def get_matches(self, code, args={}, start=0, end=None): self.args = args if end is None: end = len(self.source) skip_region = None if "skip" in args.get("", {}): resource, region = args[""]["skip"] if resource == self.pymodule.get_resource(): skip_region = region return self.raw_finder.get_matches(code, start=start, end=end, skip=skip_region) def get_match_regions(self, *args, **kwds): for match in self.get_matches(*args, **kwds): yield match.get_region() def _does_match(self, node, name): arg = self.args.get(name, "") kind = "default" if isinstance(arg, (tuple, list)): kind = arg[0] arg = arg[1] suspect = wildcards.Suspect(self.pymodule, node, name) return self.wildcards[kind].matches(suspect, arg) class RawSimilarFinder(object): """A class for finding similar expressions and statements""" def __init__(self, source, node=None, does_match=None): if node is None: try: node = ast.parse(source) except SyntaxError: # needed to parse expression containing := operator node = ast.parse("(" + source + ")") if does_match is None: self.does_match = self._simple_does_match else: self.does_match = does_match self._init_using_ast(node, source) def _simple_does_match(self, node, name): return isinstance(node, (ast.expr, ast.Name)) def _init_using_ast(self, node, source): self.source = source self._matched_asts = {} if not hasattr(node, "region"): patchedast.patch_ast(node, source) self.ast = node def get_matches(self, code, start=0, end=None, skip=None): """Search for `code` in source and return a list of `Match`-es `code` can contain wildcards. ``${name}`` matches normal names and ``${?name} can match any expression. You can use `Match.get_ast()` for getting the node that has matched a given pattern. """ if end is None: end = len(self.source) for match in self._get_matched_asts(code): match_start, match_end = match.get_region() if start <= match_start and match_end <= end: if skip is not None and (skip[0] < match_end and skip[1] > match_start): continue yield match def _get_matched_asts(self, code): if code not in self._matched_asts: wanted = self._create_pattern(code) matches = _ASTMatcher(self.ast, wanted, self.does_match).find_matches() self._matched_asts[code] = matches return self._matched_asts[code] def _create_pattern(self, expression): expression = self._replace_wildcards(expression) node = ast.parse(expression) # Getting Module.Stmt.nodes nodes = node.body if len(nodes) == 1 and isinstance(nodes[0], ast.Expr): # Getting Discard.expr wanted = nodes[0].value else: wanted = nodes return wanted def _replace_wildcards(self, expression): ropevar = _RopeVariable() template = CodeTemplate(expression) mapping = {} for name in template.get_names(): mapping[name] = ropevar.get_var(name) return template.substitute(mapping) class _ASTMatcher(object): def __init__(self, body, pattern, does_match): """Searches the given pattern in the body AST. body is an AST node and pattern can be either an AST node or a list of ASTs nodes """ self.body = body self.pattern = pattern self.matches = None self.ropevar = _RopeVariable() self.matches_callback = does_match def find_matches(self): if self.matches is None: self.matches = [] ast.call_for_nodes(self.body, self._check_node, recursive=True) return self.matches def _check_node(self, node): if isinstance(self.pattern, list): self._check_statements(node) else: self._check_expression(node) def _check_expression(self, node): mapping = {} if self._match_nodes(self.pattern, node, mapping): self.matches.append(ExpressionMatch(node, mapping)) def _check_statements(self, node): for child in ast.get_children(node): if isinstance(child, (list, tuple)): self.__check_stmt_list(child) def __check_stmt_list(self, nodes): for index in range(len(nodes)): if len(nodes) - index >= len(self.pattern): current_stmts = nodes[index : index + len(self.pattern)] mapping = {} if self._match_stmts(current_stmts, mapping): self.matches.append(StatementMatch(current_stmts, mapping)) def _match_nodes(self, expected, node, mapping): if isinstance(expected, ast.Name): if self.ropevar.is_var(expected.id): return self._match_wildcard(expected, node, mapping) if not isinstance(expected, ast.AST): return expected == node if expected.__class__ != node.__class__: return False children1 = self._get_children(expected) children2 = self._get_children(node) if len(children1) != len(children2): return False for child1, child2 in zip(children1, children2): if isinstance(child1, ast.AST): if not self._match_nodes(child1, child2, mapping): return False elif isinstance(child1, (list, tuple)): if not isinstance(child2, (list, tuple)) or len(child1) != len(child2): return False for c1, c2 in zip(child1, child2): if not self._match_nodes(c1, c2, mapping): return False else: if type(child1) is not type(child2) or child1 != child2: return False return True def _get_children(self, node): """Return not `ast.expr_context` children of `node`""" children = ast.get_children(node) return [child for child in children if not isinstance(child, ast.expr_context)] def _match_stmts(self, current_stmts, mapping): if len(current_stmts) != len(self.pattern): return False for stmt, expected in zip(current_stmts, self.pattern): if not self._match_nodes(expected, stmt, mapping): return False return True def _match_wildcard(self, node1, node2, mapping): name = self.ropevar.get_base(node1.id) if name not in mapping: if self.matches_callback(node2, name): mapping[name] = node2 return True return False else: return self._match_nodes(mapping[name], node2, {}) class Match(object): def __init__(self, mapping): self.mapping = mapping def get_region(self): """Returns match region""" def get_ast(self, name): """Return the ast node that has matched rope variables""" return self.mapping.get(name, None) class ExpressionMatch(Match): def __init__(self, ast, mapping): super(ExpressionMatch, self).__init__(mapping) self.ast = ast def get_region(self): return self.ast.region class StatementMatch(Match): def __init__(self, ast_list, mapping): super(StatementMatch, self).__init__(mapping) self.ast_list = ast_list def get_region(self): return self.ast_list[0].region[0], self.ast_list[-1].region[1] class CodeTemplate(object): def __init__(self, template): self.template = template self._find_names() def _find_names(self): self.names = {} for match in CodeTemplate._get_pattern().finditer(self.template): if "name" in match.groupdict() and match.group("name") is not None: start, end = match.span("name") name = self.template[start + 2 : end - 1] if name not in self.names: self.names[name] = [] self.names[name].append((start, end)) def get_names(self): return self.names.keys() def substitute(self, mapping): collector = codeanalyze.ChangeCollector(self.template) for name, occurrences in self.names.items(): for region in occurrences: collector.add_change(region[0], region[1], mapping[name]) result = collector.get_changed() if result is None: return self.template return result _match_pattern = None @classmethod def _get_pattern(cls): if cls._match_pattern is None: pattern = ( codeanalyze.get_comment_pattern() + "|" + codeanalyze.get_string_pattern() + "|" + r"(?P\$\{[^\s\$\}]*\})" ) cls._match_pattern = re.compile(pattern) return cls._match_pattern class _RopeVariable(object): """Transform and identify rope inserted wildcards""" _normal_prefix = "__rope__variable_normal_" _any_prefix = "__rope__variable_any_" def get_var(self, name): if name.startswith("?"): return self._get_any(name) else: return self._get_normal(name) def is_var(self, name): return self._is_normal(name) or self._is_var(name) def get_base(self, name): if self._is_normal(name): return name[len(self._normal_prefix) :] if self._is_var(name): return "?" + name[len(self._any_prefix) :] def _get_normal(self, name): return self._normal_prefix + name def _get_any(self, name): return self._any_prefix + name[1:] def _is_normal(self, name): return name.startswith(self._normal_prefix) def _is_var(self, name): return name.startswith(self._any_prefix) def make_pattern(code, variables): variables = set(variables) collector = codeanalyze.ChangeCollector(code) def does_match(node, name): return isinstance(node, ast.Name) and node.id == name finder = RawSimilarFinder(code, does_match=does_match) for variable in variables: for match in finder.get_matches("${%s}" % variable): start, end = match.get_region() collector.add_change(start, end, "${%s}" % variable) result = collector.get_changed() return result if result is not None else code def _pydefined_to_str(pydefined): address = [] if isinstance(pydefined, (builtins.BuiltinClass, builtins.BuiltinFunction)): return "__builtins__." + pydefined.get_name() else: while pydefined.parent is not None: address.insert(0, pydefined.get_name()) pydefined = pydefined.parent module_name = libutils.modname(pydefined.resource) return ".".join(module_name.split(".") + address) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/sourceutils.py0000664000175000017500000000563100000000000020145 0ustar00lieryanlieryanfrom rope.base import codeanalyze def get_indents(lines, lineno): return codeanalyze.count_line_indents(lines.get_line(lineno)) def find_minimum_indents(source_code): result = 80 lines = source_code.split("\n") for line in lines: if line.strip() == "": continue result = min(result, codeanalyze.count_line_indents(line)) return result def indent_lines(source_code, amount): if amount == 0: return source_code lines = source_code.splitlines(True) result = [] for l in lines: if l.strip() == "": result.append("\n") continue if amount < 0: indents = codeanalyze.count_line_indents(l) result.append(max(0, indents + amount) * " " + l.lstrip()) else: result.append(" " * amount + l) return "".join(result) def fix_indentation(code, new_indents): """Change the indentation of `code` to `new_indents`""" min_indents = find_minimum_indents(code) return indent_lines(code, new_indents - min_indents) def add_methods(pymodule, class_scope, methods_sources): source_code = pymodule.source_code lines = pymodule.lines insertion_line = class_scope.get_end() if class_scope.get_scopes(): insertion_line = class_scope.get_scopes()[-1].get_end() insertion_offset = lines.get_line_end(insertion_line) methods = "\n\n" + "\n\n".join(methods_sources) indented_methods = fix_indentation( methods, get_indents(lines, class_scope.get_start()) + get_indent(pymodule.pycore.project), ) result = [] result.append(source_code[:insertion_offset]) result.append(indented_methods) result.append(source_code[insertion_offset:]) return "".join(result) def get_body(pyfunction): """Return unindented function body""" # FIXME scope = pyfunction.get_scope() pymodule = pyfunction.get_module() start, end = get_body_region(pyfunction) return fix_indentation(pymodule.source_code[start:end], 0) def get_body_region(defined): """Return the start and end offsets of function body""" scope = defined.get_scope() pymodule = defined.get_module() lines = pymodule.lines node = defined.get_ast() start_line = node.lineno if defined.get_doc() is None: start_line = node.body[0].lineno elif len(node.body) > 1: start_line = node.body[1].lineno start = lines.get_line_start(start_line) scope_start = pymodule.logical_lines.logical_line_in(scope.start) if scope_start[1] >= start_line: # a one-liner! # XXX: what if colon appears in a string start = pymodule.source_code.index(":", start) + 1 while pymodule.source_code[start].isspace(): start += 1 end = min(lines.get_line_end(scope.end) + 1, len(pymodule.source_code)) return start, end def get_indent(project): return project.prefs.get("indent_size", 4) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636636936.0 rope-0.22.0/rope/refactor/suites.py0000664000175000017500000001202100000000000017067 0ustar00lieryanlieryanfrom rope.base import ast from rope.base.utils import pycompat def find_visible(node, lines): """Return the line which is visible from all `lines`""" root = ast_suite_tree(node) return find_visible_for_suite(root, lines) def find_visible_for_suite(root, lines): if len(lines) == 1: return lines[0] line1 = lines[0] line2 = find_visible_for_suite(root, lines[1:]) suite1 = root.find_suite(line1) suite2 = root.find_suite(line2) def valid(suite): return suite is not None and not suite.ignored if valid(suite1) and not valid(suite2): return line1 if not valid(suite1) and valid(suite2): return line2 if not valid(suite1) and not valid(suite2): return None while suite1 != suite2 and suite1.parent != suite2.parent: if suite1._get_level() < suite2._get_level(): line2 = suite2.get_start() suite2 = suite2.parent elif suite1._get_level() > suite2._get_level(): line1 = suite1.get_start() suite1 = suite1.parent else: line1 = suite1.get_start() line2 = suite2.get_start() suite1 = suite1.parent suite2 = suite2.parent if suite1 == suite2: return min(line1, line2) return min(suite1.get_start(), suite2.get_start()) def ast_suite_tree(node): if hasattr(node, "lineno"): lineno = node.lineno else: lineno = 1 return Suite(node.body, lineno) class Suite(object): def __init__(self, child_nodes, lineno, parent=None, ignored=False): self.parent = parent self.lineno = lineno self.child_nodes = child_nodes self._children = None self.ignored = ignored def get_start(self): if self.parent is None: if self.child_nodes: return self.local_start() else: return 1 return self.lineno def get_children(self): if self._children is None: walker = _SuiteWalker(self) for child in self.child_nodes: ast.walk(child, walker) self._children = walker.suites return self._children def local_start(self): return self.child_nodes[0].lineno def local_end(self): end = self.child_nodes[-1].lineno if self.get_children(): end = max(end, self.get_children()[-1].local_end()) return end def find_suite(self, line): if line is None: return None for child in self.get_children(): if child.local_start() <= line <= child.local_end(): return child.find_suite(line) return self def _get_level(self): if self.parent is None: return 0 return self.parent._get_level() + 1 class _SuiteWalker(object): def __init__(self, suite): self.suite = suite self.suites = [] def _If(self, node): self._add_if_like_node(node) def _For(self, node): self._add_if_like_node(node) def _While(self, node): self._add_if_like_node(node) def _With(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite)) def _AsyncWith(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite)) def _TryFinally(self, node): proceed_to_except_handler = False if len(node.finalbody) == 1: if pycompat.PY2: proceed_to_except_handler = isinstance(node.body[0], ast.TryExcept) elif pycompat.PY3: try: proceed_to_except_handler = isinstance( node.handlers[0], ast.ExceptHandler ) except IndexError: pass if proceed_to_except_handler: self._TryExcept(node if pycompat.PY3 else node.body[0]) else: self.suites.append(Suite(node.body, node.lineno, self.suite)) self.suites.append(Suite(node.finalbody, node.lineno, self.suite)) def _Try(self, node): if len(node.finalbody) == 1: self._TryFinally(node) else: self._TryExcept(node) def _TryExcept(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite)) for handler in node.handlers: self.suites.append(Suite(handler.body, node.lineno, self.suite)) if node.orelse: self.suites.append(Suite(node.orelse, node.lineno, self.suite)) def _add_if_like_node(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite)) if node.orelse: self.suites.append(Suite(node.orelse, node.lineno, self.suite)) def _FunctionDef(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite, ignored=True)) def _AsyncFunctionDef(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite, ignored=True)) def _ClassDef(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite, ignored=True)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/topackage.py0000664000175000017500000000232200000000000017514 0ustar00lieryanlieryanimport rope.refactor.importutils from rope.base.change import ChangeSet, ChangeContents, MoveResource, CreateFolder class ModuleToPackage(object): def __init__(self, project, resource): self.project = project self.resource = resource def get_changes(self): changes = ChangeSet("Transform <%s> module to package" % self.resource.path) new_content = self._transform_relatives_to_absolute(self.resource) if new_content is not None: changes.add_change(ChangeContents(self.resource, new_content)) parent = self.resource.parent name = self.resource.name[:-3] changes.add_change(CreateFolder(parent, name)) parent_path = parent.path + "/" if not parent.path: parent_path = "" new_path = parent_path + "%s/__init__.py" % name if self.resource.project == self.project: changes.add_change(MoveResource(self.resource, new_path)) return changes def _transform_relatives_to_absolute(self, resource): pymodule = self.project.get_pymodule(resource) import_tools = rope.refactor.importutils.ImportTools(self.project) return import_tools.relatives_to_absolutes(pymodule) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633618607.0 rope-0.22.0/rope/refactor/usefunction.py0000664000175000017500000001457000000000000020130 0ustar00lieryanlieryanfrom rope.base import change, taskhandle, evaluate, exceptions, pyobjects, pynames, ast from rope.base import libutils from rope.refactor import restructure, sourceutils, similarfinder class UseFunction(object): """Try to use a function wherever possible""" def __init__(self, project, resource, offset): self.project = project self.offset = offset this_pymodule = project.get_pymodule(resource) pyname = evaluate.eval_location(this_pymodule, offset) if pyname is None: raise exceptions.RefactoringError("Unresolvable name selected") self.pyfunction = pyname.get_object() if not isinstance(self.pyfunction, pyobjects.PyFunction) or not isinstance( self.pyfunction.parent, pyobjects.PyModule ): raise exceptions.RefactoringError( "Use function works for global functions, only." ) self.resource = self.pyfunction.get_module().get_resource() self._check_returns() def _check_returns(self): node = self.pyfunction.get_ast() if _yield_count(node): raise exceptions.RefactoringError( "Use function should not be used on generatorS." ) returns = _return_count(node) if returns > 1: raise exceptions.RefactoringError( "usefunction: Function has more than one return statement." ) if returns == 1 and not _returns_last(node): raise exceptions.RefactoringError( "usefunction: return should be the last statement." ) def get_changes(self, resources=None, task_handle=taskhandle.NullTaskHandle()): if resources is None: resources = self.project.get_python_files() changes = change.ChangeSet("Using function <%s>" % self.pyfunction.get_name()) if self.resource in resources: newresources = list(resources) newresources.remove(self.resource) for c in self._restructure(newresources, task_handle).changes: changes.add_change(c) if self.resource in resources: for c in self._restructure( [self.resource], task_handle, others=False ).changes: changes.add_change(c) return changes def get_function_name(self): return self.pyfunction.get_name() def _restructure(self, resources, task_handle, others=True): pattern = self._make_pattern() goal = self._make_goal(import_=others) imports = None if others: imports = ["import %s" % self._module_name()] body_region = sourceutils.get_body_region(self.pyfunction) args_value = {"skip": (self.resource, body_region)} args = {"": args_value} restructuring = restructure.Restructure( self.project, pattern, goal, args=args, imports=imports ) return restructuring.get_changes(resources=resources, task_handle=task_handle) def _find_temps(self): return find_temps(self.project, self._get_body()) def _module_name(self): return libutils.modname(self.resource) def _make_pattern(self): params = self.pyfunction.get_param_names() body = self._get_body() body = restructure.replace(body, "return", "pass") wildcards = list(params) wildcards.extend(self._find_temps()) if self._does_return(): if self._is_expression(): replacement = "${%s}" % self._rope_returned else: replacement = "%s = ${%s}" % (self._rope_result, self._rope_returned) body = restructure.replace( body, "return ${%s}" % self._rope_returned, replacement ) wildcards.append(self._rope_result) return similarfinder.make_pattern(body, wildcards) def _get_body(self): return sourceutils.get_body(self.pyfunction) def _make_goal(self, import_=False): params = self.pyfunction.get_param_names() function_name = self.pyfunction.get_name() if import_: function_name = self._module_name() + "." + function_name goal = "%s(%s)" % (function_name, ", ".join(("${%s}" % p) for p in params)) if self._does_return() and not self._is_expression(): goal = "${%s} = %s" % (self._rope_result, goal) return goal def _does_return(self): body = self._get_body() removed_return = restructure.replace(body, "return ${result}", "") return removed_return != body def _is_expression(self): return len(self.pyfunction.get_ast().body) == 1 _rope_result = "_rope__result" _rope_returned = "_rope__returned" def find_temps(project, code): code = "def f():\n" + sourceutils.indent_lines(code, 4) pymodule = libutils.get_string_module(project, code) result = [] function_scope = pymodule.get_scope().get_scopes()[0] for name, pyname in function_scope.get_names().items(): if isinstance(pyname, pynames.AssignedName): result.append(name) return result def _returns_last(node): return node.body and isinstance(node.body[-1], ast.Return) def _namedexpr_last(node): if not hasattr(ast, "NamedExpr"): # python<3.8 return False return ( bool(node.body) and len(node.body) == 1 and isinstance(node.body[-1].value, ast.NamedExpr) ) def _yield_count(node): visitor = _ReturnOrYieldFinder() visitor.start_walking(node) return visitor.yields def _return_count(node): visitor = _ReturnOrYieldFinder() visitor.start_walking(node) return visitor.returns def _named_expr_count(node): visitor = _ReturnOrYieldFinder() visitor.start_walking(node) return visitor.named_expression class _ReturnOrYieldFinder(object): def __init__(self): self.returns = 0 self.named_expression = 0 self.yields = 0 def _Return(self, node): self.returns += 1 def _NamedExpr(self, node): self.named_expression += 1 def _Yield(self, node): self.yields += 1 def _FunctionDef(self, node): pass def _ClassDef(self, node): pass def start_walking(self, node): nodes = [node] if isinstance(node, ast.FunctionDef): nodes = ast.get_child_nodes(node) for child in nodes: ast.walk(child, self) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/rope/refactor/wildcards.py0000664000175000017500000001322400000000000017535 0ustar00lieryanlieryanfrom rope.base import ast, evaluate, builtins, pyobjects from rope.refactor import patchedast, occurrences class Wildcard(object): def get_name(self): """Return the name of this wildcard""" def matches(self, suspect, arg): """Return `True` if `suspect` matches this wildcard""" class Suspect(object): def __init__(self, pymodule, node, name): self.name = name self.pymodule = pymodule self.node = node class DefaultWildcard(object): """The default restructuring wildcard The argument passed to this wildcard is in the ``key1=value1,key2=value2,...`` format. Possible keys are: * name - for checking the reference * type - for checking the type * object - for checking the object * instance - for checking types but similar to builtin isinstance * exact - matching only occurrences with the same name as the wildcard * unsure - matching unsure occurrences """ def __init__(self, project): self.project = project def get_name(self): return "default" def matches(self, suspect, arg=""): args = parse_arg(arg) if not self._check_exact(args, suspect): return False if not self._check_object(args, suspect): return False return True def _check_object(self, args, suspect): kind = None expected = None unsure = args.get("unsure", False) for check in ["name", "object", "type", "instance"]: if check in args: kind = check expected = args[check] if expected is not None: checker = _CheckObject(self.project, expected, kind, unsure=unsure) return checker(suspect.pymodule, suspect.node) return True def _check_exact(self, args, suspect): node = suspect.node if args.get("exact"): if not isinstance(node, ast.Name) or not node.id == suspect.name: return False else: if not isinstance(node, ast.expr): return False return True def parse_arg(arg): if isinstance(arg, dict): return arg result = {} tokens = arg.split(",") for token in tokens: if "=" in token: parts = token.split("=", 1) result[parts[0].strip()] = parts[1].strip() else: result[token.strip()] = True return result class _CheckObject(object): def __init__(self, project, expected, kind="object", unsure=False): self.project = project self.kind = kind self.unsure = unsure self.expected = self._evaluate(expected) def __call__(self, pymodule, node): pyname = self._evaluate_node(pymodule, node) if pyname is None or self.expected is None: return self.unsure if self._unsure_pyname(pyname, unbound=self.kind == "name"): return True if self.kind == "name": return self._same_pyname(self.expected, pyname) else: pyobject = pyname.get_object() if self.kind == "object": objects = [pyobject] if self.kind == "type": objects = [pyobject.get_type()] if self.kind == "instance": objects = [pyobject] objects.extend(self._get_super_classes(pyobject)) objects.extend(self._get_super_classes(pyobject.get_type())) for pyobject in objects: if self._same_pyobject(self.expected.get_object(), pyobject): return True return False def _get_super_classes(self, pyobject): result = [] if isinstance(pyobject, pyobjects.AbstractClass): for superclass in pyobject.get_superclasses(): result.append(superclass) result.extend(self._get_super_classes(superclass)) return result def _same_pyobject(self, expected, pyobject): return expected == pyobject def _same_pyname(self, expected, pyname): return occurrences.same_pyname(expected, pyname) def _unsure_pyname(self, pyname, unbound=True): return self.unsure and occurrences.unsure_pyname(pyname, unbound) def _split_name(self, name): parts = name.split(".") expression, kind = parts[0], parts[-1] if len(parts) == 1: kind = "name" return expression, kind def _evaluate_node(self, pymodule, node): scope = pymodule.get_scope().get_inner_scope_for_line(node.lineno) expression = node if isinstance(expression, ast.Name) and isinstance(expression.ctx, ast.Store): start, end = patchedast.node_region(expression) text = pymodule.source_code[start:end] return evaluate.eval_str(scope, text) else: return evaluate.eval_node(scope, expression) def _evaluate(self, code): attributes = code.split(".") pyname = None if attributes[0] in ("__builtin__", "__builtins__"): class _BuiltinsStub(object): def get_attribute(self, name): return builtins.builtins[name] def __getitem__(self, name): return builtins.builtins[name] def __contains__(self, name): return name in builtins.builtins pyobject = _BuiltinsStub() else: pyobject = self.project.get_module(attributes[0]) for attribute in attributes[1:]: pyname = pyobject[attribute] if pyname is None: return None pyobject = pyname.get_object() return pyname ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3513896 rope-0.22.0/rope.egg-info/0000775000175000017500000000000000000000000015072 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637593692.0 rope-0.22.0/rope.egg-info/PKG-INFO0000664000175000017500000000353100000000000016171 0ustar00lieryanlieryanMetadata-Version: 2.1 Name: rope Version: 0.22.0 Summary: a python refactoring library... Home-page: https://github.com/python-rope/rope Author: Ali Gholami Rudi Author-email: aligrudi@users.sourceforge.net License: LGPL-3.0-or-later Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent Classifier: Environment :: X11 Applications Classifier: Environment :: Win32 (MS Windows) Classifier: Environment :: MacOS X Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637593692.0 rope-0.22.0/rope.egg-info/SOURCES.txt0000664000175000017500000001006600000000000016761 0ustar00lieryanlieryanCHANGELOG.md COPYING MANIFEST.in README.rst pyproject.toml setup.py docs/contributing.rst docs/library.rst docs/overview.rst docs/release-process.rst docs/rope.rst docs/dev/issues.rst rope/__init__.py rope.egg-info/PKG-INFO rope.egg-info/SOURCES.txt 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 rope/base/astutils.py rope/base/builtins.py rope/base/change.py rope/base/codeanalyze.py rope/base/default_config.py rope/base/evaluate.py rope/base/exceptions.py rope/base/fscommands.py rope/base/history.py rope/base/libutils.py rope/base/prefs.py rope/base/project.py rope/base/pycore.py rope/base/pynames.py rope/base/pynamesdef.py rope/base/pyobjects.py rope/base/pyobjectsdef.py rope/base/pyscopes.py rope/base/resourceobserver.py rope/base/resources.py rope/base/simplify.py rope/base/stdmods.py rope/base/taskhandle.py rope/base/worder.py rope/base/oi/__init__.py rope/base/oi/doa.py rope/base/oi/memorydb.py rope/base/oi/objectdb.py rope/base/oi/objectinfo.py rope/base/oi/runmod.py rope/base/oi/soa.py rope/base/oi/soi.py rope/base/oi/transform.py rope/base/oi/type_hinting/__init__.py rope/base/oi/type_hinting/evaluate.py rope/base/oi/type_hinting/factory.py rope/base/oi/type_hinting/interfaces.py rope/base/oi/type_hinting/utils.py rope/base/oi/type_hinting/providers/__init__.py rope/base/oi/type_hinting/providers/composite.py rope/base/oi/type_hinting/providers/docstrings.py rope/base/oi/type_hinting/providers/inheritance.py rope/base/oi/type_hinting/providers/interfaces.py rope/base/oi/type_hinting/providers/numpydocstrings.py rope/base/oi/type_hinting/providers/pep0484_type_comments.py rope/base/oi/type_hinting/resolvers/__init__.py rope/base/oi/type_hinting/resolvers/composite.py rope/base/oi/type_hinting/resolvers/interfaces.py rope/base/oi/type_hinting/resolvers/types.py rope/base/utils/__init__.py rope/base/utils/datastructures.py rope/base/utils/pycompat.py rope/contrib/__init__.py rope/contrib/autoimport.py rope/contrib/changestack.py rope/contrib/codeassist.py rope/contrib/finderrors.py rope/contrib/findit.py rope/contrib/fixmodnames.py rope/contrib/fixsyntax.py rope/contrib/generate.py rope/refactor/__init__.py rope/refactor/change_signature.py rope/refactor/encapsulate_field.py rope/refactor/extract.py rope/refactor/functionutils.py rope/refactor/inline.py rope/refactor/introduce_factory.py rope/refactor/introduce_parameter.py rope/refactor/localtofield.py rope/refactor/method_object.py rope/refactor/move.py rope/refactor/multiproject.py rope/refactor/occurrences.py rope/refactor/patchedast.py rope/refactor/rename.py rope/refactor/restructure.py rope/refactor/similarfinder.py rope/refactor/sourceutils.py rope/refactor/suites.py rope/refactor/topackage.py rope/refactor/usefunction.py rope/refactor/wildcards.py rope/refactor/importutils/__init__.py rope/refactor/importutils/actions.py rope/refactor/importutils/importinfo.py rope/refactor/importutils/module_imports.py ropetest/__init__.py ropetest/advanced_oi_test.py ropetest/builtinstest.py ropetest/codeanalyzetest.py ropetest/doatest.py ropetest/historytest.py ropetest/objectdbtest.py ropetest/objectinfertest.py ropetest/projecttest.py ropetest/pycoretest.py ropetest/pyscopestest.py ropetest/runmodtest.py ropetest/simplifytest.py ropetest/testutils.py ropetest/type_hinting_test.py ropetest/contrib/__init__.py ropetest/contrib/autoimporttest.py ropetest/contrib/changestacktest.py ropetest/contrib/codeassisttest.py ropetest/contrib/finderrorstest.py ropetest/contrib/findittest.py ropetest/contrib/fixmodnamestest.py ropetest/contrib/generatetest.py ropetest/refactor/__init__.py ropetest/refactor/change_signature_test.py ropetest/refactor/extracttest.py ropetest/refactor/importutilstest.py ropetest/refactor/inlinetest.py ropetest/refactor/movetest.py ropetest/refactor/multiprojecttest.py ropetest/refactor/patchedasttest.py ropetest/refactor/renametest.py ropetest/refactor/restructuretest.py ropetest/refactor/similarfindertest.py ropetest/refactor/suitestest.py ropetest/refactor/usefunctiontest.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637593692.0 rope-0.22.0/rope.egg-info/dependency_links.txt0000664000175000017500000000000100000000000021140 0ustar00lieryanlieryan ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637593692.0 rope-0.22.0/rope.egg-info/requires.txt0000664000175000017500000000004300000000000017467 0ustar00lieryanlieryan [dev] build pytest pytest-timeout ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637593692.0 rope-0.22.0/rope.egg-info/top_level.txt0000664000175000017500000000000500000000000017617 0ustar00lieryanlieryanrope ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3713906 rope-0.22.0/ropetest/0000775000175000017500000000000000000000000014300 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/ropetest/__init__.py0000664000175000017500000000272500000000000016417 0ustar00lieryanlieryanimport sys try: import unittest2 as unittest except ImportError: import unittest import ropetest.projecttest import ropetest.codeanalyzetest import ropetest.doatest import ropetest.type_hinting_test import ropetest.pycoretest import ropetest.pyscopestest import ropetest.objectinfertest import ropetest.objectdbtest import ropetest.advanced_oi_test import ropetest.runmodtest import ropetest.builtinstest import ropetest.historytest import ropetest.simplifytest import ropetest.contrib import ropetest.refactor def suite(): result = unittest.TestSuite() result.addTests(ropetest.projecttest.suite()) result.addTests(ropetest.codeanalyzetest.suite()) result.addTests(ropetest.doatest.suite()) result.addTests(ropetest.type_hinting_test.suite()) result.addTests(ropetest.pycoretest.suite()) result.addTests(ropetest.pyscopestest.suite()) result.addTests(ropetest.objectinfertest.suite()) result.addTests(ropetest.objectdbtest.suite()) result.addTests(ropetest.advanced_oi_test.suite()) result.addTests(ropetest.runmodtest.suite()) result.addTests(ropetest.builtinstest.suite()) result.addTests(ropetest.historytest.suite()) result.addTests(ropetest.simplifytest.suite()) result.addTests(ropetest.refactor.suite()) result.addTests(ropetest.contrib.suite()) return result if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) sys.exit(not result.wasSuccessful()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/advanced_oi_test.py0000664000175000017500000011277700000000000020164 0ustar00lieryanlieryanfrom textwrap import dedent from rope.base.builtins import Str try: import unittest2 as unittest except ImportError: import unittest import rope.base.libutils import rope.base.oi from rope.base.utils import pycompat from ropetest import testutils class DynamicOITest(unittest.TestCase): def setUp(self): super(DynamicOITest, self).setUp() self.project = testutils.sample_project(validate_objectdb=True) self.pycore = self.project.pycore def tearDown(self): testutils.remove_project(self.project) super(DynamicOITest, self).tearDown() def test_simple_dti(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def a_func(arg): return eval("arg") a_var = a_func(a_func) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) self.assertEqual(pymod["a_func"].get_object(), pymod["a_var"].get_object()) def test_module_dti(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") code = dedent("""\ import mod1 def a_func(arg): return eval("arg") a_var = a_func(mod1) """) mod2.write(code) self.pycore.run_module(mod2).wait_process() pymod2 = self.project.get_pymodule(mod2) self.assertEqual(self.project.get_pymodule(mod1), pymod2["a_var"].get_object()) def test_class_from_another_module_dti(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") code1 = dedent("""\ class AClass(object): pass """) code2 = dedent("""\ from mod1 import AClass def a_func(arg): return eval("arg") a_var = a_func(AClass) """) mod1.write(code1) mod2.write(code2) self.pycore.run_module(mod2).wait_process() # pymod1 = self.project.get_pymodule(mod1) pymod2 = self.project.get_pymodule(mod2) self.assertEqual(pymod2["AClass"].get_object(), pymod2["a_var"].get_object()) def test_class_dti(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class AClass(object): pass def a_func(arg): return eval("arg") a_var = a_func(AClass) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) self.assertEqual(pymod["AClass"].get_object(), pymod["a_var"].get_object()) def test_instance_dti(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class AClass(object): pass def a_func(arg): return eval("arg()") a_var = a_func(AClass) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) self.assertEqual( pymod["AClass"].get_object(), pymod["a_var"].get_object().get_type() ) def test_method_dti(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class AClass(object): def a_method(self, arg): return eval("arg()") an_instance = AClass() a_var = an_instance.a_method(AClass) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) self.assertEqual( pymod["AClass"].get_object(), pymod["a_var"].get_object().get_type() ) def test_function_argument_dti(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def a_func(arg): pass a_func(a_func) """) mod.write(code) self.pycore.run_module(mod).wait_process() pyscope = self.project.get_pymodule(mod).get_scope() self.assertEqual( pyscope["a_func"].get_object(), pyscope.get_scopes()[0]["arg"].get_object() ) def test_classes_with_the_same_name(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def a_func(arg): class AClass(object): pass return eval("arg") class AClass(object): pass a_var = a_func(AClass) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) self.assertEqual(pymod["AClass"].get_object(), pymod["a_var"].get_object()) def test_nested_classes(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def a_func(): class AClass(object): pass return AClass def another_func(arg): return eval("arg") a_var = another_func(a_func()) """) mod.write(code) self.pycore.run_module(mod).wait_process() pyscope = self.project.get_pymodule(mod).get_scope() self.assertEqual( pyscope.get_scopes()[0]["AClass"].get_object(), pyscope["a_var"].get_object(), ) def test_function_argument_dti2(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def a_func(arg, a_builtin_type): pass a_func(a_func, []) """) mod.write(code) self.pycore.run_module(mod).wait_process() pyscope = self.project.get_pymodule(mod).get_scope() self.assertEqual( pyscope["a_func"].get_object(), pyscope.get_scopes()[0]["arg"].get_object() ) def test_dti_and_concluded_data_invalidation(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def a_func(arg): return eval("arg") a_var = a_func(a_func) """) mod.write(code) pymod = self.project.get_pymodule(mod) pymod["a_var"].get_object() self.pycore.run_module(mod).wait_process() self.assertEqual(pymod["a_func"].get_object(), pymod["a_var"].get_object()) def test_list_objects_and_dynamicoi(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): pass def a_func(arg): return eval("arg") a_var = a_func([C()])[0] """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_for_loops_and_dynamicoi(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): pass def a_func(arg): return eval("arg") for c in a_func([C()]): a_var = c """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_dict_objects_and_dynamicoi(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): pass def a_func(arg): return eval("arg") a_var = a_func({1: C()})[1] """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_dict_keys_and_dynamicoi(self): mod = testutils.create_module(self.project, "mod") if pycompat.PY3: code = dedent("""\ class C(object): pass def a_func(arg): return eval("arg") a_var = list(a_func({C(): 1}))[0] """) else: code = dedent("""\ class C(object): pass def a_func(arg): return eval("arg") a_var = a_func({C(): 1}).keys()[0] """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_dict_keys_and_dynamicoi2(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C1(object): pass class C2(object): pass def a_func(arg): return eval("arg") a, b = a_func((C1(), C2())) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_strs_and_dynamicoi(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def a_func(arg): return eval("arg") a_var = a_func("hey") """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) a_var = pymod["a_var"].get_object() self.assertTrue(isinstance(a_var.get_type(), rope.base.builtins.Str)) def test_textual_transformations(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): pass def f(): pass a_var = C() a_list = [C()] a_str = "hey" a_file = open("file.txt") """) mod.write(code) to_pyobject = rope.base.oi.transform.TextualToPyObject(self.project) to_textual = rope.base.oi.transform.PyObjectToTextual(self.project) pymod = self.project.get_pymodule(mod) def complex_to_textual(pyobject): return to_textual.transform( to_pyobject.transform(to_textual.transform(pyobject)) ) test_variables = [ ("C", ("defined", "mod.py", "C")), ("f", ("defined", "mod.py", "f")), ("a_var", ("instance", ("defined", "mod.py", "C"))), ("a_list", ("builtin", "list", ("instance", ("defined", "mod.py", "C")))), ("a_str", ("builtin", "str")), ("a_file", ("builtin", "file")), ] test_cases = [(pymod[v].get_object(), r) for v, r in test_variables] test_cases += [ (pymod, ("defined", "mod.py")), ( rope.base.builtins.builtins["enumerate"].get_object(), ("builtin", "function", "enumerate"), ), ] for var, result in test_cases: self.assertEqual(to_textual.transform(var), result) self.assertEqual(complex_to_textual(var), result) def test_arguments_with_keywords(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C1(object): pass class C2(object): pass def a_func(arg): return eval("arg") a = a_func(arg=C1()) b = a_func(arg=C2()) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_a_function_with_different_returns(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C1(object): pass class C2(object): pass def a_func(arg): return eval("arg") a = a_func(C1()) b = a_func(C2()) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_a_function_with_different_returns2(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C1(object): pass class C2(object): pass def a_func(p): if p == C1: return C1() else: return C2() a = a_func(C1) b = a_func(C2) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_ignoring_star_args(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C1(object): pass class C2(object): pass def a_func(p, *args): if p == C1: return C1() else: return C2() a = a_func(C1, 1) b = a_func(C2, 2) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_ignoring_double_star_args(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C1(object): pass class C2(object): pass def a_func(p, *kwds, **args): if p == C1: return C1() else: return C2() a = a_func(C1, kwd=1) b = a_func(C2, kwd=2) """) mod.write(code) self.pycore.run_module(mod).wait_process() pymod = self.project.get_pymodule(mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_invalidating_data_after_changing(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def a_func(arg): return eval("arg") a_var = a_func(a_func) """) mod.write(code) self.pycore.run_module(mod).wait_process() mod.write(code.replace("a_func", "newfunc")) mod.write(code) pymod = self.project.get_pymodule(mod) self.assertNotEqual(pymod["a_func"].get_object(), pymod["a_var"].get_object()) def test_invalidating_data_after_moving(self): mod2 = testutils.create_module(self.project, "mod2") mod2.write("class C(object):\n pass\n") mod = testutils.create_module(self.project, "mod") code = dedent("""\ import mod2 def a_func(arg): return eval(arg) a_var = a_func("mod2.C") """) mod.write(code) self.pycore.run_module(mod).wait_process() mod.move("newmod.py") pymod = self.project.get_module("newmod") pymod2 = self.project.get_pymodule(mod2) self.assertEqual(pymod2["C"].get_object(), pymod["a_var"].get_object()) class NewStaticOITest(unittest.TestCase): def setUp(self): super(NewStaticOITest, self).setUp() self.project = testutils.sample_project(validate_objectdb=True) self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(NewStaticOITest, self).tearDown() def test_static_oi_for_simple_function_calls(self): code = dedent("""\ class C(object): pass def f(p): pass f(C()) """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() f_scope = pymod["f"].get_object().get_scope() p_type = f_scope["p"].get_object().get_type() self.assertEqual(c_class, p_type) def test_static_oi_not_failing_when_callin_callables(self): code = dedent("""\ class C(object): pass C() """) self.mod.write(code) self.pycore.analyze_module(self.mod) def test_static_oi_for_nested_calls(self): code = dedent("""\ class C(object): pass def f(p): pass def g(p): return p f(g(C())) """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() f_scope = pymod["f"].get_object().get_scope() p_type = f_scope["p"].get_object().get_type() self.assertEqual(c_class, p_type) def test_static_oi_class_methods(self): code = dedent("""\ class C(object): def f(self, p): pass C().f(C())""") self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() f_scope = c_class["f"].get_object().get_scope() p_type = f_scope["p"].get_object().get_type() self.assertEqual(c_class, p_type) def test_static_oi_preventing_soi_maximum_recursion_exceptions(self): code = dedent("""\ item = {} for item in item.keys(): pass """) self.mod.write(code) try: self.pycore.analyze_module(self.mod) except RuntimeError as e: self.fail(str(e)) def test_static_oi_for_infer_return_typs_from_funcs_based_on_params(self): code = dedent("""\ class C(object): pass def func(p): return p a_var = func(C()) """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_a_function_with_different_returns(self): code = dedent("""\ class C1(object): pass class C2(object): pass def a_func(arg): return arg a = a_func(C1()) b = a_func(C2()) """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_not_reporting_out_of_date_information(self): code = dedent("""\ class C1(object): pass def f(arg): return C1() a_var = f() """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.mod.write(code.replace("C1", "C2")) pymod = self.project.get_pymodule(self.mod) c2_class = pymod["C2"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c2_class, a_var.get_type()) def test_invalidating_concluded_data_in_a_function(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write( dedent("""\ def func(arg): temp = arg return temp """) ) mod2.write( dedent("""\ import mod1 class C1(object): pass class C2(object): pass a_var = mod1.func(C1()) """) ) pymod2 = self.project.get_pymodule(mod2) c1_class = pymod2["C1"].get_object() a_var = pymod2["a_var"].get_object() self.assertEqual(c1_class, a_var.get_type()) mod2.write(mod2.read()[: mod2.read().rfind("C1()")] + "C2())\n") pymod2 = self.project.get_pymodule(mod2) c2_class = pymod2["C2"].get_object() a_var = pymod2["a_var"].get_object() self.assertEqual(c2_class, a_var.get_type()) def test_handling_generator_functions_for_strs(self): self.mod.write( dedent("""\ class C(object): pass def f(p): yield p() for c in f(C): a_var = c """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) # TODO: Returning a generator for functions that yield unknowns @unittest.skip("Returning a generator that yields unknowns") def xxx_test_handl_generator_functions_when_unknown_type_is_yielded(self): self.mod.write( dedent("""\ class C(object): pass def f(): yield eval("C()") a_var = f() """) ) pymod = self.project.get_pymodule(self.mod) a_var = pymod["a_var"].get_object() self.assertTrue(isinstance(a_var.get_type(), rope.base.builtins.Generator)) def test_static_oi_for_lists_depending_on_append_function(self): code = dedent("""\ class C(object): pass l = list() l.append(C()) a_var = l.pop() """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_static_oi_for_lists_per_object_for_get_item(self): code = dedent("""\ class C(object): pass l = list() l.append(C()) a_var = l[0] """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_static_oi_for_lists_per_object_for_fields(self): code = dedent("""\ class C(object): pass class A(object): def __init__(self): self.l = [] def set(self): self.l.append(C()) a = A() a.set() a_var = a.l[0] """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_static_oi_for_lists_per_object_for_set_item(self): code = dedent("""\ class C(object): pass l = [None] l[0] = C() a_var = l[0] """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_static_oi_for_lists_per_object_for_extending_lists(self): code = dedent("""\ class C(object): pass l = [] l.append(C()) l2 = [] l2.extend(l) a_var = l2[0] """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_static_oi_for_lists_per_object_for_iters(self): code = dedent("""\ class C(object): pass l = [] l.append(C()) for c in l: a_var = c """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_static_oi_for_dicts_depending_on_append_function(self): code = dedent("""\ class C1(object): pass class C2(object): pass d = {} d[C1()] = C2() a, b = d.popitem() """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_static_oi_for_dicts_depending_on_for_loops(self): code = dedent("""\ class C1(object): pass class C2(object): pass d = {} d[C1()] = C2() for k, v in d.items(): a = k b = v """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_static_oi_for_dicts_depending_on_update(self): code = dedent("""\ class C1(object): pass class C2(object): pass d = {} d[C1()] = C2() d2 = {} d2.update(d) a, b = d2.popitem() """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_static_oi_for_dicts_depending_on_update_on_seqs(self): code = dedent("""\ class C1(object): pass class C2(object): pass d = {} d.update([(C1(), C2())]) a, b = d.popitem() """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_static_oi_for_sets_per_object_for_set_item(self): code = dedent("""\ class C(object): pass s = set() s.add(C()) a_var = s.pop() """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_properties_and_calling_get_property(self): code = dedent("""\ class C1(object): pass class C2(object): c1 = C1() def get_c1(self): return self.c1 p = property(get_c1) c2 = C2() a_var = c2.p """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c1_class, a_var.get_type()) def test_soi_on_constructors(self): code = dedent("""\ class C1(object): pass class C2(object): def __init__(self, arg): self.attr = arg c2 = C2(C1()) a_var = c2.attr""") self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c1_class, a_var.get_type()) def test_soi_on_literal_assignment(self): code = 'a_var = ""' self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) a_var = pymod["a_var"].get_object() self.assertEqual(Str, type(a_var.get_type())) @testutils.only_for_versions_higher("3.6") def test_soi_on_typed_assignment(self): code = "a_var: str" self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) a_var = pymod["a_var"].get_object() self.assertEqual(Str, type(a_var.get_type())) def test_not_saving_unknown_function_returns(self): mod2 = testutils.create_module(self.project, "mod2") self.mod.write( dedent("""\ class C(object): pass l = [] l.append(C()) """) ) mod2.write( dedent("""\ import mod def f(): return mod.l.pop() a_var = f() """) ) pymod = self.project.get_pymodule(self.mod) pymod2 = self.project.get_pymodule(mod2) c_class = pymod["C"].get_object() a_var = pymod2["a_var"] self.pycore.analyze_module(mod2) self.assertNotEqual(c_class, a_var.get_object().get_type()) self.pycore.analyze_module(self.mod) self.assertEqual(c_class, a_var.get_object().get_type()) def test_using_the_best_callinfo(self): code = dedent("""\ class C1(object): pass def f(arg1, arg2, arg3): pass f("", None, C1()) f("", C1(), None) """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() f_scope = pymod["f"].get_object().get_scope() arg2 = f_scope["arg2"].get_object() self.assertEqual(c1_class, arg2.get_type()) def test_call_function_and_parameters(self): code = dedent("""\ class A(object): def __call__(self, p): pass A()("") """) self.mod.write(code) self.pycore.analyze_module(self.mod) scope = self.project.get_pymodule(self.mod).get_scope() p_object = scope.get_scopes()[0].get_scopes()[0]["p"].get_object() self.assertTrue(isinstance(p_object.get_type(), rope.base.builtins.Str)) def test_report_change_in_libutils(self): self.project.prefs["automatic_soa"] = True code = dedent("""\ class C(object): pass def f(p): pass f(C()) """) with open(self.mod.real_path, "w") as mod_file: mod_file.write(code) rope.base.libutils.report_change(self.project, self.mod.real_path, "") pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() f_scope = pymod["f"].get_object().get_scope() p_type = f_scope["p"].get_object().get_type() self.assertEqual(c_class, p_type) def test_report_libutils_and_analyze_all_modules(self): code = dedent("""\ class C(object): pass def f(p): pass f(C()) """) self.mod.write(code) rope.base.libutils.analyze_modules(self.project) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() f_scope = pymod["f"].get_object().get_scope() p_type = f_scope["p"].get_object().get_type() self.assertEqual(c_class, p_type) def test_validation_problems_for_objectdb_retrievals(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write( dedent("""\ l = [] var = l.pop() """) ) mod2.write( dedent("""\ import mod1 class C(object): pass mod1.l.append(C()) """) ) self.pycore.analyze_module(mod2) pymod2 = self.project.get_pymodule(mod2) c_class = pymod2["C"].get_object() pymod1 = self.project.get_pymodule(mod1) var_pyname = pymod1["var"] self.assertEqual(c_class, var_pyname.get_object().get_type()) mod2.write( dedent("""\ import mod1 mod1.l.append("") """) ) self.assertNotEqual( c_class, var_pyname.get_object().get_type(), "Class `C` no more exists" ) def test_validation_problems_for_changing_builtin_types(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ l = [] l.append("") """) ) self.pycore.analyze_module(mod1) mod1.write( dedent("""\ l = {} v = l["key"] """) ) pymod1 = self.project.get_pymodule(mod1) # noqa var = pymod1["v"].get_object() # noqa def test_always_returning_containing_class_for_selfs(self): code = dedent("""\ class A(object): def f(p): return p class B(object): pass b = B() b.f() """) self.mod.write(code) self.pycore.analyze_module(self.mod) pymod = self.project.get_pymodule(self.mod) a_class = pymod["A"].get_object() f_scope = a_class.get_scope().get_scopes()[0] p_type = f_scope["p"].get_object().get_type() self.assertEqual(a_class, p_type) def test_following_function_calls_when_asked_to(self): code = dedent("""\ class A(object): pass class C(object): def __init__(self, arg): self.attr = arg def f(p): return C(p) c = f(A()) x = c.attr """) self.mod.write(code) self.pycore.analyze_module(self.mod, followed_calls=1) pymod = self.project.get_pymodule(self.mod) a_class = pymod["A"].get_object() x_var = pymod["x"].get_object().get_type() self.assertEqual(a_class, x_var) def test_set_comprehension(self): code = dedent("""\ x = {s.strip() for s in X()} x.add('x') """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) x_var = pymod['x'].pyobject.get() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/builtinstest.py0000664000175000017500000006055700000000000017420 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.base import builtins, libutils, pyobjects from ropetest import testutils from rope.base.builtins import Dict class BuiltinTypesTest(unittest.TestCase): def setUp(self): super(BuiltinTypesTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(BuiltinTypesTest, self).tearDown() def test_simple_case(self): self.mod.write("l = []\n") pymod = self.project.get_pymodule(self.mod) self.assertTrue("append" in pymod["l"].get_object()) def test_holding_type_information(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] a_var = l.pop() """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_get_items(self): self.mod.write( dedent("""\ class C(object): def __getitem__(self, i): return C() c = C() a_var = c[0]""" ) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_get_items_for_lists(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] a_var = l[0] """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_get_items_from_slices(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] a_var = l[:].pop() """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_simple_for_loops(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] for c in l: a_var = c """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_definition_location_for_loop_variables(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] for c in l: pass """) ) pymod = self.project.get_pymodule(self.mod) c_var = pymod["c"] self.assertEqual((pymod, 4), c_var.get_definition_location()) def test_simple_case_for_dicts(self): self.mod.write("d = {}\n") pymod = self.project.get_pymodule(self.mod) self.assertTrue("get" in pymod["d"].get_object()) def test_get_item_for_dicts(self): self.mod.write( dedent("""\ class C(object): pass d = {1: C()} a_var = d[1] """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_dict_function_parent(self): self.mod.write( dedent("""\ d = {1: 2} a_var = d.keys()""" ) ) pymod = self.project.get_pymodule(self.mod) a_var = pymod["d"].get_object()["keys"].get_object() self.assertEqual(type(a_var.parent), Dict) def test_popping_dicts(self): self.mod.write( dedent("""\ class C(object): pass d = {1: C()} a_var = d.pop(1) """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_getting_keys_from_dicts(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass d = {C1(): C2()} for c in d.keys(): a_var = c """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C1"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_getting_values_from_dicts(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass d = {C1(): C2()} for c in d.values(): a_var = c """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C2"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_getting_iterkeys_from_dicts(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass d = {C1(): C2()} for c in d.keys(): a_var = c """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C1"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_getting_itervalues_from_dicts(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass d = {C1(): C2()} for c in d.values(): a_var = c """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C2"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_using_copy_for_dicts(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass d = {C1(): C2()} for c in d.copy(): a_var = c """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C1"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_tuple_assignments_for_items(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass d = {C1(): C2()} key, value = d.items()[0] """) ) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() key = pymod["key"].get_object() value = pymod["value"].get_object() self.assertEqual(c1_class, key.get_type()) self.assertEqual(c2_class, value.get_type()) def test_tuple_assignment_for_lists(self): self.mod.write( dedent("""\ class C(object): pass l = [C(), C()] a, b = l """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c_class, a_var.get_type()) self.assertEqual(c_class, b_var.get_type()) def test_tuple_assignments_for_iteritems_in_fors(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass d = {C1(): C2()} for x, y in d.items(): a = x; b = y """) ) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_simple_tuple_assignments(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass a, b = C1(), C2() """) ) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_overriding_builtin_names(self): self.mod.write( dedent("""\ class C(object): pass list = C """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() list_var = pymod["list"].get_object() self.assertEqual(c_class, list_var) def test_simple_builtin_scope_test(self): self.mod.write("l = list()\n") pymod = self.project.get_pymodule(self.mod) self.assertTrue("append" in pymod["l"].get_object()) def test_simple_sets(self): self.mod.write("s = set()\n") pymod = self.project.get_pymodule(self.mod) self.assertTrue("add" in pymod["s"].get_object()) def test_making_lists_using_the_passed_argument_to_init(self): self.mod.write( dedent("""\ class C(object): pass l1 = [C()] l2 = list(l1) a_var = l2.pop()""" ) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_making_tuples_using_the_passed_argument_to_init(self): self.mod.write( dedent("""\ class C(object): pass l1 = [C()] l2 = tuple(l1) a_var = l2[0]""" ) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_making_sets_using_the_passed_argument_to_init(self): self.mod.write( dedent("""\ class C(object): pass l1 = [C()] l2 = set(l1) a_var = l2.pop()""" ) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_making_dicts_using_the_passed_argument_to_init(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass l1 = [(C1(), C2())] l2 = dict(l1) a, b = l2.items()[0]""" ) ) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_range_builtin_function(self): self.mod.write("l = range(1)\n") pymod = self.project.get_pymodule(self.mod) l = pymod["l"].get_object() self.assertTrue("append" in l) def test_reversed_builtin_function(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] for x in reversed(l): a_var = x """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_sorted_builtin_function(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] a_var = sorted(l).pop() """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_super_builtin_function(self): self.mod.write( dedent("""\ class C(object): pass class A(object): def a_f(self): return C() class B(A): def b_f(self): return super(B, self).a_f() a_var = B.b_f() """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_file_builtin_type(self): self.mod.write( dedent("""\ for line in open("file.txt"): a_var = line """) ) pymod = self.project.get_pymodule(self.mod) a_var = pymod["a_var"].get_object() self.assertTrue(isinstance(a_var.get_type(), builtins.Str)) def test_property_builtin_type(self): self.mod.write("p = property()\n") pymod = self.project.get_pymodule(self.mod) p_var = pymod["p"].get_object() self.assertTrue("fget" in p_var) def test_lambda_functions(self): self.mod.write("l = lambda: 1\n") pymod = self.project.get_pymodule(self.mod) l_var = pymod["l"].get_object() self.assertEqual(pyobjects.get_base_type("Function"), l_var.get_type()) def test_lambda_function_definition(self): self.mod.write("l = lambda x, y = 2, *a, **b: x + y\n") pymod = self.project.get_pymodule(self.mod) l_var = pymod["l"].get_object() self.assertTrue(l_var.get_name() is not None) self.assertEqual(len(l_var.get_param_names()), 4) self.assertEqual((pymod, 1), pymod["l"].get_definition_location()) def test_lambdas_that_return_unknown(self): self.mod.write("a_var = (lambda: None)()\n") pymod = self.project.get_pymodule(self.mod) a_var = pymod["a_var"].get_object() self.assertTrue(a_var is not None) def test_builtin_zip_function(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass c1_list = [C1()] c2_list = [C2()] a, b = zip(c1_list, c2_list)[0]""" ) ) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_builtin_zip_function_with_more_than_two_args(self): self.mod.write( dedent("""\ class C1(object): pass class C2(object): pass c1_list = [C1()] c2_list = [C2()] a, b, c = zip(c1_list, c2_list, c1_list)[0]""" ) ) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() c2_class = pymod["C2"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() c_var = pymod["c"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) self.assertEqual(c1_class, c_var.get_type()) def test_wrong_arguments_to_zip_function(self): self.mod.write( dedent("""\ class C1(object): pass c1_list = [C1()] a, b = zip(c1_list, 1)[0]""" ) ) pymod = self.project.get_pymodule(self.mod) c1_class = pymod["C1"].get_object() a_var = pymod["a"].get_object() b_var = pymod["b"].get_object() # noqa self.assertEqual(c1_class, a_var.get_type()) def test_enumerate_builtin_function(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] for i, x in enumerate(l): a_var = x """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_builtin_class_get_name(self): self.assertEqual("object", builtins.builtins["object"].get_object().get_name()) self.assertEqual( "property", builtins.builtins["property"].get_object().get_name() ) def test_star_args_and_double_star_args(self): self.mod.write( dedent("""\ def func(p, *args, **kwds): pass """) ) pymod = self.project.get_pymodule(self.mod) func_scope = pymod["func"].get_object().get_scope() args = func_scope["args"].get_object() kwds = func_scope["kwds"].get_object() self.assertTrue(isinstance(args.get_type(), builtins.List)) self.assertTrue(isinstance(kwds.get_type(), builtins.Dict)) def test_simple_list_comprehension_test(self): self.mod.write("a_var = [i for i in range(10)]\n") pymod = self.project.get_pymodule(self.mod) a_var = pymod["a_var"].get_object() self.assertTrue(isinstance(a_var.get_type(), builtins.List)) def test_simple_list_generator_expression(self): self.mod.write("a_var = (i for i in range(10))\n") pymod = self.project.get_pymodule(self.mod) a_var = pymod["a_var"].get_object() self.assertTrue(isinstance(a_var.get_type(), builtins.Iterator)) def test_iter_builtin_function(self): self.mod.write( dedent("""\ class C(object): pass l = [C()] for c in iter(l): a_var = c """) ) pymod = self.project.get_pymodule(self.mod) c_class = pymod["C"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_simple_int_type(self): self.mod.write("l = 1\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( builtins.builtins["int"].get_object(), pymod["l"].get_object().get_type() ) def test_simple_float_type(self): self.mod.write("l = 1.0\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( builtins.builtins["float"].get_object(), pymod["l"].get_object().get_type() ) def test_simple_float_type2(self): self.mod.write("l = 1e1\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( builtins.builtins["float"].get_object(), pymod["l"].get_object().get_type() ) def test_simple_complex_type(self): self.mod.write("l = 1.0j\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( builtins.builtins["complex"].get_object(), pymod["l"].get_object().get_type(), ) def test_handling_unaryop_on_ints(self): self.mod.write("l = -(1)\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( builtins.builtins["int"].get_object(), pymod["l"].get_object().get_type() ) def test_handling_binop_on_ints(self): self.mod.write("l = 1 + 1\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( builtins.builtins["int"].get_object(), pymod["l"].get_object().get_type() ) def test_handling_compares(self): self.mod.write("l = 1 == 1\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( builtins.builtins["bool"].get_object(), pymod["l"].get_object().get_type() ) def test_handling_boolops(self): self.mod.write("l = 1 and 2\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( builtins.builtins["int"].get_object(), pymod["l"].get_object().get_type() ) def test_binary_or_left_value_unknown(self): code = "var = (asdsd or 3)\n" pymod = libutils.get_string_module(self.project, code) self.assertEqual( builtins.builtins["int"].get_object(), pymod["var"].get_object().get_type() ) def test_unknown_return_object(self): src = dedent("""\ import sys def foo(): res = set(sys.builtin_module_names) if foo: res.add(bar) """) self.project.prefs["import_dynload_stdmods"] = True self.mod.write(src) self.project.pycore.analyze_module(self.mod) def test_abstractmethods_attribute(self): # see http://bugs.python.org/issue10006 for details src = "class SubType(type): pass\nsubtype = SubType()\n" self.mod.write(src) self.project.pycore.analyze_module(self.mod) class BuiltinModulesTest(unittest.TestCase): def setUp(self): super(BuiltinModulesTest, self).setUp() self.project = testutils.sample_project( extension_modules=["time", "invalid", "invalid.sub"] ) self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(BuiltinModulesTest, self).tearDown() def test_simple_case(self): self.mod.write("import time") pymod = self.project.get_pymodule(self.mod) self.assertTrue("time" in pymod["time"].get_object()) def test_ignored_extensions(self): self.mod.write("import os") pymod = self.project.get_pymodule(self.mod) self.assertTrue("rename" not in pymod["os"].get_object()) def test_ignored_extensions_2(self): self.mod.write("import os") pymod = self.project.get_pymodule(self.mod) self.assertTrue("rename" not in pymod["os"].get_object()) def test_nonexistent_modules(self): self.mod.write("import invalid") pymod = self.project.get_pymodule(self.mod) pymod["invalid"].get_object() def test_nonexistent_modules_2(self): self.mod.write( dedent("""\ import invalid import invalid.sub""" ) ) pymod = self.project.get_pymodule(self.mod) invalid = pymod["invalid"].get_object() self.assertTrue("sub" in invalid) def test_time_in_std_mods(self): import rope.base.stdmods self.assertTrue("time" in rope.base.stdmods.standard_modules()) def test_timemodule_normalizes_to_time(self): import rope.base.stdmods self.assertEqual(rope.base.stdmods.normalize_so_name("timemodule.so"), "time") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637501104.0 rope-0.22.0/ropetest/codeanalyzetest.py0000664000175000017500000010642500000000000020060 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest import rope.base.evaluate from rope.base import libutils from rope.base import exceptions, worder, codeanalyze from rope.base.codeanalyze import SourceLinesAdapter, LogicalLineFinder, get_block_start from ropetest import testutils class SourceLinesAdapterTest(unittest.TestCase): def test_source_lines_simple(self): to_lines = SourceLinesAdapter("line1\nline2\n") self.assertEqual("line1", to_lines.get_line(1)) self.assertEqual("line2", to_lines.get_line(2)) self.assertEqual("", to_lines.get_line(3)) self.assertEqual(3, to_lines.length()) def test_source_lines_get_line_number(self): to_lines = SourceLinesAdapter("line1\nline2\n") self.assertEqual(1, to_lines.get_line_number(0)) self.assertEqual(1, to_lines.get_line_number(5)) self.assertEqual(2, to_lines.get_line_number(7)) self.assertEqual(3, to_lines.get_line_number(12)) def test_source_lines_get_line_start(self): to_lines = SourceLinesAdapter("line1\nline2\n") self.assertEqual(0, to_lines.get_line_start(1)) self.assertEqual(6, to_lines.get_line_start(2)) self.assertEqual(12, to_lines.get_line_start(3)) def test_source_lines_get_line_end(self): to_lines = SourceLinesAdapter("line1\nline2\n") self.assertEqual(5, to_lines.get_line_end(1)) self.assertEqual(11, to_lines.get_line_end(2)) self.assertEqual(12, to_lines.get_line_end(3)) def test_source_lines_last_line_with_no_new_line(self): to_lines = SourceLinesAdapter("line1") self.assertEqual(1, to_lines.get_line_number(5)) class WordRangeFinderTest(unittest.TestCase): 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: pass """) self.assertEqual("(a_var).an_attr", self._find_primary(code, code.index(":"))) def test_inside_parans(self): code = "a_func(a_var)" self.assertEqual("a_var", self._find_primary(code, 10)) def test_simple_names(self): code = "a_var = 10" self.assertEqual("a_var", self._find_primary(code, 3)) def test_function_calls(self): code = "sample_function()" self.assertEqual("sample_function", self._find_primary(code, 10)) def test_attribute_accesses(self): code = "a_var.an_attr" self.assertEqual("a_var.an_attr", self._find_primary(code, 10)) def test_word_finder_on_word_beginning(self): code = "print(a_var)\n" word_finder = worder.Worder(code) result = word_finder.get_word_at(code.index("a_var")) self.assertEqual("a_var", result) def test_word_finder_on_primary_beginning(self): code = "print(a_var)\n" result = self._find_primary(code, code.index("a_var")) self.assertEqual("a_var", result) def test_word_finder_on_word_ending(self): code = "print(a_var)\n" word_finder = worder.Worder(code) result = word_finder.get_word_at(code.index("a_var") + 5) self.assertEqual("a_var", result) def test_word_finder_on_primary_ending(self): code = "print(a_var)\n" result = self._find_primary(code, code.index("a_var") + 5) self.assertEqual("a_var", result) def test_word_finder_on_primaries_with_dots_inside_parens(self): code = "(a_var.\nattr)" result = self._find_primary(code, code.index("attr") + 1) self.assertEqual("a_var.\nattr", result) def test_word_finder_on_primary_like_keyword(self): code = "is_keyword = False\n" result = self._find_primary(code, 1) self.assertEqual("is_keyword", result) def test_keyword_before_parens_no_space(self): code = dedent("""\ if(a_var).an_attr: pass """) self.assertEqual("(a_var).an_attr", self._find_primary(code, code.index(":"))) def test_strings(self): code = '"a string".split()' self.assertEqual('"a string".split', self._find_primary(code, 14)) def test_function_calls2(self): code = 'file("afile.txt").read()' self.assertEqual('file("afile.txt").read', self._find_primary(code, 18)) def test_parens(self): code = '("afile.txt").split()' self.assertEqual('("afile.txt").split', self._find_primary(code, 18)) def test_function_with_no_param(self): code = "AClass().a_func()" self.assertEqual("AClass().a_func", self._find_primary(code, 12)) def test_function_with_multiple_param(self): code = 'AClass(a_param, another_param, "a string").a_func()' self.assertEqual( 'AClass(a_param, another_param, "a string").a_func', self._find_primary(code, 44), ) def test_param_expressions(self): code = "AClass(an_object.an_attr).a_func()" self.assertEqual("an_object.an_attr", self._find_primary(code, 20)) def test_string_parens(self): code = 'a_func("(").an_attr' self.assertEqual('a_func("(").an_attr', self._find_primary(code, 16)) def test_extra_spaces(self): code = 'a_func ( "(" ) . an_attr' self.assertEqual('a_func ( "(" ) . an_attr', self._find_primary(code, 26)) def test_relative_import(self): code = "from .module import smt" self.assertEqual(".module", self._find_primary(code, 5)) def test_functions_on_ending_parens(self): code = "A()" self.assertEqual("A()", self._find_primary(code, 2)) def test_splitted_statement(self): word_finder = worder.Worder("an_object.an_attr") self.assertEqual( ("an_object", "an_at", 10), word_finder.get_splitted_primary_before(15) ) def test_empty_splitted_statement(self): word_finder = worder.Worder("an_attr") self.assertEqual(("", "an_at", 0), word_finder.get_splitted_primary_before(5)) def test_empty_splitted_statement2(self): word_finder = worder.Worder("an_object.") self.assertEqual( ("an_object", "", 10), word_finder.get_splitted_primary_before(10) ) def test_empty_splitted_statement3(self): word_finder = worder.Worder("") self.assertEqual(("", "", 0), word_finder.get_splitted_primary_before(0)) def test_empty_splitted_statement4(self): word_finder = worder.Worder("a_var = ") self.assertEqual(("", "", 8), word_finder.get_splitted_primary_before(8)) def test_empty_splitted_statement5(self): word_finder = worder.Worder("a.") self.assertEqual(("a", "", 2), word_finder.get_splitted_primary_before(2)) def test_operators_inside_parens(self): code = "(a_var + another_var).reverse()" self.assertEqual("(a_var + another_var).reverse", self._find_primary(code, 25)) def test_dictionaries(self): code = 'print({1: "one", 2: "two"}.keys())' self.assertEqual('{1: "one", 2: "two"}.keys', self._find_primary(code, 29)) def test_following_parens(self): code = "a_var = a_func()()" result = self._find_primary(code, code.index(")(") + 3) self.assertEqual("a_func()()", result) def test_comments_for_finding_statements(self): code = "# var2 . \n var3" self.assertEqual("var3", self._find_primary(code, code.index("3"))) def test_str_in_comments_for_finding_statements(self): code = '# "var2" . \n var3' self.assertEqual("var3", self._find_primary(code, code.index("3"))) def test_comments_for_finding_statements2(self): code = 'var1 + "# var2".\n var3' self.assertEqual("var3", self._find_primary(code, 21)) def test_comments_for_finding_statements3(self): code = '"" + # var2.\n var3' self.assertEqual("var3", self._find_primary(code, 21)) 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 """) word_finder = worder.Worder(code) 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_is_import_statement_finding2(self): code = dedent("""\ import a.b.c.d result = a.b.c.d.f() """) word_finder = worder.Worder(code) self.assertFalse(word_finder.is_import_statement(code.rindex("d") + 1)) def test_word_parens_range(self): code = dedent("""\ s = str() s.title() """) word_finder = worder.Worder(code) result = word_finder.get_word_parens_range(code.rindex("()") - 1) self.assertEqual((len(code) - 3, len(code) - 1), result) def test_getting_primary_before_get_index(self): code = "\na = (b + c).d[0]()\n" result = self._find_primary(code, len(code) - 2) self.assertEqual("(b + c).d[0]()", result) def test_getting_primary_and_strings_at_the_end_of_line(self): code = "f('\\'')\n" result = self._find_primary(code, len(code) - 1) # noqa def test_getting_primary_and_not_crossing_newlines(self): code = "\na = (b + c)\n(4 + 1).x\n" result = self._find_primary(code, len(code) - 1) self.assertEqual("(4 + 1).x", result) # XXX: cancatenated string literals def xxx_test_getting_primary_cancatenating_strs(self): code = 's = "a"\n"b" "c"\n' result = self._find_primary(code, len(code) - 2) self.assertEqual('"b" "c"', result) def test_is_a_function_being_called_with_parens_on_next_line(self): code = "func\n(1, 2)\n" word_finder = worder.Worder(code) self.assertFalse(word_finder.is_a_function_being_called(1)) # XXX: handling triple quotes def xxx_test_triple_quotes(self): code = 's = """string"""\n' result = self._find_primary(code, len(code) - 1) self.assertEqual('"""string"""', result) def test_triple_quotes_spanning_multiple_lines(self): code = 's = """\\\nl1\nl2\n """\n' result = self._find_primary(code, len(code) - 2) self.assertEqual('"""\\\nl1\nl2\n """', result) def test_get_word_parens_range_and_string_literals(self): code = 'f(1, ")", 2)\n' word_finder = worder.Worder(code) result = word_finder.get_word_parens_range(0) self.assertEqual((1, len(code) - 1), result) def test_is_assigned_here_for_equality_test(self): code = "a == 1\n" word_finder = worder.Worder(code) self.assertFalse(word_finder.is_assigned_here(0)) def test_is_assigned_here_for_not_equal_test(self): code = "a != 1\n" word_finder = worder.Worder(code) self.assertFalse(word_finder.is_assigned_here(0)) # XXX: is_assigned_here should work for tuple assignments def xxx_test_is_assigned_here_for_tuple_assignment(self): code = "a, b = (1, 2)\n" 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) self.assertTrue(word_finder.is_from_statement(code.rindex("g"))) def test_is_from_with_from_import_and_line_breaks_in_the_middle(self): code = "from mod import f,\\\n g\n" word_finder = worder.Worder(code) self.assertTrue(word_finder.is_from_statement(code.rindex("g"))) 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") self.assertTrue(word_finder.is_function_keyword_parameter(index)) def test_find_parens_start(self): code = "f(p)\n" finder = worder.Worder(code) self.assertEqual(1, finder.find_parens_start_from_inside(2)) def test_underlined_find_parens_start(self): code = 'f(p="")\n' finder = worder.Worder(code) self.assertEqual(1, finder._find_parens_start(len(code) - 2)) def test_find_parens_start_with_multiple_entries(self): code = "myfunc(p1, p2, p3\n" finder = worder.Worder(code) self.assertEqual( code.index("("), finder.find_parens_start_from_inside(len(code) - 1) ) def test_find_parens_start_with_nested_parens(self): code = "myfunc(p1, (p2, p3), p4\n" finder = worder.Worder(code) self.assertEqual( code.index("("), finder.find_parens_start_from_inside(len(code) - 1) ) def test_find_parens_start_with_parens_in_strs(self): code = 'myfunc(p1, "(", p4\n' finder = worder.Worder(code) self.assertEqual( code.index("("), finder.find_parens_start_from_inside(len(code) - 1) ) def test_find_parens_start_with_parens_in_strs_in_multiple_lines(self): code = 'myfunc (\np1\n , \n "(" \n, \np4\n' finder = worder.Worder(code) self.assertEqual( code.index("("), finder.find_parens_start_from_inside(len(code) - 1) ) 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)) def test_get_word_range_with_fstring(self): code = dedent('''\ auth = 8 my_var = f"some value {auth}" print(auth) other_val = "some other"''') finder = worder.Worder(code) self.assertEqual(finder.get_word_range(45), (45, 49)) class ScopeNameFinderTest(unittest.TestCase): def setUp(self): super(ScopeNameFinderTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(ScopeNameFinderTest, self).tearDown() # FIXME: in normal scopes the interpreter raises `UnboundLocalName` # exception, but not in class bodies def xxx_test_global_name_in_class_body(self): code = dedent("""\ a_var = 10 class C(object): a_var = a_var """) scope = libutils.get_string_scope(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) result = name_finder.get_pyname_at(len(code) - 3) self.assertEqual(scope["a_var"], result) def test_class_variable_attribute_in_class_body(self): code = dedent("""\ a_var = 10 class C(object): a_var = a_var """) scope = libutils.get_string_scope(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) a_var_pyname = scope["C"].get_object()["a_var"] result = name_finder.get_pyname_at(len(code) - 12) self.assertEqual(a_var_pyname, result) def test_class_variable_attribute_in_class_body2(self): code = dedent("""\ a_var = 10 class C(object): a_var \\ = a_var """) scope = libutils.get_string_scope(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) a_var_pyname = scope["C"].get_object()["a_var"] result = name_finder.get_pyname_at(len(code) - 12) self.assertEqual(a_var_pyname, result) def test_class_method_attribute_in_class_body(self): code = dedent("""\ class C(object): def a_method(self): pass """) scope = libutils.get_string_scope(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) a_method_pyname = scope["C"].get_object()["a_method"] result = name_finder.get_pyname_at(code.index("a_method") + 2) self.assertEqual(a_method_pyname, result) def test_inner_class_attribute_in_class_body(self): code = dedent("""\ class C(object): class CC(object): pass """) scope = libutils.get_string_scope(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) a_class_pyname = scope["C"].get_object()["CC"] result = name_finder.get_pyname_at(code.index("CC") + 2) self.assertEqual(a_class_pyname, result) def test_class_method_in_class_body_but_not_indexed(self): code = dedent("""\ class C(object): def func(self, func): pass """) scope = libutils.get_string_scope(self.project, code) a_func_pyname = scope.get_scopes()[0].get_scopes()[0]["func"] name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) result = name_finder.get_pyname_at(code.index(", func") + 3) self.assertEqual(a_func_pyname, result) def test_function_but_not_indexed(self): code = dedent("""\ def a_func(a_func): pass """) scope = libutils.get_string_scope(self.project, code) a_func_pyname = scope["a_func"] name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) result = name_finder.get_pyname_at(code.index("a_func") + 3) self.assertEqual(a_func_pyname, result) 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 """)) code = "from mod import a_func\n" scope = libutils.get_string_scope(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) mod_pyobject = self.project.get_pymodule(mod) found_pyname = name_finder.get_pyname_at(code.index("mod") + 1) self.assertEqual(mod_pyobject, found_pyname.get_object()) def test_renaming_functions_with_from_import_and_parens(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write(dedent("""\ def afunc(): pass """)) code = dedent("""\ from mod1 import ( afunc as func) """) scope = libutils.get_string_scope(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(scope.pyobject) mod_pyobject = self.project.get_pymodule(mod1) afunc = mod_pyobject["afunc"] found_pyname = name_finder.get_pyname_at(code.index("afunc") + 1) self.assertEqual(afunc.get_object(), found_pyname.get_object()) @testutils.only_for("2.5") def test_relative_modules_after_from_statements(self): pkg1 = testutils.create_package(self.project, "pkg1") 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 """)) code = "from ..mod1 import a_func\n" mod2.write(code) mod2_scope = self.project.get_pymodule(mod2).get_scope() name_finder = rope.base.evaluate.ScopeNameFinder(mod2_scope.pyobject) mod1_pyobject = self.project.get_pymodule(mod1) found_pyname = name_finder.get_pyname_at(code.index("mod1") + 1) self.assertEqual(mod1_pyobject, found_pyname.get_object()) def test_relative_modules_after_from_statements2(self): mod1 = testutils.create_module(self.project, "mod1") pkg1 = testutils.create_package(self.project, "pkg1") pkg2 = testutils.create_package(self.project, "pkg2", pkg1) mod2 = testutils.create_module(self.project, "mod2", pkg2) # noqa mod1.write("import pkg1.pkg2.mod2") mod1_scope = self.project.get_pymodule(mod1).get_scope() name_finder = rope.base.evaluate.ScopeNameFinder(mod1_scope.pyobject) pkg2_pyobject = self.project.get_pymodule(pkg2) found_pyname = name_finder.get_pyname_at(mod1.read().index("pkg2") + 1) self.assertEqual(pkg2_pyobject, found_pyname.get_object()) def test_get_pyname_at_on_language_keywords(self): code = dedent("""\ def a_func(a_func): pass """) pymod = libutils.get_string_module(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(pymod) with self.assertRaises(exceptions.RopeError): name_finder.get_pyname_at(code.index("pass")) def test_one_liners(self): code = dedent("""\ var = 1 def f(): var = 2 print(var) """) pymod = libutils.get_string_module(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(pymod) pyname = name_finder.get_pyname_at(code.rindex("var")) self.assertEqual(pymod["var"], pyname) def test_one_liners_with_line_breaks(self): code = dedent("""\ var = 1 def f( ): var = 2 print(var) """) pymod = libutils.get_string_module(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(pymod) pyname = name_finder.get_pyname_at(code.rindex("var")) self.assertEqual(pymod["var"], pyname) def test_one_liners_with_line_breaks2(self): code = dedent("""\ var = 1 def f( p): var = 2 print(var) """) pymod = libutils.get_string_module(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(pymod) pyname = name_finder.get_pyname_at(code.rindex("var")) self.assertEqual(pymod["var"], pyname) def test_var_in_list_comprehension_differs_from_var_outside(self): code = "var = 1\n[var for var in range(1)]\n" pymod = libutils.get_string_module(self.project, code) name_finder = rope.base.evaluate.ScopeNameFinder(pymod) outside_pyname = name_finder.get_pyname_at(code.index("var")) inside_pyname = name_finder.get_pyname_at(code.rindex("var")) self.assertNotEqual(outside_pyname, inside_pyname) class LogicalLineFinderTest(unittest.TestCase): def _logical_finder(self, code): return LogicalLineFinder(SourceLinesAdapter(code)) def test_normal_lines(self): code = "a_var = 10" line_finder = self._logical_finder(code) self.assertEqual((1, 1), line_finder.logical_line_in(1)) def test_normal_lines2(self): code = dedent("""\ another = 10 a_var = 20 """) line_finder = self._logical_finder(code) self.assertEqual((1, 1), line_finder.logical_line_in(1)) self.assertEqual((2, 2), line_finder.logical_line_in(2)) def test_implicit_continuation(self): code = "a_var = 3 + \\\n 4 + \\\n 5" line_finder = self._logical_finder(code) self.assertEqual((1, 3), line_finder.logical_line_in(2)) def test_explicit_continuation(self): code = dedent("""\ print(2) a_var = (3 + 4, 5) """) line_finder = self._logical_finder(code) self.assertEqual((2, 4), line_finder.logical_line_in(2)) def test_explicit_continuation_comments(self): code = "#\na_var = 3\n" line_finder = self._logical_finder(code) self.assertEqual((2, 2), line_finder.logical_line_in(2)) def test_multiple_indented_ifs(self): code = dedent("""\ if True: if True: if True: pass a = 10 """) line_finder = self._logical_finder(code) self.assertEqual((5, 5), line_finder.logical_line_in(5)) def test_list_comprehensions_and_fors(self): code = dedent("""\ a_list = [i for i in range(10)] """) line_finder = self._logical_finder(code) self.assertEqual((1, 2), line_finder.logical_line_in(2)) def test_generator_expressions_and_fors(self): code = dedent("""\ a_list = (i for i in range(10)) """) line_finder = self._logical_finder(code) self.assertEqual((1, 2), line_finder.logical_line_in(2)) def test_fors_and_block_start(self): code = dedent("""\ l = range(10) for i in l: print(i) """) self.assertEqual(2, get_block_start(SourceLinesAdapter(code), 2)) def test_problems_with_inner_indentations(self): code = dedent("""\ if True: if True: if True: pass a = \\ 1 """) line_finder = self._logical_finder(code) self.assertEqual((5, 6), line_finder.logical_line_in(6)) def test_problems_with_inner_indentations2(self): code = dedent("""\ if True: if True: pass a = 1 """) line_finder = self._logical_finder(code) self.assertEqual((4, 4), line_finder.logical_line_in(4)) def test_logical_lines_for_else(self): code = dedent("""\ if True: pass else: pass """) line_finder = self._logical_finder(code) self.assertEqual((3, 3), line_finder.logical_line_in(3)) def test_logical_lines_for_lines_with_wrong_continues(self): code = "var = 1 + \\" line_finder = self._logical_finder(code) self.assertEqual((1, 1), line_finder.logical_line_in(1)) def test_logical_lines_for_multiline_string_with_extra_quotes_front(self): code = '""""Docs."""\na = 1\n' line_finder = self._logical_finder(code) self.assertEqual((2, 2), line_finder.logical_line_in(2)) def test_logical_lines_for_multiline_string_with_escaped_quotes(self): code = '"""Quotes \\""" "\\"" \' """\na = 1\n' line_finder = self._logical_finder(code) self.assertEqual((2, 2), line_finder.logical_line_in(2)) def test_generating_line_starts(self): code = dedent("""\ a = 1 a = 2 a = 3 """) line_finder = self._logical_finder(code) self.assertEqual([1, 2, 4], list(line_finder.generate_starts())) def test_generating_line_starts2(self): code = "a = 1\na = 2\n\na = \\ 3\n" line_finder = self._logical_finder(code) self.assertEqual([2, 4], list(line_finder.generate_starts(2))) def test_generating_line_starts3(self): code = "a = 1\na = 2\n\na = \\ 3\n" line_finder = self._logical_finder(code) self.assertEqual([2], list(line_finder.generate_starts(2, 3))) def test_generating_line_starts_for_multi_line_statements(self): code = "\na = \\\n 1 + \\\n 1\n" line_finder = self._logical_finder(code) self.assertEqual([2], list(line_finder.generate_starts())) def test_generating_line_starts_and_unmatched_deindents(self): code = dedent("""\ if True: if True: if True: a = 1 b = 1 """) line_finder = self._logical_finder(code) self.assertEqual([4, 5], list(line_finder.generate_starts(4))) def test_false_triple_quoted_string(self): code = dedent("""\ def foo(): a = 0 p = 'foo''' def bar(): a = 1 a += 1 """) line_finder = self._logical_finder(code) self.assertEqual([1, 2, 3, 5, 6, 7], list(line_finder.generate_starts())) self.assertEqual((3, 3), line_finder.logical_line_in(3)) self.assertEqual([5, 6, 7], list(line_finder.generate_starts(4))) class TokenizerLogicalLineFinderTest(LogicalLineFinderTest): def _logical_finder(self, code): lines = SourceLinesAdapter(code) return codeanalyze.CachingLogicalLineFinder( lines, codeanalyze.tokenizer_generator ) class CustomLogicalLineFinderTest(LogicalLineFinderTest): def _logical_finder(self, code): lines = SourceLinesAdapter(code) return codeanalyze.CachingLogicalLineFinder(lines, codeanalyze.custom_generator) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3713906 rope-0.22.0/ropetest/contrib/0000775000175000017500000000000000000000000015740 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/ropetest/contrib/__init__.py0000664000175000017500000000215300000000000020052 0ustar00lieryanlieryanimport sys try: import unittest2 as unittest except ImportError: import unittest import ropetest.contrib.autoimporttest import ropetest.contrib.changestacktest import ropetest.contrib.codeassisttest import ropetest.contrib.finderrorstest import ropetest.contrib.findittest import ropetest.contrib.fixmodnamestest import ropetest.contrib.generatetest def suite(): result = unittest.TestSuite() result.addTests(unittest.makeSuite(ropetest.contrib.generatetest.GenerateTest)) result.addTests(ropetest.contrib.codeassisttest.suite()) result.addTests(ropetest.contrib.autoimporttest.suite()) result.addTests(ropetest.contrib.findittest.suite()) result.addTests( unittest.makeSuite(ropetest.contrib.changestacktest.ChangeStackTest) ) result.addTests( unittest.makeSuite(ropetest.contrib.fixmodnamestest.FixModuleNamesTest) ) result.addTests(unittest.makeSuite(ropetest.contrib.finderrorstest.FindErrorsTest)) return result if __name__ == "__main__": runner = unittest.TextTestRunner() result = runner.run(suite()) sys.exit(not result.wasSuccessful()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/ropetest/contrib/autoimporttest.py0000664000175000017500000001401500000000000021416 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest from ropetest import testutils from rope.contrib import autoimport class AutoImportTest(unittest.TestCase): def setUp(self): super(AutoImportTest, self).setUp() self.project = testutils.sample_project(extension_modules=["sys"]) self.mod1 = testutils.create_module(self.project, "mod1") self.pkg = testutils.create_package(self.project, "pkg") self.mod2 = testutils.create_module(self.project, "mod2", self.pkg) self.importer = autoimport.AutoImport(self.project, observe=False) def tearDown(self): testutils.remove_project(self.project) super(AutoImportTest, self).tearDown() def test_simple_case(self): self.assertEqual([], self.importer.import_assist("A")) def test_update_resource(self): self.mod1.write("myvar = None\n") self.importer.update_resource(self.mod1) self.assertEqual([("myvar", "mod1")], self.importer.import_assist("myva")) def test_update_module(self): self.mod1.write("myvar = None") self.importer.update_module("mod1") self.assertEqual([("myvar", "mod1")], self.importer.import_assist("myva")) def test_update_non_existent_module(self): self.importer.update_module("does_not_exists_this") self.assertEqual([], self.importer.import_assist("myva")) def test_module_with_syntax_errors(self): self.mod1.write("this is a syntax error\n") self.importer.update_resource(self.mod1) self.assertEqual([], self.importer.import_assist("myva")) def test_excluding_imported_names(self): self.mod1.write("import pkg\n") self.importer.update_resource(self.mod1) self.assertEqual([], self.importer.import_assist("pkg")) def test_get_modules(self): self.mod1.write("myvar = None\n") self.importer.update_resource(self.mod1) self.assertEqual(["mod1"], self.importer.get_modules("myvar")) def test_get_modules_inside_packages(self): self.mod1.write("myvar = None\n") self.mod2.write("myvar = None\n") self.importer.update_resource(self.mod1) self.importer.update_resource(self.mod2) self.assertEqual( set(["mod1", "pkg.mod2"]), set(self.importer.get_modules("myvar")) ) def test_trivial_insertion_line(self): result = self.importer.find_insertion_line("") self.assertEqual(1, result) def test_insertion_line(self): result = self.importer.find_insertion_line("import mod\n") self.assertEqual(2, result) def test_insertion_line_with_pydocs(self): result = self.importer.find_insertion_line('"""docs\n\ndocs"""\nimport mod\n') self.assertEqual(5, result) def test_insertion_line_with_multiple_imports(self): result = self.importer.find_insertion_line("import mod1\n\nimport mod2\n") self.assertEqual(4, result) def test_insertion_line_with_blank_lines(self): result = self.importer.find_insertion_line("import mod1\n\n# comment\n") self.assertEqual(2, result) def test_empty_cache(self): self.mod1.write("myvar = None\n") self.importer.update_resource(self.mod1) self.assertEqual(["mod1"], self.importer.get_modules("myvar")) self.importer.clear_cache() self.assertEqual([], self.importer.get_modules("myvar")) def test_not_caching_underlined_names(self): self.mod1.write("_myvar = None\n") self.importer.update_resource(self.mod1, underlined=False) self.assertEqual([], self.importer.get_modules("_myvar")) self.importer.update_resource(self.mod1, underlined=True) self.assertEqual(["mod1"], self.importer.get_modules("_myvar")) def test_caching_underlined_names_passing_to_the_constructor(self): importer = autoimport.AutoImport(self.project, False, True) self.mod1.write("_myvar = None\n") importer.update_resource(self.mod1) self.assertEqual(["mod1"], importer.get_modules("_myvar")) def test_name_locations(self): self.mod1.write("myvar = None\n") self.importer.update_resource(self.mod1) self.assertEqual([(self.mod1, 1)], self.importer.get_name_locations("myvar")) def test_name_locations_with_multiple_occurrences(self): self.mod1.write("myvar = None\n") self.mod2.write("\nmyvar = None\n") self.importer.update_resource(self.mod1) self.importer.update_resource(self.mod2) self.assertEqual( set([(self.mod1, 1), (self.mod2, 2)]), set(self.importer.get_name_locations("myvar")), ) def test_handling_builtin_modules(self): self.importer.update_module("sys") self.assertTrue("sys" in self.importer.get_modules("exit")) def test_submodules(self): self.assertEqual(set([self.mod1]), autoimport.submodules(self.mod1)) self.assertEqual(set([self.mod2, self.pkg]), autoimport.submodules(self.pkg)) class AutoImportObservingTest(unittest.TestCase): def setUp(self): super(AutoImportObservingTest, self).setUp() self.project = testutils.sample_project() self.mod1 = testutils.create_module(self.project, "mod1") self.pkg = testutils.create_package(self.project, "pkg") self.mod2 = testutils.create_module(self.project, "mod2", self.pkg) self.importer = autoimport.AutoImport(self.project, observe=True) def tearDown(self): testutils.remove_project(self.project) super(AutoImportObservingTest, self).tearDown() def test_writing_files(self): self.mod1.write("myvar = None\n") self.assertEqual(["mod1"], self.importer.get_modules("myvar")) def test_moving_files(self): self.mod1.write("myvar = None\n") self.mod1.move("mod3.py") self.assertEqual(["mod3"], self.importer.get_modules("myvar")) def test_removing_files(self): self.mod1.write("myvar = None\n") self.mod1.remove() self.assertEqual([], self.importer.get_modules("myvar")) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/ropetest/contrib/changestacktest.py0000664000175000017500000000204400000000000021465 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest import rope.base.history import rope.contrib.changestack import rope.base.change from ropetest import testutils class ChangeStackTest(unittest.TestCase): def setUp(self): super(ChangeStackTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(ChangeStackTest, self).tearDown() def test_change_stack(self): myfile = self.project.root.create_file("myfile.txt") myfile.write("1") stack = rope.contrib.changestack.ChangeStack(self.project) stack.push(rope.base.change.ChangeContents(myfile, "2")) self.assertEqual("2", myfile.read()) stack.push(rope.base.change.ChangeContents(myfile, "3")) self.assertEqual("3", myfile.read()) stack.pop_all() self.assertEqual("1", myfile.read()) changes = stack.merged() self.project.do(changes) self.assertEqual("3", myfile.read()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/contrib/codeassisttest.py0000664000175000017500000015571300000000000021367 0ustar00lieryanlieryan# coding: utf-8 import os.path from textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.base import exceptions from rope.contrib.codeassist import ( get_definition_location, get_doc, starting_expression, code_assist, sorted_proposals, starting_offset, get_calltip, get_canonical_path, ) from ropetest import testutils try: unicode except NameError: unicode = str class CodeAssistTest(unittest.TestCase): def setUp(self): super(CodeAssistTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(CodeAssistTest, self).tearDown() def _assist(self, code, offset=None, **args): if offset is None: offset = len(code) return code_assist(self.project, code, offset, **args) def test_simple_assist(self): self._assist("", 0) def assert_completion_in_result(self, name, scope, result, type=None): for proposal in result: if proposal.name == name: self.assertEqual( scope, proposal.scope, "proposal <%s> has wrong scope, expected " "%r, got %r" % (name, scope, proposal.scope), ) if type is not None: self.assertEqual( type, proposal.type, "proposal <%s> has wrong type, expected " "%r, got %r" % (name, type, proposal.type), ) return self.fail("completion <%s> not proposed" % name) def assert_completion_not_in_result(self, name, scope, result): for proposal in result: if proposal.name == name and proposal.scope == scope: self.fail("completion <%s> was proposed" % name) def test_completing_global_variables(self): code = dedent("""\ my_global = 10 t = my""") result = self._assist(code) self.assert_completion_in_result("my_global", "global", result) def test_not_proposing_unmatched_vars(self): code = dedent("""\ my_global = 10 t = you""") result = self._assist(code) self.assert_completion_not_in_result("my_global", "global", result) def test_not_proposing_unmatched_vars_with_underlined_starting(self): code = dedent("""\ my_global = 10 t = your_""") result = self._assist(code) self.assert_completion_not_in_result("my_global", "global", result) def test_not_proposing_local_assigns_as_global_completions(self): code = dedent("""\ def f(): my_global = 10 t = my_""") result = self._assist(code) self.assert_completion_not_in_result("my_global", "global", result) def test_proposing_functions(self): code = dedent("""\ def my_func(): return 2 t = my_""") result = self._assist(code) self.assert_completion_in_result("my_func", "global", result) def test_proposing_classes(self): code = dedent("""\ class Sample(object): pass t = Sam""") result = self._assist(code) self.assert_completion_in_result("Sample", "global", result) def test_proposing_each_name_at_most_once(self): code = dedent("""\ variable = 10 variable = 20 t = vari""") result = self._assist(code) count = len([x for x in result if x.name == "variable" and x.scope == "global"]) self.assertEqual(1, count) def test_throwing_exception_in_case_of_syntax_errors(self): code = dedent("""\ sample (sdf+) """) with self.assertRaises(exceptions.ModuleSyntaxError): self._assist(code, maxfixes=0) def test_fixing_errors_with_maxfixes(self): code = dedent("""\ def f(): sldj sldj def g(): ran""") result = self._assist(code, maxfixes=2) self.assertTrue(len(result) > 0) def test_ignoring_errors_in_current_line(self): code = dedent("""\ def my_func(): return 2 t = """) result = self._assist(code) self.assert_completion_in_result("my_func", "global", result) def test_not_reporting_variables_in_current_line(self): code = dedent("""\ def my_func(): return 2 t = my_""") result = self._assist(code) self.assert_completion_not_in_result("my_", "global", result) def test_completion_result(self): code = dedent("""\ my_global = 10 t = my""") self.assertEqual(len(code) - 2, starting_offset(code, len(code))) def test_completing_imported_names(self): code = dedent("""\ import sys a = sy""") result = self._assist(code) self.assert_completion_in_result("sys", "imported", result) def test_completing_imported_names_with_as(self): code = dedent("""\ import sys as mysys a = mys""") result = self._assist(code) self.assert_completion_in_result("mysys", "imported", result) def test_not_completing_imported_names_with_as(self): code = dedent("""\ import sys as mysys a = sy""") result = self._assist(code) self.assert_completion_not_in_result("sys", "global", result) def test_including_matching_builtins_types(self): code = "my_var = Excep" result = self._assist(code) self.assert_completion_in_result("Exception", "builtin", result) self.assert_completion_not_in_result("zip", "builtin", result) def test_including_matching_builtins_functions(self): code = "my_var = zi" result = self._assist(code) self.assert_completion_in_result("zip", "builtin", result) def test_builtin_instances(self): # ``import_dynload_stdmods`` pref is disabled for test project. # we need to have it enabled to make pycore._find_module() # load ``sys`` module. self.project.prefs["import_dynload_stdmods"] = True code = dedent("""\ from sys import stdout stdout.wr""") result = self._assist(code) self.assert_completion_in_result("write", "builtin", result) self.assert_completion_in_result("writelines", "builtin", result) def test_including_keywords(self): code = "fo" result = self._assist(code) self.assert_completion_in_result("for", "keyword", result) def test_not_reporting_proposals_after_dot(self): code = dedent("""\ a_dict = {} key = 3 a_dict.ke""") result = self._assist(code) self.assert_completion_not_in_result("key", "global", result) def test_proposing_local_variables_in_functions(self): code = dedent("""\ def f(self): my_var = 10 my_""") result = self._assist(code) self.assert_completion_in_result("my_var", "local", result) def test_local_variables_override_global_ones(self): code = dedent("""\ my_var = 20 def f(self): my_var = 10 my_""") result = self._assist(code) self.assert_completion_in_result("my_var", "local", result) def test_not_including_class_body_variables(self): code = dedent("""\ class C(object): my_var = 20 def f(self): a = 20 my_""") result = self._assist(code) self.assert_completion_not_in_result("my_var", "local", result) def test_nested_functions(self): code = dedent("""\ def my_func(): func_var = 20 def inner_func(): a = 20 func""") result = self._assist(code) self.assert_completion_in_result("func_var", "local", result) def test_scope_endpoint_selection(self): code = dedent("""\ def my_func(): func_var = 20 """) result = self._assist(code) self.assert_completion_not_in_result("func_var", "local", result) def test_scope_better_endpoint_selection(self): code = dedent("""\ if True: def f(): my_var = 10 my_""") result = self._assist(code) self.assert_completion_not_in_result("my_var", "local", result) def test_imports_inside_function(self): code = dedent("""\ def f(): import sys sy""") result = self._assist(code) self.assert_completion_in_result("sys", "imported", result) def test_imports_inside_function_dont_mix_with_globals(self): code = dedent("""\ def f(): import sys sy""") result = self._assist(code) self.assert_completion_not_in_result("sys", "local", result) def test_nested_classes_local_names(self): code = dedent("""\ global_var = 10 def my_func(): func_var = 20 class C(object): def another_func(self): local_var = 10 func""") result = self._assist(code) self.assert_completion_in_result("func_var", "local", result) def test_nested_classes_global(self): code = dedent("""\ global_var = 10 def my_func(): func_var = 20 class C(object): def another_func(self): local_var = 10 globa""") result = self._assist(code) self.assert_completion_in_result("global_var", "global", result) def test_nested_classes_global_function(self): code = dedent("""\ global_var = 10 def my_func(): func_var = 20 class C(object): def another_func(self): local_var = 10 my_f""") result = self._assist(code) self.assert_completion_in_result("my_func", "global", result) def test_proposing_function_parameters_in_functions(self): code = dedent("""\ def my_func(my_param): my_var = 20 my_""") result = self._assist(code) self.assert_completion_in_result("my_param", "local", result) def test_proposing_function_keyword_parameters_in_functions(self): code = dedent("""\ def my_func(my_param, *my_list, **my_kws): my_var = 20 my_""") result = self._assist(code) self.assert_completion_in_result("my_param", "local", result) self.assert_completion_in_result("my_list", "local", result) self.assert_completion_in_result("my_kws", "local", result) def test_not_proposing_unmatching_function_parameters_in_functions(self): code = dedent("""\ def my_func(my_param): my_var = 20 you_""") result = self._assist(code) self.assert_completion_not_in_result("my_param", "local", result) def test_ignoring_current_statement(self): code = dedent("""\ my_var = 10 my_tuple = (10, my_""") result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignoring_current_statement_brackets_continuation(self): code = dedent("""\ my_var = 10 'hello'[10: my_""") result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignoring_current_statement_explicit_continuation(self): code = dedent("""\ my_var = 10 my_var2 = 2 + \\ my_""") result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignor_current_statement_while_the_first_stmnt_of_the_block(self): code = dedent("""\ my_var = 10 def f(): my_""") result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignor_current_stmnt_while_current_line_ends_with_a_colon(self): code = dedent("""\ my_var = 10 if my_: pass""") result = self._assist(code, 18) self.assert_completion_in_result("my_var", "global", result) def test_ignoring_string_contents(self): code = "my_var = '('\nmy_" result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignoring_comment_contents(self): code = "my_var = 10 #(\nmy_" result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignoring_string_contents_backslash_plus_quotes(self): code = "my_var = '\\''\nmy_" result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignoring_string_contents_backslash_plus_backslash(self): code = "my_var = '\\\\'\nmy_" result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_not_proposing_later_defined_variables_in_current_block(self): code = dedent("""\ my_ my_var = 10 """) result = self._assist(code, 3, later_locals=False) self.assert_completion_not_in_result("my_var", "global", result) def test_not_proposing_later_defined_variables_in_current_function(self): code = dedent("""\ def f(): my_ my_var = 10 """) result = self._assist(code, 16, later_locals=False) self.assert_completion_not_in_result("my_var", "local", result) def test_ignoring_string_contents_with_triple_quotes(self): code = "my_var = '''(\n'('''\nmy_" result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignoring_string_contents_with_triple_quotes_and_backslash(self): code = 'my_var = """\\"""("""\nmy_' result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_ignor_str_contents_with_triple_quotes_and_double_backslash(self): code = 'my_var = """\\\\"""\nmy_' result = self._assist(code) self.assert_completion_in_result("my_var", "global", result) def test_reporting_params_when_in_the_first_line_of_a_function(self): code = dedent("""\ def f(param): para""") result = self._assist(code) self.assert_completion_in_result("param", "local", result) def test_code_assist_when_having_a_two_line_function_header(self): code = dedent("""\ def f(param1, param2): para""") result = self._assist(code) self.assert_completion_in_result("param1", "local", result) def test_code_assist_with_function_with_two_line_return(self): code = dedent("""\ def f(param1, param2): return(param1, para""") result = self._assist(code) self.assert_completion_in_result("param2", "local", result) def test_get_definition_location(self): code = dedent("""\ def a_func(): pass a_func()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 1), result) def test_get_definition_location_underlined_names(self): code = dedent("""\ def a_sample_func(): pass a_sample_func()""") result = get_definition_location(self.project, code, len(code) - 11) self.assertEqual((None, 1), result) def test_get_definition_location_dotted_names_method(self): code = dedent("""\ class AClass(object): @staticmethod def a_method(): pass AClass.a_method()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 3), result) def test_get_definition_location_dotted_names_property(self): code = dedent("""\ class AClass(object): @property @somedecorator def a_method(): pass AClass.a_method()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 4), result) def test_get_definition_location_dotted_names_free_function(self): code = dedent("""\ @custom_decorator def a_method(): pass a_method()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 2), result) @testutils.only_for_versions_higher("3.5") def test_get_definition_location_dotted_names_async_def(self): code = dedent("""\ class AClass(object): @property @decorator2 async def a_method(): pass AClass.a_method()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 4), result) def test_get_definition_location_dotted_names_class(self): code = dedent("""\ @custom_decorator class AClass(object): def a_method(): pass AClass.a_method()""") result = get_definition_location(self.project, code, len(code) - 12) self.assertEqual((None, 2), result) def test_get_definition_location_dotted_names_with_space(self): code = dedent("""\ class AClass(object): @staticmethod def a_method(): pass AClass.a_method()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 3), result) def test_get_definition_location_dotted_names_inline_body(self): code = dedent("""\ class AClass(object): @staticmethod def a_method(): pass AClass.a_method()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 3), result) def test_get_definition_location_dotted_names_inline_body_split_arg(self): code = dedent("""\ class AClass(object): @staticmethod def a_method( self, arg1 ): pass AClass.a_method()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 3), result) def test_get_definition_location_dotted_module_names(self): module_resource = testutils.create_module(self.project, "mod") module_resource.write("def a_func():\n pass\n") code = "import mod\nmod.a_func()" result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((module_resource, 1), result) def test_get_definition_location_for_nested_packages(self): mod1 = testutils.create_module(self.project, "mod1") pkg1 = testutils.create_package(self.project, "pkg1") pkg2 = testutils.create_package(self.project, "pkg2", pkg1) mod1.write("import pkg1.pkg2.mod2") init_dot_py = pkg2.get_child("__init__.py") found_pyname = get_definition_location( self.project, mod1.read(), mod1.read().index("pkg2") + 1 ) self.assertEqual(init_dot_py, found_pyname[0]) def test_get_definition_location_unknown(self): code = "a_func()\n" result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, None), result) def test_get_definition_location_dot_spaces(self): code = dedent("""\ class AClass(object): @staticmethod def a_method(): pass AClass.\\ a_method()""") result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 3), result) def test_get_definition_location_dot_line_break_inside_parens(self): code = dedent("""\ class A(object): def a_method(self): pass (A. a_method)""") result = get_definition_location( self.project, code, code.rindex("a_method") + 1 ) self.assertEqual((None, 2), result) def test_if_scopes_in_other_scopes_for_get_definition_location(self): code = dedent("""\ def f(a_var): pass a_var = 10 if True: print(a_var) """) result = get_definition_location(self.project, code, len(code) - 3) self.assertEqual((None, 3), result) def test_get_definition_location_false_triple_quoted_string(self): code = dedent( dedent('''\ def foo(): a = 0 p = "foo""" def bar(): a = 1 a += 1 ''') ) result = get_definition_location(self.project, code, code.index("a += 1")) self.assertEqual((None, 6), result) def test_code_assists_in_parens(self): code = dedent("""\ def a_func(a_var): pass a_var = 10 a_func(a_""") result = self._assist(code) self.assert_completion_in_result("a_var", "global", result) def test_simple_type_inferencing(self): code = dedent("""\ class Sample(object): def __init__(self, a_param): pass def a_method(self): pass Sample("hey").a_""") result = self._assist(code) self.assert_completion_in_result("a_method", "attribute", result) def test_proposals_sorter(self): code = dedent("""\ def my_sample_function(self): my_sample_var = 20 my_sample_""") proposals = sorted_proposals(self._assist(code)) self.assertEqual("my_sample_var", proposals[0].name) self.assertEqual("my_sample_function", proposals[1].name) def test_proposals_sorter_for_methods_and_attributes(self): code = dedent("""\ class A(object): def __init__(self): self.my_a_var = 10 def my_b_func(self): pass def my_c_func(self): pass a_var = A() a_var.my_""") proposals = sorted_proposals(self._assist(code)) self.assertEqual("my_b_func", proposals[0].name) self.assertEqual("my_c_func", proposals[1].name) self.assertEqual("my_a_var", proposals[2].name) def test_proposals_sorter_for_global_methods_and_funcs(self): code = dedent("""\ def my_b_func(self): pass my_a_var = 10 my_""") proposals = sorted_proposals(self._assist(code)) self.assertEqual("my_b_func", proposals[0].name) self.assertEqual("my_a_var", proposals[1].name) def test_proposals_sorter_underlined_methods(self): code = dedent("""\ class A(object): def _my_func(self): self.my_a_var = 10 def my_func(self): pass a_var = A() a_var.""") proposals = sorted_proposals(self._assist(code)) self.assertEqual("my_func", proposals[0].name) self.assertEqual("_my_func", proposals[1].name) def test_proposals_sorter_and_scope_prefs(self): code = dedent("""\ my_global_var = 1 def func(self): my_local_var = 2 my_""") result = self._assist(code) proposals = sorted_proposals(result, scopepref=["global", "local"]) self.assertEqual("my_global_var", proposals[0].name) self.assertEqual("my_local_var", proposals[1].name) def test_proposals_sorter_and_type_prefs(self): code = dedent("""\ my_global_var = 1 def my_global_func(self): pass my_""") result = self._assist(code) proposals = sorted_proposals(result, typepref=["instance", "function"]) self.assertEqual("my_global_var", proposals[0].name) self.assertEqual("my_global_func", proposals[1].name) def test_proposals_sorter_and_missing_type_in_typepref(self): code = dedent("""\ my_global_var = 1 def my_global_func(): pass my_""") result = self._assist(code) proposals = sorted_proposals(result, typepref=["function"]) # noqa def test_get_pydoc_unicode(self): src = dedent('''\ # coding: utf-8 def foo(): u"юникод-объект"''') doc = get_doc(self.project, src, src.index("foo") + 1) self.assertTrue(isinstance(doc, unicode)) self.assertTrue(u"юникод-объект" in doc) def test_get_pydoc_utf8_bytestring(self): src = dedent('''\ # coding: utf-8 def foo(): "байтстринг"''') doc = get_doc(self.project, src, src.index("foo") + 1) self.assertTrue(isinstance(doc, unicode)) self.assertTrue(u"байтстринг" in doc) def test_get_pydoc_for_functions(self): src = dedent('''\ def a_func(): """a function""" a_var = 10 a_func()''') self.assertTrue(get_doc(self.project, src, len(src) - 4).endswith("a function")) get_doc(self.project, src, len(src) - 4).index("a_func()") def test_get_pydoc_for_classes(self): src = dedent("""\ class AClass(object): pass """) get_doc(self.project, src, src.index("AClass") + 1).index("AClass") def test_get_pydoc_for_classes_with_init(self): src = dedent("""\ class AClass(object): def __init__(self): pass """) get_doc(self.project, src, src.index("AClass") + 1).index("AClass") def test_get_pydoc_for_modules(self): mod = testutils.create_module(self.project, "mod") mod.write('"""a module"""\n') src = "import mod\nmod" self.assertEqual("a module", get_doc(self.project, src, len(src) - 1)) def test_get_pydoc_for_builtins(self): src = "print(object)\n" self.assertTrue(get_doc(self.project, src, src.index("obj")) is not None) def test_get_pydoc_for_methods_should_include_class_name(self): src = dedent('''\ class AClass(object): def a_method(self): """hey""" pass ''') doc = get_doc(self.project, src, src.index("a_method") + 1) doc.index("AClass.a_method") doc.index("hey") def test_get_pydoc_for_meths_should_inc_methods_from_super_classes(self): src = dedent('''\ class A(object): def a_method(self): """hey1""" pass class B(A): def a_method(self): """hey2""" pass ''') doc = get_doc(self.project, src, src.rindex("a_method") + 1) doc.index("A.a_method") doc.index("hey1") doc.index("B.a_method") doc.index("hey2") def test_get_pydoc_for_classes_should_name_super_classes(self): src = dedent("""\ class A(object): pass class B(A): pass """) doc = get_doc(self.project, src, src.rindex("B") + 1) doc.index("B(A)") def test_get_pydoc_for_builtin_functions(self): src = dedent("""\ s = "hey" s.replace """) doc = get_doc(self.project, src, src.rindex("replace") + 1) self.assertTrue(doc is not None) def test_commenting_errors_before_offset(self): src = dedent("""\ lsjd lsjdf s = "hey" s.replace() """) doc = get_doc(self.project, src, src.rindex("replace") + 1) # noqa def test_proposing_variables_defined_till_the_end_of_scope(self): code = dedent("""\ if True: a_v a_var = 10 """) result = self._assist(code, code.index("a_v") + 3) self.assert_completion_in_result("a_var", "global", result) def test_completing_in_uncomplete_try_blocks(self): code = dedent("""\ try: a_var = 10 a_""") result = self._assist(code) self.assert_completion_in_result("a_var", "global", result) def test_completing_in_uncomplete_try_blocks_in_functions(self): code = dedent("""\ def a_func(): try: a_var = 10 a_""") result = self._assist(code) self.assert_completion_in_result("a_var", "local", result) def test_already_complete_try_blocks_with_finally(self): code = dedent("""\ def a_func(): try: a_var = 10 a_""") result = self._assist(code) self.assert_completion_in_result("a_var", "local", result) def test_already_complete_try_blocks_with_finally2(self): code = dedent("""\ try: a_var = 10 a_ finally: pass """) result = self._assist(code, code.rindex("a_") + 2) self.assert_completion_in_result("a_var", "global", result) def test_already_complete_try_blocks_with_except(self): code = dedent("""\ try: a_var = 10 a_ except Exception: pass """) result = self._assist(code, code.rindex("a_") + 2) self.assert_completion_in_result("a_var", "global", result) def test_already_complete_try_blocks_with_except2(self): code = dedent("""\ a_var = 10 try: another_var = a_ another_var = 10 except Exception: pass """) result = self._assist(code, code.rindex("a_") + 2) self.assert_completion_in_result("a_var", "global", result) def test_completing_ifs_in_uncomplete_try_blocks(self): code = dedent("""\ try: if True: a_var = 10 a_""") result = self._assist(code) self.assert_completion_in_result("a_var", "global", result) def test_completing_ifs_in_uncomplete_try_blocks2(self): code = dedent("""\ try: if True: a_var = 10 a_""") result = self._assist(code) self.assert_completion_in_result("a_var", "global", result) def test_completing_excepts_in_uncomplete_try_blocks(self): code = dedent("""\ try: pass except Exc""") result = self._assist(code) self.assert_completion_in_result("Exception", "builtin", result) def test_and_normal_complete_blocks_and_single_fixing(self): code = dedent("""\ try: range. except: pass """) result = self._assist(code, code.index("."), maxfixes=1) # noqa def test_nested_blocks(self): code = dedent("""\ a_var = 10 try: try: a_v""") result = self._assist(code) self.assert_completion_in_result("a_var", "global", result) def test_proposing_function_keywords_when_calling(self): code = dedent("""\ def f(p): pass f(p""") result = self._assist(code) self.assert_completion_in_result("p=", "parameter_keyword", result) def test_proposing_function_keywords_when_calling_for_non_functions(self): code = dedent("""\ f = 1 f(p""") result = self._assist(code) # noqa def test_proposing_function_keywords_when_calling_extra_spaces(self): code = dedent("""\ def f(p): pass f( p""") result = self._assist(code) self.assert_completion_in_result("p=", "parameter_keyword", result) def test_proposing_function_keywords_when_calling_on_second_argument(self): code = dedent("""\ def f(p1, p2): pass f(1, p""") result = self._assist(code) self.assert_completion_in_result("p2=", "parameter_keyword", result) def test_proposing_function_keywords_when_calling_not_proposing_args(self): code = dedent("""\ def f(p1, *args): pass f(1, a""") result = self._assist(code) self.assert_completion_not_in_result("args=", "parameter_keyword", result) def test_propos_function_kwrds_when_call_with_no_noth_after_parens(self): code = dedent("""\ def f(p): pass f(""") result = self._assist(code) self.assert_completion_in_result("p=", "parameter_keyword", result) def test_propos_function_kwrds_when_call_with_no_noth_after_parens2(self): code = dedent("""\ def f(p): pass def g(): h = f f(""") result = self._assist(code) self.assert_completion_in_result("p=", "parameter_keyword", result) def test_codeassists_before_opening_of_parens(self): code = dedent("""\ def f(p): pass a_var = 1 f(1) """) result = self._assist(code, code.rindex("f") + 1) self.assert_completion_not_in_result("a_var", "global", result) def test_codeassist_before_single_line_indents(self): code = dedent("""\ myvar = 1 if True: (myv if True: pass """) result = self._assist(code, code.rindex("myv") + 3) self.assert_completion_not_in_result("myvar", "local", result) def test_codeassist_before_line_indents_in_a_blank_line(self): code = dedent("""\ myvar = 1 if True: if True: pass """) result = self._assist(code, code.rindex(" ") + 4) self.assert_completion_not_in_result("myvar", "local", result) def test_simple_get_calltips(self): src = dedent("""\ def f(): pass var = f() """) doc = get_calltip(self.project, src, src.rindex("f")) self.assertEqual("f()", doc) def test_get_calltips_for_classes(self): src = dedent("""\ class C(object): def __init__(self): pass C(""") doc = get_calltip(self.project, src, len(src) - 1) self.assertEqual("C.__init__(self)", doc) def test_get_calltips_for_objects_with_call(self): src = dedent("""\ class C(object): def __call__(self, p): pass c = C() c(1,""") doc = get_calltip(self.project, src, src.rindex("c")) self.assertEqual("C.__call__(self, p)", doc) def test_get_calltips_and_including_module_name(self): src = dedent("""\ class C(object): def __call__(self, p): pass c = C() c(1,""") mod = testutils.create_module(self.project, "mod") mod.write(src) doc = get_calltip(self.project, src, src.rindex("c"), mod) self.assertEqual("mod.C.__call__(self, p)", doc) def test_get_calltips_and_including_module_name_2(self): src = "range()\n" doc = get_calltip(self.project, src, 1, ignore_unknown=True) self.assertTrue(doc is None) def test_removing_self_parameter(self): src = dedent("""\ class C(object): def f(self): pass C().f()""") doc = get_calltip(self.project, src, src.rindex("f"), remove_self=True) self.assertEqual("C.f()", doc) def test_removing_self_parameter_and_more_than_one_parameter(self): src = dedent("""\ class C(object): def f(self, p1): pass C().f()""") doc = get_calltip(self.project, src, src.rindex("f"), remove_self=True) self.assertEqual("C.f(p1)", doc) def test_lambda_calltip(self): src = dedent("""\ foo = lambda x, y=1: None foo()""") doc = get_calltip(self.project, src, src.rindex("f")) self.assertEqual(doc, "lambda(x, y)") def test_keyword_before_parens(self): code = dedent("""\ if (1).: pass""") result = self._assist(code, offset=len("if (1).")) self.assertTrue(result) # TESTING PROPOSAL'S KINDS AND TYPES. # SEE RELATION MATRIX IN `CompletionProposal`'s DOCSTRING def test_local_variable_completion_proposal(self): code = dedent("""\ def foo(): xvar = 5 x""") result = self._assist(code) self.assert_completion_in_result("xvar", "local", result, "instance") def test_global_variable_completion_proposal(self): code = dedent("""\ yvar = 5 y""") result = self._assist(code) self.assert_completion_in_result("yvar", "global", result, "instance") def test_builtin_variable_completion_proposal(self): for varname in ("False", "True"): result = self._assist(varname[0]) self.assert_completion_in_result( varname, "builtin", result, type="instance" ) def test_attribute_variable_completion_proposal(self): code = dedent("""\ class AClass(object): def foo(self): self.bar = 1 self.b""") result = self._assist(code) self.assert_completion_in_result("bar", "attribute", result, type="instance") def test_local_class_completion_proposal(self): code = dedent("""\ def foo(): class LocalClass(object): pass Lo""") result = self._assist(code) self.assert_completion_in_result("LocalClass", "local", result, type="class") def test_global_class_completion_proposal(self): code = dedent("""\ class GlobalClass(object): pass Gl""") result = self._assist(code) self.assert_completion_in_result("GlobalClass", "global", result, type="class") def test_builtin_class_completion_proposal(self): for varname in ("object", "dict", "file"): result = self._assist(varname[0]) self.assert_completion_in_result(varname, "builtin", result, type="class") def test_attribute_class_completion_proposal(self): code = dedent("""\ class Outer(object): class Inner(object): pass Outer.""") result = self._assist(code) self.assert_completion_in_result("Inner", "attribute", result, type="class") def test_local_function_completion_proposal(self): code = dedent("""\ def outer(): def inner(): pass in""") result = self._assist(code) self.assert_completion_in_result("inner", "local", result, type="function") def test_global_function_completion_proposal(self): code = dedent("""\ def foo(): pass f""") result = self._assist(code) self.assert_completion_in_result("foo", "global", result, type="function") def test_builtin_function_completion_proposal(self): code = "a" result = self._assist(code) for expected in ("all", "any", "abs"): self.assert_completion_in_result( expected, "builtin", result, type="function" ) def test_attribute_function_completion_proposal(self): code = dedent("""\ class Some(object): def method(self): self.""") result = self._assist(code) self.assert_completion_in_result("method", "attribute", result, type="function") def test_local_module_completion_proposal(self): code = dedent("""\ def foo(): import types t""") result = self._assist(code) self.assert_completion_in_result("types", "imported", result, type="module") def test_global_module_completion_proposal(self): code = dedent("""\ import operator o""") result = self._assist(code) self.assert_completion_in_result("operator", "imported", result, type="module") def test_attribute_module_completion_proposal(self): code = dedent("""\ class Some(object): import os Some.o""") result = self._assist(code) self.assert_completion_in_result("os", "imported", result, type="module") def test_builtin_exception_completion_proposal(self): code = dedent("""\ def blah(): Z""") result = self._assist(code) self.assert_completion_in_result( "ZeroDivisionError", "builtin", result, type="class" ) def test_keyword_completion_proposal(self): code = "f" result = self._assist(code) self.assert_completion_in_result("for", "keyword", result, type=None) self.assert_completion_in_result("from", "keyword", result, type=None) def test_parameter_keyword_completion_proposal(self): code = dedent("""\ def func(abc, aloha, alpha, amigo): pass func(a""") result = self._assist(code) for expected in ("abc=", "aloha=", "alpha=", "amigo="): self.assert_completion_in_result( expected, "parameter_keyword", result, type=None ) def test_object_path_global(self): code = "GLOBAL_VARIABLE = 42\n" resource = testutils.create_module(self.project, "mod") resource.write(code) result = get_canonical_path(self.project, resource, 1) mod_path = os.path.join(self.project.address, "mod.py") self.assertEqual( result, [(mod_path, "MODULE"), ("GLOBAL_VARIABLE", "VARIABLE")] ) def test_object_path_attribute(self): code = dedent("""\ class Foo(object): attr = 42 """) resource = testutils.create_module(self.project, "mod") resource.write(code) result = get_canonical_path(self.project, resource, 24) mod_path = os.path.join(self.project.address, "mod.py") self.assertEqual( result, [(mod_path, "MODULE"), ("Foo", "CLASS"), ("attr", "VARIABLE")] ) def test_object_path_subclass(self): code = dedent("""\ class Foo(object): class Bar(object): pass """) resource = testutils.create_module(self.project, "mod") resource.write(code) result = get_canonical_path(self.project, resource, 30) mod_path = os.path.join(self.project.address, "mod.py") self.assertEqual( result, [(mod_path, "MODULE"), ("Foo", "CLASS"), ("Bar", "CLASS")] ) def test_object_path_method_parameter(self): code = dedent("""\ class Foo(object): def bar(self, a, b, c): pass """) resource = testutils.create_module(self.project, "mod") resource.write(code) result = get_canonical_path(self.project, resource, 41) mod_path = os.path.join(self.project.address, "mod.py") self.assertEqual( result, [ (mod_path, "MODULE"), ("Foo", "CLASS"), ("bar", "FUNCTION"), ("b", "PARAMETER"), ], ) def test_object_path_variable(self): code = dedent("""\ def bar(a): x = a + 42 """) resource = testutils.create_module(self.project, "mod") resource.write(code) result = get_canonical_path(self.project, resource, 17) mod_path = os.path.join(self.project.address, "mod.py") self.assertEqual( result, [(mod_path, "MODULE"), ("bar", "FUNCTION"), ("x", "VARIABLE")] ) class CodeAssistInProjectsTest(unittest.TestCase): def setUp(self): super(CodeAssistInProjectsTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore samplemod = testutils.create_module(self.project, "samplemod") code = dedent("""\ class SampleClass(object): def sample_method(): pass def sample_func(): pass sample_var = 10 def _underlined_func(): pass """) samplemod.write(code) package = testutils.create_package(self.project, "package") nestedmod = testutils.create_module(self.project, "nestedmod", package) # noqa def tearDown(self): testutils.remove_project(self.project) super(self.__class__, self).tearDown() def _assist(self, code, resource=None, **kwds): return code_assist(self.project, code, len(code), resource, **kwds) def assert_completion_in_result(self, name, scope, result): for proposal in result: if proposal.name == name and proposal.scope == scope: return self.fail("completion <%s> not proposed" % name) def assert_completion_not_in_result(self, name, scope, result): for proposal in result: if proposal.name == name and proposal.scope == scope: self.fail("completion <%s> was proposed" % name) def test_simple_import(self): code = dedent("""\ import samplemod sample""") result = self._assist(code) self.assert_completion_in_result("samplemod", "imported", result) def test_from_import_class(self): code = dedent("""\ from samplemod import SampleClass Sample""") result = self._assist(code) self.assert_completion_in_result("SampleClass", "imported", result) def test_from_import_function(self): code = dedent("""\ from samplemod import sample_func sample""") result = self._assist(code) self.assert_completion_in_result("sample_func", "imported", result) def test_from_import_variable(self): code = dedent("""\ from samplemod import sample_var sample""") result = self._assist(code) self.assert_completion_in_result("sample_var", "imported", result) def test_from_imports_inside_functions(self): code = dedent("""\ def f(): from samplemod import SampleClass Sample""") result = self._assist(code) self.assert_completion_in_result("SampleClass", "imported", result) def test_from_import_only_imports_imported(self): code = dedent("""\ from samplemod import sample_func Sample""") result = self._assist(code) self.assert_completion_not_in_result("SampleClass", "global", result) def test_from_import_star(self): code = dedent("""\ from samplemod import * Sample""") result = self._assist(code) self.assert_completion_in_result("SampleClass", "imported", result) def test_from_import_star2(self): code = dedent("""\ from samplemod import * sample""") result = self._assist(code) self.assert_completion_in_result("sample_func", "imported", result) self.assert_completion_in_result("sample_var", "imported", result) def test_from_import_star_not_imporing_underlined(self): code = dedent("""\ from samplemod import * _under""") result = self._assist(code) self.assert_completion_not_in_result("_underlined_func", "global", result) def test_from_package_import_mod(self): code = dedent("""\ from package import nestedmod nest""") result = self._assist(code) self.assert_completion_in_result("nestedmod", "imported", result) def test_completing_after_dot(self): code = dedent("""\ class SampleClass(object): def sample_method(self): pass SampleClass.sam""") result = self._assist(code) self.assert_completion_in_result("sample_method", "attribute", result) def test_completing_after_multiple_dots(self): code = dedent("""\ class Class1(object): class Class2(object): def sample_method(self): pass Class1.Class2.sam""") result = self._assist(code) self.assert_completion_in_result("sample_method", "attribute", result) def test_completing_after_self_dot(self): code = dedent("""\ class Sample(object): def method1(self): pass def method2(self): self.m""") result = self._assist(code) self.assert_completion_in_result("method1", "attribute", result) def test_result_start_offset_for_dotted_completions(self): code = dedent("""\ class Sample(object): def method1(self): pass Sample.me""") self.assertEqual(len(code) - 2, starting_offset(code, len(code))) def test_backslash_after_dots(self): code = dedent("""\ class Sample(object): def a_method(self): pass Sample.\\ a_m""") result = self._assist(code) self.assert_completion_in_result("a_method", "attribute", result) def test_not_proposing_global_names_after_dot(self): code = dedent("""\ class Sample(object): def a_method(self): pass Sample.""") result = self._assist(code) self.assert_completion_not_in_result("Sample", "global", result) def test_assist_on_relative_imports(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod2 = testutils.create_module(self.project, "mod2", pkg) mod1.write( dedent("""\ def a_func(): pass """) ) code = dedent("""\ import mod1 mod1.""") result = self._assist(code, resource=mod2) self.assert_completion_in_result("a_func", "imported", result) def test_get_location_on_relative_imports(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod2 = testutils.create_module(self.project, "mod2", pkg) mod1.write( dedent("""\ def a_func(): pass """) ) code = dedent("""\ import mod1 mod1.a_func """) result = get_definition_location(self.project, code, len(code) - 2, mod2) self.assertEqual((mod1, 1), result) def test_get_definition_location_for_builtins(self): code = "import sys\n" result = get_definition_location(self.project, code, len(code) - 2) self.assertEqual((None, None), result) def test_get_doc_on_relative_imports(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod2 = testutils.create_module(self.project, "mod2", pkg) mod1.write( dedent('''\ def a_func(): """hey""" pass ''') ) code = dedent("""\ import mod1 mod1.a_func """) result = get_doc(self.project, code, len(code) - 2, mod2) self.assertTrue(result.endswith("hey")) def test_get_doc_on_from_import_module(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent('''\ """mod1 docs""" var = 1 ''') ) code = "from mod1 import var\n" result = get_doc(self.project, code, code.index("mod1")) result.index("mod1 docs") def test_fixing_errors_with_maxfixes_in_resources(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ def f(): sldj sldj def g(): ran""") mod.write(code) result = self._assist(code, maxfixes=2, resource=mod) self.assertTrue(len(result) > 0) def test_completing_names_after_from_import(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("myvar = None\n") result = self._assist("from mod1 import myva", resource=mod2) self.assertTrue(len(result) > 0) self.assert_completion_in_result("myvar", "global", result) def test_completing_names_after_from_import_and_sorted_proposals(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("myvar = None\n") result = self._assist("from mod1 import myva", resource=mod2) result = sorted_proposals(result) self.assertTrue(len(result) > 0) self.assert_completion_in_result("myvar", "global", result) def test_completing_names_after_from_import2(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("myvar = None\n") result = self._assist("from mod1 import ", resource=mod2) self.assertTrue(len(result) > 0) self.assert_completion_in_result("myvar", "global", result) def test_starting_expression(self): code = dedent("""\ l = list() l.app""") self.assertEqual("l.app", starting_expression(code, len(code))) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/contrib/finderrorstest.py0000664000175000017500000000334700000000000021376 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.contrib import finderrors from ropetest import testutils class FindErrorsTest(unittest.TestCase): def setUp(self): super(FindErrorsTest, self).setUp() self.project = testutils.sample_project() self.mod = self.project.root.create_file("mod.py") def tearDown(self): testutils.remove_project(self.project) super(FindErrorsTest, self).tearDown() def test_unresolved_variables(self): self.mod.write("print(var)\n") result = finderrors.find_errors(self.project, self.mod) self.assertEqual(1, len(result)) self.assertEqual(1, result[0].lineno) def test_defined_later(self): self.mod.write( dedent("""\ print(var) var = 1 """) ) result = finderrors.find_errors(self.project, self.mod) self.assertEqual(1, len(result)) self.assertEqual(1, result[0].lineno) def test_ignoring_builtins(self): self.mod.write("range(2)\n") result = finderrors.find_errors(self.project, self.mod) self.assertEqual(0, len(result)) def test_ignoring_none(self): self.mod.write("var = None\n") result = finderrors.find_errors(self.project, self.mod) self.assertEqual(0, len(result)) def test_bad_attributes(self): code = dedent("""\ class C(object): pass c = C() print(c.var) """) self.mod.write(code) result = finderrors.find_errors(self.project, self.mod) self.assertEqual(1, len(result)) self.assertEqual(4, result[0].lineno) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/contrib/findittest.py0000664000175000017500000001340200000000000020467 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.base import exceptions from rope.contrib.findit import find_occurrences, find_implementations, find_definition from ropetest import testutils class FindItTest(unittest.TestCase): def setUp(self): super(FindItTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(FindItTest, self).tearDown() def test_finding_occurrences(self): mod = testutils.create_module(self.project, "mod") mod.write("a_var = 1\n") result = find_occurrences(self.project, mod, 1) self.assertEqual(mod, result[0].resource) self.assertEqual(0, result[0].offset) self.assertEqual(False, result[0].unsure) def test_finding_occurrences_in_more_than_one_module(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("a_var = 1\n") mod2.write( dedent("""\ import mod1 my_var = mod1.a_var""" ) ) result = find_occurrences(self.project, mod1, 1) self.assertEqual(2, len(result)) modules = (result[0].resource, result[1].resource) self.assertTrue(mod1 in modules and mod2 in modules) def test_finding_occurrences_matching_when_unsure(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ class C(object): def a_func(self): pass def f(arg): arg.a_func() """) ) result = find_occurrences( self.project, mod1, mod1.read().index("a_func"), unsure=True ) self.assertEqual(2, len(result)) def test_find_occurrences_resources_parameter(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("a_var = 1\n") mod2.write( dedent("""\ import mod1 my_var = mod1.a_var""" ) ) result = find_occurrences(self.project, mod1, 1, resources=[mod1]) self.assertEqual(1, len(result)) self.assertEqual((mod1, 0), (result[0].resource, result[0].offset)) def test_find_occurrences_and_class_hierarchies(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ class A(object): def f(): pass class B(A): def f(): pass """) ) offset = mod1.read().rindex("f") result1 = find_occurrences(self.project, mod1, offset) result2 = find_occurrences(self.project, mod1, offset, in_hierarchy=True) self.assertEqual(1, len(result1)) self.assertEqual(2, len(result2)) def test_trivial_find_implementations(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ class A(object): def f(self): pass """) ) offset = mod1.read().rindex("f(") result = find_implementations(self.project, mod1, offset) self.assertEqual([], result) def test_find_implementations_and_not_returning_parents(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ class A(object): def f(self): pass class B(A): def f(self): pass """) ) offset = mod1.read().rindex("f(") result = find_implementations(self.project, mod1, offset) self.assertEqual([], result) def test_find_implementations_real_implementation(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ class A(object): def f(self): pass class B(A): def f(self): pass """) ) offset = mod1.read().index("f(") result = find_implementations(self.project, mod1, offset) self.assertEqual(1, len(result)) self.assertEqual(mod1.read().rindex("f("), result[0].offset) def test_find_implementations_real_implementation_simple(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write("class A(object):\n pass\n") offset = mod1.read().index("A") with self.assertRaises(exceptions.BadIdentifierError): find_implementations(self.project, mod1, offset) def test_trivial_find_definition(self): code = dedent("""\ def a_func(): pass a_func()""") result = find_definition(self.project, code, code.rindex("a_func")) start = code.index("a_func") self.assertEqual(start, result.offset) self.assertEqual(None, result.resource) self.assertEqual(1, result.lineno) self.assertEqual((start, start + len("a_func")), result.region) def test_find_definition_in_other_modules(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write("var = 1\n") code = dedent("""\ import mod1 print(mod1.var) """) result = find_definition(self.project, code, code.index("var")) self.assertEqual(mod1, result.resource) self.assertEqual(0, result.offset) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/ropetest/contrib/fixmodnamestest.py0000664000175000017500000000420100000000000021521 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest from ropetest import testutils from rope.contrib.fixmodnames import FixModuleNames from rope.contrib.generate import create_module, create_package # HACK: for making this test work on case-insensitive file-systems, it # uses a name.replace('x', '_') fixer. class FixModuleNamesTest(unittest.TestCase): def setUp(self): super(FixModuleNamesTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(FixModuleNamesTest, self).tearDown() def test_simple_module_renaming(self): mod = create_module(self.project, "xod") self.project.do(FixModuleNames(self.project).get_changes(_fixer)) self.assertFalse(mod.exists()) self.assertTrue(self.project.get_resource("_od.py").exists()) def test_packages_module_renaming(self): pkg = create_package(self.project, "xkg") self.project.do(FixModuleNames(self.project).get_changes(_fixer)) self.assertFalse(pkg.exists()) self.assertTrue(self.project.get_resource("_kg/__init__.py").exists()) def test_fixing_contents(self): mod1 = create_module(self.project, "xod1") mod2 = create_module(self.project, "xod2") mod1.write("import xod2\n") mod2.write("import xod1\n") self.project.do(FixModuleNames(self.project).get_changes(_fixer)) newmod1 = self.project.get_resource("_od1.py") newmod2 = self.project.get_resource("_od2.py") self.assertEqual("import _od2\n", newmod1.read()) self.assertEqual("import _od1\n", newmod2.read()) def test_handling_nested_modules(self): pkg = create_package(self.project, "xkg") mod = create_module(self.project, "xkg.xod") # noqa self.project.do(FixModuleNames(self.project).get_changes(_fixer)) self.assertFalse(pkg.exists()) self.assertTrue(self.project.get_resource("_kg/__init__.py").exists()) self.assertTrue(self.project.get_resource("_kg/_od.py").exists()) def _fixer(name): return name.replace("x", "_") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/contrib/generatetest.py0000664000175000017500000003570200000000000021013 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.base import exceptions from rope.contrib import generate from ropetest import testutils class GenerateTest(unittest.TestCase): def setUp(self): super(GenerateTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod1") self.mod2 = testutils.create_module(self.project, "mod2") self.pkg = testutils.create_package(self.project, "pkg") def tearDown(self): testutils.remove_project(self.project) super(GenerateTest, self).tearDown() def _get_generate(self, offset): return generate.GenerateVariable(self.project, self.mod, offset) def _get_generate_class(self, offset, goal_mod=None): return generate.GenerateClass( self.project, self.mod, offset, goal_resource=goal_mod ) def _get_generate_module(self, offset): return generate.GenerateModule(self.project, self.mod, offset) def _get_generate_package(self, offset): return generate.GeneratePackage(self.project, self.mod, offset) def _get_generate_function(self, offset): return generate.GenerateFunction(self.project, self.mod, offset) def test_getting_location(self): code = "a_var = name\n" self.mod.write(code) generator = self._get_generate(code.index("name")) self.assertEqual((self.mod, 1), generator.get_location()) def test_generating_variable(self): code = dedent("""\ a_var = name """) self.mod.write(code) changes = self._get_generate(code.index("name")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ name = None a_var = name """), self.mod.read(), ) def test_generating_variable_inserting_before_statement(self): code = dedent("""\ c = 1 c = b """) self.mod.write(code) changes = self._get_generate(code.index("b")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ c = 1 b = None c = b """), self.mod.read(), ) def test_generating_variable_in_local_scopes(self): code = dedent("""\ def f(): c = 1 c = b """) self.mod.write(code) changes = self._get_generate(code.index("b")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ def f(): c = 1 b = None c = b """), self.mod.read(), ) def test_generating_variable_in_other_modules(self): code = dedent("""\ import mod2 c = mod2.b """) self.mod.write(code) generator = self._get_generate(code.index("b")) self.project.do(generator.get_changes()) self.assertEqual((self.mod2, 1), generator.get_location()) self.assertEqual("b = None\n", self.mod2.read()) def test_generating_variable_in_classes(self): code = dedent("""\ class C(object): def f(self): pass c = C() a_var = c.attr""") self.mod.write(code) changes = self._get_generate(code.index("attr")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): def f(self): pass attr = None c = C() a_var = c.attr""" ), self.mod.read(),) def test_generating_variable_in_classes_removing_pass(self): code = dedent("""\ class C(object): pass c = C() a_var = c.attr""") self.mod.write(code) changes = self._get_generate(code.index("attr")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): attr = None c = C() a_var = c.attr""" ), self.mod.read(),) def test_generating_variable_in_packages(self): code = "import pkg\na = pkg.a\n" self.mod.write(code) generator = self._get_generate(code.rindex("a")) self.project.do(generator.get_changes()) init = self.pkg.get_child("__init__.py") self.assertEqual((init, 1), generator.get_location()) self.assertEqual("a = None\n", init.read()) def test_generating_classes(self): code = "c = C()\n" self.mod.write(code) changes = self._get_generate_class(code.index("C")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): pass c = C() """), self.mod.read(), ) def test_generating_classes_in_other_module(self): code = "c = C()\n" self.mod.write(code) changes = self._get_generate_class(code.index("C"), self.mod2).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): pass """), self.mod2.read(), ) self.assertEqual( dedent("""\ from mod2 import C c = C() """), self.mod.read(), ) def test_generating_modules(self): code = dedent("""\ import pkg pkg.mod """) self.mod.write(code) generator = self._get_generate_module(code.rindex("mod")) self.project.do(generator.get_changes()) mod = self.pkg.get_child("mod.py") self.assertEqual((mod, 1), generator.get_location()) self.assertEqual( dedent("""\ import pkg.mod pkg.mod """), self.mod.read(), ) def test_generating_packages(self): code = dedent("""\ import pkg pkg.pkg2 """) self.mod.write(code) generator = self._get_generate_package(code.rindex("pkg2")) self.project.do(generator.get_changes()) pkg2 = self.pkg.get_child("pkg2") init = pkg2.get_child("__init__.py") self.assertEqual((init, 1), generator.get_location()) self.assertEqual( dedent("""\ import pkg.pkg2 pkg.pkg2 """), self.mod.read(), ) def test_generating_function(self): code = "a_func()\n" self.mod.write(code) changes = self._get_generate_function(code.index("a_func")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ def a_func(): pass a_func() """), self.mod.read(), ) def test_generating_modules_with_empty_primary(self): code = "mod\n" self.mod.write(code) generator = self._get_generate_module(code.rindex("mod")) self.project.do(generator.get_changes()) mod = self.project.root.get_child("mod.py") self.assertEqual((mod, 1), generator.get_location()) self.assertEqual("import mod\nmod\n", self.mod.read()) def test_generating_variable_already_exists(self): code = dedent("""\ b = 1 c = b """) self.mod.write(code) with self.assertRaises(exceptions.RefactoringError): self._get_generate(code.index("b")).get_changes() def test_generating_variable_primary_cannot_be_determined(self): code = "c = can_not_be_found.b\n" self.mod.write(code) with self.assertRaises(exceptions.RefactoringError): self._get_generate(code.rindex("b")).get_changes() def test_generating_modules_when_already_exists(self): code = "mod2\n" self.mod.write(code) generator = self._get_generate_module(code.rindex("mod")) with self.assertRaises(exceptions.RefactoringError): self.project.do(generator.get_changes()) def test_generating_static_methods(self): code = dedent("""\ class C(object): pass C.a_func() """) self.mod.write(code) changes = self._get_generate_function(code.index("a_func")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): @staticmethod def a_func(): pass C.a_func() """), self.mod.read(), ) def test_generating_methods(self): code = dedent("""\ class C(object): pass c = C() c.a_func() """) self.mod.write(code) changes = self._get_generate_function(code.index("a_func")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): def a_func(self): pass c = C() c.a_func() """), self.mod.read(), ) def test_generating_constructors(self): code = dedent("""\ class C(object): pass c = C() """) self.mod.write(code) changes = self._get_generate_function(code.rindex("C")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): def __init__(self): pass c = C() """), self.mod.read(), ) def test_generating_calls(self): code = dedent("""\ class C(object): pass c = C() c() """) self.mod.write(code) changes = self._get_generate_function(code.rindex("c")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): def __call__(self): pass c = C() c() """), self.mod.read(), ) def test_generating_calls_in_other_modules(self): self.mod2.write( dedent("""\ class C(object): pass """) ) code = dedent("""\ import mod2 c = mod2.C() c() """) self.mod.write(code) changes = self._get_generate_function(code.rindex("c")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ class C(object): def __call__(self): pass """), self.mod2.read(), ) def test_generating_function_handling_arguments(self): code = "a_func(1)\n" self.mod.write(code) changes = self._get_generate_function(code.index("a_func")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ def a_func(arg0): pass a_func(1) """), self.mod.read(), ) def test_generating_function_handling_keyword_xarguments(self): code = "a_func(p=1)\n" self.mod.write(code) changes = self._get_generate_function(code.index("a_func")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ def a_func(p): pass a_func(p=1) """), self.mod.read(), ) def test_generating_function_handling_arguments_better_naming(self): code = dedent("""\ a_var = 1 a_func(a_var) """) self.mod.write(code) changes = self._get_generate_function(code.index("a_func")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ a_var = 1 def a_func(a_var): pass a_func(a_var) """), self.mod.read(), ) def test_generating_variable_in_other_modules2(self): self.mod2.write("\n\n\nprint(1)\n") code = dedent("""\ import mod2 c = mod2.b """) self.mod.write(code) generator = self._get_generate(code.index("b")) self.project.do(generator.get_changes()) self.assertEqual((self.mod2, 5), generator.get_location()) self.assertEqual( dedent("""\ print(1) b = None """), self.mod2.read(), ) def test_generating_function_in_a_suite(self): code = dedent("""\ if True: a_func() """) self.mod.write(code) changes = self._get_generate_function(code.index("a_func")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ def a_func(): pass if True: a_func() """), self.mod.read(), ) def test_generating_function_in_a_suite_in_a_function(self): code = dedent("""\ def f(): a = 1 if 1: g() """) self.mod.write(code) changes = self._get_generate_function(code.index("g()")).get_changes() self.project.do(changes) self.assertEqual( dedent("""\ def f(): a = 1 def g(): pass if 1: g() """), self.mod.read(), ) def test_create_generate_class_with_goal_resource(self): code = "c = C()\n" self.mod.write(code) result = generate.create_generate( "class", self.project, self.mod, code.index("C"), goal_resource=self.mod2 ) self.assertTrue(isinstance(result, generate.GenerateClass)) self.assertEqual(result.goal_resource, self.mod2) def test_create_generate_class_without_goal_resource(self): code = "c = C()\n" self.mod.write(code) result = generate.create_generate( "class", self.project, self.mod, code.index("C") ) self.assertTrue(isinstance(result, generate.GenerateClass)) self.assertIsNone(result.goal_resource) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/ropetest/doatest.py0000664000175000017500000000561700000000000016326 0ustar00lieryanlieryanimport base64 import hashlib import hmac import multiprocessing try: import cPickle as pickle except ImportError: import pickle import socket try: import unittest2 as unittest except ImportError: import unittest from rope.base.oi import doa class DOATest(unittest.TestCase): def try_CVE_2014_3539_exploit(self, receiver, payload): # Simulated attacker writing to the socket def attacker(data_port): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("127.0.0.1", data_port)) s_file = s.makefile("wb") s_file.write(payload) s.close() # Assume the attacker guesses the port correctly; 3037 is used by # default if it is available. attacker_proc = multiprocessing.Process( target=attacker, args=(receiver.data_port,) ) attacker_proc.start() received_objs = list(receiver.receive_data()) attacker_proc.join() return received_objs def test_CVE_2014_3539_no_encoding(self): # Attacker sends pickled data to the receiver socket. receiver = doa._SocketReceiver() payload = pickle.dumps("def foo():\n return 123\n") received_objs = self.try_CVE_2014_3539_exploit(receiver, payload) # Make sure the exploit did not run self.assertEqual(0, len(received_objs)) def test_CVE_2014_3539_signature_mismatch(self): # Attacker sends well-formed data with an incorrect signature. receiver = doa._SocketReceiver() pickled_data = pickle.dumps( "def foo():\n return 123\n", pickle.HIGHEST_PROTOCOL ) digest = hmac.new(b"invalid-key", pickled_data, hashlib.sha256).digest() payload = ( base64.b64encode(digest) + b":" + base64.b64encode(pickled_data) + b"\n" ) received_objs = self.try_CVE_2014_3539_exploit(receiver, payload) # Make sure the exploit did not run self.assertEqual(0, len(received_objs)) def test_CVE_2014_3539_sanity(self): # Tests that sending valid, signed data on the socket does work. receiver = doa._SocketReceiver() pickled_data = base64.b64encode( pickle.dumps("def foo():\n return 123\n", pickle.HIGHEST_PROTOCOL) ) digest = hmac.new(receiver.key, pickled_data, hashlib.sha256).digest() payload = base64.b64encode(digest) + b":" + pickled_data + b"\n" received_objs = self.try_CVE_2014_3539_exploit(receiver, payload) # Make sure the exploit did not run self.assertEqual(1, len(received_objs)) def test_compare_digest_compat(self): self.assertTrue(doa._compat_compare_digest("", "")) self.assertTrue(doa._compat_compare_digest("abc", "abc")) self.assertFalse(doa._compat_compare_digest("abc", "abd")) self.assertFalse(doa._compat_compare_digest("abc", "abcd")) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/ropetest/historytest.py0000664000175000017500000003732200000000000017262 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest import rope.base.history from rope.base import exceptions import rope.base.change from ropetest import testutils class HistoryTest(unittest.TestCase): def setUp(self): super(HistoryTest, self).setUp() self.project = testutils.sample_project() self.history = self.project.history def tearDown(self): testutils.remove_project(self.project) super(HistoryTest, self).tearDown() def test_undoing_writes(self): my_file = self.project.root.create_file("my_file.txt") my_file.write("text1") self.history.undo() self.assertEqual("", my_file.read()) def test_moving_files(self): my_file = self.project.root.create_file("my_file.txt") my_file.move("new_file.txt") self.history.undo() self.assertEqual("", my_file.read()) def test_moving_files_to_folders(self): my_file = self.project.root.create_file("my_file.txt") my_folder = self.project.root.create_folder("my_folder") my_file.move(my_folder.path) self.history.undo() self.assertEqual("", my_file.read()) def test_writing_files_that_does_not_change_contents(self): my_file = self.project.root.create_file("my_file.txt") my_file.write("") self.project.history.undo() self.assertFalse(my_file.exists()) class IsolatedHistoryTest(unittest.TestCase): def setUp(self): super(IsolatedHistoryTest, self).setUp() self.project = testutils.sample_project() self.history = rope.base.history.History(self.project) self.file1 = self.project.root.create_file("file1.txt") self.file2 = self.project.root.create_file("file2.txt") def tearDown(self): testutils.remove_project(self.project) super(IsolatedHistoryTest, self).tearDown() def test_simple_undo(self): change = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change) self.assertEqual("1", self.file1.read()) self.history.undo() self.assertEqual("", self.file1.read()) def test_tobe_undone(self): change1 = rope.base.change.ChangeContents(self.file1, "1") self.assertEqual(None, self.history.tobe_undone) self.history.do(change1) self.assertEqual(change1, self.history.tobe_undone) change2 = rope.base.change.ChangeContents(self.file1, "2") self.history.do(change2) self.assertEqual(change2, self.history.tobe_undone) self.history.undo() self.assertEqual(change1, self.history.tobe_undone) def test_tobe_redone(self): change = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change) self.assertEqual(None, self.history.tobe_redone) self.history.undo() self.assertEqual(change, self.history.tobe_redone) def test_undo_limit(self): history = rope.base.history.History(self.project, maxundos=1) history.do(rope.base.change.ChangeContents(self.file1, "1")) history.do(rope.base.change.ChangeContents(self.file1, "2")) try: history.undo() with self.assertRaises(exceptions.HistoryError): history.undo() finally: self.assertEqual("1", self.file1.read()) def test_simple_redo(self): change = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change) self.history.undo() self.history.redo() self.assertEqual("1", self.file1.read()) def test_simple_re_undo(self): change = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change) self.history.undo() self.history.redo() self.history.undo() self.assertEqual("", self.file1.read()) def test_multiple_undos(self): change = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change) change = rope.base.change.ChangeContents(self.file1, "2") self.history.do(change) self.history.undo() self.assertEqual("1", self.file1.read()) change = rope.base.change.ChangeContents(self.file1, "3") self.history.do(change) self.history.undo() self.assertEqual("1", self.file1.read()) self.history.redo() self.assertEqual("3", self.file1.read()) def test_undo_list_underflow(self): with self.assertRaises(exceptions.HistoryError): self.history.undo() def test_redo_list_underflow(self): with self.assertRaises(exceptions.HistoryError): self.history.redo() def test_dropping_undone_changes(self): self.file1.write("1") with self.assertRaises(exceptions.HistoryError): self.history.undo(drop=True) self.history.redo() def test_undoing_choosen_changes(self): change = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change) self.history.undo(change) self.assertEqual("", self.file1.read()) self.assertFalse(self.history.undo_list) def test_undoing_choosen_changes2(self): change1 = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change1) self.history.do(rope.base.change.ChangeContents(self.file1, "2")) self.history.undo(change1) self.assertEqual("", self.file1.read()) self.assertFalse(self.history.undo_list) def test_undoing_choosen_changes_not_undoing_others(self): change1 = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change1) self.history.do(rope.base.change.ChangeContents(self.file2, "2")) self.history.undo(change1) self.assertEqual("", self.file1.read()) self.assertEqual("2", self.file2.read()) def test_undoing_writing_after_moving(self): change1 = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change1) self.history.do(rope.base.change.MoveResource(self.file1, "file3.txt")) file3 = self.project.get_resource("file3.txt") self.history.undo(change1) self.assertEqual("", self.file1.read()) self.assertFalse(file3.exists()) def test_undoing_folder_movements_for_undoing_writes_inside_it(self): folder = self.project.root.create_folder("folder") file3 = folder.create_file("file3.txt") change1 = rope.base.change.ChangeContents(file3, "1") self.history.do(change1) self.history.do(rope.base.change.MoveResource(folder, "new_folder")) new_folder = self.project.get_resource("new_folder") self.history.undo(change1) self.assertEqual("", file3.read()) self.assertFalse(new_folder.exists()) def test_undoing_changes_that_depend_on_a_dependant_change(self): change1 = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change1) changes = rope.base.change.ChangeSet("2nd change") changes.add_change(rope.base.change.ChangeContents(self.file1, "2")) changes.add_change(rope.base.change.ChangeContents(self.file2, "2")) self.history.do(changes) self.history.do(rope.base.change.MoveResource(self.file2, "file3.txt")) file3 = self.project.get_resource("file3.txt") self.history.undo(change1) self.assertEqual("", self.file1.read()) self.assertEqual("", self.file2.read()) self.assertFalse(file3.exists()) def test_undoing_writes_for_undoing_folder_movements_containing_it(self): folder = self.project.root.create_folder("folder") old_file = folder.create_file("file3.txt") change1 = rope.base.change.MoveResource(folder, "new_folder") self.history.do(change1) new_file = self.project.get_resource("new_folder/file3.txt") self.history.do(rope.base.change.ChangeContents(new_file, "1")) self.history.undo(change1) self.assertEqual("", old_file.read()) self.assertFalse(new_file.exists()) def test_undoing_not_available_change(self): change = rope.base.change.ChangeContents(self.file1, "1") with self.assertRaises(exceptions.HistoryError): self.history.undo(change) def test_ignoring_ignored_resources(self): self.project.set("ignored_resources", ["ignored*"]) ignored = self.project.get_file("ignored.txt") change = rope.base.change.CreateResource(ignored) self.history.do(change) self.assertTrue(ignored.exists()) self.assertEqual(0, len(self.history.undo_list)) def test_get_file_undo_list_simple(self): change = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change) self.assertEqual( set([change]), set(self.history.get_file_undo_list(self.file1)) ) def test_get_file_undo_list_for_moves(self): change = rope.base.change.MoveResource(self.file1, "file2.txt") self.history.do(change) self.assertEqual( set([change]), set(self.history.get_file_undo_list(self.file1)) ) # XXX: What happens for moves before the file is created? def xxx_test_get_file_undo_list_and_moving_its_contining_folder(self): folder = self.project.root.create_folder("folder") old_file = folder.create_file("file3.txt") change1 = rope.base.change.MoveResource(folder, "new_folder") self.history.do(change1) self.assertEqual(set([change1]), set(self.history.get_file_undo_list(old_file))) def test_clearing_redo_list_after_do(self): change = rope.base.change.ChangeContents(self.file1, "1") self.history.do(change) self.history.undo() self.history.do(change) self.assertEqual(0, len(self.history.redo_list)) def test_undoing_a_not_yet_performed_change(self): change = rope.base.change.ChangeContents(self.file1, "1") str(change) with self.assertRaises(exceptions.HistoryError): change.undo() def test_clearing_up_the_history(self): change1 = rope.base.change.ChangeContents(self.file1, "1") change2 = rope.base.change.ChangeContents(self.file1, "2") self.history.do(change1) self.history.do(change2) self.history.undo() self.history.clear() self.assertEqual(0, len(self.history.undo_list)) self.assertEqual(0, len(self.history.redo_list)) def test_redoing_choosen_changes_not_undoing_others(self): change1 = rope.base.change.ChangeContents(self.file1, "1") change2 = rope.base.change.ChangeContents(self.file2, "2") self.history.do(change1) self.history.do(change2) self.history.undo() self.history.undo() redone = self.history.redo(change2) self.assertEqual([change2], redone) self.assertEqual("", self.file1.read()) self.assertEqual("2", self.file2.read()) class SavingHistoryTest(unittest.TestCase): def setUp(self): super(SavingHistoryTest, self).setUp() self.project = testutils.sample_project() self.history = rope.base.history.History(self.project) self.to_data = rope.base.change.ChangeToData() self.to_change = rope.base.change.DataToChange(self.project) def tearDown(self): testutils.remove_project(self.project) super(SavingHistoryTest, self).tearDown() def test_simple_set_saving(self): data = self.to_data(rope.base.change.ChangeSet("testing")) change = self.to_change(data) self.assertEqual("testing", str(change)) def test_simple_change_content_saving(self): myfile = self.project.get_file("myfile.txt") myfile.create() myfile.write("1") data = self.to_data(rope.base.change.ChangeContents(myfile, "2")) change = self.to_change(data) self.history.do(change) self.assertEqual("2", myfile.read()) self.history.undo() self.assertEqual("1", change.old_contents) def test_move_resource_saving(self): myfile = self.project.root.create_file("myfile.txt") myfolder = self.project.root.create_folder("myfolder") data = self.to_data(rope.base.change.MoveResource(myfile, "myfolder")) change = self.to_change(data) self.history.do(change) self.assertFalse(myfile.exists()) self.assertTrue(myfolder.has_child("myfile.txt")) self.history.undo() self.assertTrue(myfile.exists()) self.assertFalse(myfolder.has_child("myfile.txt")) def test_move_resource_saving_for_folders(self): myfolder = self.project.root.create_folder("myfolder") newfolder = self.project.get_folder("newfolder") change = rope.base.change.MoveResource(myfolder, "newfolder") self.history.do(change) data = self.to_data(change) change = self.to_change(data) change.undo() self.assertTrue(myfolder.exists()) self.assertFalse(newfolder.exists()) def test_create_file_saving(self): myfile = self.project.get_file("myfile.txt") data = self.to_data( rope.base.change.CreateFile(self.project.root, "myfile.txt") ) change = self.to_change(data) self.history.do(change) self.assertTrue(myfile.exists()) self.history.undo() self.assertFalse(myfile.exists()) def test_create_folder_saving(self): myfolder = self.project.get_folder("myfolder") data = self.to_data( rope.base.change.CreateFolder(self.project.root, "myfolder") ) change = self.to_change(data) self.history.do(change) self.assertTrue(myfolder.exists()) self.history.undo() self.assertFalse(myfolder.exists()) def test_create_resource_saving(self): myfile = self.project.get_file("myfile.txt") data = self.to_data(rope.base.change.CreateResource(myfile)) change = self.to_change(data) self.history.do(change) self.assertTrue(myfile.exists()) self.history.undo() self.assertFalse(myfile.exists()) def test_remove_resource_saving(self): myfile = self.project.root.create_file("myfile.txt") data = self.to_data(rope.base.change.RemoveResource(myfile)) change = self.to_change(data) self.history.do(change) self.assertFalse(myfile.exists()) def test_change_set_saving(self): change = rope.base.change.ChangeSet("testing") myfile = self.project.get_file("myfile.txt") change.add_change(rope.base.change.CreateResource(myfile)) change.add_change(rope.base.change.ChangeContents(myfile, "1")) data = self.to_data(change) change = self.to_change(data) self.history.do(change) self.assertEqual("1", myfile.read()) self.history.undo() self.assertFalse(myfile.exists()) def test_writing_and_reading_history(self): history_file = self.project.get_file("history.pickle") # noqa self.project.set("save_history", True) history = rope.base.history.History(self.project) myfile = self.project.get_file("myfile.txt") history.do(rope.base.change.CreateResource(myfile)) history.write() history = rope.base.history.History(self.project) history.undo() self.assertFalse(myfile.exists()) def test_writing_and_reading_history2(self): history_file = self.project.get_file("history.pickle") # noqa self.project.set("save_history", True) history = rope.base.history.History(self.project) myfile = self.project.get_file("myfile.txt") history.do(rope.base.change.CreateResource(myfile)) history.undo() history.write() history = rope.base.history.History(self.project) history.redo() self.assertTrue(myfile.exists()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632913225.0 rope-0.22.0/ropetest/objectdbtest.py0000664000175000017500000001146200000000000017332 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest from rope.base.oi import objectdb, memorydb from ropetest import testutils def _do_for_all_dbs(function): def called(self): for db in self.dbs: function(self, db) return called class _MockValidation(object): def is_value_valid(self, value): return value != -1 def is_more_valid(self, new, old): return new != -1 def is_file_valid(self, path): return path != "invalid" def is_scope_valid(self, path, key): return path != "invalid" and key != "invalid" class _MockFileListObserver(object): log = "" def added(self, path): self.log += "added %s " % path def removed(self, path): self.log += "removed %s " % path class ObjectDBTest(unittest.TestCase): def setUp(self): super(ObjectDBTest, self).setUp() self.project = testutils.sample_project() validation = _MockValidation() self.dbs = [objectdb.ObjectDB(memorydb.MemoryDB(self.project), validation)] def tearDown(self): for db in self.dbs: db.write() testutils.remove_project(self.project) super(ObjectDBTest, self).tearDown() @_do_for_all_dbs def test_simple_per_name(self, db): db.add_pername("file", "key", "name", 1) self.assertEqual(1, db.get_pername("file", "key", "name")) @_do_for_all_dbs def test_simple_per_name_does_not_exist(self, db): self.assertEqual(None, db.get_pername("file", "key", "name")) @_do_for_all_dbs def test_simple_per_name_after_syncing(self, db): db.add_pername("file", "key", "name", 1) db.write() self.assertEqual(1, db.get_pername("file", "key", "name")) @_do_for_all_dbs def test_getting_returned(self, db): db.add_callinfo("file", "key", (1, 2), 3) self.assertEqual(3, db.get_returned("file", "key", (1, 2))) @_do_for_all_dbs def test_getting_returned_when_does_not_match(self, db): db.add_callinfo("file", "key", (1, 2), 3) self.assertEqual(None, db.get_returned("file", "key", (1, 1))) @_do_for_all_dbs def test_getting_call_info(self, db): db.add_callinfo("file", "key", (1, 2), 3) call_infos = list(db.get_callinfos("file", "key")) self.assertEqual(1, len(call_infos)) self.assertEqual((1, 2), call_infos[0].get_parameters()) self.assertEqual(3, call_infos[0].get_returned()) @_do_for_all_dbs def test_invalid_per_name(self, db): db.add_pername("file", "key", "name", -1) self.assertEqual(None, db.get_pername("file", "key", "name")) @_do_for_all_dbs def test_overwriting_per_name(self, db): db.add_pername("file", "key", "name", 1) db.add_pername("file", "key", "name", 2) self.assertEqual(2, db.get_pername("file", "key", "name")) @_do_for_all_dbs def test_not_overwriting_with_invalid_per_name(self, db): db.add_pername("file", "key", "name", 1) db.add_pername("file", "key", "name", -1) self.assertEqual(1, db.get_pername("file", "key", "name")) @_do_for_all_dbs def test_getting_invalid_returned(self, db): db.add_callinfo("file", "key", (1, 2), -1) self.assertEqual(None, db.get_returned("file", "key", (1, 2))) @_do_for_all_dbs def test_not_overwriting_with_invalid_returned(self, db): db.add_callinfo("file", "key", (1, 2), 3) db.add_callinfo("file", "key", (1, 2), -1) self.assertEqual(3, db.get_returned("file", "key", (1, 2))) @_do_for_all_dbs def test_get_files(self, db): db.add_callinfo("file1", "key", (1, 2), 3) db.add_callinfo("file2", "key", (1, 2), 3) self.assertEqual(set(["file1", "file2"]), set(db.get_files())) @_do_for_all_dbs def test_validating_files(self, db): db.add_callinfo("invalid", "key", (1, 2), 3) db.validate_files() self.assertEqual(0, len(db.get_files())) @_do_for_all_dbs def test_validating_file_for_scopes(self, db): db.add_callinfo("file", "invalid", (1, 2), 3) db.validate_file("file") self.assertEqual(1, len(db.get_files())) self.assertEqual(0, len(list(db.get_callinfos("file", "invalid")))) @_do_for_all_dbs def test_validating_file_moved(self, db): db.add_callinfo("file", "key", (1, 2), 3) db.file_moved("file", "newfile") self.assertEqual(1, len(db.get_files())) self.assertEqual(1, len(list(db.get_callinfos("newfile", "key")))) @_do_for_all_dbs def test_using_file_list_observer(self, db): db.add_callinfo("invalid", "key", (1, 2), 3) observer = _MockFileListObserver() db.add_file_list_observer(observer) db.validate_files() self.assertEqual("removed invalid ", observer.log) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/objectinfertest.py0000664000175000017500000003670400000000000020056 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest import rope.base.project import rope.base.builtins from rope.base import libutils from ropetest import testutils class ObjectInferTest(unittest.TestCase): def setUp(self): super(ObjectInferTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(ObjectInferTest, self).tearDown() def test_simple_type_inferencing(self): code = dedent("""\ class Sample(object): pass a_var = Sample() """) scope = libutils.get_string_scope(self.project, code) sample_class = scope["Sample"].get_object() a_var = scope["a_var"].get_object() self.assertEqual(sample_class, a_var.get_type()) def test_simple_type_inferencing_classes_defined_in_holding_scope(self): code = dedent("""\ class Sample(object): pass def a_func(): a_var = Sample() """) scope = libutils.get_string_scope(self.project, code) sample_class = scope["Sample"].get_object() a_var = scope["a_func"].get_object().get_scope()["a_var"].get_object() self.assertEqual(sample_class, a_var.get_type()) def test_simple_type_inferencing_classes_in_class_methods(self): code = dedent("""\ class Sample(object): pass class Another(object): def a_method(): a_var = Sample() """) scope = libutils.get_string_scope(self.project, code) sample_class = scope["Sample"].get_object() another_class = scope["Another"].get_object() a_var = another_class["a_method"].get_object().get_scope()["a_var"].get_object() self.assertEqual(sample_class, a_var.get_type()) def test_simple_type_inferencing_class_attributes(self): code = dedent("""\ class Sample(object): pass class Another(object): def __init__(self): self.a_var = Sample() """) scope = libutils.get_string_scope(self.project, code) sample_class = scope["Sample"].get_object() another_class = scope["Another"].get_object() a_var = another_class["a_var"].get_object() self.assertEqual(sample_class, a_var.get_type()) def test_simple_type_inferencing_for_in_class_assignments(self): code = dedent("""\ class Sample(object): pass class Another(object): an_attr = Sample() """) scope = libutils.get_string_scope(self.project, code) sample_class = scope["Sample"].get_object() another_class = scope["Another"].get_object() an_attr = another_class["an_attr"].get_object() self.assertEqual(sample_class, an_attr.get_type()) def test_simple_type_inferencing_for_chained_assignments(self): mod = dedent("""\ class Sample(object): pass copied_sample = Sample""") mod_scope = libutils.get_string_scope(self.project, mod) sample_class = mod_scope["Sample"] copied_sample = mod_scope["copied_sample"] self.assertEqual(sample_class.get_object(), copied_sample.get_object()) def test_following_chained_assignments_avoiding_circles(self): mod = dedent("""\ class Sample(object): pass sample_class = Sample sample_class = sample_class """) mod_scope = libutils.get_string_scope(self.project, mod) sample_class = mod_scope["Sample"] sample_class_var = mod_scope["sample_class"] self.assertEqual(sample_class.get_object(), sample_class_var.get_object()) def test_function_returned_object_static_type_inference1(self): src = dedent("""\ class Sample(object): pass def a_func(): return Sample a_var = a_func() """) scope = libutils.get_string_scope(self.project, src) sample_class = scope["Sample"] a_var = scope["a_var"] self.assertEqual(sample_class.get_object(), a_var.get_object()) def test_function_returned_object_static_type_inference2(self): src = dedent("""\ class Sample(object): pass def a_func(): return Sample() a_var = a_func() """) scope = libutils.get_string_scope(self.project, src) sample_class = scope["Sample"].get_object() a_var = scope["a_var"].get_object() self.assertEqual(sample_class, a_var.get_type()) def test_recursive_function_returned_object_static_type_inference(self): src = dedent("""\ class Sample(object): pass def a_func(): if True: return Sample() else: return a_func() a_var = a_func() """) scope = libutils.get_string_scope(self.project, src) sample_class = scope["Sample"].get_object() a_var = scope["a_var"].get_object() self.assertEqual(sample_class, a_var.get_type()) def test_func_returned_obj_using_call_spec_func_static_type_infer(self): src = dedent("""\ class Sample(object): def __call__(self): return Sample sample = Sample() a_var = sample()""") scope = libutils.get_string_scope(self.project, src) sample_class = scope["Sample"] a_var = scope["a_var"] self.assertEqual(sample_class.get_object(), a_var.get_object()) def test_list_type_inferencing(self): src = dedent("""\ class Sample(object): pass a_var = [Sample()] """) scope = libutils.get_string_scope(self.project, src) sample_class = scope["Sample"].get_object() a_var = scope["a_var"].get_object() self.assertNotEqual(sample_class, a_var.get_type()) def test_attributed_object_inference(self): src = dedent("""\ class Sample(object): def __init__(self): self.a_var = None def set(self): self.a_var = Sample() """) scope = libutils.get_string_scope(self.project, src) sample_class = scope["Sample"].get_object() a_var = sample_class["a_var"].get_object() self.assertEqual(sample_class, a_var.get_type()) def test_getting_property_attributes(self): src = dedent("""\ class A(object): pass def f(*args): return A() class B(object): p = property(f) a_var = B().p """) pymod = libutils.get_string_module(self.project, src) a_class = pymod["A"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(a_class, a_var.get_type()) def test_getting_property_attributes_with_method_getters(self): src = dedent("""\ class A(object): pass class B(object): def p_get(self): return A() p = property(p_get) a_var = B().p """) pymod = libutils.get_string_module(self.project, src) a_class = pymod["A"].get_object() a_var = pymod["a_var"].get_object() self.assertEqual(a_class, a_var.get_type()) def test_lambda_functions(self): code = dedent("""\ class C(object): pass l = lambda: C() a_var = l()""") mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_mixing_subscript_with_tuple_assigns(self): code = dedent("""\ class C(object): attr = 0 d = {} d[0], b = (0, C()) """) mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["b"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_mixing_ass_attr_with_tuple_assignment(self): code = dedent("""\ class C(object): attr = 0 c = C() c.attr, b = (0, C()) """) mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["b"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_mixing_slice_with_tuple_assigns(self): code = dedent("""\ class C(object): attr = 0 d = [None] * 3 d[0:2], b = ((0,), C()) """) mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["b"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_nested_tuple_assignments(self): code = dedent("""\ class C1(object): pass class C2(object): pass a, (b, c) = (C1(), (C2(), C1())) """) mod = libutils.get_string_module(self.project, code) c1_class = mod["C1"].get_object() c2_class = mod["C2"].get_object() a_var = mod["a"].get_object() b_var = mod["b"].get_object() c_var = mod["c"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) self.assertEqual(c1_class, c_var.get_type()) def test_empty_tuples(self): code = dedent("""\ t = () a, b = t """) mod = libutils.get_string_module(self.project, code) a = mod["a"].get_object() # noqa def test_handling_generator_functions(self): code = dedent("""\ class C(object): pass def f(): yield C() for c in f(): a_var = c """) mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_handling_generator_functions_for_strs(self): mod = testutils.create_module(self.project, "mod") mod.write( dedent("""\ def f(): yield "" for s in f(): a_var = s """) ) pymod = self.project.get_pymodule(mod) a_var = pymod["a_var"].get_object() self.assertTrue(isinstance(a_var.get_type(), rope.base.builtins.Str)) def test_considering_nones_to_be_unknowns(self): code = dedent("""\ class C(object): pass a_var = None a_var = C() a_var = None """) mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_basic_list_comprehensions(self): code = dedent("""\ class C(object): pass l = [C() for i in range(1)] a_var = l[0] """) mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_basic_generator_expressions(self): code = dedent("""\ class C(object): pass l = (C() for i in range(1)) a_var = list(l)[0] """) mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_list_comprehensions_and_loop_var(self): code = dedent("""\ class C(object): pass c_objects = [C(), C()] l = [c for c in c_objects] a_var = l[0] """) mod = libutils.get_string_module(self.project, code) c_class = mod["C"].get_object() a_var = mod["a_var"].get_object() self.assertEqual(c_class, a_var.get_type()) def test_list_comprehensions_and_multiple_loop_var(self): code = dedent("""\ class C1(object): pass class C2(object): pass l = [(c1, c2) for c1 in [C1()] for c2 in [C2()]] a, b = l[0] """) mod = libutils.get_string_module(self.project, code) c1_class = mod["C1"].get_object() c2_class = mod["C2"].get_object() a_var = mod["a"].get_object() b_var = mod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_list_comprehensions_and_multiple_iters(self): code = dedent("""\ class C1(object): pass class C2(object): pass l = [(c1, c2) for c1, c2 in [(C1(), C2())]] a, b = l[0] """) mod = libutils.get_string_module(self.project, code) c1_class = mod["C1"].get_object() c2_class = mod["C2"].get_object() a_var = mod["a"].get_object() b_var = mod["b"].get_object() self.assertEqual(c1_class, a_var.get_type()) self.assertEqual(c2_class, b_var.get_type()) def test_we_know_the_type_of_catched_exceptions(self): code = dedent("""\ class MyError(Exception): pass try: raise MyError() except MyError as e: pass """) mod = libutils.get_string_module(self.project, code) my_error = mod["MyError"].get_object() e_var = mod["e"].get_object() self.assertEqual(my_error, e_var.get_type()) def test_we_know_the_type_of_catched_multiple_excepts(self): code = dedent("""\ class MyError(Exception): pass try: raise MyError() except (MyError, Exception) as e: pass """) mod = libutils.get_string_module(self.project, code) my_error = mod["MyError"].get_object() e_var = mod["e"].get_object() self.assertEqual(my_error, e_var.get_type()) def test_using_property_as_decorators(self): code = dedent("""\ class A(object): pass class B(object): @property def f(self): return A() b = B() var = b.f """) mod = libutils.get_string_module(self.project, code) var = mod["var"].get_object() a = mod["A"].get_object() self.assertEqual(a, var.get_type()) def test_using_property_as_decorators_and_passing_parameter(self): code = dedent("""\ class B(object): @property def f(self): return self b = B() var = b.f """) mod = libutils.get_string_module(self.project, code) var = mod["var"].get_object() a = mod["B"].get_object() self.assertEqual(a, var.get_type()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/projecttest.py0000664000175000017500000012653000000000000017227 0ustar00lieryanlieryanimport os from textwrap import dedent import os.path import shutil try: import unittest2 as unittest except ImportError: import unittest from rope.base.exceptions import RopeError, ResourceNotFoundError from rope.base.fscommands import FileSystemCommands from rope.base.libutils import path_to_resource from rope.base.project import Project, NoProject, _realpath from ropetest import testutils from rope.base.resourceobserver import FilteredResourceObserver class ProjectTest(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self) self.project = testutils.sample_project( foldername="sampleproject", ropefolder=None ) self.project_root = self.project.address self._make_sample_project() self.no_project = NoProject() def _make_sample_project(self): self.sample_file = "sample_file.txt" self.sample_path = os.path.join(self.project_root, "sample_file.txt") if not os.path.exists(self.project_root): os.mkdir(self.project_root) self.sample_folder = "sample_folder" os.mkdir(os.path.join(self.project_root, self.sample_folder)) sample = open(self.sample_path, "w") sample.write("sample text\n") sample.close() def tearDown(self): testutils.remove_project(self.project) unittest.TestCase.tearDown(self) def test_project_creation(self): self.assertEqual(_realpath(self.project_root), self.project.address) def test_getting_project_file(self): project_file = self.project.get_resource(self.sample_file) self.assertTrue(project_file is not None) def test_project_file_reading(self): projectFile = self.project.get_resource(self.sample_file) self.assertEqual("sample text\n", projectFile.read()) def test_getting_not_existing_project_file(self): with self.assertRaises(ResourceNotFoundError): self.project.get_resource("DoesNotExistFile.txt") def test_writing_in_project_files(self): project_file = self.project.get_resource(self.sample_file) project_file.write("another text\n") self.assertEqual("another text\n", project_file.read()) def test_creating_files(self): project_file = "newfile.txt" self.project.root.create_file(project_file) newFile = self.project.get_resource(project_file) self.assertTrue(newFile is not None) def test_creating_files_that_already_exist(self): with self.assertRaises(RopeError): self.project.root.create_file(self.sample_file) def test_making_root_folder_if_it_does_not_exist(self): project = Project("sampleproject2") try: self.assertTrue( os.path.exists("sampleproject2") and os.path.isdir("sampleproject2") ) finally: testutils.remove_project(project) def test_failure_when_project_root_exists_and_is_a_file(self): project_root = "sampleproject2" try: open(project_root, "w").close() with self.assertRaises(RopeError): Project(project_root) finally: testutils.remove_recursively(project_root) def test_creating_folders(self): folderName = "SampleFolder" self.project.root.create_folder(folderName) folderPath = os.path.join(self.project.address, folderName) self.assertTrue(os.path.exists(folderPath) and os.path.isdir(folderPath)) def test_making_folder_that_already_exists(self): folderName = "SampleFolder" with self.assertRaises(RopeError): self.project.root.create_folder(folderName) self.project.root.create_folder(folderName) def test_failing_if_creating_folder_while_file_already_exists(self): folderName = "SampleFolder" with self.assertRaises(RopeError): self.project.root.create_file(folderName) self.project.root.create_folder(folderName) def test_creating_file_inside_folder(self): folder_name = "sampleFolder" file_name = "sample2.txt" file_path = folder_name + "/" + file_name parent_folder = self.project.root.create_folder(folder_name) parent_folder.create_file(file_name) file = self.project.get_resource(file_path) file.write("sample notes") self.assertEqual(file_path, file.path) self.assertEqual( "sample notes", open(os.path.join(self.project.address, file_path)).read() ) def test_failing_when_creating_file_inside_non_existent_folder(self): with self.assertRaises(ResourceNotFoundError): self.project.root.create_file("NonexistentFolder/SomeFile.txt") def test_nested_directories(self): folder_name = "SampleFolder" parent = self.project.root.create_folder(folder_name) parent.create_folder(folder_name) folder_path = os.path.join(self.project.address, folder_name, folder_name) self.assertTrue(os.path.exists(folder_path) and os.path.isdir(folder_path)) def test_removing_files(self): self.assertTrue(os.path.exists(self.sample_path)) self.project.get_resource(self.sample_file).remove() self.assertFalse(os.path.exists(self.sample_path)) def test_removing_files_invalidating_in_project_resource_pool(self): root_folder = self.project.root my_file = root_folder.create_file("my_file.txt") my_file.remove() self.assertFalse(root_folder.has_child("my_file.txt")) def test_removing_directories(self): self.assertTrue( os.path.exists(os.path.join(self.project.address, self.sample_folder)) ) self.project.get_resource(self.sample_folder).remove() self.assertFalse( os.path.exists(os.path.join(self.project.address, self.sample_folder)) ) def test_removing_non_existent_files(self): with self.assertRaises(ResourceNotFoundError): self.project.get_resource("NonExistentFile.txt").remove() def test_removing_nested_files(self): file_name = self.sample_folder + "/sample_file.txt" self.project.root.create_file(file_name) self.project.get_resource(file_name).remove() self.assertTrue( os.path.exists(os.path.join(self.project.address, self.sample_folder)) ) self.assertTrue( not os.path.exists(os.path.join(self.project.address, file_name)) ) def test_file_get_name(self): file = self.project.get_resource(self.sample_file) self.assertEqual(self.sample_file, file.name) file_name = "nestedFile.txt" parent = self.project.get_resource(self.sample_folder) filePath = self.sample_folder + "/" + file_name parent.create_file(file_name) nestedFile = self.project.get_resource(filePath) self.assertEqual(file_name, nestedFile.name) def test_folder_get_name(self): folder = self.project.get_resource(self.sample_folder) self.assertEqual(self.sample_folder, folder.name) def test_file_get_path(self): file = self.project.get_resource(self.sample_file) self.assertEqual(self.sample_file, file.path) fileName = "nestedFile.txt" parent = self.project.get_resource(self.sample_folder) filePath = self.sample_folder + "/" + fileName parent.create_file(fileName) nestedFile = self.project.get_resource(filePath) self.assertEqual(filePath, nestedFile.path) def test_folder_get_path(self): folder = self.project.get_resource(self.sample_folder) self.assertEqual(self.sample_folder, folder.path) def test_is_folder(self): self.assertTrue(self.project.get_resource(self.sample_folder).is_folder()) self.assertTrue(not self.project.get_resource(self.sample_file).is_folder()) def testget_children(self): children = self.project.get_resource(self.sample_folder).get_children() self.assertEqual([], children) def test_nonempty_get_children(self): file_name = "nestedfile.txt" filePath = self.sample_folder + "/" + file_name parent = self.project.get_resource(self.sample_folder) parent.create_file(file_name) children = parent.get_children() self.assertEqual(1, len(children)) self.assertEqual(filePath, children[0].path) def test_nonempty_get_children2(self): file_name = "nestedfile.txt" folder_name = "nestedfolder.txt" filePath = self.sample_folder + "/" + file_name folderPath = self.sample_folder + "/" + folder_name parent = self.project.get_resource(self.sample_folder) parent.create_file(file_name) parent.create_folder(folder_name) children = parent.get_children() self.assertEqual(2, len(children)) self.assertTrue(filePath == children[0].path or filePath == children[1].path) self.assertTrue( folderPath == children[0].path or folderPath == children[1].path ) def test_does_not_fail_for_permission_denied(self): bad_dir = os.path.join(self.sample_folder, "bad_dir") os.makedirs(bad_dir) self.addCleanup(shutil.rmtree, bad_dir) os.chmod(bad_dir, 0o000) try: parent = self.project.get_resource(self.sample_folder) parent.get_children() finally: os.chmod(bad_dir, 0o755) def test_getting_files(self): files = self.project.root.get_files() self.assertEqual(1, len(files)) self.assertTrue(self.project.get_resource(self.sample_file) in files) def test_getting_folders(self): folders = self.project.root.get_folders() self.assertEqual(1, len(folders)) self.assertTrue(self.project.get_resource(self.sample_folder) in folders) def test_nested_folder_get_files(self): parent = self.project.root.create_folder("top") parent.create_file("file1.txt") parent.create_file("file2.txt") files = parent.get_files() self.assertEqual(2, len(files)) self.assertTrue(self.project.get_resource("top/file2.txt") in files) self.assertEqual(0, len(parent.get_folders())) def test_nested_folder_get_folders(self): parent = self.project.root.create_folder("top") parent.create_folder("dir1") parent.create_folder("dir2") folders = parent.get_folders() self.assertEqual(2, len(folders)) self.assertTrue(self.project.get_resource("top/dir1") in folders) self.assertEqual(0, len(parent.get_files())) def test_root_folder(self): root_folder = self.project.root self.assertEqual(2, len(root_folder.get_children())) self.assertEqual("", root_folder.path) self.assertEqual("", root_folder.name) def test_get_all_files(self): files = tuple(self.project.get_files()) self.assertEqual(1, len(files)) self.assertEqual(self.sample_file, files[0].name) def test_get_all_files_after_changing(self): self.assertEqual(1, len(self.project.get_files())) myfile = self.project.root.create_file("myfile.txt") self.assertEqual(2, len(self.project.get_files())) myfile.move("newfile.txt") self.assertEqual(2, len(self.project.get_files())) self.project.get_file("newfile.txt").remove() self.assertEqual(1, len(self.project.get_files())) def test_multifile_get_all_files(self): fileName = "nestedFile.txt" parent = self.project.get_resource(self.sample_folder) parent.create_file(fileName) files = list(self.project.get_files()) self.assertEqual(2, len(files)) self.assertTrue(fileName == files[0].name or fileName == files[1].name) def test_ignoring_dot_pyc_files_in_get_files(self): root = self.project.address src_folder = os.path.join(root, "src") os.mkdir(src_folder) test_pyc = os.path.join(src_folder, "test.pyc") open(test_pyc, "w").close() for x in self.project.get_files(): self.assertNotEqual("src/test.pyc", x.path) def test_folder_creating_files(self): projectFile = "NewFile.txt" self.project.root.create_file(projectFile) new_file = self.project.get_resource(projectFile) self.assertTrue(new_file is not None and not new_file.is_folder()) def test_folder_creating_nested_files(self): project_file = "NewFile.txt" parent_folder = self.project.get_resource(self.sample_folder) parent_folder.create_file(project_file) new_file = self.project.get_resource(self.sample_folder + "/" + project_file) self.assertTrue(new_file is not None and not new_file.is_folder()) def test_folder_creating_files2(self): projectFile = "newfolder" self.project.root.create_folder(projectFile) new_folder = self.project.get_resource(projectFile) self.assertTrue(new_folder is not None and new_folder.is_folder()) def test_folder_creating_nested_files2(self): project_file = "newfolder" parent_folder = self.project.get_resource(self.sample_folder) parent_folder.create_folder(project_file) new_folder = self.project.get_resource(self.sample_folder + "/" + project_file) self.assertTrue(new_folder is not None and new_folder.is_folder()) def test_folder_get_child(self): folder = self.project.root folder.create_file("myfile.txt") folder.create_folder("myfolder") self.assertEqual( self.project.get_resource("myfile.txt"), folder.get_child("myfile.txt") ) self.assertEqual( self.project.get_resource("myfolder"), folder.get_child("myfolder") ) def test_folder_get_child_nested(self): root = self.project.root folder = root.create_folder("myfolder") folder.create_file("myfile.txt") folder.create_folder("myfolder") self.assertEqual( self.project.get_resource("myfolder/myfile.txt"), folder.get_child("myfile.txt"), ) self.assertEqual( self.project.get_resource("myfolder/myfolder"), folder.get_child("myfolder") ) def test_project_root_is_root_folder(self): self.assertEqual("", self.project.root.path) def test_moving_files(self): root_folder = self.project.root my_file = root_folder.create_file("my_file.txt") my_file.move("my_other_file.txt") self.assertFalse(my_file.exists()) root_folder.get_child("my_other_file.txt") def test_moving_folders(self): root_folder = self.project.root my_folder = root_folder.create_folder("my_folder") my_file = my_folder.create_file("my_file.txt") my_folder.move("new_folder") self.assertFalse(root_folder.has_child("my_folder")) self.assertFalse(my_file.exists()) self.assertTrue(root_folder.get_child("new_folder") is not None) def test_moving_destination_folders(self): root_folder = self.project.root my_folder = root_folder.create_folder("my_folder") my_file = root_folder.create_file("my_file.txt") my_file.move("my_folder") self.assertFalse(root_folder.has_child("my_file.txt")) self.assertFalse(my_file.exists()) my_folder.get_child("my_file.txt") def test_moving_files_and_resource_objects(self): root_folder = self.project.root my_file = root_folder.create_file("my_file.txt") old_hash = hash(my_file) my_file.move("my_other_file.txt") self.assertEqual(old_hash, hash(my_file)) def test_file_encoding_reading(self): sample_file = self.project.root.create_file("my_file.txt") contents = ( b"# -*- coding: utf-8 -*-\n" + br"#\N{LATIN SMALL LETTER I WITH DIAERESIS}\n" ).decode("utf8") file = open(sample_file.real_path, "wb") file.write(contents.encode("utf-8")) file.close() self.assertEqual(contents, sample_file.read()) def test_file_encoding_writing(self): sample_file = self.project.root.create_file("my_file.txt") contents = ( b"# -*- coding: utf-8 -*-\n" + br"\N{LATIN SMALL LETTER I WITH DIAERESIS}\n" ).decode("utf8") sample_file.write(contents) self.assertEqual(contents, sample_file.read()) def test_using_utf8_when_writing_in_case_of_errors(self): sample_file = self.project.root.create_file("my_file.txt") contents = br"\n\N{LATIN SMALL LETTER I WITH DIAERESIS}\n".decode("utf8") sample_file.write(contents) self.assertEqual(contents, sample_file.read()) def test_encoding_declaration_in_the_second_line(self): sample_file = self.project.root.create_file("my_file.txt") contents = b"\n# -*- coding: latin-1 -*-\n\xa9\n" file = open(sample_file.real_path, "wb") file.write(contents) file.close() self.assertEqual(contents, sample_file.read().encode("latin-1")) def test_not_an_encoding_declaration(self): sample_file = self.project.root.create_file("my_file.txt") contents = b"def my_method(self, encoding='latin-1'):\n var = {}\n\xc2\xa9\n" file = open(sample_file.real_path, "wb") file.write(contents) file.close() self.assertEqual(contents, sample_file.read().encode("utf-8")) self.assertNotEqual(contents, sample_file.read().encode("latin-1")) def test_read_bytes(self): sample_file = self.project.root.create_file("my_file.txt") contents = b"\n# -*- coding: latin-1 -*-\n\xa9\n" file = open(sample_file.real_path, "wb") file.write(contents) file.close() self.assertEqual(contents, sample_file.read_bytes()) # TODO: Detecting utf-16 encoding def xxx_test_using_utf16(self): sample_file = self.project.root.create_file("my_file.txt") contents = b"# -*- coding: utf-16 -*-\n# This is a sample file ...\n" file = open(sample_file.real_path, "w") file.write(contents.encode("utf-16")) file.close() sample_file.write(contents) self.assertEqual(contents, sample_file.read()) # XXX: supporting utf_8_sig def xxx_test_file_encoding_reading_for_notepad_styles(self): sample_file = self.project.root.create_file("my_file.txt") contents = "#\N{LATIN SMALL LETTER I WITH DIAERESIS}\n" file = open(sample_file.real_path, "w") # file.write('\xef\xbb\xbf') file.write(contents.encode("utf-8-sig")) file.close() self.assertEqual(contents, sample_file.read()) def test_using_project_get_file(self): myfile = self.project.get_file(self.sample_file) self.assertTrue(myfile.exists()) def test_using_file_create(self): myfile = self.project.get_file("myfile.txt") self.assertFalse(myfile.exists()) myfile.create() self.assertTrue(myfile.exists()) self.assertFalse(myfile.is_folder()) def test_using_folder_create(self): myfolder = self.project.get_folder("myfolder") self.assertFalse(myfolder.exists()) myfolder.create() self.assertTrue(myfolder.exists()) self.assertTrue(myfolder.is_folder()) def test_exception_when_creating_twice(self): with self.assertRaises(RopeError): myfile = self.project.get_file("myfile.txt") myfile.create() myfile.create() def test_exception_when_parent_does_not_exist(self): with self.assertRaises(ResourceNotFoundError): myfile = self.project.get_file("myfolder/myfile.txt") myfile.create() def test_simple_path_to_resource(self): myfile = self.project.root.create_file("myfile.txt") self.assertEqual(myfile, path_to_resource(self.project, myfile.real_path)) self.assertEqual( myfile, path_to_resource(self.project, myfile.real_path, type="file") ) myfolder = self.project.root.create_folder("myfolder") self.assertEqual(myfolder, path_to_resource(self.project, myfolder.real_path)) self.assertEqual( myfolder, path_to_resource(self.project, myfolder.real_path, type="folder") ) @testutils.skipNotPOSIX() def test_ignoring_symlinks_inside_project(self): project2 = testutils.sample_project(folder_name="sampleproject2") mod = project2.root.create_file("mod.py") try: path = os.path.join(self.project.address, "linkedfile.txt") os.symlink(mod.real_path, path) files = self.project.root.get_files() self.assertEqual(1, len(files)) finally: testutils.remove_project(project2) def test_getting_empty_source_folders(self): self.assertEqual([], self.project.get_source_folders()) def test_root_source_folder(self): self.project.root.create_file("sample.py") source_folders = self.project.get_source_folders() self.assertEqual(1, len(source_folders)) self.assertTrue(self.project.root in source_folders) def test_root_source_folder2(self): self.project.root.create_file("mod1.py") self.project.root.create_file("mod2.py") source_folders = self.project.get_source_folders() self.assertEqual(1, len(source_folders)) self.assertTrue(self.project.root in source_folders) def test_src_source_folder(self): src = self.project.root.create_folder("src") src.create_file("sample.py") source_folders = self.project.get_source_folders() self.assertEqual(1, len(source_folders)) self.assertTrue(self.project.get_resource("src") in source_folders) def test_packages(self): src = self.project.root.create_folder("src") pkg = src.create_folder("package") pkg.create_file("__init__.py") source_folders = self.project.get_source_folders() self.assertEqual(1, len(source_folders)) self.assertTrue(src in source_folders) def test_multi_source_folders(self): src = self.project.root.create_folder("src") package = src.create_folder("package") package.create_file("__init__.py") test = self.project.root.create_folder("test") test.create_file("alltests.py") source_folders = self.project.get_source_folders() self.assertEqual(2, len(source_folders)) self.assertTrue(src in source_folders) self.assertTrue(test in source_folders) def test_multi_source_folders2(self): testutils.create_module(self.project, "mod1") src = self.project.root.create_folder("src") package = testutils.create_package(self.project, "package", src) testutils.create_module(self.project, "mod2", package) source_folders = self.project.get_source_folders() self.assertEqual(2, len(source_folders)) self.assertTrue(self.project.root in source_folders and src in source_folders) class ResourceObserverTest(unittest.TestCase): def setUp(self): super(ResourceObserverTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(ResourceObserverTest, self).tearDown() def test_resource_change_observer(self): sample_file = self.project.root.create_file("my_file.txt") sample_file.write("a sample file version 1") sample_observer = _SampleObserver() self.project.add_observer(sample_observer) sample_file.write("a sample file version 2") self.assertEqual(1, sample_observer.change_count) self.assertEqual(sample_file, sample_observer.last_changed) def test_resource_change_observer_after_removal(self): sample_file = self.project.root.create_file("my_file.txt") sample_file.write("text") sample_observer = _SampleObserver() self.project.add_observer( FilteredResourceObserver(sample_observer, [sample_file]) ) sample_file.remove() self.assertEqual(1, sample_observer.change_count) self.assertEqual(sample_file, sample_observer.last_removed) def test_resource_change_observer2(self): sample_file = self.project.root.create_file("my_file.txt") sample_observer = _SampleObserver() self.project.add_observer(sample_observer) self.project.remove_observer(sample_observer) sample_file.write("a sample file version 2") self.assertEqual(0, sample_observer.change_count) def test_resource_change_observer_for_folders(self): root_folder = self.project.root my_folder = root_folder.create_folder("my_folder") my_folder_observer = _SampleObserver() root_folder_observer = _SampleObserver() self.project.add_observer( FilteredResourceObserver(my_folder_observer, [my_folder]) ) self.project.add_observer( FilteredResourceObserver(root_folder_observer, [root_folder]) ) my_file = my_folder.create_file("my_file.txt") self.assertEqual(1, my_folder_observer.change_count) my_file.move("another_file.txt") self.assertEqual(2, my_folder_observer.change_count) self.assertEqual(1, root_folder_observer.change_count) self.project.get_resource("another_file.txt").remove() self.assertEqual(2, my_folder_observer.change_count) self.assertEqual(2, root_folder_observer.change_count) def test_resource_change_observer_after_moving(self): sample_file = self.project.root.create_file("my_file.txt") sample_observer = _SampleObserver() self.project.add_observer(sample_observer) sample_file.move("new_file.txt") self.assertEqual(1, sample_observer.change_count) self.assertEqual( (sample_file, self.project.get_resource("new_file.txt")), sample_observer.last_moved, ) def test_revalidating_files(self): root = self.project.root my_file = root.create_file("my_file.txt") sample_observer = _SampleObserver() self.project.add_observer(FilteredResourceObserver(sample_observer, [my_file])) os.remove(my_file.real_path) self.project.validate(root) self.assertEqual(my_file, sample_observer.last_removed) self.assertEqual(1, sample_observer.change_count) def test_revalidating_files_and_no_changes2(self): root = self.project.root my_file = root.create_file("my_file.txt") sample_observer = _SampleObserver() self.project.add_observer(FilteredResourceObserver(sample_observer, [my_file])) self.project.validate(root) self.assertEqual(None, sample_observer.last_moved) self.assertEqual(0, sample_observer.change_count) def test_revalidating_folders(self): root = self.project.root my_folder = root.create_folder("myfolder") my_file = my_folder.create_file("myfile.txt") # noqa sample_observer = _SampleObserver() self.project.add_observer( FilteredResourceObserver(sample_observer, [my_folder]) ) testutils.remove_recursively(my_folder.real_path) self.project.validate(root) self.assertEqual(my_folder, sample_observer.last_removed) self.assertEqual(1, sample_observer.change_count) def test_removing_and_adding_resources_to_filtered_observer(self): my_file = self.project.root.create_file("my_file.txt") sample_observer = _SampleObserver() filtered_observer = FilteredResourceObserver(sample_observer) self.project.add_observer(filtered_observer) my_file.write("1") self.assertEqual(0, sample_observer.change_count) filtered_observer.add_resource(my_file) my_file.write("2") self.assertEqual(1, sample_observer.change_count) filtered_observer.remove_resource(my_file) my_file.write("3") self.assertEqual(1, sample_observer.change_count) def test_validation_and_changing_files(self): my_file = self.project.root.create_file("my_file.txt") sample_observer = _SampleObserver() timekeeper = _MockChangeIndicator() filtered_observer = FilteredResourceObserver( sample_observer, [my_file], timekeeper=timekeeper ) self.project.add_observer(filtered_observer) self._write_file(my_file.real_path) timekeeper.set_indicator(my_file, 1) self.project.validate(self.project.root) self.assertEqual(1, sample_observer.change_count) def test_validation_and_changing_files2(self): my_file = self.project.root.create_file("my_file.txt") sample_observer = _SampleObserver() timekeeper = _MockChangeIndicator() self.project.add_observer( FilteredResourceObserver(sample_observer, [my_file], timekeeper=timekeeper) ) timekeeper.set_indicator(my_file, 1) my_file.write("hey") self.assertEqual(1, sample_observer.change_count) self.project.validate(self.project.root) self.assertEqual(1, sample_observer.change_count) def test_not_reporting_multiple_changes_to_folders(self): root = self.project.root file1 = root.create_file("file1.txt") file2 = root.create_file("file2.txt") sample_observer = _SampleObserver() self.project.add_observer( FilteredResourceObserver(sample_observer, [root, file1, file2]) ) os.remove(file1.real_path) os.remove(file2.real_path) self.assertEqual(0, sample_observer.change_count) self.project.validate(self.project.root) self.assertEqual(3, sample_observer.change_count) def _write_file(self, path): my_file = open(path, "w") my_file.write("\n") my_file.close() def test_moving_and_being_interested_about_a_folder_and_a_child(self): my_folder = self.project.root.create_folder("my_folder") my_file = my_folder.create_file("my_file.txt") sample_observer = _SampleObserver() filtered_observer = FilteredResourceObserver( sample_observer, [my_folder, my_file] ) self.project.add_observer(filtered_observer) my_folder.move("new_folder") self.assertEqual(2, sample_observer.change_count) def test_contains_for_folders(self): folder1 = self.project.root.create_folder("folder") folder2 = self.project.root.create_folder("folder2") self.assertFalse(folder1.contains(folder2)) def test_validating_when_created(self): root = self.project.root my_file = self.project.get_file("my_file.txt") sample_observer = _SampleObserver() self.project.add_observer(FilteredResourceObserver(sample_observer, [my_file])) open(my_file.real_path, "w").close() self.project.validate(root) self.assertEqual(my_file, sample_observer.last_created) self.assertEqual(1, sample_observer.change_count) def test_validating_twice_when_created(self): root = self.project.root my_file = self.project.get_file("my_file.txt") sample_observer = _SampleObserver() self.project.add_observer(FilteredResourceObserver(sample_observer, [my_file])) open(my_file.real_path, "w").close() self.project.validate(root) self.project.validate(root) self.assertEqual(my_file, sample_observer.last_created) self.assertEqual(1, sample_observer.change_count) def test_changes_and_adding_resources(self): root = self.project.root # noqa file1 = self.project.get_file("file1.txt") file2 = self.project.get_file("file2.txt") file1.create() sample_observer = _SampleObserver() self.project.add_observer( FilteredResourceObserver(sample_observer, [file1, file2]) ) file1.move(file2.path) self.assertEqual(2, sample_observer.change_count) self.assertEqual(file2, sample_observer.last_created) self.assertEqual((file1, file2), sample_observer.last_moved) def test_validating_get_files_list(self): root = self.project.root # noqa self.assertEqual(0, len(self.project.get_files())) file = open(os.path.join(self.project.address, "myfile.txt"), "w") file.close() self.project.validate() self.assertEqual(1, len(self.project.get_files())) def test_clear_observered_resources_for_filtered_observers(self): sample_file = self.project.root.create_file("myfile.txt") sample_observer = _SampleObserver() filtered = FilteredResourceObserver(sample_observer) self.project.add_observer(filtered) filtered.add_resource(sample_file) filtered.clear_resources() sample_file.write("1") self.assertEqual(0, sample_observer.change_count) class _MockChangeIndicator(object): def __init__(self): self.times = {} def set_indicator(self, resource, time): self.times[resource] = time def get_indicator(self, resource): return self.times.get(resource, 0) class _SampleObserver(object): def __init__(self): self.change_count = 0 self.last_changed = None self.last_moved = None self.last_created = None self.last_removed = None def resource_changed(self, resource): self.last_changed = resource self.change_count += 1 def resource_moved(self, resource, new_resource): self.last_moved = (resource, new_resource) self.change_count += 1 def resource_created(self, resource): self.last_created = resource self.change_count += 1 def resource_removed(self, resource): self.last_removed = resource self.change_count += 1 class OutOfProjectTest(unittest.TestCase): def setUp(self): super(OutOfProjectTest, self).setUp() self.test_directory = "temp_test_directory" testutils.remove_recursively(self.test_directory) os.mkdir(self.test_directory) self.project = testutils.sample_project() self.no_project = NoProject() def tearDown(self): testutils.remove_project(self.project) testutils.remove_recursively(self.test_directory) super(OutOfProjectTest, self).tearDown() def test_simple_out_of_project_file(self): sample_file_path = os.path.join(self.test_directory, "sample.txt") sample_file = open(sample_file_path, "w") sample_file.write("sample content\n") sample_file.close() sample_resource = self.no_project.get_resource(sample_file_path) self.assertEqual("sample content\n", sample_resource.read()) def test_simple_out_of_project_folder(self): sample_folder_path = os.path.join(self.test_directory, "sample_folder") os.mkdir(sample_folder_path) sample_folder = self.no_project.get_resource(sample_folder_path) self.assertEqual([], sample_folder.get_children()) sample_file_path = os.path.join(sample_folder_path, "sample.txt") open(sample_file_path, "w").close() sample_resource = self.no_project.get_resource(sample_file_path) self.assertEqual(sample_resource, sample_folder.get_children()[0]) def test_using_absolute_path(self): sample_file_path = os.path.join(self.test_directory, "sample.txt") open(sample_file_path, "w").close() normal_sample_resource = self.no_project.get_resource(sample_file_path) absolute_sample_resource = self.no_project.get_resource( os.path.abspath(sample_file_path) ) self.assertEqual(normal_sample_resource, absolute_sample_resource) def test_folder_get_child(self): sample_folder_path = os.path.join(self.test_directory, "sample_folder") os.mkdir(sample_folder_path) sample_folder = self.no_project.get_resource(sample_folder_path) self.assertEqual([], sample_folder.get_children()) sample_file_path = os.path.join(sample_folder_path, "sample.txt") open(sample_file_path, "w").close() sample_resource = self.no_project.get_resource(sample_file_path) self.assertTrue(sample_folder.has_child("sample.txt")) self.assertFalse(sample_folder.has_child("doesnothave.txt")) self.assertEqual(sample_resource, sample_folder.get_child("sample.txt")) def test_out_of_project_files_and_path_to_resource(self): sample_file_path = os.path.join(self.test_directory, "sample.txt") sample_file = open(sample_file_path, "w") sample_file.write("sample content\n") sample_file.close() sample_resource = self.no_project.get_resource(sample_file_path) self.assertEqual( sample_resource, path_to_resource(self.project, sample_file_path) ) class _MockFSCommands(object): def __init__(self): self.log = "" self.fscommands = FileSystemCommands() def create_file(self, path): self.log += "create_file " self.fscommands.create_file(path) def create_folder(self, path): self.log += "create_folder " self.fscommands.create_folder(path) def move(self, path, new_location): self.log += "move " self.fscommands.move(path, new_location) def remove(self, path): self.log += "remove " self.fscommands.remove(path) def read(self, path): self.log += "read " return self.fscommands.read(path) class _DeprecatedFSCommands(object): def __init__(self): self.log = "" self.fscommands = FileSystemCommands() def create_file(self, path): self.log += "create_file " self.fscommands.create_file(path) def create_folder(self, path): self.log += "create_folder " self.fscommands.create_folder(path) def move(self, path, new_location): self.log += "move " self.fscommands.move(path, new_location) def remove(self, path): self.log += "remove " self.fscommands.remove(path) class RopeFolderTest(unittest.TestCase): def setUp(self): super(RopeFolderTest, self).setUp() self.project = None def tearDown(self): if self.project: testutils.remove_project(self.project) super(RopeFolderTest, self).tearDown() def test_none_project_rope_folder(self): self.project = testutils.sample_project(ropefolder=None) self.assertTrue(self.project.ropefolder is None) def test_getting_project_rope_folder(self): self.project = testutils.sample_project(ropefolder=".ropeproject") self.assertTrue(self.project.ropefolder.exists()) self.assertTrue(".ropeproject", self.project.ropefolder.path) def test_setting_ignored_resources(self): self.project = testutils.sample_project(ignored_resources=["myfile.txt"]) myfile = self.project.get_file("myfile.txt") file2 = self.project.get_file("file2.txt") self.assertTrue(self.project.is_ignored(myfile)) self.assertFalse(self.project.is_ignored(file2)) def test_ignored_folders(self): self.project = testutils.sample_project(ignored_resources=["myfolder"]) myfolder = self.project.root.create_folder("myfolder") self.assertTrue(self.project.is_ignored(myfolder)) myfile = myfolder.create_file("myfile.txt") self.assertTrue(self.project.is_ignored(myfile)) def test_ignored_resources_and_get_files(self): self.project = testutils.sample_project( ignored_resources=["myfile.txt"], ropefolder=None ) myfile = self.project.get_file("myfile.txt") self.assertEqual(0, len(self.project.get_files())) myfile.create() self.assertEqual(0, len(self.project.get_files())) def test_ignored_resources_and_get_files2(self): self.project = testutils.sample_project( ignored_resources=["myfile.txt"], ropefolder=None ) myfile = self.project.root.create_file("myfile.txt") # noqa self.assertEqual(0, len(self.project.get_files())) def test_setting_ignored_resources_patterns(self): self.project = testutils.sample_project(ignored_resources=["m?file.*"]) myfile = self.project.get_file("myfile.txt") file2 = self.project.get_file("file2.txt") self.assertTrue(self.project.is_ignored(myfile)) self.assertFalse(self.project.is_ignored(file2)) def test_star_should_not_include_slashes(self): self.project = testutils.sample_project(ignored_resources=["f*.txt"]) folder = self.project.root.create_folder("folder") file1 = folder.create_file("myfile.txt") file2 = folder.create_file("file2.txt") self.assertFalse(self.project.is_ignored(file1)) self.assertTrue(self.project.is_ignored(file2)) def test_normal_fscommands(self): fscommands = _MockFSCommands() self.project = testutils.sample_project(fscommands=fscommands) myfile = self.project.get_file("myfile.txt") myfile.create() self.assertTrue("create_file ", fscommands.log) def test_fscommands_and_ignored_resources(self): fscommands = _MockFSCommands() self.project = testutils.sample_project( fscommands=fscommands, ignored_resources=["myfile.txt"], ropefolder=None ) myfile = self.project.get_file("myfile.txt") myfile.create() self.assertEqual("", fscommands.log) def test_deprecated_fscommands(self): fscommands = _DeprecatedFSCommands() self.project = testutils.sample_project(fscommands=fscommands) myfile = self.project.get_file("myfile.txt") myfile.create() self.assertTrue("create_file ", fscommands.log) def test_ignored_resources_and_prefixes(self): self.project = testutils.sample_project(ignored_resources=[".hg"]) myfile = self.project.root.create_file(".hgignore") self.assertFalse(self.project.is_ignored(myfile)) def test_loading_config_dot_py(self): self.project = testutils.sample_project(ropefolder=".ropeproject") config = self.project.get_file(".ropeproject/config.py") if not config.exists(): config.create() config.write( dedent("""\ def set_prefs(prefs): prefs["ignored_resources"] = ["myfile.txt"] def project_opened(project): project.root.create_file("loaded") """) ) self.project.close() self.project = Project(self.project.address, ropefolder=".ropeproject") self.assertTrue(self.project.get_file("loaded").exists()) myfile = self.project.get_file("myfile.txt") self.assertTrue(self.project.is_ignored(myfile)) def test_ignoring_syntax_errors(self): self.project = testutils.sample_project( ropefolder=None, ignore_syntax_errors=True ) mod = testutils.create_module(self.project, "mod") mod.write("xyz print") pymod = self.project.get_pymodule(mod) # noqa def test_compressed_history(self): self.project = testutils.sample_project(compress_history=True) mod = testutils.create_module(self.project, "mod") mod.write("") def test_compressed_objectdb(self): self.project = testutils.sample_project(compress_objectdb=True) mod = testutils.create_module(self.project, "mod") self.project.pycore.analyze_module(mod) def test_nested_dot_ropeproject_folder(self): self.project = testutils.sample_project(ropefolder=".f1/f2") ropefolder = self.project.ropefolder self.assertEqual(".f1/f2", ropefolder.path) self.assertTrue(ropefolder.exists()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/pycoretest.py0000664000175000017500000014766400000000000017075 0ustar00lieryanlieryanimport sys from textwrap import dedent from rope.base.builtins import File, BuiltinClass try: import unittest2 as unittest except ImportError: import unittest from rope.base import exceptions from rope.base import libutils from rope.base.pycore import _TextChangeDetector from rope.base.pyobjects import get_base_type, AbstractFunction from rope.base.pynamesdef import AssignedName from ropetest import testutils class PyCoreTest(unittest.TestCase): def setUp(self): super(PyCoreTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore def tearDown(self): testutils.remove_project(self.project) super(PyCoreTest, self).tearDown() def test_simple_module(self): testutils.create_module(self.project, "mod") result = self.project.get_module("mod") self.assertEqual(get_base_type("Module"), result.type) self.assertEqual(0, len(result.get_attributes())) def test_nested_modules(self): pkg = testutils.create_package(self.project, "pkg") mod = testutils.create_module(self.project, "mod", pkg) # noqa package = self.project.get_module("pkg") self.assertEqual(get_base_type("Module"), package.get_type()) self.assertEqual(1, len(package.get_attributes())) module = package["mod"].get_object() self.assertEqual(get_base_type("Module"), module.get_type()) def test_package(self): pkg = testutils.create_package(self.project, "pkg") mod = testutils.create_module(self.project, "mod", pkg) # noqa result = self.project.get_module("pkg") self.assertEqual(get_base_type("Module"), result.type) def test_simple_class(self): mod = testutils.create_module(self.project, "mod") mod.write( dedent("""\ class SampleClass(object): pass """) ) mod_element = self.project.get_module("mod") result = mod_element["SampleClass"].get_object() self.assertEqual(get_base_type("Type"), result.get_type()) def test_simple_function(self): mod = testutils.create_module(self.project, "mod") mod.write( dedent("""\ def sample_function(): pass """) ) mod_element = self.project.get_module("mod") result = mod_element["sample_function"].get_object() self.assertEqual(get_base_type("Function"), result.get_type()) def test_class_methods(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class SampleClass(object): def sample_method(self): pass """) mod.write(code) mod_element = self.project.get_module("mod") sample_class = mod_element["SampleClass"].get_object() self.assertTrue("sample_method" in sample_class) method = sample_class["sample_method"].get_object() self.assertEqual(get_base_type("Function"), method.get_type()) def test_global_variable_without_type_annotation(self): mod = testutils.create_module(self.project, "mod") mod.write("var = 10") mod_element = self.project.get_module("mod") var = mod_element["var"] self.assertEqual(AssignedName, type(var)) @testutils.only_for_versions_higher("3.6") def test_global_variable_with_type_annotation(self): mod = testutils.create_module(self.project, "mod") mod.write("py3_var: str = foo_bar") mod_element = self.project.get_module("mod") py3_var = mod_element["py3_var"] self.assertEqual(AssignedName, type(py3_var)) def test_class_variables(self): mod = testutils.create_module(self.project, "mod") mod.write( dedent("""\ class SampleClass(object): var = 10 """) ) mod_element = self.project.get_module("mod") sample_class = mod_element["SampleClass"].get_object() var = sample_class["var"] # noqa def test_class_attributes_set_in_init(self): mod = testutils.create_module(self.project, "mod") mod.write( dedent("""\ class C(object): def __init__(self): self.var = 20 """) ) mod_element = self.project.get_module("mod") sample_class = mod_element["C"].get_object() var = sample_class["var"] # noqa def test_class_attributes_set_in_init_overwriting_a_defined(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): def __init__(self): self.f = 20 def f(): pass """) mod.write(code) mod_element = self.project.get_module("mod") sample_class = mod_element["C"].get_object() f = sample_class["f"].get_object() self.assertTrue(isinstance(f, AbstractFunction)) def test_classes_inside_other_classes(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class SampleClass(object): class InnerClass(object): pass """) mod.write(code) mod_element = self.project.get_module("mod") sample_class = mod_element["SampleClass"].get_object() var = sample_class["InnerClass"].get_object() self.assertEqual(get_base_type("Type"), var.get_type()) def test_non_existent_module(self): with self.assertRaises(exceptions.ModuleNotFoundError): self.project.get_module("doesnotexistmodule") def test_imported_names(self): testutils.create_module(self.project, "mod1") mod = testutils.create_module(self.project, "mod2") mod.write("import mod1\n") module = self.project.get_module("mod2") imported_sys = module["mod1"].get_object() self.assertEqual(get_base_type("Module"), imported_sys.get_type()) def test_imported_as_names(self): testutils.create_module(self.project, "mod1") mod = testutils.create_module(self.project, "mod2") mod.write("import mod1 as my_import\n") module = self.project.get_module("mod2") imported_mod = module["my_import"].get_object() self.assertEqual(get_base_type("Module"), imported_mod.get_type()) def test_get_string_module(self): code = dedent("""\ class Sample(object): pass """) mod = libutils.get_string_module(self.project, code) sample_class = mod["Sample"].get_object() self.assertEqual(get_base_type("Type"), sample_class.get_type()) def test_get_string_module_with_extra_spaces(self): code = "a = 10\n " mod = libutils.get_string_module(self.project, code) # noqa def test_parameter_info_for_functions(self): code = dedent("""\ def func(param1, param2=10, *param3, **param4): pass""") mod = libutils.get_string_module(self.project, code) sample_function = mod["func"] self.assertEqual( ["param1", "param2", "param3", "param4"], sample_function.get_object().get_param_names(), ) # FIXME: Not found modules def xxx_test_not_found_module_is_module(self): code = "import doesnotexist\n" mod = libutils.get_string_module(self.project, code) self.assertEqual( get_base_type("Module"), mod["doesnotexist"].get_object().get_type() ) def test_mixing_scopes_and_objects_hierarchy(self): code = "var = 200\n" mod = libutils.get_string_module(self.project, code) scope = mod.get_scope() self.assertTrue("var" in scope.get_names()) def test_inheriting_base_class_attributes(self): code = dedent("""\ class Base(object): def method(self): pass class Derived(Base): pass """) mod = libutils.get_string_module(self.project, code) derived = mod["Derived"].get_object() self.assertTrue("method" in derived) self.assertEqual( get_base_type("Function"), derived["method"].get_object().get_type() ) def test_inheriting_multiple_base_class_attributes(self): code = dedent("""\ class Base1(object): def method1(self): pass class Base2(object): def method2(self): pass class Derived(Base1, Base2): pass """) mod = libutils.get_string_module(self.project, code) derived = mod["Derived"].get_object() self.assertTrue("method1" in derived) self.assertTrue("method2" in derived) def test_inherit_multiple_base_class_attrs_with_the_same_name(self): code = dedent("""\ class Base1(object): def method(self): pass class Base2(object): def method(self): pass class Derived(Base1, Base2): pass """) mod = libutils.get_string_module(self.project, code) base1 = mod["Base1"].get_object() derived = mod["Derived"].get_object() self.assertEqual(base1["method"].get_object(), derived["method"].get_object()) def test_inheriting_unknown_base_class(self): code = dedent("""\ class Derived(NotFound): def f(self): pass """) mod = libutils.get_string_module(self.project, code) derived = mod["Derived"].get_object() self.assertTrue("f" in derived) def test_module_creation(self): new_module = testutils.create_module(self.project, "module") self.assertFalse(new_module.is_folder()) self.assertEqual(self.project.get_resource("module.py"), new_module) def test_packaged_module_creation(self): package = self.project.root.create_folder("package") # noqa new_module = testutils.create_module(self.project, "package.module") self.assertEqual(self.project.get_resource("package/module.py"), new_module) def test_packaged_module_creation_with_nested_src(self): src = self.project.root.create_folder("src") src.create_folder("pkg") new_module = testutils.create_module(self.project, "pkg.mod", src) self.assertEqual(self.project.get_resource("src/pkg/mod.py"), new_module) def test_package_creation(self): new_package = testutils.create_package(self.project, "pkg") self.assertTrue(new_package.is_folder()) self.assertEqual(self.project.get_resource("pkg"), new_package) self.assertEqual( self.project.get_resource("pkg/__init__.py"), new_package.get_child("__init__.py"), ) def test_nested_package_creation(self): testutils.create_package(self.project, "pkg1") nested_package = testutils.create_package(self.project, "pkg1.pkg2") self.assertEqual(self.project.get_resource("pkg1/pkg2"), nested_package) def test_packaged_package_creation_with_nested_src(self): src = self.project.root.create_folder("src") testutils.create_package(self.project, "pkg1", src) nested_package = testutils.create_package(self.project, "pkg1.pkg2", src) self.assertEqual(self.project.get_resource("src/pkg1/pkg2"), nested_package) def test_find_module(self): src = self.project.root.create_folder("src") samplemod = testutils.create_module(self.project, "samplemod", src) found_module = self.project.find_module("samplemod") self.assertEqual(samplemod, found_module) def test_find_nested_module(self): src = self.project.root.create_folder("src") samplepkg = testutils.create_package(self.project, "samplepkg", src) samplemod = testutils.create_module(self.project, "samplemod", samplepkg) found_module = self.project.find_module("samplepkg.samplemod") self.assertEqual(samplemod, found_module) def test_find_multiple_module(self): src = self.project.root.create_folder("src") samplemod1 = testutils.create_module(self.project, "samplemod", src) samplemod2 = testutils.create_module(self.project, "samplemod") test = self.project.root.create_folder("test") samplemod3 = testutils.create_module(self.project, "samplemod", test) found_module = self.project.find_module("samplemod") self.assertTrue( samplemod1 == found_module or samplemod2 == found_module or samplemod3 == found_module ) def test_find_module_packages(self): src = self.project.root samplepkg = testutils.create_package(self.project, "samplepkg", src) found_module = self.project.find_module("samplepkg") self.assertEqual(samplepkg, found_module) def test_find_module_when_module_and_package_with_the_same_name(self): src = self.project.root testutils.create_module(self.project, "sample", src) samplepkg = testutils.create_package(self.project, "sample", src) found_module = self.project.find_module("sample") self.assertEqual(samplepkg, found_module) def test_source_folders_preference(self): testutils.create_package(self.project, "pkg1") testutils.create_package(self.project, "pkg1.src2") lost = testutils.create_module(self.project, "pkg1.src2.lost") self.assertEqual(self.project.find_module("lost"), None) self.project.close() from rope.base.project import Project self.project = Project(self.project.address, source_folders=["pkg1/src2"]) self.assertEqual(self.project.find_module("lost"), lost) def test_get_pyname_definition_location(self): code = "a_var = 20\n" mod = libutils.get_string_module(self.project, code) a_var = mod["a_var"] self.assertEqual((mod, 1), a_var.get_definition_location()) def test_get_pyname_definition_location_functions(self): code = dedent("""\ def a_func(): pass """) mod = libutils.get_string_module(self.project, code) a_func = mod["a_func"] self.assertEqual((mod, 1), a_func.get_definition_location()) def test_get_pyname_definition_location_class(self): code = dedent("""\ class AClass(object): pass """) mod = libutils.get_string_module(self.project, code) a_class = mod["AClass"] self.assertEqual((mod, 1), a_class.get_definition_location()) def test_get_pyname_definition_location_local_variables(self): code = dedent("""\ def a_func(): a_var = 10 """) mod = libutils.get_string_module(self.project, code) a_func_scope = mod.get_scope().get_scopes()[0] a_var = a_func_scope["a_var"] self.assertEqual((mod, 2), a_var.get_definition_location()) def test_get_pyname_definition_location_reassigning(self): code = dedent("""\ a_var = 20 a_var=30 """) mod = libutils.get_string_module(self.project, code) a_var = mod["a_var"] self.assertEqual((mod, 1), a_var.get_definition_location()) def test_get_pyname_definition_location_importes(self): testutils.create_module(self.project, "mod") code = "import mod\n" mod = libutils.get_string_module(self.project, code) imported_module = self.project.get_module("mod") module_pyname = mod["mod"] self.assertEqual((imported_module, 1), module_pyname.get_definition_location()) def test_get_pyname_definition_location_imports(self): module_resource = testutils.create_module(self.project, "mod") module_resource.write( dedent("""\ def a_func(): pass """) ) imported_module = self.project.get_module("mod") code = dedent("""\ from mod import a_func """) mod = libutils.get_string_module(self.project, code) a_func = mod["a_func"] self.assertEqual((imported_module, 2), a_func.get_definition_location()) def test_get_pyname_definition_location_parameters(self): code = dedent("""\ def a_func(param1, param2): a_var = param """) mod = libutils.get_string_module(self.project, code) a_func_scope = mod.get_scope().get_scopes()[0] param1 = a_func_scope["param1"] self.assertEqual((mod, 1), param1.get_definition_location()) param2 = a_func_scope["param2"] self.assertEqual((mod, 1), param2.get_definition_location()) def test_module_get_resource(self): module_resource = testutils.create_module(self.project, "mod") module = self.project.get_module("mod") self.assertEqual(module_resource, module.get_resource()) code = dedent("""\ from mod import a_func """) string_module = libutils.get_string_module(self.project, code) self.assertEqual(None, string_module.get_resource()) def test_get_pyname_definition_location_class2(self): code = dedent("""\ class AClass(object): def __init__(self): self.an_attr = 10 """) mod = libutils.get_string_module(self.project, code) a_class = mod["AClass"].get_object() an_attr = a_class["an_attr"] self.assertEqual((mod, 3), an_attr.get_definition_location()) def test_import_not_found_module_get_definition_location(self): code = "import doesnotexist\n" mod = libutils.get_string_module(self.project, code) does_not_exist = mod["doesnotexist"] self.assertEqual((None, None), does_not_exist.get_definition_location()) def test_from_not_found_module_get_definition_location(self): code = "from doesnotexist import Sample\n" mod = libutils.get_string_module(self.project, code) sample = mod["Sample"] self.assertEqual((None, None), sample.get_definition_location()) def test_from_package_import_module_get_definition_location(self): pkg = testutils.create_package(self.project, "pkg") testutils.create_module(self.project, "mod", pkg) pkg_mod = self.project.get_module("pkg.mod") code = "from pkg import mod\n" mod = libutils.get_string_module(self.project, code) imported_mod = mod["mod"] self.assertEqual((pkg_mod, 1), imported_mod.get_definition_location()) def test_get_module_for_defined_pyobjects(self): code = dedent("""\ class AClass(object): pass """) mod = libutils.get_string_module(self.project, code) a_class = mod["AClass"].get_object() self.assertEqual(mod, a_class.get_module()) def test_get_definition_location_for_packages(self): testutils.create_package(self.project, "pkg") init_module = self.project.get_module("pkg.__init__") code = "import pkg\n" mod = libutils.get_string_module(self.project, code) pkg_pyname = mod["pkg"] self.assertEqual((init_module, 1), pkg_pyname.get_definition_location()) def test_get_definition_location_for_filtered_packages(self): pkg = testutils.create_package(self.project, "pkg") testutils.create_module(self.project, "mod", pkg) init_module = self.project.get_module("pkg.__init__") code = "import pkg.mod" mod = libutils.get_string_module(self.project, code) pkg_pyname = mod["pkg"] self.assertEqual((init_module, 1), pkg_pyname.get_definition_location()) def test_out_of_project_modules(self): code = "import rope.base.project as project\n" scope = libutils.get_string_scope(self.project, code) imported_module = scope["project"].get_object() self.assertTrue("Project" in imported_module) def test_file_encoding_reading(self): contents = ( u"# -*- coding: utf-8 -*-\n" + u"#\N{LATIN SMALL LETTER I WITH DIAERESIS}\n" ) mod = testutils.create_module(self.project, "mod") mod.write(contents) self.project.get_module("mod") def test_global_keyword(self): code = dedent("""\ a_var = 1 def a_func(): global a_var """) mod = libutils.get_string_module(self.project, code) global_var = mod["a_var"] func_scope = mod["a_func"].get_object().get_scope() local_var = func_scope["a_var"] self.assertEqual(global_var, local_var) def test_not_leaking_for_vars_inside_parent_scope(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): def f(self): for my_var1, my_var2 in []: pass """) mod.write(code) pymod = self.pycore.resource_to_pyobject(mod) c_class = pymod["C"].get_object() self.assertFalse("my_var1" in c_class) self.assertFalse("my_var2" in c_class) def test_not_leaking_for_vars_inside_parent_scope2(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): def f(self): for my_var in []: pass """) mod.write(code) pymod = self.pycore.resource_to_pyobject(mod) c_class = pymod["C"].get_object() self.assertFalse("my_var" in c_class) def test_variables_defined_in_excepts(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ try: myvar1 = 1 except: myvar2 = 1 finally: myvar3 = 1 """) mod.write(code) pymod = self.pycore.resource_to_pyobject(mod) self.assertTrue("myvar1" in pymod) self.assertTrue("myvar2" in pymod) self.assertTrue("myvar3" in pymod) def test_not_leaking_tuple_assigned_names_inside_parent_scope(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): def f(self): var1, var2 = range(2) """) mod.write(code) pymod = self.pycore.resource_to_pyobject(mod) c_class = pymod["C"].get_object() self.assertFalse("var1" in c_class) @testutils.only_for("2.5") def test_with_statement_variables(self): code = dedent("""\ import threading with threading.lock() as var: pass """) if sys.version_info < (2, 6, 0): code = "from __future__ import with_statement\n" + code pymod = libutils.get_string_module(self.project, code) self.assertTrue("var" in pymod) @testutils.only_for("2.5") def test_with_statement_variables_and_tuple_assignment(self): code = dedent("""\ class A(object): def __enter__(self): return (1, 2) def __exit__(self, type, value, tb): pass with A() as (a, b): pass """) if sys.version_info < (2, 6, 0): code = "from __future__ import with_statement\n" + code pymod = libutils.get_string_module(self.project, code) self.assertTrue("a" in pymod) self.assertTrue("b" in pymod) @testutils.only_for("2.5") def test_with_statement_variable_type(self): code = dedent("""\ class A(object): def __enter__(self): return self def __exit__(self, type, value, tb): pass with A() as var: pass """) if sys.version_info < (2, 6, 0): code = "from __future__ import with_statement\n" + code pymod = libutils.get_string_module(self.project, code) a_class = pymod["A"].get_object() var = pymod["var"].get_object() self.assertEqual(a_class, var.get_type()) @testutils.only_for("2.7") def test_nested_with_statement_variable_type(self): code = dedent("""\ class A(object): def __enter__(self): return self def __exit__(self, type, value, tb): pass class B(object): def __enter__(self): return self def __exit__(self, type, value, tb): pass with A() as var_a, B() as var_b: pass """) if sys.version_info < (2, 6, 0): code = "from __future__ import with_statement\n" + code pymod = libutils.get_string_module(self.project, code) a_class = pymod["A"].get_object() var_a = pymod["var_a"].get_object() self.assertEqual(a_class, var_a.get_type()) b_class = pymod["B"].get_object() var_b = pymod["var_b"].get_object() self.assertEqual(b_class, var_b.get_type()) @testutils.only_for("2.5") def test_with_statement_with_no_vars(self): code = dedent("""\ with open("file"): pass """) if sys.version_info < (2, 6, 0): code = "from __future__ import with_statement\n" + code pymod = libutils.get_string_module(self.project, code) pymod.get_attributes() def test_with_statement(self): code = dedent("""\ a = 10 with open("file") as f: pass """) pymod = libutils.get_string_module(self.project, code) assigned = pymod.get_attribute("a") self.assertEqual(BuiltinClass, type(assigned.get_object().get_type())) assigned = pymod.get_attribute("f") self.assertEqual(File, type(assigned.get_object().get_type())) def test_check_for_else_block(self): code = dedent("""\ for i in range(10): pass else: myvar = 1 """) mod = libutils.get_string_module(self.project, code) a_var = mod["myvar"] self.assertEqual((mod, 4), a_var.get_definition_location()) def test_check_names_defined_in_whiles(self): code = dedent("""\ while False: myvar = 1 """) mod = libutils.get_string_module(self.project, code) a_var = mod["myvar"] self.assertEqual((mod, 2), a_var.get_definition_location()) def test_get_definition_location_in_tuple_assnames(self): code = dedent("""\ def f(x): x.z, a = range(2) """) mod = libutils.get_string_module(self.project, code) x = mod["f"].get_object().get_scope()["x"] a = mod["f"].get_object().get_scope()["a"] self.assertEqual((mod, 1), x.get_definition_location()) self.assertEqual((mod, 2), a.get_definition_location()) def test_syntax_errors_in_code(self): with self.assertRaises(exceptions.ModuleSyntaxError): libutils.get_string_module(self.project, "xyx print\n") def test_holding_error_location_information(self): try: libutils.get_string_module(self.project, "xyx print\n") except exceptions.ModuleSyntaxError as e: self.assertEqual(1, e.lineno) def test_no_exceptions_on_module_encoding_problems(self): mod = testutils.create_module(self.project, "mod") contents = b"\nsdsdsd\n\xa9\n" file = open(mod.real_path, "wb") file.write(contents) file.close() mod.read() def test_syntax_errors_when_cannot_decode_file2(self): mod = testutils.create_module(self.project, "mod") contents = b"\n\xa9\n" file = open(mod.real_path, "wb") file.write(contents) file.close() with self.assertRaises(exceptions.ModuleSyntaxError): self.pycore.resource_to_pyobject(mod) def test_syntax_errors_when_null_bytes(self): mod = testutils.create_module(self.project, "mod") contents = b"\n\x00\n" file = open(mod.real_path, "wb") file.write(contents) file.close() with self.assertRaises(exceptions.ModuleSyntaxError): self.pycore.resource_to_pyobject(mod) def test_syntax_errors_when_bad_strs(self): mod = testutils.create_module(self.project, "mod") contents = b'\n"\\x0"\n' file = open(mod.real_path, "wb") file.write(contents) file.close() with self.assertRaises(exceptions.ModuleSyntaxError): self.pycore.resource_to_pyobject(mod) def test_not_reaching_maximum_recursions_with_from_star_imports(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("from mod2 import *\n") mod2.write("from mod1 import *\n") pymod1 = self.pycore.resource_to_pyobject(mod1) pymod1.get_attributes() def test_not_reaching_maximum_recursions_when_importing_variables(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("from mod2 import myvar\n") mod2.write("from mod1 import myvar\n") pymod1 = self.pycore.resource_to_pyobject(mod1) pymod1["myvar"].get_object() def test_not_reaching_maximum_recursions_when_importing_variables2(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write("from mod1 import myvar\n") pymod1 = self.pycore.resource_to_pyobject(mod1) pymod1["myvar"].get_object() def test_pyobject_equality_should_compare_types(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ var1 = "" var2 = "" """) ) pymod1 = self.pycore.resource_to_pyobject(mod1) self.assertEqual(pymod1["var1"].get_object(), pymod1["var2"].get_object()) class PyCoreInProjectsTest(unittest.TestCase): def setUp(self): super(self.__class__, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore samplemod = testutils.create_module(self.project, "samplemod") code = dedent("""\ class SampleClass(object): def sample_method(): pass def sample_func(): pass sample_var = 10 def _underlined_func(): pass """) samplemod.write(code) package = testutils.create_package(self.project, "package") testutils.create_module(self.project, "nestedmod", package) def tearDown(self): testutils.remove_project(self.project) super(self.__class__, self).tearDown() def test_simple_import(self): code = "import samplemod\n" mod = libutils.get_string_module(self.project, code) samplemod = mod["samplemod"].get_object() self.assertEqual(get_base_type("Module"), samplemod.get_type()) def test_from_import_class(self): code = "from samplemod import SampleClass\n" mod = libutils.get_string_module(self.project, code) result = mod["SampleClass"].get_object() self.assertEqual(get_base_type("Type"), result.get_type()) self.assertTrue("sample_func" not in mod.get_attributes()) def test_from_import_star(self): code = "from samplemod import *\n" mod = libutils.get_string_module(self.project, code) self.assertEqual( get_base_type("Type"), mod["SampleClass"].get_object().get_type() ) self.assertEqual( get_base_type("Function"), mod["sample_func"].get_object().get_type() ) self.assertTrue(mod["sample_var"] is not None) def test_from_import_star_overwriting(self): code = dedent("""\ from samplemod import * class SampleClass(object): pass """) mod = libutils.get_string_module(self.project, code) samplemod = self.project.get_module("samplemod") sample_class = samplemod["SampleClass"].get_object() self.assertNotEqual( sample_class, mod.get_attributes()["SampleClass"].get_object() ) def test_from_import_star_not_imporing_underlined(self): code = "from samplemod import *" mod = libutils.get_string_module(self.project, code) self.assertTrue("_underlined_func" not in mod.get_attributes()) def test_from_import_star_imports_in_functions(self): code = dedent("""\ def f(): from os import * """) mod = libutils.get_string_module(self.project, code) mod["f"].get_object().get_scope().get_names() def test_from_package_import_mod(self): code = "from package import nestedmod\n" mod = libutils.get_string_module(self.project, code) self.assertEqual( get_base_type("Module"), mod["nestedmod"].get_object().get_type() ) # XXX: Deciding to import everything on import start from packages def xxx_test_from_package_import_star(self): code = "from package import *\n" mod = libutils.get_string_module(self.project, code) self.assertTrue("nestedmod" not in mod.get_attributes()) def test_unknown_when_module_cannot_be_found(self): code = "from doesnotexist import nestedmod\n" mod = libutils.get_string_module(self.project, code) self.assertTrue("nestedmod" in mod) def test_from_import_function(self): code = dedent("""\ def f(): from samplemod import SampleClass """) scope = libutils.get_string_scope(self.project, code) self.assertEqual( get_base_type("Type"), scope.get_scopes()[0]["SampleClass"].get_object().get_type(), ) def test_circular_imports(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("import mod2\n") mod2.write("import mod1\n") self.project.get_module("mod1") def test_circular_imports2(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write( dedent("""\ from mod2 import Sample2 class Sample1(object): pass """) ) mod2.write( dedent("""\ from mod1 import Sample1 class Sample2(object): pass """) ) self.project.get_module("mod1").get_attributes() def test_multi_dot_imports(self): pkg = testutils.create_package(self.project, "pkg") pkg_mod = testutils.create_module(self.project, "mod", pkg) pkg_mod.write( dedent("""\ def sample_func(): pass """) ) code = "import pkg.mod\n" mod = libutils.get_string_module(self.project, code) self.assertTrue("pkg" in mod) self.assertTrue("sample_func" in mod["pkg"].get_object()["mod"].get_object()) def test_multi_dot_imports2(self): pkg = testutils.create_package(self.project, "pkg") testutils.create_module(self.project, "mod1", pkg) testutils.create_module(self.project, "mod2", pkg) code = dedent("""\ import pkg.mod1 import pkg.mod2 """) mod = libutils.get_string_module(self.project, code) package = mod["pkg"].get_object() self.assertEqual(2, len(package.get_attributes())) self.assertTrue("mod1" in package and "mod2" in package) def test_multi_dot_imports3(self): pkg1 = testutils.create_package(self.project, "pkg1") pkg2 = testutils.create_package(self.project, "pkg2", pkg1) testutils.create_module(self.project, "mod1", pkg2) testutils.create_module(self.project, "mod2", pkg2) code = dedent("""\ import pkg1.pkg2.mod1 import pkg1.pkg2.mod2 """) mod = libutils.get_string_module(self.project, code) package1 = mod["pkg1"].get_object() package2 = package1["pkg2"].get_object() self.assertEqual(2, len(package2.get_attributes())) self.assertTrue("mod1" in package2 and "mod2" in package2) def test_multi_dot_imports_as(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod1.write( dedent("""\ def f(): pass """) ) code = "import pkg.mod1 as mod1\n" mod = libutils.get_string_module(self.project, code) module = mod["mod1"].get_object() self.assertTrue("f" in module) # TODO: not showing unimported names as attributes of packages def xxx_test_from_package_import_package(self): pkg1 = testutils.create_package(self.project, "pkg1") pkg2 = testutils.create_package(self.project, "pkg2", pkg1) testutils.create_module(self.project, "mod", pkg2) code = "from pkg1 import pkg2\n" mod = libutils.get_string_module(self.project, code) package = mod["pkg2"] self.assertEqual(0, len(package.get_attributes())) def test_invalidating_cache_after_resource_change(self): module = testutils.create_module(self.project, "mod") module.write("import sys\n") mod1 = self.project.get_module("mod") self.assertTrue("var" not in mod1.get_attributes()) module.write("var = 10\n") mod2 = self.project.get_module("mod") self.assertTrue("var" in mod2) def test_invalidating_cache_after_resource_change_for_init_dot_pys(self): pkg = testutils.create_package(self.project, "pkg") mod = testutils.create_module(self.project, "mod") init_dot_py = pkg.get_child("__init__.py") init_dot_py.write("a_var = 10\n") mod.write("import pkg\n") pymod = self.project.get_module("mod") self.assertTrue("a_var" in pymod["pkg"].get_object()) init_dot_py.write("new_var = 10\n") self.assertTrue("a_var" not in pymod["pkg"].get_object().get_attributes()) def test_invalidating_cache_after_rsrc_chng_for_nested_init_dot_pys(self): pkg1 = testutils.create_package(self.project, "pkg1") pkg2 = testutils.create_package(self.project, "pkg2", pkg1) mod = testutils.create_module(self.project, "mod") init_dot_py = pkg2.get_child("__init__.py") init_dot_py.write("a_var = 10\n") mod.write("import pkg1\n") pymod = self.project.get_module("mod") self.assertTrue("a_var" in pymod["pkg1"].get_object()["pkg2"].get_object()) init_dot_py.write("new_var = 10\n") self.assertTrue("a_var" not in pymod["pkg1"].get_object()["pkg2"].get_object()) def test_from_import_nonexistent_module(self): code = "from doesnotexistmod import DoesNotExistClass\n" mod = libutils.get_string_module(self.project, code) self.assertTrue("DoesNotExistClass" in mod) self.assertEqual( get_base_type("Unknown"), mod["DoesNotExistClass"].get_object().get_type() ) def test_from_import_nonexistent_name(self): code = "from samplemod import DoesNotExistClass\n" mod = libutils.get_string_module(self.project, code) self.assertTrue("DoesNotExistClass" in mod) self.assertEqual( get_base_type("Unknown"), mod["DoesNotExistClass"].get_object().get_type() ) def test_not_considering_imported_names_as_sub_scopes(self): code = "from samplemod import SampleClass\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual(0, len(scope.get_scopes())) def test_not_considering_imported_modules_as_sub_scopes(self): code = "import samplemod\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual(0, len(scope.get_scopes())) def test_inheriting_dotted_base_class(self): code = dedent("""\ import samplemod class Derived(samplemod.SampleClass): pass """) mod = libutils.get_string_module(self.project, code) derived = mod["Derived"].get_object() self.assertTrue("sample_method" in derived) def test_self_in_methods(self): code = dedent("""\ class Sample(object): def func(self): pass """) scope = libutils.get_string_scope(self.project, code) sample_class = scope["Sample"].get_object() func_scope = scope.get_scopes()[0].get_scopes()[0] self.assertEqual(sample_class, func_scope["self"].get_object().get_type()) self.assertTrue("func" in func_scope["self"].get_object()) def test_none_assignments_in_classes(self): code = dedent("""\ class C(object): var = "" def f(self): self.var += "".join([]) """) scope = libutils.get_string_scope(self.project, code) c_class = scope["C"].get_object() self.assertTrue("var" in c_class) def test_self_in_methods_with_decorators(self): code = dedent("""\ class Sample(object): @staticmethod def func(self): pass """) scope = libutils.get_string_scope(self.project, code) sample_class = scope["Sample"].get_object() func_scope = scope.get_scopes()[0].get_scopes()[0] self.assertNotEqual(sample_class, func_scope["self"].get_object().get_type()) def test_location_of_imports_when_importing(self): mod = testutils.create_module(self.project, "mod") mod.write("from samplemod import SampleClass\n") code = "from mod import SampleClass\n" scope = libutils.get_string_scope(self.project, code) sample_class = scope["SampleClass"] samplemod = self.project.get_module("samplemod") self.assertEqual((samplemod, 1), sample_class.get_definition_location()) def test_nested_modules(self): pkg = testutils.create_package(self.project, "pkg") testutils.create_module(self.project, "mod", pkg) imported_module = self.project.get_module("pkg.mod") code = "import pkg.mod\n" scope = libutils.get_string_scope(self.project, code) mod_pyobject = scope["pkg"].get_object()["mod"] self.assertEqual((imported_module, 1), mod_pyobject.get_definition_location()) def test_reading_init_dot_py(self): pkg = testutils.create_package(self.project, "pkg") init_dot_py = pkg.get_child("__init__.py") init_dot_py.write("a_var = 1\n") pkg_object = self.project.get_module("pkg") self.assertTrue("a_var" in pkg_object) def test_relative_imports(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod2 = testutils.create_module(self.project, "mod2", pkg) mod2.write("import mod1\n") mod1_object = self.pycore.resource_to_pyobject(mod1) mod2_object = self.pycore.resource_to_pyobject(mod2) self.assertEqual(mod1_object, mod2_object.get_attributes()["mod1"].get_object()) def test_relative_froms(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod2 = testutils.create_module(self.project, "mod2", pkg) mod1.write( dedent("""\ def a_func(): pass """) ) mod2.write("from mod1 import a_func\n") mod1_object = self.pycore.resource_to_pyobject(mod1) mod2_object = self.pycore.resource_to_pyobject(mod2) self.assertEqual( mod1_object["a_func"].get_object(), mod2_object["a_func"].get_object() ) def test_relative_imports_for_string_modules(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod2 = testutils.create_module(self.project, "mod2", pkg) mod2.write("import mod1\n") mod1_object = self.pycore.resource_to_pyobject(mod1) mod2_object = libutils.get_string_module(self.project, mod2.read(), mod2) self.assertEqual(mod1_object, mod2_object["mod1"].get_object()) def test_relative_imports_for_string_scopes(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod2 = testutils.create_module(self.project, "mod2", pkg) mod2.write("import mod1\n") mod1_object = self.pycore.resource_to_pyobject(mod1) mod2_scope = libutils.get_string_scope(self.project, mod2.read(), mod2) self.assertEqual(mod1_object, mod2_scope["mod1"].get_object()) @testutils.only_for("2.5") def test_new_style_relative_imports(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod2 = testutils.create_module(self.project, "mod2", pkg) mod2.write("from . import mod1\n") mod1_object = self.pycore.resource_to_pyobject(mod1) mod2_object = self.pycore.resource_to_pyobject(mod2) self.assertEqual(mod1_object, mod2_object["mod1"].get_object()) @testutils.only_for("2.5") def test_new_style_relative_imports2(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2", pkg) mod1.write( dedent("""\ def a_func(): pass """) ) mod2.write("from ..mod1 import a_func\n") mod1_object = self.pycore.resource_to_pyobject(mod1) mod2_object = self.pycore.resource_to_pyobject(mod2) self.assertEqual( mod1_object["a_func"].get_object(), mod2_object["a_func"].get_object() ) def test_invalidating_cache_for_from_imports_after_resource_change(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ def a_func(): print(1) """) ) mod1.write( dedent("""\ from mod2 import a_func a_func() """) ) pymod1 = self.project.get_module("mod1") pymod2 = self.project.get_module("mod2") self.assertEqual(pymod1["a_func"].get_object(), pymod2["a_func"].get_object()) mod2.write(mod2.read() + "\n") pymod2 = self.project.get_module("mod2") self.assertEqual(pymod1["a_func"].get_object(), pymod2["a_func"].get_object()) def test_invalidating_superclasses_after_change(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write( dedent("""\ class A(object): def func1(self): pass """) ) mod2.write( dedent("""\ import mod1 class B(mod1.A): pass """) ) b_class = self.project.get_module("mod2")["B"].get_object() self.assertTrue("func1" in b_class) mod1.write( dedent("""\ class A(object): def func2(self): pass """) ) self.assertTrue("func2" in b_class) def test_caching_pymodule_with_syntax_errors(self): self.project.prefs["ignore_syntax_errors"] = True self.project.prefs["automatic_soa"] = True self.project.pycore._init_automatic_soa() source = dedent("""\ import sys ab cd""") mod = testutils.create_module(self.project, "mod") mod.write(source) from rope.contrib import fixsyntax fixer = fixsyntax.FixSyntax(self.project, source, mod, 10) pymodule = fixer.get_pymodule() self.assertTrue(pymodule.source_code.startswith("import sys\npass\n")) class TextChangeDetectorTest(unittest.TestCase): def test_trivial_case(self): detector = _TextChangeDetector("\n", "\n") self.assertFalse(detector.is_changed(1, 1)) def test_one_line_change(self): detector = _TextChangeDetector("1\n2\n", "1\n3\n") self.assertFalse(detector.is_changed(1, 1)) self.assertTrue(detector.is_changed(2, 2)) def test_line_expansion(self): detector = _TextChangeDetector("1\n2\n", "1\n3\n4\n2\n") self.assertFalse(detector.is_changed(1, 1)) self.assertFalse(detector.is_changed(2, 2)) def test_line_removals(self): detector = _TextChangeDetector("1\n3\n4\n2\n", "1\n2\n") self.assertFalse(detector.is_changed(1, 1)) self.assertTrue(detector.is_changed(2, 3)) self.assertFalse(detector.is_changed(4, 4)) def test_multi_line_checks(self): detector = _TextChangeDetector("1\n2\n", "1\n3\n") self.assertTrue(detector.is_changed(1, 2)) def test_consume_change(self): detector = _TextChangeDetector("1\n2\n", "1\n3\n") self.assertTrue(detector.is_changed(1, 2)) self.assertTrue(detector.consume_changes(1, 2)) self.assertFalse(detector.is_changed(1, 2)) class PyCoreProjectConfigsTest(unittest.TestCase): def setUp(self): super(PyCoreProjectConfigsTest, self).setUp() self.project = None def tearDown(self): if self.project: testutils.remove_project(self.project) super(PyCoreProjectConfigsTest, self).tearDown() def test_python_files_config(self): self.project = testutils.sample_project(python_files=["myscript"]) myscript = self.project.root.create_file("myscript") self.assertTrue(self.project.pycore.is_python_file(myscript)) def test_ignore_bad_imports(self): self.project = testutils.sample_project(ignore_bad_imports=True) code = "import some_nonexistent_module\n" pymod = libutils.get_string_module(self.project, code) self.assertFalse("some_nonexistent_module" in pymod) def test_ignore_bad_imports_for_froms(self): self.project = testutils.sample_project(ignore_bad_imports=True) code = "from some_nonexistent_module import var\n" pymod = libutils.get_string_module(self.project, code) self.assertFalse("var" in pymod) def test_reporting_syntax_errors_with_force_errors(self): self.project = testutils.sample_project(ignore_syntax_errors=True) mod = testutils.create_module(self.project, "mod") mod.write("syntax error ...\n") with self.assertRaises(exceptions.ModuleSyntaxError): self.project.pycore.resource_to_pyobject(mod, force_errors=True) def test_reporting_syntax_errors_in_strings_with_force_errors(self): self.project = testutils.sample_project(ignore_syntax_errors=True) with self.assertRaises(exceptions.ModuleSyntaxError): libutils.get_string_module( self.project, "syntax error ...", force_errors=True ) def test_not_raising_errors_for_strings_with_ignore_errors(self): self.project = testutils.sample_project(ignore_syntax_errors=True) libutils.get_string_module(self.project, "syntax error ...") def test_reporting_syntax_errors_with_force_errors_for_packages(self): self.project = testutils.sample_project(ignore_syntax_errors=True) pkg = testutils.create_package(self.project, "pkg") pkg.get_child("__init__.py").write("syntax error ...\n") with self.assertRaises(exceptions.ModuleSyntaxError): self.project.pycore.resource_to_pyobject(pkg, force_errors=True) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633802493.0 rope-0.22.0/ropetest/pyscopestest.py0000664000175000017500000004611700000000000017430 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest from textwrap import dedent from rope.base import libutils from rope.base.pyobjects import get_base_type from ropetest import testutils class PyCoreScopesTest(unittest.TestCase): def setUp(self): super(PyCoreScopesTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore def tearDown(self): testutils.remove_project(self.project) super(PyCoreScopesTest, self).tearDown() def test_simple_scope(self): code = dedent("""\ def sample_func(): pass """) scope = libutils.get_string_scope(self.project, code) sample_func = scope["sample_func"].get_object() self.assertEqual(get_base_type("Function"), sample_func.get_type()) def test_simple_function_scope(self): code = dedent("""\ def sample_func(): a = 10 """) scope = libutils.get_string_scope(self.project, code) self.assertEqual(1, len(scope.get_scopes())) sample_func_scope = scope.get_scopes()[0] self.assertEqual(1, len(sample_func_scope.get_names())) self.assertEqual(0, len(sample_func_scope.get_scopes())) def test_classes_inside_function_scopes(self): code = dedent("""\ def sample_func(): class SampleClass(object): pass """) scope = libutils.get_string_scope(self.project, code) self.assertEqual(1, len(scope.get_scopes())) sample_func_scope = scope.get_scopes()[0] # noqa self.assertEqual( get_base_type("Type"), scope.get_scopes()[0]["SampleClass"].get_object().get_type(), ) def test_list_comprehension_scope_inside_assignment(self): code = "a_var = [b_var + d_var for b_var, c_var in e_var]\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual( list(sorted(scope.get_defined_names())), ["a_var"], ) self.assertEqual( list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) def test_list_comprehension_scope(self): code = "[b_var + d_var for b_var, c_var in e_var]\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual( list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) def test_set_comprehension_scope(self): code = "{b_var + d_var for b_var, c_var in e_var}\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual( list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) def test_generator_comprehension_scope(self): code = "(b_var + d_var for b_var, c_var in e_var)\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual( list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) def test_dict_comprehension_scope(self): code = "{b_var: d_var for b_var, c_var in e_var}\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual( list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) @testutils.only_for_versions_higher("3.8") def test_inline_assignment(self): code = """values = (a_var := 2,)""" scope = libutils.get_string_scope(self.project, code) self.assertEqual( list(sorted(scope.get_defined_names())), ["a_var", "values"], ) @testutils.only_for_versions_higher("3.8") def test_inline_assignment_in_comprehensions(self): code = dedent("""\ [ (a_var := b_var + (f_var := g_var)) for b_var in [(j_var := i_var) for i_var in c_var] if a_var + (h_var := d_var) ] """) scope = libutils.get_string_scope(self.project, code) self.assertEqual( list(sorted(scope.get_scopes()[0].get_defined_names())), ["a_var", "b_var", "f_var"], ) self.assertEqual( list(sorted(scope.get_scopes()[0].get_scopes()[0].get_defined_names())), ["i_var", "j_var"], ) def test_nested_comprehension(self): code = dedent("""\ [ b_var + d_var for b_var, c_var in [ e_var for e_var in f_var ] ] """) scope = libutils.get_string_scope(self.project, code) self.assertEqual( list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) self.assertEqual( list(sorted(scope.get_scopes()[0].get_scopes()[0].get_defined_names())), ["e_var"], ) def test_simple_class_scope(self): code = dedent("""\ class SampleClass(object): def f(self): var = 10 """) scope = libutils.get_string_scope(self.project, code) self.assertEqual(1, len(scope.get_scopes())) sample_class_scope = scope.get_scopes()[0] self.assertTrue("f" in sample_class_scope) self.assertEqual(1, len(sample_class_scope.get_scopes())) f_in_class = sample_class_scope.get_scopes()[0] self.assertTrue("var" in f_in_class) def test_get_lineno(self): code = dedent("""\ def sample_func(): a = 10 """) scope = libutils.get_string_scope(self.project, code) self.assertEqual(1, len(scope.get_scopes())) sample_func_scope = scope.get_scopes()[0] self.assertEqual(1, scope.get_start()) self.assertEqual(2, sample_func_scope.get_start()) def test_scope_kind(self): code = dedent("""\ class SampleClass(object): pass def sample_func(): pass """) scope = libutils.get_string_scope(self.project, code) sample_class_scope = scope.get_scopes()[0] sample_func_scope = scope.get_scopes()[1] self.assertEqual("Module", scope.get_kind()) self.assertEqual("Class", sample_class_scope.get_kind()) self.assertEqual("Function", sample_func_scope.get_kind()) def test_function_parameters_in_scope_names(self): code = dedent("""\ def sample_func(param): a = 10 """) scope = libutils.get_string_scope(self.project, code) sample_func_scope = scope.get_scopes()[0] self.assertTrue("param" in sample_func_scope) def test_get_names_contains_only_names_defined_in_a_scope(self): code = dedent("""\ var1 = 10 def sample_func(param): var2 = 20 """) scope = libutils.get_string_scope(self.project, code) sample_func_scope = scope.get_scopes()[0] self.assertTrue("var1" not in sample_func_scope) def test_scope_lookup(self): code = dedent("""\ var1 = 10 def sample_func(param): var2 = 20 """) scope = libutils.get_string_scope(self.project, code) self.assertTrue(scope.lookup("var2") is None) self.assertEqual( get_base_type("Function"), scope.lookup("sample_func").get_object().get_type(), ) sample_func_scope = scope.get_scopes()[0] self.assertTrue(sample_func_scope.lookup("var1") is not None) def test_function_scopes(self): code = dedent("""\ def func(): var = 10 """) scope = libutils.get_string_scope(self.project, code) func_scope = scope.get_scopes()[0] self.assertTrue("var" in func_scope) def test_function_scopes_classes(self): code = dedent("""\ def func(): class Sample(object): pass """) scope = libutils.get_string_scope(self.project, code) func_scope = scope.get_scopes()[0] self.assertTrue("Sample" in func_scope) def test_function_getting_scope(self): code = dedent("""\ def func(): var = 10 """) mod = libutils.get_string_module(self.project, code) func_scope = mod["func"].get_object().get_scope() self.assertTrue("var" in func_scope) def test_scopes_in_function_scopes(self): code = dedent("""\ def func(): def inner(): var = 10 """) scope = libutils.get_string_scope(self.project, code) func_scope = scope.get_scopes()[0] inner_scope = func_scope.get_scopes()[0] self.assertTrue("var" in inner_scope) def test_for_variables_in_scopes(self): code = dedent("""\ for a_var in range(10): pass """) scope = libutils.get_string_scope(self.project, code) self.assertTrue("a_var" in scope) def test_assists_inside_fors(self): code = dedent("""\ for i in range(10): a_var = i """) scope = libutils.get_string_scope(self.project, code) self.assertTrue("a_var" in scope) def test_first_parameter_of_a_method(self): code = dedent("""\ class AClass(object): def a_func(self, param): pass """) a_class = libutils.get_string_module(self.project, code)["AClass"].get_object() function_scope = a_class["a_func"].get_object().get_scope() self.assertEqual(a_class, function_scope["self"].get_object().get_type()) self.assertNotEqual(a_class, function_scope["param"].get_object().get_type()) def test_first_parameter_of_static_methods(self): code = dedent("""\ class AClass(object): @staticmethod def a_func(param): pass """) a_class = libutils.get_string_module(self.project, code)["AClass"].get_object() function_scope = a_class["a_func"].get_object().get_scope() self.assertNotEqual(a_class, function_scope["param"].get_object().get_type()) def test_first_parameter_of_class_methods(self): code = dedent("""\ class AClass(object): @classmethod def a_func(cls): pass """) a_class = libutils.get_string_module(self.project, code)["AClass"].get_object() function_scope = a_class["a_func"].get_object().get_scope() self.assertEqual(a_class, function_scope["cls"].get_object()) def test_first_parameter_with_self_as_name_and_unknown_decorator(self): code = dedent("""\ def my_decorator(func): return func class AClass(object): @my_decorator def a_func(self): pass """) a_class = libutils.get_string_module(self.project, code)["AClass"].get_object() function_scope = a_class["a_func"].get_object().get_scope() self.assertEqual(a_class, function_scope["self"].get_object().get_type()) def test_inside_class_scope_attribute_lookup(self): code = dedent("""\ class C(object): an_attr = 1 def a_func(self): pass""") scope = libutils.get_string_scope(self.project, code) self.assertEqual(1, len(scope.get_scopes())) c_scope = scope.get_scopes()[0] self.assertTrue("an_attr" in c_scope.get_names()) self.assertTrue(c_scope.lookup("an_attr") is not None) f_in_c = c_scope.get_scopes()[0] self.assertTrue(f_in_c.lookup("an_attr") is None) def test_inside_class_scope_attribute_lookup2(self): code = dedent("""\ class C(object): def __init__(self): self.an_attr = 1 def a_func(self): pass""") scope = libutils.get_string_scope(self.project, code) self.assertEqual(1, len(scope.get_scopes())) c_scope = scope.get_scopes()[0] f_in_c = c_scope.get_scopes()[0] self.assertTrue(f_in_c.lookup("an_attr") is None) def test_get_inner_scope_for_staticmethods(self): code = dedent("""\ class C(object): @staticmethod def a_func(self): pass """) scope = libutils.get_string_scope(self.project, code) c_scope = scope.get_scopes()[0] f_in_c = c_scope.get_scopes()[0] self.assertEqual(f_in_c, scope.get_inner_scope_for_line(4)) def test_get_scope_for_offset_for_comprehension(self): code = "a = [i for i in range(10)]\n" scope = libutils.get_string_scope(self.project, code) c_scope = scope.get_scopes()[0] self.assertEqual(c_scope, scope.get_inner_scope_for_offset(10)) self.assertEqual(scope, scope.get_inner_scope_for_offset(1)) def test_get_scope_for_offset_for_in_nested_comprehension(self): code = "[i for i in [j for j in k]]\n" scope = libutils.get_string_scope(self.project, code) c_scope = scope.get_scopes()[0] self.assertEqual(c_scope, scope.get_inner_scope_for_offset(5)) inner_scope = c_scope.get_scopes()[0] self.assertEqual(inner_scope, scope.get_inner_scope_for_offset(15)) def test_get_scope_for_offset_for_scope_with_indent(self): code = dedent("""\ def f(a): print(a) """) scope = libutils.get_string_scope(self.project, code) inner_scope = scope.get_scopes()[0] self.assertEqual(inner_scope, scope.get_inner_scope_for_offset(10)) @testutils.only_for("3.5") def test_get_scope_for_offset_for_function_scope_and_async_with_statement(self): scope = libutils.get_string_scope( self.project, dedent("""\ async def func(): async with a_func() as var: print(var) """), ) inner_scope = scope.get_scopes()[0] self.assertEqual(inner_scope, scope.get_inner_scope_for_offset(27)) def test_getting_overwritten_scopes(self): code = dedent("""\ def f(): pass def f(): pass """) scope = libutils.get_string_scope(self.project, code) self.assertEqual(2, len(scope.get_scopes())) f1_scope = scope.get_scopes()[0] f2_scope = scope.get_scopes()[1] self.assertNotEqual(f1_scope, f2_scope) def test_assigning_builtin_names(self): code = "range = 1\n" mod = libutils.get_string_module(self.project, code) range = mod.get_scope().lookup("range") self.assertEqual((mod, 1), range.get_definition_location()) def test_get_inner_scope_and_logical_lines(self): code = dedent('''\ class C(object): def f(): s = """ 1 2 """ a = 1 ''') scope = libutils.get_string_scope(self.project, code) c_scope = scope.get_scopes()[0] f_in_c = c_scope.get_scopes()[0] self.assertEqual(f_in_c, scope.get_inner_scope_for_line(7)) def test_getting_defined_names_for_classes(self): code = dedent("""\ class A(object): def a(self): pass class B(A): def b(self): pass """) scope = libutils.get_string_scope(self.project, code) a_scope = scope["A"].get_object().get_scope() # noqa b_scope = scope["B"].get_object().get_scope() self.assertTrue("a" in b_scope.get_names()) self.assertTrue("b" in b_scope.get_names()) self.assertTrue("a" not in b_scope.get_defined_names()) self.assertTrue("b" in b_scope.get_defined_names()) def test_getting_defined_names_for_modules(self): code = dedent("""\ class A(object): pass """) scope = libutils.get_string_scope(self.project, code) self.assertTrue("open" in scope.get_names()) self.assertTrue("A" in scope.get_names()) self.assertTrue("open" not in scope.get_defined_names()) self.assertTrue("A" in scope.get_defined_names()) def test_get_inner_scope_for_list_comprhension_with_many_targets(self): code = "a = [(i, j) for i,j in enumerate(range(10))]\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual(len(scope.get_scopes()), 1) self.assertNotIn("i", scope) self.assertNotIn("j", scope) self.assertIn("i", scope.get_scopes()[0]) self.assertIn("j", scope.get_scopes()[0]) def test_get_inner_scope_for_generator(self): code = "a = (i for i in range(10))\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual(len(scope.get_scopes()), 1) self.assertNotIn("i", scope) self.assertIn("i", scope.get_scopes()[0]) def test_get_inner_scope_for_set_comprehension(self): code = "a = {i for i in range(10)}\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual(len(scope.get_scopes()), 1) self.assertNotIn("i", scope) self.assertIn("i", scope.get_scopes()[0]) def test_get_inner_scope_for_dict_comprehension(self): code = "a = {i:i for i in range(10)}\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual(len(scope.get_scopes()), 1) self.assertNotIn("i", scope) self.assertIn("i", scope.get_scopes()[0]) def test_get_inner_scope_for_nested_list_comprhension(self): code = "a = [[i + j for j in range(10)] for i in range(10)]\n" scope = libutils.get_string_scope(self.project, code) self.assertEqual(len(scope.get_scopes()), 1) self.assertNotIn("i", scope) self.assertNotIn("j", scope) self.assertIn("i", scope.get_scopes()[0]) self.assertEqual(len(scope.get_scopes()[0].get_scopes()), 1) self.assertIn("j", scope.get_scopes()[0].get_scopes()[0]) self.assertIn("i", scope.get_scopes()[0].get_scopes()[0]) def test_get_scope_region(self): scope = libutils.get_string_scope( self.project, dedent(""" def func1(ala): pass def func2(o): pass """), ) self.assertEqual(scope.get_region(), (0, 48)) self.assertEqual(scope.get_scopes()[0].get_region(), (1, 24)) self.assertEqual(scope.get_scopes()[1].get_region(), (26, 47)) def test_only_get_inner_scope_region(self): scope = libutils.get_string_scope( self.project, dedent(""" def func1(ala): pass def func2(o): pass """), ) self.assertEqual(scope.get_scopes()[1].get_region(), (26, 47)) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3753905 rope-0.22.0/ropetest/refactor/0000775000175000017500000000000000000000000016105 5ustar00lieryanlieryan././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633441152.0 rope-0.22.0/ropetest/refactor/__init__.py0000664000175000017500000011331100000000000020216 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest import rope.base.taskhandle import rope.refactor.introduce_parameter import ropetest.refactor.extracttest import ropetest.refactor.importutilstest import ropetest.refactor.inlinetest import ropetest.refactor.movetest import ropetest.refactor.multiprojecttest import ropetest.refactor.patchedasttest import ropetest.refactor.renametest import ropetest.refactor.restructuretest import ropetest.refactor.suitestest import ropetest.refactor.usefunctiontest from rope.base.exceptions import RefactoringError, InterruptedTaskError from rope.refactor.encapsulate_field import EncapsulateField from rope.refactor.introduce_factory import IntroduceFactory from rope.refactor.localtofield import LocalToField from rope.refactor.method_object import MethodObject from ropetest import testutils from ropetest.refactor import change_signature_test, similarfindertest class MethodObjectTest(unittest.TestCase): def setUp(self): super(MethodObjectTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(MethodObjectTest, self).tearDown() def test_empty_method(self): code = dedent("""\ def func(): pass """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) expected = dedent("""\ class _New(object): def __call__(self): pass """) self.assertEqual( expected, replacer.get_new_class("_New"), ) def test_trivial_return(self): code = dedent("""\ def func(): return 1 """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) expected = dedent("""\ class _New(object): def __call__(self): return 1 """) self.assertEqual( expected, replacer.get_new_class("_New"), ) def test_multi_line_header(self): code = dedent("""\ def func( ): return 1 """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) expected = dedent("""\ class _New(object): def __call__(self): return 1 """) self.assertEqual( expected, replacer.get_new_class("_New"), ) def test_a_single_parameter(self): code = dedent("""\ def func(param): return 1 """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) expected = dedent("""\ class _New(object): def __init__(self, param): self.param = param def __call__(self): return 1 """) self.assertEqual( expected, replacer.get_new_class("_New"), ) def test_self_parameter(self): code = dedent("""\ def func(self): return 1 """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) expected = dedent("""\ class _New(object): def __init__(self, host): self.self = host def __call__(self): return 1 """) self.assertEqual( expected, replacer.get_new_class("_New"), ) def test_simple_using_passed_parameters(self): code = dedent("""\ def func(param): return param """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) expected = dedent("""\ class _New(object): def __init__(self, param): self.param = param def __call__(self): return self.param """) self.assertEqual( expected, replacer.get_new_class("_New"), ) def test_self_keywords_and_args_parameters(self): code = dedent("""\ def func(arg, *args, **kwds): result = arg + args[0] + kwds[arg] return result """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) expected = dedent("""\ class _New(object): def __init__(self, arg, args, kwds): self.arg = arg self.args = args self.kwds = kwds def __call__(self): result = self.arg + self.args[0] + self.kwds[self.arg] return result """) self.assertEqual(expected, replacer.get_new_class("_New")) def test_performing_on_not_a_function(self): code = dedent("""\ my_var = 10 """) self.mod.write(code) with self.assertRaises(RefactoringError): MethodObject(self.project, self.mod, code.index("my_var")) def test_changing_the_module(self): code = dedent("""\ def func(): return 1 """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) self.project.do(replacer.get_changes("_New")) expected = dedent("""\ def func(): return _New()() class _New(object): def __call__(self): return 1 """) self.assertEqual(expected, self.mod.read()) def test_changing_the_module_and_class_methods(self): code = dedent("""\ class C(object): def a_func(self): return 1 def another_func(self): pass """) self.mod.write(code) replacer = MethodObject(self.project, self.mod, code.index("func")) self.project.do(replacer.get_changes("_New")) expected = dedent("""\ class C(object): def a_func(self): return _New(self)() def another_func(self): pass class _New(object): def __init__(self, host): self.self = host def __call__(self): return 1 """) self.assertEqual(expected, self.mod.read()) class IntroduceFactoryTest(unittest.TestCase): def setUp(self): super(IntroduceFactoryTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore def tearDown(self): testutils.remove_project(self.project) super(IntroduceFactoryTest, self).tearDown() def _introduce_factory(self, resource, offset, *args, **kwds): factory_introducer = IntroduceFactory(self.project, resource, offset) changes = factory_introducer.get_changes(*args, **kwds) self.project.do(changes) def test_adding_the_method(self): code = dedent("""\ class AClass(object): an_attr = 10 """) mod = testutils.create_module(self.project, "mod") mod.write(code) expected = dedent("""\ class AClass(object): an_attr = 10 @staticmethod def create(*args, **kwds): return AClass(*args, **kwds) """) self._introduce_factory(mod, mod.read().index("AClass") + 1, "create") self.assertEqual(expected, mod.read()) def test_changing_occurrences_in_the_main_module(self): code = dedent("""\ class AClass(object): an_attr = 10 a_var = AClass()""") mod = testutils.create_module(self.project, "mod") mod.write(code) expected = dedent("""\ class AClass(object): an_attr = 10 @staticmethod def create(*args, **kwds): return AClass(*args, **kwds) a_var = AClass.create()""") self._introduce_factory(mod, mod.read().index("AClass") + 1, "create") self.assertEqual(expected, mod.read()) def test_changing_occurrences_with_arguments(self): code = dedent("""\ class AClass(object): def __init__(self, arg): pass a_var = AClass(10) """) mod = testutils.create_module(self.project, "mod") mod.write(code) expected = dedent("""\ class AClass(object): def __init__(self, arg): pass @staticmethod def create(*args, **kwds): return AClass(*args, **kwds) a_var = AClass.create(10) """) self._introduce_factory(mod, mod.read().index("AClass") + 1, "create") self.assertEqual(expected, mod.read()) def test_changing_occurrences_in_other_modules(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("class AClass(object):\n an_attr = 10\n") mod2.write("import mod1\na_var = mod1.AClass()\n") self._introduce_factory(mod1, mod1.read().index("AClass") + 1, "create") expected1 = dedent("""\ class AClass(object): an_attr = 10 @staticmethod def create(*args, **kwds): return AClass(*args, **kwds) """) expected2 = dedent("""\ import mod1 a_var = mod1.AClass.create() """) self.assertEqual(expected1, mod1.read()) self.assertEqual(expected2, mod2.read()) def test_raising_exception_for_non_classes(self): mod = testutils.create_module(self.project, "mod") mod.write("def a_func():\n pass\n") with self.assertRaises(RefactoringError): self._introduce_factory(mod, mod.read().index("a_func") + 1, "create") def test_undoing_introduce_factory(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") code1 = dedent("""\ class AClass(object): an_attr = 10 """) mod1.write(code1) code2 = dedent("""\ from mod1 import AClass a_var = AClass() """) mod2.write(code2) self._introduce_factory(mod1, mod1.read().index("AClass") + 1, "create") self.project.history.undo() self.assertEqual(code1, mod1.read()) self.assertEqual(code2, mod2.read()) def test_using_on_an_occurrence_outside_the_main_module(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("class AClass(object):\n an_attr = 10\n") mod2.write("import mod1\na_var = mod1.AClass()\n") self._introduce_factory(mod2, mod2.read().index("AClass") + 1, "create") expected1 = dedent("""\ class AClass(object): an_attr = 10 @staticmethod def create(*args, **kwds): return AClass(*args, **kwds) """) expected2 = "import mod1\n" "a_var = mod1.AClass.create()\n" self.assertEqual(expected1, mod1.read()) self.assertEqual(expected2, mod2.read()) def test_introduce_factory_in_nested_scopes(self): code = dedent("""\ def create_var(): class AClass(object): an_attr = 10 return AClass() """) mod = testutils.create_module(self.project, "mod") mod.write(code) expected = dedent("""\ def create_var(): class AClass(object): an_attr = 10 @staticmethod def create(*args, **kwds): return AClass(*args, **kwds) return AClass.create() """) self._introduce_factory(mod, mod.read().index("AClass") + 1, "create") self.assertEqual(expected, mod.read()) def test_adding_factory_for_global_factories(self): code = dedent("""\ class AClass(object): an_attr = 10 """) mod = testutils.create_module(self.project, "mod") mod.write(code) expected = dedent("""\ class AClass(object): an_attr = 10 def create(*args, **kwds): return AClass(*args, **kwds) """) self._introduce_factory( mod, mod.read().index("AClass") + 1, "create", global_factory=True ) self.assertEqual(expected, mod.read()) def test_get_name_for_factories(self): code = dedent("""\ class C(object): pass """) mod = testutils.create_module(self.project, "mod") mod.write(code) factory = IntroduceFactory(self.project, mod, mod.read().index("C") + 1) self.assertEqual("C", factory.get_name()) def test_raising_exception_for_global_factory_for_nested_classes(self): code = dedent("""\ def create_var(): class AClass(object): an_attr = 10 return AClass() """) mod = testutils.create_module(self.project, "mod") mod.write(code) with self.assertRaises(RefactoringError): self._introduce_factory( mod, mod.read().index("AClass") + 1, "create", global_factory=True ) def test_changing_occurrences_in_the_main_module_for_global_factories(self): code = dedent("""\ class AClass(object): an_attr = 10 a_var = AClass()""") mod = testutils.create_module(self.project, "mod") mod.write(code) expected = dedent("""\ class AClass(object): an_attr = 10 def create(*args, **kwds): return AClass(*args, **kwds) a_var = create()""") self._introduce_factory( mod, mod.read().index("AClass") + 1, "create", global_factory=True ) self.assertEqual(expected, mod.read()) def test_changing_occurrences_in_other_modules_for_global_factories(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("class AClass(object):\n an_attr = 10\n") mod2.write("import mod1\na_var = mod1.AClass()\n") self._introduce_factory( mod1, mod1.read().index("AClass") + 1, "create", global_factory=True ) expected1 = dedent("""\ class AClass(object): an_attr = 10 def create(*args, **kwds): return AClass(*args, **kwds) """) expected2 = "import mod1\n" "a_var = mod1.create()\n" self.assertEqual(expected1, mod1.read()) self.assertEqual(expected2, mod2.read()) def test_import_if_necessary_in_other_mods_for_global_factories(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("class AClass(object):\n an_attr = 10\n") mod2.write("from mod1 import AClass\npair = AClass(), AClass\n") self._introduce_factory( mod1, mod1.read().index("AClass") + 1, "create", global_factory=True ) expected1 = dedent("""\ class AClass(object): an_attr = 10 def create(*args, **kwds): return AClass(*args, **kwds) """) expected2 = dedent("""\ from mod1 import AClass, create pair = create(), AClass """) self.assertEqual(expected1, mod1.read()) self.assertEqual(expected2, mod2.read()) def test_changing_occurrences_for_renamed_classes(self): code = dedent("""\ class AClass(object): an_attr = 10 a_class = AClass a_var = a_class()""") mod = testutils.create_module(self.project, "mod") mod.write(code) expected = dedent("""\ class AClass(object): an_attr = 10 @staticmethod def create(*args, **kwds): return AClass(*args, **kwds) a_class = AClass a_var = a_class()""") self._introduce_factory(mod, mod.read().index("a_class") + 1, "create") self.assertEqual(expected, mod.read()) def test_changing_occurrs_in_the_same_module_with_conflict_ranges(self): mod = testutils.create_module(self.project, "mod") code = dedent("""\ class C(object): def create(self): return C() """) mod.write(code) self._introduce_factory(mod, mod.read().index("C"), "create_c", True) expected = dedent("""\ class C(object): def create(self): return create_c() """) self.assertTrue(mod.read().startswith(expected)) def _transform_module_to_package(self, resource): self.project.do( rope.refactor.ModuleToPackage(self.project, resource).get_changes() ) def test_transform_module_to_package(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write("import mod2\nfrom mod2 import AClass\n") mod2 = testutils.create_module(self.project, "mod2") mod2.write("class AClass(object):\n pass\n") self._transform_module_to_package(mod2) mod2 = self.project.get_resource("mod2") root_folder = self.project.root self.assertFalse(root_folder.has_child("mod2.py")) self.assertEqual( "class AClass(object):\n pass\n", root_folder.get_child("mod2").get_child("__init__.py").read(), ) def test_transform_module_to_package_undoing(self): pkg = testutils.create_package(self.project, "pkg") mod = testutils.create_module(self.project, "mod", pkg) self._transform_module_to_package(mod) self.assertFalse(pkg.has_child("mod.py")) self.assertTrue(pkg.get_child("mod").has_child("__init__.py")) self.project.history.undo() self.assertTrue(pkg.has_child("mod.py")) self.assertFalse(pkg.has_child("mod")) def test_transform_module_to_package_with_relative_imports(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod1.write("import mod2\nfrom mod2 import AClass\n") mod2 = testutils.create_module(self.project, "mod2", pkg) mod2.write("class AClass(object):\n pass\n") self._transform_module_to_package(mod1) new_init = self.project.get_resource("pkg/mod1/__init__.py") self.assertEqual( "import pkg.mod2\nfrom pkg.mod2 import AClass\n", new_init.read() ) def test_resources_parameter(self): code = dedent("""\ class A(object): an_attr = 10 """) code1 = dedent("""\ import mod a = mod.A() """) mod = testutils.create_module(self.project, "mod") mod1 = testutils.create_module(self.project, "mod1") mod.write(code) mod1.write(code1) expected = dedent("""\ class A(object): an_attr = 10 @staticmethod def create(*args, **kwds): return A(*args, **kwds) """) self._introduce_factory( mod, mod.read().index("A") + 1, "create", resources=[mod] ) self.assertEqual(expected, mod.read()) self.assertEqual(code1, mod1.read()) class EncapsulateFieldTest(unittest.TestCase): def setUp(self): super(EncapsulateFieldTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") self.mod1 = testutils.create_module(self.project, "mod1") self.a_class = dedent("""\ class A(object): def __init__(self): self.attr = 1 """) self.added_methods = ( "\n" " def get_attr(self):\n" " return self.attr\n\n" " def set_attr(self, value):\n" " self.attr = value\n" ) self.encapsulated = self.a_class + self.added_methods def tearDown(self): testutils.remove_project(self.project) super(EncapsulateFieldTest, self).tearDown() def _encapsulate(self, resource, offset, **args): changes = EncapsulateField(self.project, resource, offset).get_changes(**args) self.project.do(changes) def test_adding_getters_and_setters(self): code = self.a_class self.mod.write(code) self._encapsulate(self.mod, code.index("attr") + 1) self.assertEqual(self.encapsulated, self.mod.read()) def test_changing_getters_in_other_modules(self): code = dedent("""\ import mod a_var = mod.A() range(a_var.attr) """) self.mod1.write(code) self.mod.write(self.a_class) self._encapsulate(self.mod, self.mod.read().index("attr") + 1) expected = dedent("""\ import mod a_var = mod.A() range(a_var.get_attr()) """) self.assertEqual(expected, self.mod1.read()) def test_changing_setters_in_other_modules(self): code = dedent("""\ import mod a_var = mod.A() a_var.attr = 1 """) self.mod1.write(code) self.mod.write(self.a_class) self._encapsulate(self.mod, self.mod.read().index("attr") + 1) expected = dedent("""\ import mod a_var = mod.A() a_var.set_attr(1) """) self.assertEqual(expected, self.mod1.read()) def test_changing_getters_in_setters(self): code = dedent("""\ import mod a_var = mod.A() a_var.attr = 1 + a_var.attr """) self.mod1.write(code) self.mod.write(self.a_class) self._encapsulate(self.mod, self.mod.read().index("attr") + 1) expected = dedent("""\ import mod a_var = mod.A() a_var.set_attr(1 + a_var.get_attr()) """) self.assertEqual(expected, self.mod1.read()) def test_appending_to_class_end(self): self.mod1.write(self.a_class + "a_var = A()\n") self._encapsulate(self.mod1, self.mod1.read().index("attr") + 1) self.assertEqual(self.encapsulated + "a_var = A()\n", self.mod1.read()) def test_performing_in_other_modules(self): code = dedent("""\ import mod a_var = mod.A() range(a_var.attr) """) self.mod1.write(code) self.mod.write(self.a_class) self._encapsulate(self.mod1, self.mod1.read().index("attr") + 1) self.assertEqual(self.encapsulated, self.mod.read()) expected = dedent("""\ import mod a_var = mod.A() range(a_var.get_attr()) """) self.assertEqual(expected, self.mod1.read()) def test_changing_main_module_occurrences(self): code = self.a_class + "a_var = A()\n" "a_var.attr = a_var.attr * 2\n" self.mod1.write(code) self._encapsulate(self.mod1, self.mod1.read().index("attr") + 1) expected = ( self.encapsulated + "a_var = A()\n" "a_var.set_attr(a_var.get_attr() * 2)\n" ) self.assertEqual(expected, self.mod1.read()) def test_raising_exception_when_performed_on_non_attributes(self): self.mod1.write("attr = 10") with self.assertRaises(RefactoringError): self._encapsulate(self.mod1, self.mod1.read().index("attr") + 1) def test_raising_exception_on_tuple_assignments(self): self.mod.write(self.a_class) code = dedent("""\ import mod a_var = mod.A() a_var.attr = 1 a_var.attr, b = 1, 2 """) self.mod1.write(code) with self.assertRaises(RefactoringError): self._encapsulate(self.mod1, self.mod1.read().index("attr") + 1) def test_raising_exception_on_tuple_assignments2(self): self.mod.write(self.a_class) code = dedent("""\ import mod a_var = mod.A() a_var.attr = 1 b, a_var.attr = 1, 2 """) self.mod1.write(code) with self.assertRaises(RefactoringError): self._encapsulate(self.mod1, self.mod1.read().index("attr") + 1) def test_tuple_assignments_and_function_calls(self): code = dedent("""\ import mod def func(a1=0, a2=0): pass a_var = mod.A() func(a_var.attr, a2=2) """) self.mod1.write(code) self.mod.write(self.a_class) self._encapsulate(self.mod, self.mod.read().index("attr") + 1) expected = dedent("""\ import mod def func(a1=0, a2=0): pass a_var = mod.A() func(a_var.get_attr(), a2=2) """) self.assertEqual(expected, self.mod1.read()) def test_tuple_assignments(self): code = dedent("""\ import mod a_var = mod.A() a, b = a_var.attr, 1 """) self.mod1.write(code) self.mod.write(self.a_class) self._encapsulate(self.mod, self.mod.read().index("attr") + 1) expected = dedent("""\ import mod a_var = mod.A() a, b = a_var.get_attr(), 1 """) self.assertEqual(expected, self.mod1.read()) def test_changing_augmented_assignments(self): code = "import mod\n" "a_var = mod.A()\n" "a_var.attr += 1\n" self.mod1.write(code) self.mod.write(self.a_class) self._encapsulate(self.mod, self.mod.read().index("attr") + 1) expected = dedent("""\ import mod a_var = mod.A() a_var.set_attr(a_var.get_attr() + 1) """) self.assertEqual(expected, self.mod1.read()) def test_changing_augmented_assignments2(self): code = dedent("""\ import mod a_var = mod.A() a_var.attr <<= 1 """) self.mod1.write(code) self.mod.write(self.a_class) self._encapsulate(self.mod, self.mod.read().index("attr") + 1) expected = dedent("""\ import mod a_var = mod.A() a_var.set_attr(a_var.get_attr() << 1) """) self.assertEqual(expected, self.mod1.read()) def test_changing_occurrences_inside_the_class(self): new_class = ( self.a_class + "\n" " def a_func(self):\n" " self.attr = 1\n" ) self.mod.write(new_class) self._encapsulate(self.mod, self.mod.read().index("attr") + 1) expected = ( self.a_class + "\n" " def a_func(self):\n" " self.set_attr(1)\n" + self.added_methods ) self.assertEqual(expected, self.mod.read()) def test_getter_and_setter_parameters(self): self.mod.write(self.a_class) self._encapsulate( self.mod, self.mod.read().index("attr") + 1, getter="getAttr", setter="setAttr", ) new_methods = self.added_methods.replace("get_attr", "getAttr").replace( "set_attr", "setAttr" ) expected = self.a_class + new_methods self.assertEqual(expected, self.mod.read()) def test_using_resources_parameter(self): self.mod1.write("import mod\na = mod.A()\nvar = a.attr\n") self.mod.write(self.a_class) self._encapsulate( self.mod, self.mod.read().index("attr") + 1, resources=[self.mod] ) self.assertEqual("import mod\na = mod.A()\nvar = a.attr\n", self.mod1.read()) expected = self.a_class + self.added_methods self.assertEqual(expected, self.mod.read()) class LocalToFieldTest(unittest.TestCase): def setUp(self): super(LocalToFieldTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(LocalToFieldTest, self).tearDown() def _perform_convert_local_variable_to_field(self, resource, offset): changes = LocalToField(self.project, resource, offset).get_changes() self.project.do(changes) def test_simple_local_to_field(self): code = dedent("""\ class A(object): def a_func(self): var = 10 """) self.mod.write(code) self._perform_convert_local_variable_to_field(self.mod, code.index("var") + 1) expected = dedent("""\ class A(object): def a_func(self): self.var = 10 """) self.assertEqual(expected, self.mod.read()) def test_raising_exception_when_performed_on_a_global_var(self): self.mod.write("var = 10\n") with self.assertRaises(RefactoringError): self._perform_convert_local_variable_to_field( self.mod, self.mod.read().index("var") + 1 ) def test_raising_exception_when_performed_on_field(self): code = dedent("""\ class A(object): def a_func(self): self.var = 10 """) self.mod.write(code) with self.assertRaises(RefactoringError): self._perform_convert_local_variable_to_field( self.mod, self.mod.read().index("var") + 1 ) def test_raising_exception_when_performed_on_a_parameter(self): code = dedent("""\ class A(object): def a_func(self, var): a = var """) self.mod.write(code) with self.assertRaises(RefactoringError): self._perform_convert_local_variable_to_field( self.mod, self.mod.read().index("var") + 1 ) # NOTE: This situation happens alot and is normally not an error # @testutils.assert_raises(RefactoringError) def test_not_rais_exception_when_there_is_a_field_with_the_same_name(self): code = dedent("""\ class A(object): def __init__(self): self.var = 1 def a_func(self): var = 10 """) self.mod.write(code) self._perform_convert_local_variable_to_field( self.mod, self.mod.read().rindex("var") + 1 ) def test_local_to_field_with_self_renamed(self): code = dedent("""\ class A(object): def a_func(myself): var = 10 """) self.mod.write(code) self._perform_convert_local_variable_to_field(self.mod, code.index("var") + 1) expected = dedent("""\ class A(object): def a_func(myself): myself.var = 10 """) self.assertEqual(expected, self.mod.read()) class IntroduceParameterTest(unittest.TestCase): def setUp(self): super(IntroduceParameterTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(IntroduceParameterTest, self).tearDown() def _introduce_parameter(self, offset, name): rope.refactor.introduce_parameter.IntroduceParameter( self.project, self.mod, offset ).get_changes(name).do() def test_simple_case(self): code = dedent("""\ var = 1 def f(): b = var """) self.mod.write(code) offset = self.mod.read().rindex("var") self._introduce_parameter(offset, "var") expected = dedent("""\ var = 1 def f(var=var): b = var """) self.assertEqual(expected, self.mod.read()) def test_changing_function_body(self): code = dedent("""\ var = 1 def f(): b = var """) self.mod.write(code) offset = self.mod.read().rindex("var") self._introduce_parameter(offset, "p1") expected = dedent("""\ var = 1 def f(p1=var): b = p1 """) self.assertEqual(expected, self.mod.read()) def test_unknown_variables(self): self.mod.write("def f():\n b = var + c\n") offset = self.mod.read().rindex("var") with self.assertRaises(RefactoringError): self._introduce_parameter(offset, "p1") self.assertEqual("def f(p1=var):\n b = p1 + c\n", self.mod.read()) def test_failing_when_not_inside(self): self.mod.write("var = 10\nb = var\n") offset = self.mod.read().rindex("var") with self.assertRaises(RefactoringError): self._introduce_parameter(offset, "p1") def test_attribute_accesses(self): code = dedent("""\ class C(object): a = 10 c = C() def f(): b = c.a """) self.mod.write(code) offset = self.mod.read().rindex("a") self._introduce_parameter(offset, "p1") expected = dedent("""\ class C(object): a = 10 c = C() def f(p1=c.a): b = p1 """) self.assertEqual(expected, self.mod.read()) def test_introducing_parameters_for_methods(self): code = dedent("""\ var = 1 class C(object): def f(self): b = var """) self.mod.write(code) offset = self.mod.read().rindex("var") self._introduce_parameter(offset, "p1") expected = dedent("""\ var = 1 class C(object): def f(self, p1=var): b = p1 """) self.assertEqual(expected, self.mod.read()) class _MockTaskObserver(object): def __init__(self): self.called = 0 def __call__(self): self.called += 1 class TaskHandleTest(unittest.TestCase): def test_trivial_case(self): handle = rope.base.taskhandle.TaskHandle() self.assertFalse(handle.is_stopped()) def test_stopping(self): handle = rope.base.taskhandle.TaskHandle() handle.stop() self.assertTrue(handle.is_stopped()) def test_job_sets(self): handle = rope.base.taskhandle.TaskHandle() jobs = handle.create_jobset() self.assertEqual([jobs], handle.get_jobsets()) def test_starting_and_finishing_jobs(self): handle = rope.base.taskhandle.TaskHandle() jobs = handle.create_jobset(name="test job set", count=1) jobs.started_job("job1") jobs.finished_job() def test_test_checking_status(self): handle = rope.base.taskhandle.TaskHandle() jobs = handle.create_jobset() handle.stop() with self.assertRaises(InterruptedTaskError): jobs.check_status() def test_test_checking_status_when_starting(self): handle = rope.base.taskhandle.TaskHandle() jobs = handle.create_jobset() handle.stop() with self.assertRaises(InterruptedTaskError): jobs.started_job("job1") def test_calling_the_observer_after_stopping(self): handle = rope.base.taskhandle.TaskHandle() observer = _MockTaskObserver() handle.add_observer(observer) handle.stop() self.assertEqual(1, observer.called) def test_calling_the_observer_after_creating_job_sets(self): handle = rope.base.taskhandle.TaskHandle() observer = _MockTaskObserver() handle.add_observer(observer) jobs = handle.create_jobset() # noqa self.assertEqual(1, observer.called) def test_calling_the_observer_when_starting_and_finishing_jobs(self): handle = rope.base.taskhandle.TaskHandle() observer = _MockTaskObserver() handle.add_observer(observer) jobs = handle.create_jobset(name="test job set", count=1) jobs.started_job("job1") jobs.finished_job() self.assertEqual(3, observer.called) def test_job_set_get_percent_done(self): handle = rope.base.taskhandle.TaskHandle() jobs = handle.create_jobset(name="test job set", count=2) self.assertEqual(0, jobs.get_percent_done()) jobs.started_job("job1") jobs.finished_job() self.assertEqual(50, jobs.get_percent_done()) jobs.started_job("job2") jobs.finished_job() self.assertEqual(100, jobs.get_percent_done()) def test_getting_job_name(self): handle = rope.base.taskhandle.TaskHandle() jobs = handle.create_jobset(name="test job set", count=1) self.assertEqual("test job set", jobs.get_name()) self.assertEqual(None, jobs.get_active_job_name()) jobs.started_job("job1") self.assertEqual("job1", jobs.get_active_job_name()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/change_signature_test.py0000664000175000017500000007012100000000000023025 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest import rope.base.exceptions from rope.refactor import change_signature from ropetest import testutils class ChangeSignatureTest(unittest.TestCase): def setUp(self): super(ChangeSignatureTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(ChangeSignatureTest, self).tearDown() def test_normalizing_parameters_for_trivial_case(self): code = dedent("""\ def a_func(): pass a_func()""") self.mod.write(code) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual(code, self.mod.read()) def test_normalizing_parameters_for_trivial_case2(self): code = dedent("""\ def a_func(param): pass a_func(2)""") self.mod.write(code) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual(code, self.mod.read()) def test_normalizing_parameters_for_unneeded_keyword(self): self.mod.write( dedent("""\ def a_func(param): pass a_func(param=1)""" ) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual( dedent("""\ def a_func(param): pass a_func(1)""" ), self.mod.read() ) def test_normalizing_parameters_for_unneeded_keyword_for_methods(self): code = dedent("""\ class A(object): def a_func(self, param): pass a_var = A() a_var.a_func(param=1) """) self.mod.write(code) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) expected = dedent("""\ class A(object): def a_func(self, param): pass a_var = A() a_var.a_func(1) """) self.assertEqual(expected, self.mod.read()) def test_normalizing_parameters_for_unsorted_keyword(self): self.mod.write( dedent("""\ def a_func(p1, p2): pass a_func(p2=2, p1=1)""" ) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual( dedent("""\ def a_func(p1, p2): pass a_func(1, 2)""" ), self.mod.read() ) def test_raising_exceptions_for_non_functions(self): self.mod.write("a_var = 10") with self.assertRaises(rope.base.exceptions.RefactoringError): change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_var") + 1 ) def test_normalizing_parameters_for_args_parameter(self): self.mod.write( dedent("""\ def a_func(*arg): pass a_func(1, 2) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual( dedent("""\ def a_func(*arg): pass a_func(1, 2) """), self.mod.read(), ) def test_normalizing_parameters_for_args_parameter_and_keywords(self): self.mod.write( dedent("""\ def a_func(param, *args): pass a_func(*[1, 2, 3]) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual( dedent("""\ def a_func(param, *args): pass a_func(*[1, 2, 3]) """), self.mod.read(), ) def test_normalizing_functions_from_other_modules(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ def a_func(param): pass """) ) self.mod.write( dedent("""\ import mod1 mod1.a_func(param=1) """) ) signature = change_signature.ChangeSignature( self.project, mod1, mod1.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual( dedent("""\ import mod1 mod1.a_func(1) """), self.mod.read(), ) def test_normalizing_parameters_for_keyword_parameters(self): self.mod.write( dedent("""\ def a_func(p1, **kwds): pass a_func(p2=2, p1=1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual( dedent("""\ def a_func(p1, **kwds): pass a_func(1, p2=2) """), self.mod.read(), ) def test_removing_arguments(self): self.mod.write( dedent("""\ def a_func(p1): pass a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(0)])) self.assertEqual( dedent("""\ def a_func(): pass a_func() """), self.mod.read(), ) def test_removing_arguments_with_multiple_args(self): self.mod.write( dedent("""\ def a_func(p1, p2): pass a_func(1, 2) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(0)])) self.assertEqual( dedent("""\ def a_func(p2): pass a_func(2) """), self.mod.read(), ) def test_removing_arguments_passed_as_keywords(self): self.mod.write( dedent("""\ def a_func(p1): pass a_func(p1=1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(0)])) self.assertEqual( dedent("""\ def a_func(): pass a_func() """), self.mod.read(), ) def test_removing_arguments_with_defaults(self): self.mod.write( dedent("""\ def a_func(p1=1): pass a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(0)])) self.assertEqual( dedent("""\ def a_func(): pass a_func() """), self.mod.read(), ) def test_removing_arguments_star_args(self): self.mod.write( dedent("""\ def a_func(p1, *args): pass a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(1)])) self.assertEqual( dedent("""\ def a_func(p1): pass a_func(1) """), self.mod.read(), ) def test_removing_keyword_arg(self): self.mod.write( dedent("""\ def a_func(p1, **kwds): pass a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(1)])) self.assertEqual( dedent("""\ def a_func(p1): pass a_func(1) """), self.mod.read(), ) def test_removing_keyword_arg2(self): self.mod.write( dedent("""\ def a_func(p1, *args, **kwds): pass a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(2)])) self.assertEqual( dedent("""\ def a_func(p1, *args): pass a_func(1) """), self.mod.read(), ) # XXX: What to do here for star args? @unittest.skip("How to deal with start args?") def xxx_test_removing_arguments_star_args2(self): self.mod.write( dedent("""\ def a_func(p1, *args): pass a_func(2, 3, p1=1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(1)])) self.assertEqual( dedent("""\ def a_func(p1): pass a_func(p1=1) """), self.mod.read(), ) # XXX: What to do here for star args? def xxx_test_removing_arguments_star_args3(self): self.mod.write( dedent("""\ def a_func(p1, *args): pass a_func(*[1, 2, 3]) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(1)])) self.assertEqual( dedent("""\ def a_func(p1): pass a_func(*[1, 2, 3]) """), self.mod.read(), ) def test_adding_arguments_for_normal_args_changing_definition(self): self.mod.write( dedent("""\ def a_func(): pass """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do( signature.get_changes([change_signature.ArgumentAdder(0, "p1")]) ) self.assertEqual( dedent("""\ def a_func(p1): pass """), self.mod.read(), ) def test_adding_arguments_for_normal_args_with_defaults(self): self.mod.write( dedent("""\ def a_func(): pass a_func() """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) adder = change_signature.ArgumentAdder(0, "p1", "None") self.project.do(signature.get_changes([adder])) self.assertEqual( dedent("""\ def a_func(p1=None): pass a_func() """), self.mod.read(), ) def test_adding_arguments_for_normal_args_changing_calls(self): self.mod.write( dedent("""\ def a_func(): pass a_func() """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) adder = change_signature.ArgumentAdder(0, "p1", "None", "1") self.project.do(signature.get_changes([adder])) self.assertEqual( dedent("""\ def a_func(p1=None): pass a_func(1) """), self.mod.read(), ) def test_adding_arguments_for_norm_args_chang_calls_with_kwords(self): self.mod.write( dedent("""\ def a_func(p1=0): pass a_func() """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) adder = change_signature.ArgumentAdder(1, "p2", "0", "1") self.project.do(signature.get_changes([adder])) self.assertEqual( dedent("""\ def a_func(p1=0, p2=0): pass a_func(p2=1) """), self.mod.read(), ) def test_adding_arguments_for_norm_args_chang_calls_with_no_value(self): self.mod.write( dedent("""\ def a_func(p2=0): pass a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) adder = change_signature.ArgumentAdder(0, "p1", "0", None) self.project.do(signature.get_changes([adder])) self.assertEqual( dedent("""\ def a_func(p1=0, p2=0): pass a_func(p2=1) """), self.mod.read(), ) def test_adding_duplicate_parameter_and_raising_exceptions(self): self.mod.write( dedent("""\ def a_func(p1): pass """) ) with self.assertRaises(rope.base.exceptions.RefactoringError): signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do( signature.get_changes([change_signature.ArgumentAdder(1, "p1")]) ) def test_inlining_default_arguments(self): self.mod.write( dedent("""\ def a_func(p1=0): pass a_func() """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do( signature.get_changes([change_signature.ArgumentDefaultInliner(0)]) ) self.assertEqual( dedent("""\ def a_func(p1=0): pass a_func(0) """), self.mod.read(), ) def test_inlining_default_arguments2(self): self.mod.write( dedent("""\ def a_func(p1=0): pass a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do( signature.get_changes([change_signature.ArgumentDefaultInliner(0)]) ) self.assertEqual( dedent("""\ def a_func(p1=0): pass a_func(1) """), self.mod.read(), ) def test_preserving_args_and_keywords_order(self): self.mod.write( dedent("""\ def a_func(*args, **kwds): pass a_func(3, 1, 2, a=1, c=3, b=2) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentNormalizer()])) self.assertEqual( dedent("""\ def a_func(*args, **kwds): pass a_func(3, 1, 2, a=1, c=3, b=2) """), self.mod.read(), ) def test_change_order_for_only_one_parameter(self): self.mod.write( dedent("""\ def a_func(p1): pass a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do( signature.get_changes([change_signature.ArgumentReorderer([0])]) ) self.assertEqual( dedent("""\ def a_func(p1): pass a_func(1) """), self.mod.read(), ) def test_change_order_for_two_parameter(self): self.mod.write( dedent("""\ def a_func(p1, p2): pass a_func(1, 2) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do( signature.get_changes([change_signature.ArgumentReorderer([1, 0])]) ) self.assertEqual( dedent("""\ def a_func(p2, p1): pass a_func(2, 1) """), self.mod.read(), ) def test_reordering_multi_line_function_headers(self): self.mod.write( dedent("""\ def a_func(p1, p2): pass a_func(1, 2) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do( signature.get_changes([change_signature.ArgumentReorderer([1, 0])]) ) self.assertEqual( dedent("""\ def a_func(p2, p1): pass a_func(2, 1) """), self.mod.read(), ) def test_changing_order_with_static_params(self): self.mod.write( dedent("""\ def a_func(p1, p2=0, p3=0): pass a_func(1, 2) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do( signature.get_changes([change_signature.ArgumentReorderer([0, 2, 1])]) ) self.assertEqual( dedent("""\ def a_func(p1, p3=0, p2=0): pass a_func(1, p2=2) """), self.mod.read(), ) def test_doing_multiple_changes(self): changers = [] self.mod.write( dedent("""\ def a_func(p1): pass a_func(1) """) ) changers.append(change_signature.ArgumentRemover(0)) changers.append(change_signature.ArgumentAdder(0, "p2", None, None)) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) signature.get_changes(changers).do() self.assertEqual( dedent("""\ def a_func(p2): pass a_func() """), self.mod.read(), ) def test_doing_multiple_changes2(self): changers = [] self.mod.write( dedent("""\ def a_func(p1, p2): pass a_func(p2=2) """) ) changers.append(change_signature.ArgumentAdder(2, "p3", None, "3")) changers.append(change_signature.ArgumentReorderer([1, 0, 2])) changers.append(change_signature.ArgumentRemover(1)) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) signature.get_changes(changers).do() self.assertEqual( dedent("""\ def a_func(p2, p3): pass a_func(2, 3) """), self.mod.read(), ) def test_changing_signature_in_subclasses(self): self.mod.write( dedent("""\ class A(object): def a_method(self): pass class B(A): def a_method(self): pass """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_method") + 1 ) signature.get_changes( [change_signature.ArgumentAdder(1, "p1")], in_hierarchy=True ).do() self.assertEqual( dedent("""\ class A(object): def a_method(self, p1): pass class B(A): def a_method(self, p1): pass """), self.mod.read(), ) def test_differentiating_class_accesses_from_instance_accesses(self): self.mod.write( dedent("""\ class A(object): def a_func(self, param): pass a_var = A() A.a_func(a_var, param=1)""" ) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("a_func") + 1 ) self.project.do(signature.get_changes([change_signature.ArgumentRemover(1)])) self.assertEqual( dedent("""\ class A(object): def a_func(self): pass a_var = A() A.a_func(a_var)""" ), self.mod.read() ) def test_changing_signature_for_constructors(self): self.mod.write( dedent("""\ class C(object): def __init__(self, p): pass c = C(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("C") + 1 ) signature.get_changes([change_signature.ArgumentRemover(1)]).do() self.assertEqual( dedent("""\ class C(object): def __init__(self): pass c = C() """), self.mod.read(), ) def test_changing_signature_for_constructors2(self): self.mod.write( dedent("""\ class C(object): def __init__(self, p): pass c = C(1) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("__init__") + 1 ) signature.get_changes([change_signature.ArgumentRemover(1)]).do() self.assertEqual( dedent("""\ class C(object): def __init__(self): pass c = C() """), self.mod.read(), ) def test_changing_signature_for_constructors_when_using_super(self): self.mod.write( dedent("""\ class A(object): def __init__(self, p): pass class B(A): def __init__(self, p): super(B, self).__init__(p) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().index("__init__") + 1 ) signature.get_changes([change_signature.ArgumentRemover(1)]).do() self.assertEqual( dedent("""\ class A(object): def __init__(self): pass class B(A): def __init__(self, p): super(B, self).__init__() """), self.mod.read(), ) def test_redordering_arguments_reported_by_mft(self): self.mod.write( dedent("""\ def f(a, b, c): pass f(1, 2, 3) """) ) signature = change_signature.ChangeSignature( self.project, self.mod, self.mod.read().rindex("f") ) signature.get_changes([change_signature.ArgumentReorderer([1, 2, 0])]).do() self.assertEqual( dedent("""\ def f(b, c, a): pass f(2, 3, 1) """), self.mod.read(), ) def test_resources_parameter(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write("def a_func(param):\n pass\n") self.mod.write( dedent("""\ import mod1 mod1.a_func(1) """) ) signature = change_signature.ChangeSignature( self.project, mod1, mod1.read().index("a_func") + 1 ) signature.get_changes( [change_signature.ArgumentRemover(0)], resources=[mod1] ).do() self.assertEqual( dedent("""\ import mod1 mod1.a_func(1) """), self.mod.read(), ) self.assertEqual( dedent("""\ def a_func(): pass """), mod1.read(), ) def test_reordering_and_automatic_defaults(self): code = dedent("""\ def f(p1, p2=2): pass f(1, 2) """) self.mod.write(code) signature = change_signature.ChangeSignature( self.project, self.mod, code.index("f(") ) reorder = change_signature.ArgumentReorderer([1, 0], autodef="1") signature.get_changes([reorder]).do() expected = dedent("""\ def f(p2=2, p1=1): pass f(2, 1) """) self.assertEqual(expected, self.mod.read()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/extracttest.py0000664000175000017500000026425500000000000021047 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest import rope.base.codeanalyze import rope.base.exceptions from rope.refactor import extract from ropetest import testutils class ExtractMethodTest(unittest.TestCase): def setUp(self): super(ExtractMethodTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore def tearDown(self): testutils.remove_project(self.project) super(ExtractMethodTest, self).tearDown() def do_extract_method(self, source_code, start, end, extracted, **kwds): testmod = testutils.create_module(self.project, "testmod") testmod.write(source_code) extractor = extract.ExtractMethod(self.project, testmod, start, end) self.project.do(extractor.get_changes(extracted, **kwds)) return testmod.read() def do_extract_variable(self, source_code, start, end, extracted, **kwds): testmod = testutils.create_module(self.project, "testmod") testmod.write(source_code) extractor = extract.ExtractVariable(self.project, testmod, start, end) self.project.do(extractor.get_changes(extracted, **kwds)) return testmod.read() def _convert_line_range_to_offset(self, code, start, end): lines = rope.base.codeanalyze.SourceLinesAdapter(code) return lines.get_line_start(start), lines.get_line_end(end) def test_simple_extract_function(self): code = dedent("""\ def a_func(): print('one') print('two') """) start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "extracted") expected = dedent("""\ def a_func(): extracted() print('two') def extracted(): print('one') """) self.assertEqual(expected, refactored) def test_simple_extract_function_one_line(self): code = dedent("""\ def a_func(): resp = 'one' print(resp) """) selected = "'one'" start, end = code.index(selected), code.index(selected) + len(selected) refactored = self.do_extract_method(code, start, end, "extracted") expected = dedent("""\ def a_func(): resp = extracted() print(resp) def extracted(): return 'one' """) self.assertEqual(expected, refactored) def test_extract_function_at_the_end_of_file(self): code = dedent("""\ def a_func(): print('one')""") start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "extracted") expected = dedent("""\ def a_func(): extracted() def extracted(): print('one') """) self.assertEqual(expected, refactored) def test_extract_function_after_scope(self): code = dedent("""\ def a_func(): print('one') print('two') print('hey') """) start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "extracted") expected = dedent("""\ def a_func(): extracted() print('two') def extracted(): print('one') print('hey') """) self.assertEqual(expected, refactored) @testutils.only_for("3.5") def test_extract_function_containing_dict_generalized_unpacking(self): code = dedent("""\ def a_func(dict1): dict2 = {} a_var = {a: b, **dict1, **dict2} """) start = code.index("{a") end = code.index("2}") + len("2}") refactored = self.do_extract_method(code, start, end, "extracted") expected = dedent("""\ def a_func(dict1): dict2 = {} a_var = extracted(dict1, dict2) def extracted(dict1, dict2): return {a: b, **dict1, **dict2} """) self.assertEqual(expected, refactored) def test_simple_extract_function_with_parameter(self): code = dedent("""\ def a_func(): a_var = 10 print(a_var) """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): a_var = 10 new_func(a_var) def new_func(a_var): print(a_var) """) self.assertEqual(expected, refactored) def test_not_unread_variables_as_parameter(self): code = dedent("""\ def a_func(): a_var = 10 print('hey') """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): a_var = 10 new_func() def new_func(): print('hey') """) self.assertEqual(expected, refactored) def test_simple_extract_function_with_two_parameter(self): code = dedent("""\ def a_func(): a_var = 10 another_var = 20 third_var = a_var + another_var """) start, end = self._convert_line_range_to_offset(code, 4, 4) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): a_var = 10 another_var = 20 new_func(a_var, another_var) def new_func(a_var, another_var): third_var = a_var + another_var """) self.assertEqual(expected, refactored) def test_simple_extract_function_with_return_value(self): code = dedent("""\ def a_func(): a_var = 10 print(a_var) """) start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): a_var = new_func() print(a_var) def new_func(): a_var = 10 return a_var """) self.assertEqual(expected, refactored) def test_extract_function_with_multiple_return_values(self): code = dedent("""\ def a_func(): a_var = 10 another_var = 20 third_var = a_var + another_var """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): a_var, another_var = new_func() third_var = a_var + another_var def new_func(): a_var = 10 another_var = 20 return a_var, another_var """) self.assertEqual(expected, refactored) def test_simple_extract_method(self): code = dedent("""\ class AClass(object): def a_func(self): print(1) print(2) """) start, end = self._convert_line_range_to_offset(code, 4, 4) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ class AClass(object): def a_func(self): self.new_func() print(2) def new_func(self): print(1) """) self.assertEqual(expected, refactored) def test_extract_method_with_args_and_returns(self): code = dedent("""\ class AClass(object): def a_func(self): a_var = 10 another_var = a_var * 3 third_var = a_var + another_var """) start, end = self._convert_line_range_to_offset(code, 4, 4) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ class AClass(object): def a_func(self): a_var = 10 another_var = self.new_func(a_var) third_var = a_var + another_var def new_func(self, a_var): another_var = a_var * 3 return another_var """) self.assertEqual(expected, refactored) def test_extract_method_with_self_as_argument(self): code = dedent("""\ class AClass(object): def a_func(self): print(self) """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ class AClass(object): def a_func(self): self.new_func() def new_func(self): print(self) """) self.assertEqual(expected, refactored) def test_extract_method_with_no_self_as_argument(self): code = dedent("""\ class AClass(object): def a_func(): print(1) """) start, end = self._convert_line_range_to_offset(code, 3, 3) with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_method_with_multiple_methods(self): code = dedent("""\ class AClass(object): def a_func(self): print(self) def another_func(self): pass """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ class AClass(object): def a_func(self): self.new_func() def new_func(self): print(self) def another_func(self): pass """) self.assertEqual(expected, refactored) def test_extract_function_with_function_returns(self): code = dedent("""\ def a_func(): def inner_func(): pass inner_func() """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): inner_func = new_func() inner_func() def new_func(): def inner_func(): pass return inner_func """) self.assertEqual(expected, refactored) def test_simple_extract_global_function(self): code = dedent("""\ print('one') print('two') print('three') """) start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ print('one') def new_func(): print('two') new_func() print('three') """) self.assertEqual(expected, refactored) def test_extract_global_function_inside_ifs(self): code = dedent("""\ if True: a = 10 """) start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def new_func(): a = 10 if True: new_func() """) self.assertEqual(expected, refactored) def test_extract_function_while_inner_function_reads(self): code = dedent("""\ def a_func(): a_var = 10 def inner_func(): print(a_var) return inner_func """) start, end = self._convert_line_range_to_offset(code, 3, 4) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): a_var = 10 inner_func = new_func(a_var) return inner_func def new_func(a_var): def inner_func(): print(a_var) return inner_func """) self.assertEqual(expected, refactored) def test_extract_method_bad_range(self): code = dedent("""\ def a_func(): pass a_var = 10 """) start, end = self._convert_line_range_to_offset(code, 2, 3) with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_method_bad_range2(self): code = dedent("""\ class AClass(object): pass """) start, end = self._convert_line_range_to_offset(code, 1, 1) with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_method_containing_return(self): code = dedent("""\ def a_func(arg): if arg: return arg * 2 return 1""") start, end = self._convert_line_range_to_offset(code, 2, 4) with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_method_containing_yield(self): code = dedent("""\ def a_func(arg): yield arg * 2 """) start, end = self._convert_line_range_to_offset(code, 2, 2) with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_method_containing_uncomplete_lines(self): code = dedent("""\ a_var = 20 another_var = 30 """) start = code.index("20") end = code.index("30") + 2 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_method_containing_uncomplete_lines2(self): code = dedent("""\ a_var = 20 another_var = 30 """) start = code.index("20") end = code.index("another") + 5 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_function_and_argument_as_paramenter(self): code = dedent("""\ def a_func(arg): print(arg) """) start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(arg): new_func(arg) def new_func(arg): print(arg) """) self.assertEqual(expected, refactored) def test_extract_function_and_end_as_the_start_of_a_line(self): code = dedent("""\ print("hey") if True: pass """) start = 0 end = code.index("\n") + 1 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def new_func(): print("hey") new_func() if True: pass """) self.assertEqual(expected, refactored) def test_extract_function_and_indented_blocks(self): code = dedent("""\ def a_func(arg): if True: if True: print(arg) """) start, end = self._convert_line_range_to_offset(code, 3, 4) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(arg): if True: new_func(arg) def new_func(arg): if True: print(arg) """) self.assertEqual(expected, refactored) def test_extract_method_and_multi_line_headers(self): code = dedent("""\ def a_func( arg): print(arg) """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func( arg): new_func(arg) def new_func(arg): print(arg) """) self.assertEqual(expected, refactored) def test_single_line_extract_function(self): code = dedent("""\ a_var = 10 + 20 """) start = code.index("10") end = code.index("20") + 2 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def new_func(): return 10 + 20 a_var = new_func() """) self.assertEqual(expected, refactored) def test_single_line_extract_function2(self): code = dedent("""\ def a_func(): a = 10 b = a * 20 """) start = code.rindex("a") end = code.index("20") + 2 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): a = 10 b = new_func(a) def new_func(a): return a * 20 """) self.assertEqual(expected, refactored) def test_single_line_extract_method_and_logical_lines(self): code = dedent("""\ a_var = 10 +\\ 20 """) start = code.index("10") end = code.index("20") + 2 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def new_func(): return 10 + 20 a_var = new_func() """) self.assertEqual(expected, refactored) def test_single_line_extract_method_and_logical_lines2(self): code = dedent("""\ a_var = (10,\\ 20) """) start = code.index("10") - 1 end = code.index("20") + 3 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def new_func(): return (10, 20) a_var = new_func() """) self.assertEqual(expected, refactored) def test_single_line_extract_method(self): code = dedent("""\ class AClass(object): def a_func(self): a = 10 b = a * a """) start = code.rindex("=") + 2 end = code.rindex("a") + 1 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ class AClass(object): def a_func(self): a = 10 b = self.new_func(a) def new_func(self, a): return a * a """) self.assertEqual(expected, refactored) def test_single_line_extract_function_if_condition(self): code = dedent("""\ if True: pass """) start = code.index("True") end = code.index("True") + 4 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def new_func(): return True if new_func(): pass """) self.assertEqual(expected, refactored) def test_unneeded_params(self): code = dedent("""\ class A(object): def a_func(self): a_var = 10 a_var += 2 """) start = code.rindex("2") end = code.rindex("2") + 1 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ class A(object): def a_func(self): a_var = 10 a_var += self.new_func() def new_func(self): return 2 """) self.assertEqual(expected, refactored) def test_breaks_and_continues_inside_loops(self): code = dedent("""\ def a_func(): for i in range(10): continue """) start = code.index("for") end = len(code) - 1 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): new_func() def new_func(): for i in range(10): continue """) self.assertEqual(expected, refactored) def test_breaks_and_continues_outside_loops(self): code = dedent("""\ def a_func(): for i in range(10): a = i continue """) start = code.index("a = i") end = len(code) - 1 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_for_loop_variable_scope(self): code = dedent("""\ def my_func(): i = 0 for dummy in range(10): i += 1 print(i) """) start, end = self._convert_line_range_to_offset(code, 4, 5) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def my_func(): i = 0 for dummy in range(10): i = new_func(i) def new_func(i): i += 1 print(i) return i """) self.assertEqual(expected, refactored) def test_for_loop_variable_scope_read_then_write(self): code = dedent("""\ def my_func(): i = 0 for dummy in range(10): a = i + 1 i = a + 1 """) start, end = self._convert_line_range_to_offset(code, 4, 5) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def my_func(): i = 0 for dummy in range(10): i = new_func(i) def new_func(i): a = i + 1 i = a + 1 return i """) self.assertEqual(expected, refactored) def test_for_loop_variable_scope_write_then_read(self): code = dedent("""\ def my_func(): i = 0 for dummy in range(10): i = 'hello' print(i) """) start, end = self._convert_line_range_to_offset(code, 4, 5) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def my_func(): i = 0 for dummy in range(10): new_func() def new_func(): i = 'hello' print(i) """) self.assertEqual(expected, refactored) def test_for_loop_variable_scope_write_only(self): code = dedent("""\ def my_func(): i = 0 for num in range(10): i = 'hello' + num print(i) """) start, end = self._convert_line_range_to_offset(code, 4, 4) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def my_func(): i = 0 for num in range(10): i = new_func(num) print(i) def new_func(num): i = 'hello' + num return i """) self.assertEqual(expected, refactored) def test_variable_writes_followed_by_variable_reads_after_extraction(self): code = dedent("""\ def a_func(): a = 1 a = 2 b = a """) start = code.index("a = 1") end = code.index("a = 2") - 1 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): new_func() a = 2 b = a def new_func(): a = 1 """) self.assertEqual(expected, refactored) def test_var_writes_followed_by_var_reads_inside_extraction(self): code = dedent("""\ def a_func(): a = 1 a = 2 b = a """) start = code.index("a = 2") end = len(code) - 1 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): a = 1 new_func() def new_func(): a = 2 b = a """) self.assertEqual(expected, refactored) def test_extract_variable(self): code = dedent("""\ a_var = 10 + 20 """) start = code.index("10") end = code.index("20") + 2 refactored = self.do_extract_variable(code, start, end, "new_var") expected = dedent("""\ new_var = 10 + 20 a_var = new_var """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.6") def test_extract_variable_f_string(self): code = dedent("""\ foo(f"abc {a_var} def", 10) """) start = code.index('f"') end = code.index('def"') + 4 refactored = self.do_extract_variable(code, start, end, "new_var") expected = dedent("""\ new_var = f"abc {a_var} def" foo(new_var, 10) """) self.assertEqual(expected, refactored) def test_extract_variable_multiple_lines(self): code = dedent("""\ a = 1 b = 2 """) start = code.index("1") end = code.index("1") + 1 refactored = self.do_extract_variable(code, start, end, "c") expected = dedent("""\ c = 1 a = c b = 2 """) self.assertEqual(expected, refactored) def test_extract_variable_in_the_middle_of_statements(self): code = dedent("""\ a = 1 + 2 """) start = code.index("1") end = code.index("1") + 1 refactored = self.do_extract_variable(code, start, end, "c") expected = dedent("""\ c = 1 a = c + 2 """) self.assertEqual(expected, refactored) def test_extract_variable_for_a_tuple(self): code = dedent("""\ a = 1, 2 """) start = code.index("1") end = code.index("2") + 1 refactored = self.do_extract_variable(code, start, end, "c") expected = dedent("""\ c = 1, 2 a = c """) self.assertEqual(expected, refactored) def test_extract_variable_for_a_string(self): code = dedent("""\ def a_func(): a = "hey!" """) start = code.index('"') end = code.rindex('"') + 1 refactored = self.do_extract_variable(code, start, end, "c") expected = dedent("""\ def a_func(): c = "hey!" a = c """) self.assertEqual(expected, refactored) def test_extract_variable_inside_ifs(self): code = dedent("""\ if True: a = 1 + 2 """) start = code.index("1") end = code.rindex("2") + 1 refactored = self.do_extract_variable(code, start, end, "b") expected = dedent("""\ if True: b = 1 + 2 a = b """) self.assertEqual(expected, refactored) def test_extract_variable_inside_ifs_and_logical_lines(self): code = dedent("""\ if True: a = (3 + (1 + 2)) """) start = code.index("1") end = code.index("2") + 1 refactored = self.do_extract_variable(code, start, end, "b") expected = dedent("""\ if True: b = 1 + 2 a = (3 + (b)) """) self.assertEqual(expected, refactored) # TODO: Handle when extracting a subexpression def xxx_test_extract_variable_for_a_subexpression(self): code = dedent("""\ a = 3 + 1 + 2 """) start = code.index("1") end = code.index("2") + 1 refactored = self.do_extract_variable(code, start, end, "b") expected = dedent("""\ b = 1 + 2 a = 3 + b """) self.assertEqual(expected, refactored) def test_extract_variable_starting_from_the_start_of_the_line(self): code = dedent("""\ a_dict = {1: 1} a_dict.values().count(1) """) start = code.rindex("a_dict") end = code.index("count") - 1 refactored = self.do_extract_variable(code, start, end, "values") expected = dedent("""\ a_dict = {1: 1} values = a_dict.values() values.count(1) """) self.assertEqual(expected, refactored) def test_extract_variable_on_the_last_line_of_a_function(self): code = dedent("""\ def f(): a_var = {} a_var.keys() """) start = code.rindex("a_var") end = code.index(".keys") refactored = self.do_extract_variable(code, start, end, "new_var") expected = dedent("""\ def f(): a_var = {} new_var = a_var new_var.keys() """) self.assertEqual(expected, refactored) def test_extract_variable_on_the_indented_function_statement(self): code = dedent("""\ def f(): if True: a_var = 1 + 2 """) start = code.index("1") end = code.index("2") + 1 refactored = self.do_extract_variable(code, start, end, "new_var") expected = dedent("""\ def f(): if True: new_var = 1 + 2 a_var = new_var """) self.assertEqual(expected, refactored) def test_extract_method_on_the_last_line_of_a_function(self): code = dedent("""\ def f(): a_var = {} a_var.keys() """) start = code.rindex("a_var") end = code.index(".keys") refactored = self.do_extract_method(code, start, end, "new_f") expected = dedent("""\ def f(): a_var = {} new_f(a_var).keys() def new_f(a_var): return a_var """) self.assertEqual(expected, refactored) def test_raising_exception_when_on_incomplete_variables(self): code = dedent("""\ a_var = 10 + 20 """) start = code.index("10") + 1 end = code.index("20") + 2 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_raising_exception_when_on_incomplete_variables_on_end(self): code = dedent("""\ a_var = 10 + 20 """) start = code.index("10") end = code.index("20") + 1 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_raising_exception_on_bad_parens(self): code = dedent("""\ a_var = (10 + 20) + 30 """) start = code.index("20") end = code.index("30") + 2 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_raising_exception_on_bad_operators(self): code = dedent("""\ a_var = 10 + 20 + 30 """) start = code.index("10") end = code.rindex("+") + 1 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") # FIXME: Extract method should be more intelligent about bad ranges def xxx_test_raising_exception_on_function_parens(self): code = dedent("""\ a = range(10)""") start = code.index("(") end = code.rindex(")") + 1 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_method_and_extra_blank_lines(self): code = dedent("""\ print(1) """) refactored = self.do_extract_method(code, 0, len(code), "new_f") expected = dedent("""\ def new_f(): print(1) new_f() """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.6") def test_extract_method_f_string_extract_method(self): code = dedent("""\ def func(a_var): foo(f"abc {a_var}", 10) """) start = code.index('f"') end = code.index('}"') + 2 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def func(a_var): foo(new_func(a_var), 10) def new_func(a_var): return f"abc {a_var}" """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.6") def test_extract_method_f_string_extract_method_complex_expression(self): code = dedent("""\ def func(a_var): b_var = int c_var = 10 fill = 10 foo(f"abc {a_var + f'{b_var(a_var)}':{fill}16}" f"{c_var}", 10) """) start = code.index('f"') end = code.index('c_var}"') + 7 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def func(a_var): b_var = int c_var = 10 fill = 10 foo(new_func(a_var, b_var, c_var, fill), 10) def new_func(a_var, b_var, c_var, fill): return f"abc {a_var + f'{b_var(a_var)}':{fill}16}" f"{c_var}" """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.6") def test_extract_method_f_string_false_comment(self): code = dedent("""\ def func(a_var): foo(f"abc {a_var} # ", 10) """) start = code.index('f"') end = code.index('# "') + 3 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def func(a_var): foo(new_func(a_var), 10) def new_func(a_var): return f"abc {a_var} # " """) self.assertEqual(expected, refactored) @unittest.expectedFailure @testutils.only_for_versions_higher("3.6") def test_extract_method_f_string_false_format_value_in_regular_string(self): code = dedent("""\ def func(a_var): b_var = 1 foo(f"abc {a_var} " "{b_var}" f"{b_var} def", 10) """) start = code.index('f"') end = code.index('def"') + 4 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def func(a_var): b_var = 1 foo(new_func(a_var, b_var), 10) def new_func(a_var, b_var): return f"abc {a_var} " "{b_var}" f"{b_var} def" """) self.assertEqual(expected, refactored) def test_variable_writes_in_the_same_line_as_variable_read(self): code = dedent("""\ a = 1 a = 1 + a """) start = code.index("\n") + 1 end = len(code) refactored = self.do_extract_method(code, start, end, "new_f", global_=True) expected = dedent("""\ a = 1 def new_f(a): a = 1 + a new_f(a) """) self.assertEqual(expected, refactored) def test_variable_writes_in_the_same_line_as_variable_read2(self): code = dedent("""\ a = 1 a += 1 """) start = code.index("\n") + 1 end = len(code) refactored = self.do_extract_method(code, start, end, "new_f", global_=True) expected = dedent("""\ a = 1 def new_f(a): a += 1 new_f(a) """) self.assertEqual(expected, refactored) def test_variable_writes_in_the_same_line_as_variable_read3(self): code = dedent("""\ a = 1 a += 1 print(a) """) start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "new_f") expected = dedent("""\ a = 1 def new_f(a): a += 1 return a a = new_f(a) print(a) """) self.assertEqual(expected, refactored) def test_variable_writes_only(self): code = dedent("""\ i = 1 print(i) """) start, end = self._convert_line_range_to_offset(code, 1, 1) refactored = self.do_extract_method(code, start, end, "new_f") expected = dedent("""\ def new_f(): i = 1 return i i = new_f() print(i) """) self.assertEqual(expected, refactored) def test_variable_and_similar_expressions(self): code = dedent("""\ a = 1 b = 1 """) start = code.index("1") end = start + 1 refactored = self.do_extract_variable(code, start, end, "one", similar=True) expected = dedent("""\ one = 1 a = one b = one """) self.assertEqual(expected, refactored) def test_definition_should_appear_before_the_first_use(self): code = dedent("""\ a = 1 b = 1 """) start = code.rindex("1") end = start + 1 refactored = self.do_extract_variable(code, start, end, "one", similar=True) expected = dedent("""\ one = 1 a = one b = one """) self.assertEqual(expected, refactored) def test_extract_method_and_similar_expressions(self): code = dedent("""\ a = 1 b = 1 """) start = code.index("1") end = start + 1 refactored = self.do_extract_method(code, start, end, "one", similar=True) expected = dedent("""\ def one(): return 1 a = one() b = one() """) self.assertEqual(expected, refactored) def test_simple_extract_method_and_similar_statements(self): code = dedent("""\ class AClass(object): def func1(self): a = 1 + 2 b = a def func2(self): a = 1 + 2 b = a """) start, end = self._convert_line_range_to_offset(code, 4, 4) refactored = self.do_extract_method(code, start, end, "new_func", similar=True) expected = dedent("""\ class AClass(object): def func1(self): a = self.new_func() b = a def new_func(self): a = 1 + 2 return a def func2(self): a = self.new_func() b = a """) self.assertEqual(expected, refactored) def test_extract_method_and_similar_statements2(self): code = dedent("""\ class AClass(object): def func1(self, p1): a = p1 + 2 def func2(self, p2): a = p2 + 2 """) start = code.rindex("p1") end = code.index("2\n") + 1 refactored = self.do_extract_method(code, start, end, "new_func", similar=True) expected = dedent("""\ class AClass(object): def func1(self, p1): a = self.new_func(p1) def new_func(self, p1): return p1 + 2 def func2(self, p2): a = self.new_func(p2) """) self.assertEqual(expected, refactored) def test_extract_method_and_similar_sttemnts_return_is_different(self): code = dedent("""\ class AClass(object): def func1(self, p1): a = p1 + 2 def func2(self, p2): self.attr = p2 + 2 """) start = code.rindex("p1") end = code.index("2\n") + 1 refactored = self.do_extract_method(code, start, end, "new_func", similar=True) expected = dedent("""\ class AClass(object): def func1(self, p1): a = self.new_func(p1) def new_func(self, p1): return p1 + 2 def func2(self, p2): self.attr = self.new_func(p2) """) self.assertEqual(expected, refactored) def test_extract_method_and_similar_sttemnts_overlapping_regions(self): code = dedent("""\ def func(p): a = p b = a c = b d = c return d""") start = code.index("a") end = code.rindex("a") + 1 refactored = self.do_extract_method(code, start, end, "new_func", similar=True) expected = dedent("""\ def func(p): b = new_func(p) d = new_func(b) return d def new_func(p): a = p b = a return b """) self.assertEqual(expected, refactored) def test_definition_should_appear_where_it_is_visible(self): code = dedent("""\ if True: a = 1 else: b = 1 """) start = code.rindex("1") end = start + 1 refactored = self.do_extract_variable(code, start, end, "one", similar=True) expected = dedent("""\ one = 1 if True: a = one else: b = one """) self.assertEqual(expected, refactored) def test_extract_variable_and_similar_statements_in_classes(self): code = dedent("""\ class AClass(object): def func1(self): a = 1 def func2(self): b = 1 """) start = code.index(" 1") + 1 refactored = self.do_extract_variable( code, start, start + 1, "one", similar=True ) expected = dedent("""\ class AClass(object): def func1(self): one = 1 a = one def func2(self): b = 1 """) self.assertEqual(expected, refactored) def test_extract_method_in_staticmethods(self): code = dedent("""\ class AClass(object): @staticmethod def func2(): b = 1 """) start = code.index(" 1") + 1 refactored = self.do_extract_method(code, start, start + 1, "one", similar=True) expected = dedent("""\ class AClass(object): @staticmethod def func2(): b = AClass.one() @staticmethod def one(): return 1 """) self.assertEqual(expected, refactored) def test_extract_normal_method_with_staticmethods(self): code = dedent("""\ class AClass(object): @staticmethod def func1(): b = 1 def func2(self): b = 1 """) start = code.rindex(" 1") + 1 refactored = self.do_extract_method(code, start, start + 1, "one", similar=True) expected = dedent("""\ class AClass(object): @staticmethod def func1(): b = 1 def func2(self): b = self.one() def one(self): return 1 """) self.assertEqual(expected, refactored) def test_extract_variable_with_no_new_lines_at_the_end(self): code = "a_var = 10" start = code.index("10") end = start + 2 refactored = self.do_extract_variable(code, start, end, "new_var") expected = dedent("""\ new_var = 10 a_var = new_var""") self.assertEqual(expected, refactored) def test_extract_method_containing_return_in_functions(self): code = dedent("""\ def f(arg): return arg print(f(1)) """) start, end = self._convert_line_range_to_offset(code, 1, 3) refactored = self.do_extract_method(code, start, end, "a_func") expected = dedent("""\ def a_func(): def f(arg): return arg print(f(1)) a_func() """) self.assertEqual(expected, refactored) def test_extract_method_and_varying_first_parameter(self): code = dedent("""\ class C(object): def f1(self): print(str(self)) def f2(self): print(str(1)) """) start = code.index("print(") + 6 end = code.index("))\n") + 1 refactored = self.do_extract_method(code, start, end, "to_str", similar=True) expected = dedent("""\ class C(object): def f1(self): print(self.to_str()) def to_str(self): return str(self) def f2(self): print(str(1)) """) self.assertEqual(expected, refactored) def test_extract_method_when_an_attribute_exists_in_function_scope(self): code = dedent("""\ class A(object): def func(self): pass a = A() def f(): func = a.func() print(func) """) start, end = self._convert_line_range_to_offset(code, 6, 6) refactored = self.do_extract_method(code, start, end, "g") refactored = refactored[refactored.index("A()") + 4 :] expected = dedent("""\ def f(): func = g() print(func) def g(): func = a.func() return func """) self.assertEqual(expected, refactored) def test_global_option_for_extract_method(self): code = dedent("""\ def a_func(): print(1) """) start, end = self._convert_line_range_to_offset(code, 2, 2) refactored = self.do_extract_method(code, start, end, "extracted", global_=True) expected = dedent("""\ def a_func(): extracted() def extracted(): print(1) """) self.assertEqual(expected, refactored) def test_global_extract_method(self): code = dedent("""\ class AClass(object): def a_func(self): print(1) """) start, end = self._convert_line_range_to_offset(code, 4, 4) refactored = self.do_extract_method(code, start, end, "new_func", global_=True) expected = dedent("""\ class AClass(object): def a_func(self): new_func() def new_func(): print(1) """) self.assertEqual(expected, refactored) def test_global_extract_method_with_multiple_methods(self): code = dedent("""\ class AClass(object): def a_func(self): print(1) def another_func(self): pass """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func", global_=True) expected = dedent("""\ class AClass(object): def a_func(self): new_func() def another_func(self): pass def new_func(): print(1) """) self.assertEqual(expected, refactored) def test_where_to_seach_when_extracting_global_names(self): code = dedent("""\ def a(): return 1 def b(): return 1 b = 1 """) start = code.index("1") end = start + 1 refactored = self.do_extract_variable( code, start, end, "one", similar=True, global_=True ) expected = dedent("""\ def a(): return one one = 1 def b(): return one b = one """) self.assertEqual(expected, refactored) def test_extracting_pieces_with_distinct_temp_names(self): code = dedent("""\ a = 1 print(a) b = 1 print(b) """) start = code.index("a") end = code.index("\nb") refactored = self.do_extract_method( code, start, end, "f", similar=True, global_=True ) expected = dedent("""\ def f(): a = 1 print(a) f() f() """) self.assertEqual(expected, refactored) def test_extract_methods_in_glob_funcs_should_be_glob(self): code = dedent("""\ def f(): a = 1 def g(): b = 1 """) start = code.rindex("1") refactored = self.do_extract_method( code, start, start + 1, "one", similar=True, global_=False ) expected = dedent("""\ def f(): a = one() def g(): b = one() def one(): return 1 """) self.assertEqual(expected, refactored) def test_extract_methods_in_glob_funcs_should_be_glob_2(self): code = dedent("""\ if 1: var = 2 """) start = code.rindex("2") refactored = self.do_extract_method( code, start, start + 1, "two", similar=True, global_=False ) expected = dedent("""\ def two(): return 2 if 1: var = two() """) self.assertEqual(expected, refactored) def test_extract_method_and_try_blocks(self): code = dedent("""\ def f(): try: pass except Exception: pass """) start, end = self._convert_line_range_to_offset(code, 2, 5) refactored = self.do_extract_method(code, start, end, "g") expected = dedent("""\ def f(): g() def g(): try: pass except Exception: pass """) self.assertEqual(expected, refactored) def test_extract_and_not_passing_global_functions(self): code = dedent("""\ def next(p): return p + 1 var = next(1) """) start = code.rindex("next") refactored = self.do_extract_method(code, start, len(code) - 1, "two") expected = dedent("""\ def next(p): return p + 1 def two(): return next(1) var = two() """) self.assertEqual(expected, refactored) def test_extracting_with_only_one_return(self): code = dedent("""\ def f(): var = 1 return var """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, "g") expected = dedent("""\ def f(): return g() def g(): var = 1 return var """) self.assertEqual(expected, refactored) def test_extracting_variable_and_implicit_continuations(self): code = dedent("""\ s = ("1" "2") """) start = code.index('"') end = code.rindex('"') + 1 refactored = self.do_extract_variable(code, start, end, "s2") expected = dedent("""\ s2 = "1" "2" s = (s2) """) self.assertEqual(expected, refactored) def test_extracting_method_and_implicit_continuations(self): code = dedent("""\ s = ("1" "2") """) start = code.index('"') end = code.rindex('"') + 1 refactored = self.do_extract_method(code, start, end, "f") expected = dedent("""\ def f(): return "1" "2" s = (f()) """) self.assertEqual(expected, refactored) def test_passing_conditional_updated_vars_in_extracted(self): code = dedent("""\ def f(a): if 0: a = 1 print(a) """) start, end = self._convert_line_range_to_offset(code, 2, 4) refactored = self.do_extract_method(code, start, end, "g") expected = dedent("""\ def f(a): g(a) def g(a): if 0: a = 1 print(a) """) self.assertEqual(expected, refactored) def test_returning_conditional_updated_vars_in_extracted(self): code = dedent("""\ def f(a): if 0: a = 1 print(a) """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, "g") expected = dedent("""\ def f(a): a = g(a) print(a) def g(a): if 0: a = 1 return a """) self.assertEqual(expected, refactored) def test_extract_method_with_variables_possibly_written_to(self): code = dedent("""\ def a_func(b): if b > 0: a = 2 print(a) """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, "extracted") expected = dedent("""\ def a_func(b): a = extracted(b) print(a) def extracted(b): if b > 0: a = 2 return a """) self.assertEqual(expected, refactored) def test_extract_method_with_list_comprehension(self): code = dedent("""\ def foo(): x = [e for e in []] f = 23 for e, f in []: def bar(): e[42] = 1 """) start, end = self._convert_line_range_to_offset(code, 4, 7) refactored = self.do_extract_method(code, start, end, "baz") expected = dedent("""\ def foo(): x = [e for e in []] f = 23 baz() def baz(): for e, f in []: def bar(): e[42] = 1 """) self.assertEqual(expected, refactored) def test_extract_method_with_list_comprehension_and_iter(self): code = dedent("""\ def foo(): x = [e for e in []] f = 23 for x, f in x: def bar(): x[42] = 1 """) start, end = self._convert_line_range_to_offset(code, 4, 7) refactored = self.do_extract_method(code, start, end, "baz") expected = dedent("""\ def foo(): x = [e for e in []] f = 23 baz(x) def baz(x): for x, f in x: def bar(): x[42] = 1 """) self.assertEqual(expected, refactored) def test_extract_method_with_list_comprehension_and_orelse(self): code = dedent("""\ def foo(): x = [e for e in []] f = 23 for e, f in []: def bar(): e[42] = 1 """) start, end = self._convert_line_range_to_offset(code, 4, 7) refactored = self.do_extract_method(code, start, end, "baz") expected = dedent("""\ def foo(): x = [e for e in []] f = 23 baz() def baz(): for e, f in []: def bar(): e[42] = 1 """) self.assertEqual(expected, refactored) def test_extract_function_with_for_else_statemant(self): code = dedent("""\ def a_func(): for i in range(10): a = i else: a = None """) start = code.index("for") end = len(code) - 1 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): new_func() def new_func(): for i in range(10): a = i else: a = None """) self.assertEqual(expected, refactored) def test_extract_function_with_for_else_statemant_more(self): """TODO: fixed code to test passed""" code = dedent("""\ def a_func(): for i in range(10): a = i else: for i in range(5): b = i else: b = None a = None """) start = code.index("for") end = len(code) - 1 refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def a_func(): new_func() def new_func(): for i in range(10): a = i else: for i in range(5): b = i else: b = None a = None """) self.assertEqual(expected, refactored) def test_extract_function_with_for_else_statemant_outside_loops(self): code = dedent("""\ def a_func(): for i in range(10): a = i else: a=None """) start = code.index("a = i") end = len(code) - 1 with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") def test_extract_function_with_inline_assignment_in_method(self): code = dedent("""\ def foo(): i = 1 i += 1 print(i) """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def foo(): i = 1 i = new_func(i) print(i) def new_func(i): i += 1 return i """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.8") def test_extract_function_statement_with_inline_assignment_in_condition(self): code = dedent("""\ def foo(a): if i := a == 5: i += 1 print(i) """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def foo(a): i = new_func(a) print(i) def new_func(a): if i := a == 5: i += 1 return i """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.8") def test_extract_function_expression_with_inline_assignment_in_condition(self): code = dedent("""\ def foo(a): if i := a == 5: i += 1 print(i) """) extract_target = "i := a == 5" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def foo(a): if i := new_func(a): i += 1 print(i) def new_func(a): return (i := a == 5) """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.8") def test_extract_function_expression_with_inline_assignment_complex(self): code = dedent("""\ def foo(a): if i := a == (c := 5): i += 1 c += 1 print(i) """) extract_target = "i := a == (c := 5)" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def foo(a): if i, c := new_func(a): i += 1 c += 1 print(i) def new_func(a): return (i := a == (c := 5)) """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.8") def test_extract_function_expression_with_inline_assignment_in_inner_expression( self, ): code = dedent("""\ def foo(a): if a == (c := 5): c += 1 print(i) """) extract_target = "a == (c := 5)" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) with self.assertRaisesRegexp( rope.base.exceptions.RefactoringError, "Extracted piece cannot contain named expression \\(:= operator\\).", ): self.do_extract_method(code, start, end, "new_func") def test_extract_exec(self): code = dedent("""\ exec("def f(): pass", {}) """) start, end = self._convert_line_range_to_offset(code, 1, 1) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def new_func(): exec("def f(): pass", {}) new_func() """) self.assertEqual(expected, refactored) @testutils.only_for_versions_lower("3") def test_extract_exec_statement(self): code = dedent("""\ exec "def f(): pass" in {} """) start, end = self._convert_line_range_to_offset(code, 1, 1) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def new_func(): exec "def f(): pass" in {} new_func() """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.5") def test_extract_async_function(self): code = dedent("""\ async def my_func(my_list): for x in my_list: var = x + 1 return var """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ async def my_func(my_list): for x in my_list: var = new_func(x) return var def new_func(x): var = x + 1 return var """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.5") def test_extract_inner_async_function(self): code = dedent("""\ def my_func(my_list): async def inner_func(my_list): for x in my_list: var = x + 1 return inner_func """) start, end = self._convert_line_range_to_offset(code, 2, 4) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def my_func(my_list): inner_func = new_func(my_list) return inner_func def new_func(my_list): async def inner_func(my_list): for x in my_list: var = x + 1 return inner_func """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.5") def test_extract_around_inner_async_function(self): code = dedent("""\ def my_func(lst): async def inner_func(obj): for x in obj: var = x + 1 return map(inner_func, lst) """) start, end = self._convert_line_range_to_offset(code, 5, 5) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ def my_func(lst): async def inner_func(obj): for x in obj: var = x + 1 return new_func(inner_func, lst) def new_func(inner_func, lst): return map(inner_func, lst) """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.5") def test_extract_refactor_around_async_for_loop(self): code = dedent("""\ async def my_func(my_list): async for x in my_list: var = x + 1 return var """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ async def my_func(my_list): async for x in my_list: var = new_func(x) return var def new_func(x): var = x + 1 return var """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.5") @testutils.only_for_versions_lower("3.8") def test_extract_refactor_containing_async_for_loop_should_error_before_py38(self): """ Refactoring async/await syntaxes is only supported in Python 3.8 and higher because support for ast.PyCF_ALLOW_TOP_LEVEL_AWAIT was only added to the standard library in Python 3.8. """ code = dedent("""\ async def my_func(my_list): async for x in my_list: var = x + 1 return var """) start, end = self._convert_line_range_to_offset(code, 2, 3) with self.assertRaisesRegexp( rope.base.exceptions.RefactoringError, "Extracted piece can only have async/await statements if Rope is running on Python 3.8 or higher", ): self.do_extract_method(code, start, end, "new_func") @testutils.only_for_versions_higher("3.8") def test_extract_refactor_containing_async_for_loop_is_supported_after_py38(self): code = dedent("""\ async def my_func(my_list): async for x in my_list: var = x + 1 return var """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ async def my_func(my_list): var = new_func(my_list) return var def new_func(my_list): async for x in my_list: var = x + 1 return var """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.5") def test_extract_await_expression(self): code = dedent("""\ async def my_func(my_list): for url in my_list: resp = await request(url) return resp """) selected = "request(url)" start, end = code.index(selected), code.index(selected) + len(selected) refactored = self.do_extract_method(code, start, end, "new_func") expected = dedent("""\ async def my_func(my_list): for url in my_list: resp = await new_func(url) return resp def new_func(url): return request(url) """) self.assertEqual(expected, refactored) def test_extract_to_staticmethod(self): code = dedent("""\ class A: def first_method(self): a_var = 1 b_var = a_var + 1 """) extract_target = "a_var + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method( code, start, end, "second_method", kind="staticmethod" ) expected = dedent("""\ class A: def first_method(self): a_var = 1 b_var = A.second_method(a_var) @staticmethod def second_method(a_var): return a_var + 1 """) self.assertEqual(expected, refactored) def test_extract_to_staticmethod_when_self_in_body(self): code = dedent("""\ class A: def first_method(self): a_var = 1 b_var = self.a_var + 1 """) extract_target = "self.a_var + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method( code, start, end, "second_method", kind="staticmethod" ) expected = dedent("""\ class A: def first_method(self): a_var = 1 b_var = A.second_method(self) @staticmethod def second_method(self): return self.a_var + 1 """) self.assertEqual(expected, refactored) def test_extract_from_function_to_staticmethod_raises_exception(self): code = dedent("""\ def first_method(): a_var = 1 b_var = a_var + 1 """) extract_target = "a_var + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) with self.assertRaisesRegexp( rope.base.exceptions.RefactoringError, "Cannot extract to staticmethod/classmethod outside class", ): self.do_extract_method( code, start, end, "second_method", kind="staticmethod" ) def test_extract_method_in_classmethods(self): code = dedent("""\ class AClass(object): @classmethod def func2(cls): b = 1 """) start = code.index(" 1") + 1 refactored = self.do_extract_method(code, start, start + 1, "one", similar=True) expected = dedent("""\ class AClass(object): @classmethod def func2(cls): b = AClass.one() @classmethod def one(cls): return 1 """) self.assertEqual(expected, refactored) def test_extract_from_function_to_classmethod_raises_exception(self): code = dedent("""\ def first_method(): a_var = 1 b_var = a_var + 1 """) extract_target = "a_var + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) with self.assertRaisesRegexp( rope.base.exceptions.RefactoringError, "Cannot extract to staticmethod/classmethod outside class", ): self.do_extract_method( code, start, end, "second_method", kind="classmethod" ) def test_extract_to_classmethod_when_self_in_body(self): code = dedent("""\ class A: def first_method(self): a_var = 1 b_var = self.a_var + 1 """) extract_target = "self.a_var + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method( code, start, end, "second_method", kind="classmethod" ) expected = dedent("""\ class A: def first_method(self): a_var = 1 b_var = A.second_method(self) @classmethod def second_method(cls, self): return self.a_var + 1 """) self.assertEqual(expected, refactored) def test_extract_to_classmethod(self): code = dedent("""\ class A: def first_method(self): a_var = 1 b_var = a_var + 1 """) extract_target = "a_var + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method( code, start, end, "second_method", kind="classmethod" ) expected = dedent("""\ class A: def first_method(self): a_var = 1 b_var = A.second_method(a_var) @classmethod def second_method(cls, a_var): return a_var + 1 """) self.assertEqual(expected, refactored) def test_extract_to_classmethod_when_name_starts_with_at_sign(self): code = dedent("""\ class A: def first_method(self): a_var = 1 b_var = a_var + 1 """) extract_target = "a_var + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "@second_method") expected = dedent("""\ class A: def first_method(self): a_var = 1 b_var = A.second_method(a_var) @classmethod def second_method(cls, a_var): return a_var + 1 """) self.assertEqual(expected, refactored) def test_extract_to_staticmethod_when_name_starts_with_dollar_sign(self): code = dedent("""\ class A: def first_method(self): a_var = 1 b_var = a_var + 1 """) extract_target = "a_var + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "$second_method") expected = dedent("""\ class A: def first_method(self): a_var = 1 b_var = A.second_method(a_var) @staticmethod def second_method(a_var): return a_var + 1 """) self.assertEqual(expected, refactored) def test_raises_exception_when_sign_in_name_and_kind_mismatch(self): with self.assertRaisesRegexp( rope.base.exceptions.RefactoringError, "Kind and shortcut in name mismatch" ): self.do_extract_method("code", 0, 1, "$second_method", kind="classmethod") def test_extracting_from_static_with_function_arg(self): code = dedent("""\ class A: @staticmethod def first_method(someargs): b_var = someargs + 1 """) extract_target = "someargs + 1" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "second_method") expected = dedent("""\ class A: @staticmethod def first_method(someargs): b_var = A.second_method(someargs) @staticmethod def second_method(someargs): return someargs + 1 """) self.assertEqual(expected, refactored) def test_extract_with_list_comprehension(self): code = dedent("""\ def f(): y = [1,2,3,4] a = sum([x for x in y]) b = sum([x for x in y]) print(a, b) f() """) extract_target = " a = sum([x for x in y])\n" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "_a") expected = dedent("""\ def f(): y = [1,2,3,4] a = _a(y) b = sum([x for x in y]) print(a, b) def _a(y): a = sum([x for x in y]) return a f() """) self.assertEqual(expected, refactored) def test_extract_with_generator(self): code = dedent("""\ def f(): y = [1,2,3,4] a = sum(x for x in y) b = sum(x for x in y) print(a, b) f() """) extract_target = " a = sum(x for x in y)\n" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "_a") expected = dedent("""\ def f(): y = [1,2,3,4] a = _a(y) b = sum(x for x in y) print(a, b) def _a(y): a = sum(x for x in y) return a f() """) self.assertEqual(expected, refactored) def test_extract_with_set_comprehension(self): code = dedent("""\ def f(): y = [1,2,3,4] a = sum({x for x in y}) b = sum({x for x in y}) print(a, b) f() """) extract_target = " a = sum({x for x in y})\n" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "_a") expected = dedent("""\ def f(): y = [1,2,3,4] a = _a(y) b = sum({x for x in y}) print(a, b) def _a(y): a = sum({x for x in y}) return a f() """) self.assertEqual(expected, refactored) def test_extract_with_dict_comprehension(self): code = dedent("""\ def f(): y = [1,2,3,4] a = sum({x: x for x in y}) b = sum({x: x for x in y}) print(a, b) f() """) extract_target = " a = sum({x: x for x in y})\n" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "_a") expected = dedent("""\ def f(): y = [1,2,3,4] a = _a(y) b = sum({x: x for x in y}) print(a, b) def _a(y): a = sum({x: x for x in y}) return a f() """) self.assertEqual(expected, refactored) def test_extract_function_expression_with_assignment_to_attribute(self): code = dedent("""\ class A(object): def func(self): self.var_a = 1 var_bb = self.var_a """) extract_target = "= self.var_a" start, end = ( code.index(extract_target) + 2, code.index(extract_target) + 2 + len(extract_target) - 2, ) refactored = self.do_extract_method(code, start, end, "new_func", similar=True) expected = dedent("""\ class A(object): def func(self): self.var_a = 1 var_bb = self.new_func() def new_func(self): return self.var_a """) self.assertEqual(expected, refactored) def test_extract_function_expression_with_assignment_index(self): code = dedent("""\ class A(object): def func(self, val): self[val] = 1 var_bb = self[val] """) extract_target = "= self[val]" start, end = ( code.index(extract_target) + 2, code.index(extract_target) + 2 + len(extract_target) - 2, ) refactored = self.do_extract_method(code, start, end, "new_func", similar=True) expected = dedent("""\ class A(object): def func(self, val): self[val] = 1 var_bb = self.new_func(val) def new_func(self, val): return self[val] """) self.assertEqual(expected, refactored) def test_extraction_method_with_global_variable(self): code = dedent("""\ g = None def f(): global g g = 2 f() print(g) """) extract_target = "g = 2" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "_g") expected = dedent("""\ g = None def f(): global g _g() def _g(): global g g = 2 f() print(g) """) self.assertEqual(expected, refactored) def test_extraction_method_with_global_variable_and_global_declaration(self): code = dedent("""\ g = None def f(): global g g = 2 f() print(g) """) start, end = 23, 42 refactored = self.do_extract_method(code, start, end, "_g") expected = dedent("""\ g = None def f(): _g() def _g(): global g g = 2 f() print(g) """) self.assertEqual(expected, refactored) def test_extraction_one_line_with_global_variable_read_only(self): code = dedent("""\ g = None def f(): global g a = g f() print(g) """) extract_target = "= g" start, end = code.index(extract_target) + 2, code.index(extract_target) + 3 refactored = self.do_extract_method(code, start, end, "_g") expected = dedent("""\ g = None def f(): global g a = _g() def _g(): return g f() print(g) """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.8") def test_extraction_one_line_with_global_variable(self): code = dedent("""\ g = None def f(): global g while g := 4: pass f() print(g) """) extract_target = "g := 4" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "_g") expected = dedent("""\ g = None def f(): global g while _g(): pass def _g(): global g return (g := 4) f() print(g) """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.8") def test_extraction_one_line_with_global_variable_has_postread(self): code = dedent("""\ g = None def f(): global g while g := 4: print(g) f() print(g) """) extract_target = "g := 4" start, end = code.index(extract_target), code.index(extract_target) + len( extract_target ) refactored = self.do_extract_method(code, start, end, "_g") expected = dedent("""\ g = None def f(): global g while g := _g(): print(g) def _g(): global g return (g := 4) f() print(g) """) self.assertEqual(expected, refactored) def test_extract_method_with_nested_double_with_as(self): code = dedent("""\ with open("test") as file1: with open("test") as file2: print(file1, file2) """) start, end = self._convert_line_range_to_offset(code, 3, 4) refactored = self.do_extract_method(code, start, end, "extracted", global_=True) expected = dedent("""\ def extracted(file1, file2): print(file1, file2) with open("test") as file1: with open("test") as file2: extracted(file1, file2) """) self.assertEqual(expected, refactored) def test_extract_method_with_double_with_as(self): code = dedent("""\ with open("test") as file1, open("test") as file2: print(file1, file2) """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, "extracted", global_=True) expected = dedent("""\ def extracted(file1, file2): print(file1, file2) with open("test") as file1, open("test") as file2: extracted(file1, file2) """) self.assertEqual(expected, refactored) def test_extract_method_with_nested_double_with_as_and_misleading_comment(self): code = dedent("""\ with open("test") as file1, open("test") as file2: # with in comment bar() """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, "extracted", global_=True) expected = dedent("""\ def extracted(): bar() with open("test") as file1, open("test") as file2: # with in comment extracted() """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher('3.8') def test_extract_method_async_with_simple(self): code = dedent("""\ async def afunc(): async with open("test") as file1: print(file1) """) start, end = self._convert_line_range_to_offset(code, 2, 3) refactored = self.do_extract_method(code, start, end, 'extracted', global_=True) expected = dedent("""\ async def afunc(): extracted() def extracted(): async with open("test") as file1: print(file1) """) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher('3.8') def test_extract_method_containing_async_with(self): code = dedent("""\ async def afunc(): async with open("test") as file1, open("test") as file2: print(file1, file2) """) start, end = self._convert_line_range_to_offset(code, 3, 3) refactored = self.do_extract_method(code, start, end, 'extracted', global_=True) expected = dedent("""\ async def afunc(): async with open("test") as file1, open("test") as file2: extracted(file1, file2) def extracted(file1, file2): print(file1, file2) """) self.assertEqual(expected, refactored) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/importutilstest.py0000664000175000017500000021004000000000000021747 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest from textwrap import dedent from rope.refactor.importutils import ImportTools, importinfo, add_import from ropetest import testutils class ImportUtilsTest(unittest.TestCase): def setUp(self): super(ImportUtilsTest, self).setUp() self.project = testutils.sample_project() self.import_tools = ImportTools(self.project) self.mod = testutils.create_module(self.project, "mod") self.pkg1 = testutils.create_package(self.project, "pkg1") self.mod1 = testutils.create_module(self.project, "mod1", self.pkg1) self.pkg2 = testutils.create_package(self.project, "pkg2") self.mod2 = testutils.create_module(self.project, "mod2", self.pkg2) self.mod3 = testutils.create_module(self.project, "mod3", self.pkg2) p1 = testutils.create_package(self.project, "p1") p2 = testutils.create_package(self.project, "p2", p1) p3 = testutils.create_package(self.project, "p3", p2) m1 = testutils.create_module(self.project, "m1", p3) # noqa l = testutils.create_module(self.project, "l", p3) # noqa def tearDown(self): testutils.remove_project(self.project) super(ImportUtilsTest, self).tearDown() def test_get_import_for_module(self): mod = self.project.find_module("mod") import_statement = self.import_tools.get_import(mod) self.assertEqual("import mod", import_statement.get_import_statement()) def test_get_import_for_module_in_nested_modules(self): mod = self.project.find_module("pkg1.mod1") import_statement = self.import_tools.get_import(mod) self.assertEqual("import pkg1.mod1", import_statement.get_import_statement()) def test_get_import_for_module_in_init_dot_py(self): init_dot_py = self.pkg1.get_child("__init__.py") import_statement = self.import_tools.get_import(init_dot_py) self.assertEqual("import pkg1", import_statement.get_import_statement()) def test_get_from_import_for_module(self): mod = self.project.find_module("mod") import_statement = self.import_tools.get_from_import(mod, "a_func") self.assertEqual( "from mod import a_func", import_statement.get_import_statement() ) def test_get_from_import_for_module_in_nested_modules(self): mod = self.project.find_module("pkg1.mod1") import_statement = self.import_tools.get_from_import(mod, "a_func") self.assertEqual( "from pkg1.mod1 import a_func", import_statement.get_import_statement() ) def test_get_from_import_for_module_in_init_dot_py(self): init_dot_py = self.pkg1.get_child("__init__.py") import_statement = self.import_tools.get_from_import(init_dot_py, "a_func") self.assertEqual( "from pkg1 import a_func", import_statement.get_import_statement() ) def test_get_import_statements(self): self.mod.write("import pkg1\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports self.assertEqual("import pkg1", imports[0].import_info.get_import_statement()) def test_get_import_statements_with_alias(self): self.mod.write("import pkg1.mod1 as mod1\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports self.assertEqual( "import pkg1.mod1 as mod1", imports[0].import_info.get_import_statement() ) def test_get_import_statements_for_froms(self): self.mod.write("from pkg1 import mod1\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports self.assertEqual( "from pkg1 import mod1", imports[0].import_info.get_import_statement() ) def test_get_multi_line_import_statements_for_froms(self): self.mod.write("from pkg1 \\\n import mod1\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports self.assertEqual( "from pkg1 import mod1", imports[0].import_info.get_import_statement() ) def test_get_import_statements_for_from_star(self): self.mod.write("from pkg1 import *\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports self.assertEqual( "from pkg1 import *", imports[0].import_info.get_import_statement() ) @testutils.only_for("2.5") def test_get_import_statements_for_new_relatives(self): self.mod2.write("from .mod3 import x\n") pymod = self.project.get_module("pkg2.mod2") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports self.assertEqual( "from .mod3 import x", imports[0].import_info.get_import_statement() ) def test_ignoring_indented_imports(self): self.mod.write( dedent("""\ if True: import pkg1 """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports self.assertEqual(0, len(imports)) def test_import_get_names(self): self.mod.write("import pkg1 as pkg\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports context = importinfo.ImportContext(self.project, self.project.root) self.assertEqual(["pkg"], imports[0].import_info.get_imported_names(context)) def test_import_get_names_with_alias(self): self.mod.write("import pkg1.mod1\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports context = importinfo.ImportContext(self.project, self.project.root) self.assertEqual(["pkg1"], imports[0].import_info.get_imported_names(context)) def test_import_get_names_with_alias2(self): self.mod1.write( dedent("""\ def a_func(): pass """) ) self.mod.write("from pkg1.mod1 import *\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.imports context = importinfo.ImportContext(self.project, self.project.root) self.assertEqual(["a_func"], imports[0].import_info.get_imported_names(context)) def test_empty_getting_used_imports(self): self.mod.write("") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.get_used_imports(pymod) self.assertEqual(0, len(imports)) def test_empty_getting_used_imports2(self): self.mod.write("import pkg\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.get_used_imports(pymod) self.assertEqual(0, len(imports)) def test_simple_getting_used_imports(self): self.mod.write("import pkg\nprint(pkg)\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.get_used_imports(pymod) self.assertEqual(1, len(imports)) self.assertEqual("import pkg", imports[0].get_import_statement()) def test_simple_getting_used_imports2(self): self.mod.write( dedent("""\ import pkg def a_func(): print(pkg) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.get_used_imports(pymod) self.assertEqual(1, len(imports)) self.assertEqual("import pkg", imports[0].get_import_statement()) def test_getting_used_imports_for_nested_scopes(self): self.mod.write( dedent("""\ import pkg1 print(pkg1) def a_func(): pass print(pkg1) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.get_used_imports(pymod["a_func"].get_object()) self.assertEqual(0, len(imports)) def test_getting_used_imports_for_nested_scopes2(self): self.mod.write( dedent("""\ from pkg1 import mod1 def a_func(): print(mod1) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.get_used_imports(pymod["a_func"].get_object()) self.assertEqual(1, len(imports)) self.assertEqual("from pkg1 import mod1", imports[0].get_import_statement()) def test_empty_removing_unused_imports(self): self.mod.write("import pkg1\nprint(pkg1)\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( "import pkg1\nprint(pkg1)\n", module_with_imports.get_changed_source() ) def test_simple_removing_unused_imports(self): self.mod.write("import pkg1\n\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual("", module_with_imports.get_changed_source()) def test_simple_removing_unused_imports_for_froms(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import a_func, another_func a_func() """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ from pkg1.mod1 import a_func a_func() """), module_with_imports.get_changed_source(), ) def test_simple_removing_unused_imports_for_from_stars(self): self.mod.write( dedent("""\ from pkg1.mod1 import * """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual("", module_with_imports.get_changed_source()) def test_simple_removing_unused_imports_for_nested_modules(self): self.mod1.write( dedent("""\ def a_func(): pass """) ) self.mod.write( dedent("""\ import pkg1.mod1 pkg1.mod1.a_func()""" ) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( "import pkg1.mod1\npkg1.mod1.a_func()", module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_functions_of_the_same_name(self): self.mod.write( dedent("""\ def a_func(): pass def a_func(): pass """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ def a_func(): pass def a_func(): pass """), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_for_from_import_with_as(self): self.mod.write("a_var = 1\n") self.mod1.write( dedent("""\ from mod import a_var as myvar a_var = myvar """) ) pymod = self.project.get_pymodule(self.mod1) module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ from mod import a_var as myvar a_var = myvar """), module_with_imports.get_changed_source(), ) def test_not_removing_imports_that_conflict_with_class_names(self): code = dedent("""\ import pkg1 class A(object): pkg1 = 0 def f(self): a_var = pkg1 """) self.mod.write(code) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual(code, module_with_imports.get_changed_source()) def test_adding_imports(self): self.mod.write("\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) new_import = self.import_tools.get_import(self.mod1) module_with_imports.add_import(new_import) self.assertEqual("import pkg1.mod1\n", module_with_imports.get_changed_source()) def test_adding_imports_no_pull_to_top(self): self.mod.write( dedent("""\ import pkg2.mod3 class A(object): pass import pkg2.mod2 """) ) pymod = self.project.get_module("mod") self.project.prefs["pull_imports_to_top"] = False module_with_imports = self.import_tools.module_imports(pymod) new_import = self.import_tools.get_import(self.mod1) module_with_imports.add_import(new_import) self.assertEqual( dedent("""\ import pkg2.mod3 class A(object): pass import pkg2.mod2 import pkg1.mod1 """), module_with_imports.get_changed_source(), ) def test_adding_from_imports(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write("from pkg1.mod1 import a_func\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) new_import = self.import_tools.get_from_import(self.mod1, "another_func") module_with_imports.add_import(new_import) self.assertEqual( "from pkg1.mod1 import a_func, another_func\n", module_with_imports.get_changed_source(), ) def test_adding_to_star_imports(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write("from pkg1.mod1 import *\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) new_import = self.import_tools.get_from_import(self.mod1, "another_func") module_with_imports.add_import(new_import) self.assertEqual( "from pkg1.mod1 import *\n", module_with_imports.get_changed_source() ) def test_adding_star_imports(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write("from pkg1.mod1 import a_func\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) new_import = self.import_tools.get_from_import(self.mod1, "*") module_with_imports.add_import(new_import) self.assertEqual( "from pkg1.mod1 import *\n", module_with_imports.get_changed_source() ) def test_adding_imports_and_preserving_spaces_after_imports(self): self.mod.write( dedent("""\ import pkg1 print(pkg1) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) new_import = self.import_tools.get_import(self.pkg2) module_with_imports.add_import(new_import) self.assertEqual( dedent("""\ import pkg1 import pkg2 print(pkg1) """), module_with_imports.get_changed_source(), ) def test_not_changing_the_format_of_unchanged_imports(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import (a_func, another_func) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) self.assertEqual( dedent("""\ from pkg1.mod1 import (a_func, another_func) """), module_with_imports.get_changed_source(), ) def test_not_changing_the_format_of_unchanged_imports2(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import (a_func) a_func() """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ from pkg1.mod1 import (a_func) a_func() """), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_reoccuring_names(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import * from pkg1.mod1 import a_func a_func() """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ from pkg1.mod1 import * a_func() """), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_reoccuring_names2(self): self.mod.write( dedent("""\ import pkg2.mod2 import pkg2.mod3 print(pkg2.mod2, pkg2.mod3)""" ) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ import pkg2.mod2 import pkg2.mod3 print(pkg2.mod2, pkg2.mod3)""" ), module_with_imports.get_changed_source() ) def test_removing_unused_imports_and_common_packages(self): self.mod.write( dedent("""\ import pkg1.mod1 import pkg1 print(pkg1, pkg1.mod1) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ import pkg1.mod1 print(pkg1, pkg1.mod1) """), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_common_packages_reversed(self): self.mod.write( dedent("""\ import pkg1 import pkg1.mod1 print(pkg1, pkg1.mod1) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_duplicates() self.assertEqual( dedent("""\ import pkg1.mod1 print(pkg1, pkg1.mod1) """), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_common_packages2(self): self.mod.write( dedent("""\ import pkg1.mod1 import pkg1.mod2 print(pkg1) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ import pkg1.mod1 print(pkg1) """), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_froms(self): self.mod1.write( dedent("""\ def func1(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import func1 """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual("", module_with_imports.get_changed_source()) def test_removing_unused_imports_and_froms2(self): self.mod1.write( dedent("""\ def func1(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import func1 func1()""" ) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ from pkg1.mod1 import func1 func1()""" ), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_froms3(self): self.mod1.write( dedent("""\ def func1(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import func1 def a_func(): func1() """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ from pkg1.mod1 import func1 def a_func(): func1() """), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_froms4(self): self.mod1.write( dedent("""\ def func1(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import func1 class A(object): def a_func(self): func1() """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ from pkg1.mod1 import func1 class A(object): def a_func(self): func1() """), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_and_getting_attributes(self): self.mod1.write( dedent("""\ class A(object): def f(self): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import A var = A().f()""" ) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ from pkg1.mod1 import A var = A().f()""" ), module_with_imports.get_changed_source(), ) def test_removing_unused_imports_function_parameters(self): self.mod1.write( dedent("""\ def func1(): pass """) ) self.mod.write( dedent("""\ import pkg1 def a_func(pkg1): my_var = pkg1 """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ def a_func(pkg1): my_var = pkg1 """), module_with_imports.get_changed_source(), ) def test_trivial_expanding_star_imports(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write("from pkg1.mod1 import *\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.expand_stars() self.assertEqual("", module_with_imports.get_changed_source()) def test_expanding_star_imports(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write("from pkg1.mod1 import *\na_func()\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.expand_stars() self.assertEqual( "from pkg1.mod1 import a_func\na_func()\n", module_with_imports.get_changed_source(), ) def test_removing_duplicate_imports(self): self.mod.write("import pkg1\nimport pkg1\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_duplicates() self.assertEqual("import pkg1\n", module_with_imports.get_changed_source()) def test_removing_duplicates_and_reoccuring_names(self): self.mod.write("import pkg2.mod2\nimport pkg2.mod3\n") pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_duplicates() self.assertEqual( "import pkg2.mod2\nimport pkg2.mod3\n", module_with_imports.get_changed_source(), ) def test_removing_duplicate_imports_for_froms(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write( dedent("""\ from pkg1 import a_func from pkg1 import a_func, another_func """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_duplicates() self.assertEqual( "from pkg1 import a_func, another_func\n", module_with_imports.get_changed_source(), ) def test_transforming_froms_to_normal_changing_imports(self): self.mod1.write( dedent("""\ def a_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import a_func print(a_func) """) ) pymod = self.project.get_module("mod") changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual( dedent("""\ import pkg1.mod1 print(pkg1.mod1.a_func) """), changed_module, ) def test_transforming_froms_to_normal_changing_occurrences(self): self.mod1.write("def a_func():\n pass\n") self.mod.write("from pkg1.mod1 import a_func\na_func()") pymod = self.project.get_module("mod") changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual("import pkg1.mod1\npkg1.mod1.a_func()", changed_module) def test_transforming_froms_to_normal_for_multi_imports(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import * a_func() another_func() """) ) pymod = self.project.get_module("mod") changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual( dedent("""\ import pkg1.mod1 pkg1.mod1.a_func() pkg1.mod1.another_func() """), changed_module, ) def test_transform_froms_to_norm_for_multi_imports_inside_parens(self): self.mod1.write( dedent("""\ def a_func(): pass def another_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import (a_func, another_func) a_func() another_func() """) ) pymod = self.project.get_module("mod") changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual( dedent("""\ import pkg1.mod1 pkg1.mod1.a_func() pkg1.mod1.another_func() """), changed_module, ) def test_transforming_froms_to_normal_from_stars(self): self.mod1.write( dedent("""\ def a_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import * a_func() """) ) pymod = self.project.get_module("mod") changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual( dedent("""\ import pkg1.mod1 pkg1.mod1.a_func() """), changed_module, ) def test_transforming_froms_to_normal_from_stars2(self): self.mod1.write("a_var = 10") self.mod.write( dedent("""\ import pkg1.mod1 from pkg1.mod1 import a_var def a_func(): print(pkg1.mod1, a_var) """) ) pymod = self.project.get_module("mod") changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual( dedent("""\ import pkg1.mod1 def a_func(): print(pkg1.mod1, pkg1.mod1.a_var) """), changed_module, ) def test_transforming_froms_to_normal_from_with_alias(self): self.mod1.write( dedent("""\ def a_func(): pass """) ) self.mod.write( dedent("""\ from pkg1.mod1 import a_func as another_func another_func() """) ) pymod = self.project.get_module("mod") changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual( dedent("""\ import pkg1.mod1 pkg1.mod1.a_func() """), changed_module, ) def test_transforming_froms_to_normal_for_relatives(self): self.mod2.write( dedent("""\ def a_func(): pass """) ) self.mod3.write( dedent("""\ from mod2 import * a_func() """) ) pymod = self.project.get_pymodule(self.mod3) changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual( dedent("""\ import pkg2.mod2 pkg2.mod2.a_func() """), changed_module, ) def test_transforming_froms_to_normal_for_os_path(self): self.mod.write("from os import path\npath.exists('.')\n") pymod = self.project.get_pymodule(self.mod) changed_module = self.import_tools.froms_to_imports(pymod) self.assertEqual("import os\nos.path.exists('.')\n", changed_module) def test_transform_relatives_imports_to_abs_imports_doing_nothing(self): self.mod2.write("from pkg1 import mod1\nimport mod1\n") pymod = self.project.get_pymodule(self.mod2) self.assertEqual( "from pkg1 import mod1\nimport mod1\n", self.import_tools.relatives_to_absolutes(pymod), ) def test_transform_relatives_to_absolute_imports_for_normal_imports(self): self.mod2.write("import mod3\n") pymod = self.project.get_pymodule(self.mod2) self.assertEqual( "import pkg2.mod3\n", self.import_tools.relatives_to_absolutes(pymod) ) def test_transform_relatives_imports_to_absolute_imports_for_froms(self): self.mod3.write( dedent("""\ def a_func(): pass """) ) self.mod2.write("from mod3 import a_func\n") pymod = self.project.get_pymodule(self.mod2) self.assertEqual( "from pkg2.mod3 import a_func\n", self.import_tools.relatives_to_absolutes(pymod), ) @testutils.only_for("2.5") def test_transform_rel_imports_to_abs_imports_for_new_relatives(self): self.mod3.write( dedent("""\ def a_func(): pass """) ) self.mod2.write("from .mod3 import a_func\n") pymod = self.project.get_pymodule(self.mod2) self.assertEqual( "from pkg2.mod3 import a_func\n", self.import_tools.relatives_to_absolutes(pymod), ) def test_transform_relatives_to_absolute_imports_for_normal_imports2(self): self.mod2.write("import mod3\nprint(mod3)") pymod = self.project.get_pymodule(self.mod2) self.assertEqual( "import pkg2.mod3\nprint(pkg2.mod3)", self.import_tools.relatives_to_absolutes(pymod), ) def test_transform_relatives_to_absolute_imports_for_aliases(self): self.mod2.write("import mod3 as mod3\nprint(mod3)") pymod = self.project.get_pymodule(self.mod2) self.assertEqual( "import pkg2.mod3 as mod3\nprint(mod3)", self.import_tools.relatives_to_absolutes(pymod), ) def test_organizing_imports(self): self.mod1.write("import mod1\n") pymod = self.project.get_pymodule(self.mod1) self.assertEqual("", self.import_tools.organize_imports(pymod)) def test_organizing_imports_without_deduplication(self): contents = dedent("""\ from pkg2 import mod2 from pkg2 import mod3 """) self.mod.write(contents) pymod = self.project.get_pymodule(self.mod) self.project.prefs["split_imports"] = True self.assertEqual( contents, self.import_tools.organize_imports(pymod, unused=False) ) def test_splitting_imports(self): self.mod.write( dedent("""\ from pkg1 import mod1 from pkg2 import mod2, mod3 """) ) pymod = self.project.get_pymodule(self.mod) self.project.prefs["split_imports"] = True self.assertEqual( dedent("""\ from pkg1 import mod1 from pkg2 import mod2 from pkg2 import mod3 """), self.import_tools.organize_imports(pymod, unused=False), ) def test_splitting_imports_no_pull_to_top(self): self.mod.write( dedent("""\ from pkg2 import mod3, mod4 from pkg1 import mod2 from pkg1 import mod1 """) ) pymod = self.project.get_pymodule(self.mod) self.project.prefs["split_imports"] = True self.project.prefs["pull_imports_to_top"] = False self.assertEqual( dedent("""\ from pkg1 import mod2 from pkg1 import mod1 from pkg2 import mod3 from pkg2 import mod4 """), self.import_tools.organize_imports(pymod, sort=False, unused=False), ) def test_splitting_imports_with_filter(self): self.mod.write( dedent("""\ from pkg1 import mod1, mod2 from pkg2 import mod3, mod4 """) ) pymod = self.project.get_pymodule(self.mod) self.project.prefs["split_imports"] = True def import_filter(stmt): return stmt.import_info.module_name == "pkg1" self.assertEqual( dedent("""\ from pkg1 import mod1 from pkg1 import mod2 from pkg2 import mod3, mod4 """), self.import_tools.organize_imports( pymod, unused=False, import_filter=import_filter ), ) def test_splitting_duplicate_imports(self): self.mod.write( dedent("""\ from pkg2 import mod1 from pkg2 import mod1, mod2 """) ) pymod = self.project.get_pymodule(self.mod) self.project.prefs["split_imports"] = True self.assertEqual( dedent("""\ from pkg2 import mod1 from pkg2 import mod2 """), self.import_tools.organize_imports(pymod, unused=False), ) def test_splitting_duplicate_imports2(self): self.mod.write( dedent("""\ from pkg2 import mod1, mod3 from pkg2 import mod1, mod2 from pkg2 import mod2, mod3 """) ) pymod = self.project.get_pymodule(self.mod) self.project.prefs["split_imports"] = True self.assertEqual( dedent("""\ from pkg2 import mod1 from pkg2 import mod2 from pkg2 import mod3 """), self.import_tools.organize_imports(pymod, unused=False), ) def test_removing_self_imports(self): self.mod.write( dedent("""\ import mod mod.a_var = 1 print(mod.a_var) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ a_var = 1 print(a_var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports2(self): self.mod1.write( dedent("""\ import pkg1.mod1 pkg1.mod1.a_var = 1 print(pkg1.mod1.a_var) """) ) pymod = self.project.get_pymodule(self.mod1) self.assertEqual( dedent("""\ a_var = 1 print(a_var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_with_as(self): self.mod.write( dedent("""\ import mod as mymod mymod.a_var = 1 print(mymod.a_var) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ a_var = 1 print(a_var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_for_froms(self): self.mod1.write( dedent("""\ from pkg1 import mod1 mod1.a_var = 1 print(mod1.a_var) """) ) pymod = self.project.get_pymodule(self.mod1) self.assertEqual( dedent("""\ a_var = 1 print(a_var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_for_froms_with_as(self): self.mod1.write( dedent("""\ from pkg1 import mod1 as mymod mymod.a_var = 1 print(mymod.a_var) """) ) pymod = self.project.get_pymodule(self.mod1) self.assertEqual( dedent("""\ a_var = 1 print(a_var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_for_froms2(self): self.mod.write( dedent("""\ from mod import a_var a_var = 1 print(a_var) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ a_var = 1 print(a_var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_for_froms3(self): self.mod.write( dedent("""\ from mod import a_var a_var = 1 print(a_var) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ a_var = 1 print(a_var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_for_froms4(self): self.mod.write( dedent("""\ from mod import a_var as myvar a_var = 1 print(myvar) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ a_var = 1 print(a_var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_with_no_dot_after_mod(self): self.mod.write( dedent("""\ import mod print(mod) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import mod print(mod) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_with_no_dot_after_mod2(self): self.mod.write( dedent("""\ import mod a_var = 1 print(mod\\ \\ .var) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ a_var = 1 print(var) """), self.import_tools.organize_imports(pymod), ) def test_removing_self_imports_for_from_import_star(self): self.mod.write( dedent("""\ from mod import * a_var = 1 print(myvar) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ a_var = 1 print(myvar) """), self.import_tools.organize_imports(pymod), ) def test_not_removing_future_imports(self): self.mod.write("from __future__ import division\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( "from __future__ import division\n", self.import_tools.organize_imports(pymod), ) def test_sorting_empty_imports(self): self.mod.write("") pymod = self.project.get_pymodule(self.mod) self.assertEqual("", self.import_tools.sort_imports(pymod)) def test_sorting_one_import(self): self.mod.write("import pkg1.mod1\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual("import pkg1.mod1\n", self.import_tools.sort_imports(pymod)) def test_sorting_imports_alphabetically(self): self.mod.write("import pkg2.mod2\nimport pkg1.mod1\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( "import pkg1.mod1\nimport pkg2.mod2\n", self.import_tools.sort_imports(pymod), ) def test_sorting_imports_purely_alphabetically(self): self.mod.write( dedent("""\ from pkg2 import mod3 as mod0 import pkg2.mod2 import pkg1.mod1 """) ) pymod = self.project.get_pymodule(self.mod) self.project.prefs["sort_imports_alphabetically"] = True self.assertEqual( dedent("""\ import pkg1.mod1 import pkg2.mod2 from pkg2 import mod3 as mod0 """), self.import_tools.sort_imports(pymod), ) def test_sorting_imports_and_froms(self): self.mod.write("import pkg2.mod2\nfrom pkg1 import mod1\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( "import pkg2.mod2\nfrom pkg1 import mod1\n", self.import_tools.sort_imports(pymod), ) def test_sorting_imports_and_standard_modules(self): self.mod.write( dedent("""\ import pkg1 import sys """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import sys import pkg1 """), self.import_tools.sort_imports(pymod), ) def test_sorting_imports_and_standard_modules2(self): self.mod.write( dedent("""\ import sys import time """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import sys import time """), self.import_tools.sort_imports(pymod), ) def test_sorting_only_standard_modules(self): self.mod.write("import sys\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual("import sys\n", self.import_tools.sort_imports(pymod)) def test_sorting_third_party(self): self.mod.write( dedent("""\ import pkg1 import a_third_party """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import a_third_party import pkg1 """), self.import_tools.sort_imports(pymod), ) def test_sorting_only_third_parties(self): self.mod.write( dedent("""\ import a_third_party a_var = 1 """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import a_third_party a_var = 1 """), self.import_tools.sort_imports(pymod), ) def test_simple_handling_long_imports(self): self.mod.write( dedent("""\ import pkg1.mod1 m = pkg1.mod1 """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import pkg1.mod1 m = pkg1.mod1 """), self.import_tools.handle_long_imports(pymod, maxdots=2), ) def test_handling_long_imports_for_many_dots(self): self.mod.write( dedent("""\ import p1.p2.p3.m1 m = p1.p2.p3.m1 """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ from p1.p2.p3 import m1 m = m1 """), self.import_tools.handle_long_imports(pymod, maxdots=2), ) def test_handling_long_imports_for_their_length(self): self.mod.write( dedent("""\ import p1.p2.p3.m1 m = p1.p2.p3.m1 """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import p1.p2.p3.m1 m = p1.p2.p3.m1 """), self.import_tools.handle_long_imports(pymod, maxdots=3, maxlength=20), ) def test_handling_long_imports_for_many_dots2(self): self.mod.write( dedent("""\ import p1.p2.p3.m1 m = p1.p2.p3.m1 """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ from p1.p2.p3 import m1 m = m1 """), self.import_tools.handle_long_imports(pymod, maxdots=3, maxlength=10), ) def test_handling_long_imports_with_one_letter_last(self): self.mod.write( dedent("""\ import p1.p2.p3.l m = p1.p2.p3.l """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ from p1.p2.p3 import l m = l """), self.import_tools.handle_long_imports(pymod, maxdots=2), ) def test_empty_removing_unused_imports_and_eating_blank_lines(self): self.mod.write( dedent("""\ import pkg1 import pkg2 print(pkg1) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) module_with_imports.remove_unused_imports() self.assertEqual( dedent("""\ import pkg1 print(pkg1) """), module_with_imports.get_changed_source(), ) def test_sorting_imports_moving_to_top(self): self.mod.write( dedent("""\ import mod def f(): print(mod, pkg1, pkg2) import pkg1 import pkg2 """) ) pymod = self.project.get_module("mod") self.assertEqual( dedent("""\ import mod import pkg1 import pkg2 def f(): print(mod, pkg1, pkg2) """), self.import_tools.sort_imports(pymod), ) def test_sorting_imports_moving_to_top2(self): self.mod.write( dedent("""\ def f(): print(mod) import mod """) ) pymod = self.project.get_module("mod") self.assertEqual( dedent("""\ import mod def f(): print(mod) """), self.import_tools.sort_imports(pymod), ) # Sort pulls imports to the top anyway def test_sorting_imports_no_pull_to_top(self): code = dedent("""\ import pkg2 def f(): print(mod, pkg1, pkg2) import pkg1 import mod """) self.mod.write(code) pymod = self.project.get_module("mod") self.project.prefs["pull_imports_to_top"] = False self.assertEqual( dedent("""\ import mod import pkg1 import pkg2 def f(): print(mod, pkg1, pkg2) """), self.import_tools.sort_imports(pymod), ) def test_sorting_imports_moving_to_top_and_module_docs(self): self.mod.write( dedent('''\ """ docs """ def f(): print(mod) import mod ''') ) pymod = self.project.get_module("mod") self.assertEqual( dedent('''\ """ docs """ import mod def f(): print(mod) '''), self.import_tools.sort_imports(pymod), ) def test_sorting_imports_moving_to_top_and_module_docs2(self): self.mod.write( dedent('''\ """ docs """ import bbb import aaa def f(): print(mod) import mod ''') ) pymod = self.project.get_module("mod") self.assertEqual( dedent('''\ """ docs """ import aaa import bbb import mod def f(): print(mod) '''), self.import_tools.sort_imports(pymod), ) def test_get_changed_source_preserves_blank_lines(self): self.mod.write( dedent("""\ __author__ = "author" import aaa import bbb def f(): print(mod) """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) self.assertEqual( dedent("""\ import aaa import bbb __author__ = "author" def f(): print(mod) """), module_with_imports.get_changed_source(), ) def test_sorting_future_imports(self): self.mod.write( dedent("""\ import os from __future__ import devision """) ) pymod = self.project.get_module("mod") self.assertEqual( dedent("""\ from __future__ import devision import os """), self.import_tools.sort_imports(pymod), ) def test_organizing_imports_all_star(self): code = expected = dedent("""\ from package import some_name __all__ = ["some_name"] """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) self.assertEqual(expected, self.import_tools.organize_imports(pymod)) def test_organizing_imports_all_star_with_variables(self): code = expected = dedent("""\ from package import name_one, name_two if something(): foo = 'name_one' else: foo = 'name_two' __all__ = [foo] """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) self.assertEqual(expected, self.import_tools.organize_imports(pymod)) def test_organizing_imports_all_star_with_inline_if(self): code = expected = dedent("""\ from package import name_one, name_two __all__ = ['name_one' if something() else 'name_two'] """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) self.assertEqual(expected, self.import_tools.organize_imports(pymod)) @testutils.only_for_versions_higher("3") def test_organizing_imports_all_star_tolerates_non_list_of_str_1(self): code = expected = dedent("""\ from package import name_one, name_two foo = 'name_two' __all__ = [bar, *abc] + mylist __all__ = [foo, 'name_one', *abc] __all__ = [it for it in mylist] """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) self.assertEqual(expected, self.import_tools.organize_imports(pymod)) def test_organizing_imports_all_star_tolerates_non_list_of_str_2(self): code = expected = dedent("""\ from package import name_one, name_two foo = 'name_two' __all__ = [foo, 3, 'name_one'] __all__ = [it for it in mylist] """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) self.assertEqual(expected, self.import_tools.organize_imports(pymod)) @testutils.time_limit(60) def test_organizing_imports_all_star_no_infinite_loop(self): code = expected = dedent("""\ from package import name_one, name_two foo = bar bar = foo __all__ = [foo, 'name_one', 'name_two'] """) self.mod.write(code) pymod = self.project.get_pymodule(self.mod) self.assertEqual(expected, self.import_tools.organize_imports(pymod)) def test_customized_import_organization(self): self.mod.write( dedent("""\ import sys import sys """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( "import sys\n", self.import_tools.organize_imports(pymod, unused=False) ) def test_customized_import_organization2(self): self.mod.write("import sys\n") pymod = self.project.get_pymodule(self.mod) self.assertEqual( "import sys\n", self.import_tools.organize_imports(pymod, unused=False) ) def test_customized_import_organization3(self): self.mod.write( dedent("""\ import sys import mod var = 1 print(mod.var) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import sys var = 1 print(var) """), self.import_tools.organize_imports(pymod, unused=False), ) def test_trivial_filtered_expand_stars(self): self.pkg1.get_child("__init__.py").write("var1 = 1\n") self.pkg2.get_child("__init__.py").write("var2 = 1\n") self.mod.write( dedent("""\ from pkg1 import * from pkg2 import * print(var1, var2) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ from pkg1 import * from pkg2 import * print(var1, var2) """), self.import_tools.expand_stars(pymod, lambda stmt: False), ) def _line_filter(self, lineno): def import_filter(import_stmt): return import_stmt.start_line <= lineno < import_stmt.end_line return import_filter def test_filtered_expand_stars(self): self.pkg1.get_child("__init__.py").write("var1 = 1\n") self.pkg2.get_child("__init__.py").write("var2 = 1\n") self.mod.write( dedent("""\ from pkg1 import * from pkg2 import * print(var1, var2) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ from pkg1 import * from pkg2 import var2 print(var1, var2) """), self.import_tools.expand_stars(pymod, self._line_filter(2)), ) def test_filtered_relative_to_absolute(self): self.mod3.write("var = 1") self.mod2.write("import mod3\n\nprint(mod3.var)\n") pymod = self.project.get_pymodule(self.mod2) self.assertEqual( dedent("""\ import mod3 print(mod3.var) """), self.import_tools.relatives_to_absolutes(pymod, lambda stmt: False), ) self.assertEqual( dedent("""\ import pkg2.mod3 print(pkg2.mod3.var) """), self.import_tools.relatives_to_absolutes(pymod, self._line_filter(1)), ) def test_filtered_froms_to_normals(self): self.pkg1.get_child("__init__.py").write("var1 = 1\n") self.pkg2.get_child("__init__.py").write("var2 = 1\n") self.mod.write( dedent("""\ from pkg1 import var1 from pkg2 import var2 print(var1, var2) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ from pkg1 import var1 from pkg2 import var2 print(var1, var2) """), self.import_tools.expand_stars(pymod, lambda stmt: False), ) self.assertEqual( dedent("""\ from pkg1 import var1 import pkg2 print(var1, pkg2.var2) """), self.import_tools.froms_to_imports(pymod, self._line_filter(2)), ) def test_filtered_froms_to_normals2(self): self.pkg1.get_child("__init__.py").write("var1 = 1\n") self.pkg2.get_child("__init__.py").write("var2 = 1\n") self.mod.write( dedent("""\ from pkg1 import * from pkg2 import * print(var1, var2) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ from pkg1 import * import pkg2 print(var1, pkg2.var2) """), self.import_tools.froms_to_imports(pymod, self._line_filter(2)), ) def test_filtered_handle_long_imports(self): self.mod.write( dedent("""\ import p1.p2.p3.m1 import pkg1.mod1 m = p1.p2.p3.m1, pkg1.mod1 """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ import p1.p2.p3.m1 from pkg1 import mod1 m = p1.p2.p3.m1, mod1 """), self.import_tools.handle_long_imports( pymod, maxlength=5, import_filter=self._line_filter(2) ), ) def test_filtering_and_import_actions_with_more_than_one_phase(self): self.pkg1.get_child("__init__.py").write("var1 = 1\n") self.pkg2.get_child("__init__.py").write("var2 = 1\n") self.mod.write( dedent("""\ from pkg1 import * from pkg2 import * print(var2) """) ) pymod = self.project.get_pymodule(self.mod) self.assertEqual( dedent("""\ from pkg2 import * print(var2) """), self.import_tools.expand_stars(pymod, self._line_filter(1)), ) def test_non_existent_module_and_used_imports(self): self.mod.write( dedent("""\ from does_not_exist import func func() """) ) pymod = self.project.get_module("mod") module_with_imports = self.import_tools.module_imports(pymod) imports = module_with_imports.get_used_imports(pymod) self.assertEqual(1, len(imports)) class AddImportTest(unittest.TestCase): def setUp(self): super(AddImportTest, self).setUp() self.project = testutils.sample_project() self.mod1 = testutils.create_module(self.project, "mod1") self.mod2 = testutils.create_module(self.project, "mod2") self.pkg = testutils.create_package(self.project, "pkg") self.mod3 = testutils.create_module(self.project, "mod3", self.pkg) def tearDown(self): testutils.remove_project(self.project) super(AddImportTest, self).tearDown() def test_normal_imports(self): self.mod2.write("myvar = None\n") self.mod1.write("\n") pymod = self.project.get_module("mod1") result, name = add_import(self.project, pymod, "mod2", "myvar") self.assertEqual("import mod2\n", result) self.assertEqual("mod2.myvar", name) def test_not_reimporting_a_name(self): self.mod2.write("myvar = None\n") self.mod1.write("from mod2 import myvar\n") pymod = self.project.get_module("mod1") result, name = add_import(self.project, pymod, "mod2", "myvar") self.assertEqual("from mod2 import myvar\n", result) self.assertEqual("myvar", name) def test_adding_import_when_siblings_are_imported(self): self.mod2.write("var1 = None\nvar2 = None\n") self.mod1.write("from mod2 import var1\n") pymod = self.project.get_module("mod1") result, name = add_import(self.project, pymod, "mod2", "var2") self.assertEqual("from mod2 import var1, var2\n", result) self.assertEqual("var2", name) def test_adding_import_when_the_package_is_imported(self): self.pkg.get_child("__init__.py").write("var1 = None\n") self.mod3.write("var2 = None\n") self.mod1.write("from pkg import var1\n") pymod = self.project.get_module("mod1") result, name = add_import(self.project, pymod, "pkg.mod3", "var2") self.assertEqual("from pkg import var1, mod3\n", result) self.assertEqual("mod3.var2", name) def test_adding_import_for_modules_instead_of_names(self): self.pkg.get_child("__init__.py").write("var1 = None\n") self.mod3.write("\n") self.mod1.write("from pkg import var1\n") pymod = self.project.get_module("mod1") result, name = add_import(self.project, pymod, "pkg.mod3", None) self.assertEqual("from pkg import var1, mod3\n", result) self.assertEqual("mod3", name) def test_adding_import_for_modules_with_normal_duplicate_imports(self): self.pkg.get_child("__init__.py").write("var1 = None\n") self.mod3.write("\n") self.mod1.write("import pkg.mod3\n") pymod = self.project.get_module("mod1") result, name = add_import(self.project, pymod, "pkg.mod3", None) self.assertEqual("import pkg.mod3\n", result) self.assertEqual("pkg.mod3", name) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/inlinetest.py0000664000175000017500000011601000000000000020634 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest import rope.base.exceptions from rope.refactor import inline from ropetest import testutils class InlineTest(unittest.TestCase): def setUp(self): super(InlineTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") self.mod2 = testutils.create_module(self.project, "mod2") def tearDown(self): testutils.remove_project(self.project) super(InlineTest, self).tearDown() def _inline(self, code, offset, **kwds): self.mod.write(code) self._inline2(self.mod, offset, **kwds) return self.mod.read() def _inline2(self, resource, offset, **kwds): inliner = inline.create_inline(self.project, resource, offset) changes = inliner.get_changes(**kwds) self.project.do(changes) return self.mod.read() def test_simple_case(self): code = dedent("""\ a_var = 10 another_var = a_var """) refactored = self._inline(code, code.index("a_var") + 1) self.assertEqual("another_var = 10\n", refactored) def test_empty_case(self): code = "a_var = 10\n" refactored = self._inline(code, code.index("a_var") + 1) self.assertEqual("", refactored) def test_long_definition(self): code = dedent("""\ a_var = 10 + (10 + 10) another_var = a_var """) refactored = self._inline(code, code.index("a_var") + 1) self.assertEqual("another_var = 10 + (10 + 10)\n", refactored) def test_explicit_continuation(self): code = dedent("""\ a_var = (10 + 10) another_var = a_var """) refactored = self._inline(code, code.index("a_var") + 1) self.assertEqual("another_var = (10 + 10)\n", refactored) def test_implicit_continuation(self): code = dedent("""\ a_var = 10 +\\ 10 another_var = a_var """) refactored = self._inline(code, code.index("a_var") + 1) self.assertEqual("another_var = 10 + 10\n", refactored) def test_inlining_at_the_end_of_input(self): code = dedent("""\ a = 1 b = a""") refactored = self._inline(code, code.index("a") + 1) self.assertEqual("b = 1", refactored) def test_on_classes(self): code = dedent("""\ class AClass(object): pass """) with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline(code, code.index("AClass") + 1) def test_multiple_assignments(self): code = dedent("""\ a_var = 10 a_var = 20 """) with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline(code, code.index("a_var") + 1) def test_tuple_assignments(self): code = "a_var, another_var = (20, 30)\n" with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline(code, code.index("a_var") + 1) def test_on_unknown_vars(self): code = "a_var = another_var\n" with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline(code, code.index("another_var") + 1) def test_attribute_inlining(self): code = dedent("""\ class A(object): def __init__(self): self.an_attr = 3 range(self.an_attr) """) refactored = self._inline(code, code.index("an_attr") + 1) expected = dedent("""\ class A(object): def __init__(self): range(3) """) self.assertEqual(expected, refactored) def test_attribute_inlining2(self): code = dedent("""\ class A(object): def __init__(self): self.an_attr = 3 range(self.an_attr) a = A() range(a.an_attr)""") refactored = self._inline(code, code.index("an_attr") + 1) expected = dedent("""\ class A(object): def __init__(self): range(3) a = A() range(3)""") self.assertEqual(expected, refactored) def test_a_function_with_no_occurrence(self): self.mod.write( dedent("""\ def a_func(): pass """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("", self.mod.read()) def test_a_function_with_no_occurrence2(self): self.mod.write( dedent("""\ a_var = 10 def a_func(): pass print(a_var) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ a_var = 10 print(a_var) """), self.mod.read(), ) def test_replacing_calls_with_function_definition_in_other_modules(self): self.mod.write( dedent("""\ def a_func(): print(1) """) ) mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ import mod mod.a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ import mod print(1) """), mod1.read(), ) def test_replacing_calls_with_function_definition_in_other_modules2(self): self.mod.write( dedent("""\ def a_func(): print(1) """) ) mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ import mod if True: mod.a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ import mod if True: print(1) """), mod1.read(), ) def test_replacing_calls_with_method_definition_in_other_modules(self): self.mod.write( dedent("""\ class A(object): var = 10 def a_func(self): print(1) """) ) mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ import mod mod.A().a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ import mod print(1) """), mod1.read(), ) self.assertEqual( dedent("""\ class A(object): var = 10 """), self.mod.read(), ) def test_replacing_calls_with_function_definition_in_defining_module(self): self.mod.write( dedent("""\ def a_func(): print(1) a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("print(1)\n", self.mod.read()) def test_replac_calls_with_function_definition_in_defining_module2(self): self.mod.write( dedent("""\ def a_func(): for i in range(10): print(1) a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ for i in range(10): print(1) """), self.mod.read(), ) def test_replacing_calls_with_method_definition_in_defining_modules(self): self.mod.write( dedent("""\ class A(object): var = 10 def a_func(self): print(1) A().a_func()""" ) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ class A(object): var = 10 print(1) """), self.mod.read(), ) def test_parameters_with_the_same_name_as_passed(self): self.mod.write( dedent("""\ def a_func(var): print(var) var = 1 a_func(var) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ var = 1 print(var) """), self.mod.read(), ) def test_parameters_with_the_same_name_as_passed2(self): self.mod.write( dedent("""\ def a_func(var): print(var) var = 1 a_func(var=var) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ var = 1 print(var) """), self.mod.read(), ) def test_simple_parameters_renaming(self): self.mod.write( dedent("""\ def a_func(param): print(param) var = 1 a_func(var) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ var = 1 print(var) """), self.mod.read(), ) def test_simple_parameters_renaming_for_multiple_params(self): self.mod.write( dedent("""\ def a_func(param1, param2): p = param1 + param2 var1 = 1 var2 = 1 a_func(var1, var2) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ var1 = 1 var2 = 1 p = var1 + var2 """), self.mod.read(), ) def test_parameters_renaming_for_passed_constants(self): self.mod.write( dedent("""\ def a_func(param): print(param) a_func(1) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("print(1)\n", self.mod.read()) def test_parameters_renaming_for_passed_statements(self): self.mod.write( dedent("""\ def a_func(param): print(param) a_func((1 + 2) / 3) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ print((1 + 2) / 3) """), self.mod.read(), ) def test_simple_parameters_renam_for_multiple_params_using_keywords(self): self.mod.write( dedent("""\ def a_func(param1, param2): p = param1 + param2 var1 = 1 var2 = 1 a_func(param2=var1, param1=var2) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ var1 = 1 var2 = 1 p = var2 + var1 """), self.mod.read(), ) def test_simple_params_renam_for_multi_params_using_mixed_keywords(self): self.mod.write( dedent("""\ def a_func(param1, param2): p = param1 + param2 var1 = 1 var2 = 1 a_func(var2, param2=var1) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ var1 = 1 var2 = 1 p = var2 + var1 """), self.mod.read(), ) def test_simple_putting_in_default_arguments(self): self.mod.write( dedent("""\ def a_func(param=None): print(param) a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("print(None)\n", self.mod.read()) def test_overriding_default_arguments(self): self.mod.write( dedent("""\ def a_func(param1=1, param2=2): print(param1, param2) a_func(param2=3) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("print(1, 3)\n", self.mod.read()) def test_arguments_containing_comparisons(self): self.mod.write( dedent("""\ def a_func(param1, param2, param3): param2.name a_func(2 <= 1, item, True) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("item.name\n", self.mod.read()) def test_badly_formatted_text(self): self.mod.write( dedent("""\ def a_func ( param1 = 1 ,param2 = 2 ) : print(param1, param2) a_func ( param2 = 3 ) """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("print(1, 3)\n", self.mod.read()) def test_passing_first_arguments_for_methods(self): a_class = dedent("""\ class A(object): def __init__(self): self.var = 1 self.a_func(self.var) def a_func(self, param): print(param) """) self.mod.write(a_class) self._inline2(self.mod, self.mod.read().index("a_func") + 1) expected = dedent("""\ class A(object): def __init__(self): self.var = 1 print(self.var) """) self.assertEqual(expected, self.mod.read()) def test_passing_first_arguments_for_methods2(self): a_class = dedent("""\ class A(object): def __init__(self): self.var = 1 def a_func(self, param): print(param, self.var) an_a = A() an_a.a_func(1) """) self.mod.write(a_class) self._inline2(self.mod, self.mod.read().index("a_func") + 1) expected = dedent("""\ class A(object): def __init__(self): self.var = 1 an_a = A() print(1, an_a.var) """) self.assertEqual(expected, self.mod.read()) def test_passing_first_arguments_for_methods3(self): a_class = dedent("""\ class A(object): def __init__(self): self.var = 1 def a_func(self, param): print(param, self.var) an_a = A() A.a_func(an_a, 1) """) self.mod.write(a_class) self._inline2(self.mod, self.mod.read().index("a_func") + 1) expected = dedent("""\ class A(object): def __init__(self): self.var = 1 an_a = A() print(1, an_a.var) """) self.assertEqual(expected, self.mod.read()) def test_inlining_staticmethods(self): a_class = dedent("""\ class A(object): @staticmethod def a_func(param): print(param) A.a_func(1) """) self.mod.write(a_class) self._inline2(self.mod, self.mod.read().index("a_func") + 1) expected = dedent("""\ class A(object): pass print(1) """) self.assertEqual(expected, self.mod.read()) def test_static_methods2(self): a_class = dedent("""\ class A(object): var = 10 @staticmethod def a_func(param): print(param) an_a = A() an_a.a_func(1) A.a_func(2) """) self.mod.write(a_class) self._inline2(self.mod, self.mod.read().index("a_func") + 1) expected = dedent("""\ class A(object): var = 10 an_a = A() print(1) print(2) """) self.assertEqual(expected, self.mod.read()) def test_inlining_classmethods(self): a_class = dedent("""\ class A(object): @classmethod def a_func(cls, param): print(param) A.a_func(1) """) self.mod.write(a_class) self._inline2(self.mod, self.mod.read().index("a_func") + 1) expected = dedent("""\ class A(object): pass print(1) """) self.assertEqual(expected, self.mod.read()) def test_inlining_classmethods2(self): a_class = dedent("""\ class A(object): @classmethod def a_func(cls, param): return cls print(A.a_func(1)) """) self.mod.write(a_class) self._inline2(self.mod, self.mod.read().index("a_func") + 1) expected = dedent("""\ class A(object): pass print(A) """) self.assertEqual(expected, self.mod.read()) def test_simple_return_values_and_inlining_functions(self): self.mod.write( dedent("""\ def a_func(): return 1 a = a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("a = 1\n", self.mod.read()) def test_simple_return_values_and_inlining_lonely_functions(self): self.mod.write( dedent("""\ def a_func(): return 1 a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("1\n", self.mod.read()) def test_empty_returns_and_inlining_lonely_functions(self): self.mod.write( dedent("""\ def a_func(): if True: return a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ if True: pass """), self.mod.read(), ) def test_multiple_returns(self): self.mod.write( dedent("""\ def less_than_five(var): if var < 5: return True return False a = less_than_five(2) """) ) with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline2(self.mod, self.mod.read().index("less") + 1) def test_multiple_returns_and_not_using_the_value(self): self.mod.write( dedent("""\ def less_than_five(var): if var < 5: return True return False less_than_five(2) """) ) self._inline2(self.mod, self.mod.read().index("less") + 1) self.assertEqual( dedent("""\ if 2 < 5: True False """), self.mod.read(), ) def test_raising_exception_for_list_arguments(self): self.mod.write( dedent("""\ def a_func(*args): print(args) a_func(1) """) ) with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline2(self.mod, self.mod.read().index("a_func") + 1) def test_raising_exception_for_list_keywods(self): self.mod.write( dedent("""\ def a_func(**kwds): print(kwds) a_func(n=1) """) ) with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline2(self.mod, self.mod.read().index("a_func") + 1) def test_function_parameters_and_returns_in_other_functions(self): code = dedent("""\ def a_func(param1, param2): return param1 + param2 range(a_func(20, param2=abs(10))) """) self.mod.write(code) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("range(20 + abs(10))\n", self.mod.read()) def test_function_references_other_than_call(self): self.mod.write( dedent("""\ def a_func(param): print(param) f = a_func """) ) with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline2(self.mod, self.mod.read().index("a_func") + 1) def test_function_referencing_itself(self): self.mod.write( dedent("""\ def a_func(var): func = a_func """) ) with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline2(self.mod, self.mod.read().index("a_func") + 1) def test_recursive_functions(self): self.mod.write( dedent("""\ def a_func(var): a_func(var) """) ) with self.assertRaises(rope.base.exceptions.RefactoringError): self._inline2(self.mod, self.mod.read().index("a_func") + 1) # TODO: inlining on function parameters def xxx_test_inlining_function_default_parameters(self): self.mod.write( dedent("""\ def a_func(p1=1): pass a_func() """) ) self._inline2(self.mod, self.mod.read().index("p1") + 1) self.assertEqual( dedent("""\ def a_func(p1=1): pass a_func() """), self.mod.read(), ) def test_simple_inlining_after_extra_indented_lines(self): self.mod.write( dedent("""\ def a_func(): for i in range(10): pass if True: pass a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ if True: pass for i in range(10): pass """), self.mod.read(), ) def test_inlining_a_function_with_pydoc(self): self.mod.write( dedent('''\ def a_func(): """docs""" a = 1 a_func()''' ) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("a = 1\n", self.mod.read()) def test_inlining_methods(self): self.mod.write( dedent("""\ class A(object): name = 'hey' def get_name(self): return self.name a = A() name = a.get_name() """) ) self._inline2(self.mod, self.mod.read().rindex("get_name") + 1) self.assertEqual( dedent("""\ class A(object): name = 'hey' a = A() name = a.name """), self.mod.read(), ) def test_simple_returns_with_backslashes(self): self.mod.write( dedent("""\ def a_func(): return 1\\ + 2 a = a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual("a = 1 + 2\n", self.mod.read()) def test_a_function_with_pass_body(self): self.mod.write( dedent("""\ def a_func(): print(1) a = a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func") + 1) self.assertEqual( dedent("""\ print(1) a = None """), self.mod.read(), ) def test_inlining_the_last_method_of_a_class(self): self.mod.write( dedent("""\ class A(object): def a_func(self): pass """) ) self._inline2(self.mod, self.mod.read().rindex("a_func") + 1) self.assertEqual( dedent("""\ class A(object): pass """), self.mod.read(), ) def test_adding_needed_imports_in_the_dest_module(self): self.mod.write( dedent("""\ import sys def ver(): print(sys.version) """) ) self.mod2.write( dedent("""\ import mod mod.ver()""" ) ) self._inline2(self.mod, self.mod.read().index("ver") + 1) self.assertEqual( dedent("""\ import mod import sys print(sys.version) """), self.mod2.read(), ) def test_adding_needed_imports_in_the_dest_module_removing_selfs(self): self.mod.write( dedent("""\ import mod2 def f(): print(mod2.var) """) ) self.mod2.write( dedent("""\ import mod var = 1 mod.f() """) ) self._inline2(self.mod, self.mod.read().index("f(") + 1) self.assertEqual( dedent("""\ import mod var = 1 print(var) """), self.mod2.read(), ) def test_handling_relative_imports_when_inlining(self): pkg = testutils.create_package(self.project, "pkg") mod3 = testutils.create_module(self.project, "mod3", pkg) mod4 = testutils.create_module(self.project, "mod4", pkg) mod4.write("var = 1\n") mod3.write( dedent("""\ from . import mod4 def f(): print(mod4.var) """) ) self.mod.write( dedent("""\ import pkg.mod3 pkg.mod3.f() """) ) self._inline2(self.mod, self.mod.read().index("f(") + 1) # Cannot determine the exact import self.assertTrue("\n\nprint(mod4.var)\n" in self.mod.read()) def test_adding_needed_imports_for_elements_in_source(self): self.mod.write( dedent("""\ def f1(): return f2() def f2(): return 1 """) ) self.mod2.write( dedent("""\ import mod print(mod.f1()) """) ) self._inline2(self.mod, self.mod.read().index("f1") + 1) self.assertEqual( dedent("""\ import mod from mod import f2 print(f2()) """), self.mod2.read(), ) def test_relative_imports_and_changing_inlining_body(self): pkg = testutils.create_package(self.project, "pkg") mod3 = testutils.create_module(self.project, "mod3", pkg) mod4 = testutils.create_module(self.project, "mod4", pkg) mod4.write("var = 1\n") mod3.write( dedent("""\ import mod4 def f(): print(mod4.var) """) ) self.mod.write( dedent("""\ import pkg.mod3 pkg.mod3.f() """) ) self._inline2(self.mod, self.mod.read().index("f(") + 1) self.assertEqual( dedent("""\ import pkg.mod3 import pkg.mod4 print(pkg.mod4.var) """), self.mod.read(), ) def test_inlining_with_different_returns(self): self.mod.write( dedent("""\ def f(p): return p print(f(1)) print(f(2)) print(f(1)) """) ) self._inline2(self.mod, self.mod.read().index("f(") + 1) self.assertEqual( dedent("""\ print(1) print(2) print(1) """), self.mod.read(), ) def test_not_removing_definition_for_variables(self): code = dedent("""\ a_var = 10 another_var = a_var """) refactored = self._inline(code, code.index("a_var") + 1, remove=False) self.assertEqual( dedent("""\ a_var = 10 another_var = 10 """), refactored, ) def test_not_removing_definition_for_methods(self): code = dedent("""\ def func(): print(1) func() """) refactored = self._inline(code, code.index("func") + 1, remove=False) self.assertEqual( dedent("""\ def func(): print(1) print(1) """), refactored, ) def test_only_current_for_methods(self): code = dedent("""\ def func(): print(1) func() func() """) refactored = self._inline( code, code.rindex("func") + 1, remove=False, only_current=True ) self.assertEqual( dedent("""\ def func(): print(1) func() print(1) """), refactored, ) def test_only_current_for_variables(self): code = dedent("""\ one = 1 a = one b = one """) refactored = self._inline( code, code.rindex("one") + 1, remove=False, only_current=True ) self.assertEqual( dedent("""\ one = 1 a = one b = 1 """), refactored, ) def test_inlining_one_line_functions(self): code = dedent("""\ def f(): return 1 var = f() """) refactored = self._inline(code, code.rindex("f")) self.assertEqual("var = 1\n", refactored) def test_inlining_one_line_functions_with_breaks(self): code = dedent("""\ def f( p): return p var = f(1) """) refactored = self._inline(code, code.rindex("f")) self.assertEqual("var = 1\n", refactored) def test_inlining_one_line_functions_with_breaks2(self): code = dedent("""\ def f( ): return 1 var = f() """) refactored = self._inline(code, code.rindex("f")) self.assertEqual("var = 1\n", refactored) def test_resources_parameter(self): self.mod.write( dedent("""\ def a_func(): print(1) """) ) mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ import mod mod.a_func() """) ) self._inline2(self.mod, self.mod.read().index("a_func"), resources=[self.mod]) self.assertEqual("", self.mod.read()) self.assertEqual( dedent("""\ import mod mod.a_func() """), mod1.read(), ) def test_inlining_parameters(self): code = dedent("""\ def f(p=1): pass f() """) result = self._inline(code, code.index("p")) self.assertEqual( dedent("""\ def f(p=1): pass f(1) """), result, ) def test_inlining_function_with_line_breaks_in_args(self): code = dedent("""\ def f(p): return p var = f(1 + 1) """) refactored = self._inline(code, code.rindex("f")) self.assertEqual("var = 1 + 1\n", refactored) def test_inlining_variables_before_comparison(self): code = "start = 1\nprint(start <= 2)\n" refactored = self._inline(code, code.index("start")) self.assertEqual("print(1 <= 2)\n", refactored) def test_inlining_variables_in_other_modules(self): self.mod.write("myvar = 1\n") self.mod2.write( dedent("""\ import mod print(mod.myvar) """) ) self._inline2(self.mod, 2) self.assertEqual( dedent("""\ import mod print(1) """), self.mod2.read(), ) def test_inlining_variables_and_back_importing(self): self.mod.write( dedent("""\ mainvar = 1 myvar = mainvar """) ) self.mod2.write( dedent("""\ import mod print(mod.myvar) """) ) self._inline2(self.mod, self.mod.read().index("myvar")) expected = dedent("""\ import mod from mod import mainvar print(mainvar) """) self.assertEqual(expected, self.mod2.read()) def test_inlining_variables_and_importing_used_imports(self): self.mod.write( dedent("""\ import sys myvar = sys.argv """) ) self.mod2.write( dedent("""\ import mod print(mod.myvar) """) ) self._inline2(self.mod, self.mod.read().index("myvar")) expected = dedent("""\ import mod import sys print(sys.argv) """) self.assertEqual(expected, self.mod2.read()) def test_inlining_variables_and_removing_old_froms(self): self.mod.write("var = 1\n") self.mod2.write( dedent("""\ from mod import var print(var) """) ) self._inline2(self.mod2, self.mod2.read().rindex("var")) self.assertEqual("print(1)\n", self.mod2.read()) def test_inlining_method_and_removing_old_froms(self): self.mod.write( dedent("""\ def f(): return 1 """) ) self.mod2.write( dedent("""\ from mod import f print(f()) """) ) self._inline2(self.mod2, self.mod2.read().rindex("f")) self.assertEqual("print(1)\n", self.mod2.read()) def test_inlining_functions_in_other_modules_and_only_current(self): code1 = dedent("""\ def f(): return 1 print(f()) """) code2 = dedent("""\ import mod print(mod.f()) print(mod.f()) """) self.mod.write(code1) self.mod2.write(code2) self._inline2( self.mod2, self.mod2.read().rindex("f"), remove=False, only_current=True ) expected2 = dedent("""\ import mod print(mod.f()) print(1) """) self.assertEqual(code1, self.mod.read()) self.assertEqual(expected2, self.mod2.read()) def test_inlining_variables_in_other_modules_and_only_current(self): code1 = dedent("""\ var = 1 print(var) """) code2 = dedent("""\ import mod print(mod.var) print(mod.var) """) self.mod.write(code1) self.mod2.write(code2) self._inline2( self.mod2, self.mod2.read().rindex("var"), remove=False, only_current=True ) expected2 = "import mod\n" "print(mod.var)\n" "print(1)\n" self.assertEqual(code1, self.mod.read()) self.assertEqual(expected2, self.mod2.read()) def test_inlining_does_not_change_string_constants(self): code = dedent("""\ var = 1 print("var\\ ") """) expected = dedent("""\ var = 1 print("var\\ ") """) refactored = self._inline( code, code.rindex("var"), remove=False, only_current=True, docs=False ) self.assertEqual(expected, refactored) def test_inlining_does_change_string_constants_if_docs_is_set(self): code = dedent("""\ var = 1 print("var\\ ") """) expected = dedent("""\ var = 1 print("1\\ ") """) refactored = self._inline( code, code.rindex("var"), remove=False, only_current=True, docs=True ) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.6") def test_inlining_into_format_string(self): code = dedent("""\ var = 123 print(f"{var}") """) expected = dedent("""\ print(f"{123}") """) refactored = self._inline(code, code.rindex("var")) self.assertEqual(expected, refactored) @testutils.only_for_versions_higher("3.6") def test_inlining_into_format_string_containing_quotes(self): code = dedent('''\ var = 123 print(f" '{var}' ") print(f""" "{var}" """) print(f' "{var}" ') ''') expected = dedent('''\ print(f" '{123}' ") print(f""" "{123}" """) print(f' "{123}" ') ''') refactored = self._inline(code, code.rindex("var")) self.assertEqual(expected, refactored) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/movetest.py0000664000175000017500000012040100000000000020323 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.base import exceptions from rope.refactor import move from ropetest import testutils class MoveRefactoringTest(unittest.TestCase): def setUp(self): super(MoveRefactoringTest, self).setUp() self.project = testutils.sample_project() self.mod1 = testutils.create_module(self.project, "mod1") self.mod2 = testutils.create_module(self.project, "mod2") self.mod3 = testutils.create_module(self.project, "mod3") self.pkg = testutils.create_package(self.project, "pkg") self.mod4 = testutils.create_module(self.project, "mod4", self.pkg) self.mod5 = testutils.create_module(self.project, "mod5", self.pkg) def tearDown(self): testutils.remove_project(self.project) super(MoveRefactoringTest, self).tearDown() def _move(self, resource, offset, dest_resource): changes = move.create_move(self.project, resource, offset).get_changes( dest_resource ) self.project.do(changes) def test_move_constant(self): self.mod1.write("foo = 123\n") self._move(self.mod1, self.mod1.read().index("foo") + 1, self.mod2) self.assertEqual("", self.mod1.read()) self.assertEqual("foo = 123\n", self.mod2.read()) def test_move_constant_2(self): self.mod1.write("bar = 321\nfoo = 123\n") self._move(self.mod1, self.mod1.read().index("foo") + 1, self.mod2) self.assertEqual("bar = 321\n", self.mod1.read()) self.assertEqual("foo = 123\n", self.mod2.read()) def test_move_constant_multiline(self): self.mod1.write( dedent("""\ foo = ( 123 ) """) ) self._move(self.mod1, self.mod1.read().index("foo") + 1, self.mod2) self.assertEqual("", self.mod1.read()) self.assertEqual( dedent("""\ foo = ( 123 ) """), self.mod2.read(), ) def test_move_constant_multiple_statements(self): self.mod1.write( dedent("""\ foo = 123 foo += 3 foo = 4 """) ) self._move(self.mod1, self.mod1.read().index("foo") + 1, self.mod2) self.assertEqual( dedent("""\ import mod2 mod2.foo += 3 mod2.foo = 4 """), self.mod1.read(), ) self.assertEqual("foo = 123\n", self.mod2.read()) def test_simple_moving(self): self.mod1.write( dedent("""\ class AClass(object): pass """) ) self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual("", self.mod1.read()) self.assertEqual( dedent("""\ class AClass(object): pass """), self.mod2.read(), ) def test_moving_with_comment_prefix(self): self.mod1.write( dedent("""\ a = 1 # 1 # 2 class AClass(object): pass """) ) self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual("a = 1\n", self.mod1.read()) self.assertEqual( dedent("""\ # 1 # 2 class AClass(object): pass """), self.mod2.read(), ) def test_moving_with_comment_prefix_imports(self): self.mod1.write( dedent("""\ import foo a = 1 # 1 # 2 class AClass(foo.FooClass): pass """) ) self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual("a = 1\n", self.mod1.read()) self.assertEqual( dedent("""\ import foo # 1 # 2 class AClass(foo.FooClass): pass """), self.mod2.read(), ) def test_changing_other_modules_replacing_normal_imports(self): self.mod1.write("class AClass(object):\n pass\n") self.mod3.write("import mod1\na_var = mod1.AClass()\n") self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual( dedent("""\ import mod2 a_var = mod2.AClass() """), self.mod3.read(), ) def test_changing_other_modules_adding_normal_imports(self): self.mod1.write( dedent("""\ class AClass(object): pass def a_function(): pass """) ) self.mod3.write( dedent("""\ import mod1 a_var = mod1.AClass() mod1.a_function()""" ) ) self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual( dedent("""\ import mod1 import mod2 a_var = mod2.AClass() mod1.a_function()""" ), self.mod3.read(), ) def test_adding_imports_prefer_from_module(self): self.project.prefs["prefer_module_from_imports"] = True self.mod1.write( dedent("""\ class AClass(object): pass def a_function(): pass """) ) self.mod3.write( dedent("""\ import mod1 a_var = mod1.AClass() mod1.a_function()""" ) ) # Move to mod4 which is in a different package self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod4) self.assertEqual( dedent("""\ import mod1 from pkg import mod4 a_var = mod4.AClass() mod1.a_function()""" ), self.mod3.read(), ) def test_adding_imports_noprefer_from_module(self): self.project.prefs["prefer_module_from_imports"] = False self.mod1.write( dedent("""\ class AClass(object): pass def a_function(): pass """) ) self.mod3.write( dedent("""\ import mod1 a_var = mod1.AClass() mod1.a_function()""" ) ) # Move to mod4 which is in a different package self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod4) self.assertEqual( dedent("""\ import mod1 import pkg.mod4 a_var = pkg.mod4.AClass() mod1.a_function()""" ), self.mod3.read(), ) def test_adding_imports_prefer_from_module_top_level_module(self): self.project.prefs["prefer_module_from_imports"] = True self.mod1.write( dedent("""\ class AClass(object): pass def a_function(): pass """) ) self.mod3.write( dedent("""\ import mod1 a_var = mod1.AClass() mod1.a_function()""" ) ) self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual( dedent("""\ import mod1 import mod2 a_var = mod2.AClass() mod1.a_function()""" ), self.mod3.read(), ) def test_changing_other_modules_removing_from_imports(self): self.mod1.write( dedent("""\ class AClass(object): pass """) ) self.mod3.write( dedent("""\ from mod1 import AClass a_var = AClass() """) ) self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual( dedent("""\ import mod2 a_var = mod2.AClass() """), self.mod3.read(), ) def test_changing_source_module(self): self.mod1.write( dedent("""\ class AClass(object): pass a_var = AClass() """) ) self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual( dedent("""\ import mod2 a_var = mod2.AClass() """), self.mod1.read(), ) def test_changing_destination_module(self): self.mod1.write( dedent("""\ class AClass(object): pass """) ) self.mod2.write( dedent("""\ from mod1 import AClass a_var = AClass() """) ) self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) self.assertEqual( dedent("""\ class AClass(object): pass a_var = AClass() """), self.mod2.read(), ) def test_folder_destination(self): folder = self.project.root.create_folder("folder") self.mod1.write( dedent("""\ class AClass(object): pass """) ) with self.assertRaises(exceptions.RefactoringError): self._move(self.mod1, self.mod1.read().index("AClass") + 1, folder) def test_raising_exception_for_moving_non_global_elements(self): self.mod1.write( dedent("""\ def a_func(): class AClass(object): pass """) ) with self.assertRaises(exceptions.RefactoringError): self._move(self.mod1, self.mod1.read().index("AClass") + 1, self.mod2) def test_raising_an_exception_for_moving_non_global_variable(self): code = dedent("""\ class TestClass: CONSTANT = 5 """) self.mod1.write(code) with self.assertRaises(exceptions.RefactoringError): mover = move.create_move( self.project, self.mod1, code.index("CONSTANT") + 1 ) def test_raising_exception_for_mov_glob_elemnts_to_the_same_module(self): self.mod1.write("def a_func():\n pass\n") with self.assertRaises(exceptions.RefactoringError): self._move(self.mod1, self.mod1.read().index("a_func"), self.mod1) def test_moving_used_imports_to_destination_module(self): self.mod3.write("a_var = 10") code = dedent("""\ import mod3 from mod3 import a_var def a_func(): print(mod3, a_var) """) self.mod1.write(code) self._move(self.mod1, code.index("a_func") + 1, self.mod2) expected = dedent("""\ import mod3 from mod3 import a_var def a_func(): print(mod3, a_var) """) self.assertEqual(expected, self.mod2.read()) def test_moving_used_names_to_destination_module2(self): code = dedent("""\ a_var = 10 def a_func(): print(a_var) """) self.mod1.write(code) self._move(self.mod1, code.index("a_func") + 1, self.mod2) self.assertEqual( dedent("""\ a_var = 10 """), self.mod1.read(), ) expected = dedent("""\ from mod1 import a_var def a_func(): print(a_var) """) self.assertEqual(expected, self.mod2.read()) def test_moving_used_underlined_names_to_destination_module(self): code = dedent("""\ _var = 10 def a_func(): print(_var) """) self.mod1.write(code) self._move(self.mod1, code.index("a_func") + 1, self.mod2) expected = dedent("""\ from mod1 import _var def a_func(): print(_var) """) self.assertEqual(expected, self.mod2.read()) def test_moving_and_used_relative_imports(self): code = dedent("""\ import mod5 def a_func(): print(mod5) """) self.mod4.write(code) self._move(self.mod4, code.index("a_func") + 1, self.mod1) expected = dedent("""\ import pkg.mod5 def a_func(): print(pkg.mod5) """) self.assertEqual(expected, self.mod1.read()) self.assertEqual("", self.mod4.read()) def test_moving_modules(self): code = "import mod1\nprint(mod1)" self.mod2.write(code) self._move(self.mod2, code.index("mod1") + 1, self.pkg) expected = "import pkg.mod1\nprint(pkg.mod1)" self.assertEqual(expected, self.mod2.read()) self.assertTrue( not self.mod1.exists() and self.project.find_module("pkg.mod1") is not None ) def test_moving_modules_and_removing_out_of_date_imports(self): code = "import pkg.mod4\nprint(pkg.mod4)" self.mod2.write(code) self._move(self.mod2, code.index("mod4") + 1, self.project.root) expected = "import mod4\nprint(mod4)" self.assertEqual(expected, self.mod2.read()) self.assertTrue(self.project.find_module("mod4") is not None) def test_moving_modules_and_removing_out_of_date_froms(self): code = "from pkg import mod4\nprint(mod4)" self.mod2.write(code) self._move(self.mod2, code.index("mod4") + 1, self.project.root) self.assertEqual("import mod4\nprint(mod4)", self.mod2.read()) def test_moving_modules_and_removing_out_of_date_froms2(self): self.mod4.write("a_var = 10") code = "from pkg.mod4 import a_var\nprint(a_var)\n" self.mod2.write(code) self._move(self.mod2, code.index("mod4") + 1, self.project.root) expected = "from mod4 import a_var\nprint(a_var)\n" self.assertEqual(expected, self.mod2.read()) def test_moving_modules_and_relative_import(self): self.mod4.write("import mod5\nprint(mod5)\n") code = "import pkg.mod4\nprint(pkg.mod4)" self.mod2.write(code) self._move(self.mod2, code.index("mod4") + 1, self.project.root) moved = self.project.find_module("mod4") expected = "import pkg.mod5\nprint(pkg.mod5)\n" self.assertEqual(expected, moved.read()) def test_moving_module_kwarg_same_name_as_old(self): self.mod1.write("def foo(mod1=0):\n pass") code = "import mod1\nmod1.foo(mod1=1)" self.mod2.write(code) self._move(self.mod1, None, self.pkg) moved = self.project.find_module("mod2") expected = "import pkg.mod1\npkg.mod1.foo(mod1=1)" self.assertEqual(expected, moved.read()) def test_moving_packages(self): pkg2 = testutils.create_package(self.project, "pkg2") code = "import pkg.mod4\nprint(pkg.mod4)" self.mod1.write(code) self._move(self.mod1, code.index("pkg") + 1, pkg2) self.assertFalse(self.pkg.exists()) self.assertTrue(self.project.find_module("pkg2.pkg.mod4") is not None) self.assertTrue(self.project.find_module("pkg2.pkg.mod4") is not None) self.assertTrue(self.project.find_module("pkg2.pkg.mod5") is not None) expected = "import pkg2.pkg.mod4\nprint(pkg2.pkg.mod4)" self.assertEqual(expected, self.mod1.read()) def test_moving_modules_with_self_imports(self): self.mod1.write("import mod1\nprint(mod1)\n") self.mod2.write("import mod1\n") self._move(self.mod2, self.mod2.read().index("mod1") + 1, self.pkg) moved = self.project.find_module("pkg.mod1") self.assertEqual( dedent("""\ import pkg.mod1 print(pkg.mod1) """), moved.read(), ) def test_moving_modules_with_from_imports(self): pkg2 = testutils.create_package(self.project, "pkg2") code = dedent("""\ from pkg import mod4 print(mod4)""") self.mod1.write(code) self._move(self.mod1, code.index("pkg") + 1, pkg2) self.assertFalse(self.pkg.exists()) self.assertTrue(self.project.find_module("pkg2.pkg.mod4") is not None) self.assertTrue(self.project.find_module("pkg2.pkg.mod5") is not None) expected = dedent("""\ from pkg2.pkg import mod4 print(mod4)""") self.assertEqual(expected, self.mod1.read()) def test_moving_modules_with_from_import(self): pkg2 = testutils.create_package(self.project, "pkg2") pkg3 = testutils.create_package(self.project, "pkg3", pkg2) pkg4 = testutils.create_package(self.project, "pkg4", pkg3) code = dedent("""\ from pkg import mod4 print(mod4)""") self.mod1.write(code) self._move(self.mod4, None, pkg4) self.assertTrue(self.project.find_module("pkg2.pkg3.pkg4.mod4") is not None) expected = dedent("""\ from pkg2.pkg3.pkg4 import mod4 print(mod4)""") self.assertEqual(expected, self.mod1.read()) def test_moving_modules_with_multi_from_imports(self): pkg2 = testutils.create_package(self.project, "pkg2") pkg3 = testutils.create_package(self.project, "pkg3", pkg2) pkg4 = testutils.create_package(self.project, "pkg4", pkg3) code = dedent("""\ from pkg import mod4, mod5 print(mod4)""") self.mod1.write(code) self._move(self.mod4, None, pkg4) self.assertTrue(self.project.find_module("pkg2.pkg3.pkg4.mod4") is not None) expected = dedent("""\ from pkg import mod5 from pkg2.pkg3.pkg4 import mod4 print(mod4)""") self.assertEqual(expected, self.mod1.read()) def test_moving_modules_with_from_and_normal_imports(self): pkg2 = testutils.create_package(self.project, "pkg2") pkg3 = testutils.create_package(self.project, "pkg3", pkg2) pkg4 = testutils.create_package(self.project, "pkg4", pkg3) code = dedent("""\ from pkg import mod4 import pkg.mod4 print(mod4) print(pkg.mod4)""") self.mod1.write(code) self._move(self.mod4, None, pkg4) self.assertTrue(self.project.find_module("pkg2.pkg3.pkg4.mod4") is not None) expected = dedent("""\ import pkg2.pkg3.pkg4.mod4 from pkg2.pkg3.pkg4 import mod4 print(mod4) print(pkg2.pkg3.pkg4.mod4)""") self.assertEqual(expected, self.mod1.read()) def test_moving_modules_with_normal_and_from_imports(self): pkg2 = testutils.create_package(self.project, "pkg2") pkg3 = testutils.create_package(self.project, "pkg3", pkg2) pkg4 = testutils.create_package(self.project, "pkg4", pkg3) code = dedent("""\ import pkg.mod4 from pkg import mod4 print(mod4) print(pkg.mod4)""") self.mod1.write(code) self._move(self.mod4, None, pkg4) self.assertTrue(self.project.find_module("pkg2.pkg3.pkg4.mod4") is not None) expected = dedent("""\ import pkg2.pkg3.pkg4.mod4 from pkg2.pkg3.pkg4 import mod4 print(mod4) print(pkg2.pkg3.pkg4.mod4)""") self.assertEqual(expected, self.mod1.read()) def test_moving_modules_from_import_variable(self): pkg2 = testutils.create_package(self.project, "pkg2") pkg3 = testutils.create_package(self.project, "pkg3", pkg2) pkg4 = testutils.create_package(self.project, "pkg4", pkg3) code = dedent("""\ from pkg.mod4 import foo print(foo)""") self.mod1.write(code) self._move(self.mod4, None, pkg4) self.assertTrue(self.project.find_module("pkg2.pkg3.pkg4.mod4") is not None) expected = dedent("""\ from pkg2.pkg3.pkg4.mod4 import foo print(foo)""") self.assertEqual(expected, self.mod1.read()) def test_moving_modules_normal_import(self): pkg2 = testutils.create_package(self.project, "pkg2") pkg3 = testutils.create_package(self.project, "pkg3", pkg2) pkg4 = testutils.create_package(self.project, "pkg4", pkg3) code = dedent("""\ import pkg.mod4 print(pkg.mod4)""") self.mod1.write(code) self._move(self.mod4, None, pkg4) self.assertTrue(self.project.find_module("pkg2.pkg3.pkg4.mod4") is not None) expected = dedent("""\ import pkg2.pkg3.pkg4.mod4 print(pkg2.pkg3.pkg4.mod4)""") self.assertEqual(expected, self.mod1.read()) def test_moving_package_with_from_and_normal_imports(self): pkg2 = testutils.create_package(self.project, "pkg2") code = dedent("""\ from pkg import mod4 import pkg.mod4 print(pkg.mod4) print(mod4)""") self.mod1.write(code) self._move(self.mod1, code.index("pkg") + 1, pkg2) self.assertFalse(self.pkg.exists()) self.assertTrue(self.project.find_module("pkg2.pkg.mod4") is not None) self.assertTrue(self.project.find_module("pkg2.pkg.mod5") is not None) expected = dedent("""\ from pkg2.pkg import mod4 import pkg2.pkg.mod4 print(pkg2.pkg.mod4) print(mod4)""") self.assertEqual(expected, self.mod1.read()) def test_moving_package_with_from_and_normal_imports2(self): pkg2 = testutils.create_package(self.project, "pkg2") code = dedent("""\ import pkg.mod4 from pkg import mod4 print(pkg.mod4) print(mod4)""") self.mod1.write(code) self._move(self.mod1, code.index("pkg") + 1, pkg2) self.assertFalse(self.pkg.exists()) self.assertTrue(self.project.find_module("pkg2.pkg.mod4") is not None) self.assertTrue(self.project.find_module("pkg2.pkg.mod5") is not None) expected = dedent("""\ import pkg2.pkg.mod4 from pkg2.pkg import mod4 print(pkg2.pkg.mod4) print(mod4)""") self.assertEqual(expected, self.mod1.read()) def test_moving_package_and_retaining_blank_lines(self): pkg2 = testutils.create_package(self.project, "pkg2", self.pkg) code = dedent('''\ """Docstring followed by blank lines.""" import pkg.mod4 from pkg import mod4 from x import y from y import z from a import b from b import c print(pkg.mod4) print(mod4)''') self.mod1.write(code) self._move(self.mod4, None, pkg2) expected = dedent('''\ """Docstring followed by blank lines.""" import pkg.pkg2.mod4 from x import y from y import z from a import b from b import c from pkg.pkg2 import mod4 print(pkg.pkg2.mod4) print(mod4)''') self.assertEqual(expected, self.mod1.read()) def test_moving_functions_to_imported_module(self): code = dedent("""\ import mod1 def a_func(): var = mod1.a_var """) self.mod1.write("a_var = 1\n") self.mod2.write(code) self._move(self.mod2, code.index("a_func") + 1, self.mod1) expected = dedent("""\ def a_func(): var = a_var a_var = 1 """) self.assertEqual(expected, self.mod1.read()) def test_moving_resources_using_move_module_refactoring(self): self.mod1.write("a_var = 1") self.mod2.write( dedent("""\ import mod1 my_var = mod1.a_var """) ) mover = move.create_move(self.project, self.mod1) mover.get_changes(self.pkg).do() expected = dedent("""\ import pkg.mod1 my_var = pkg.mod1.a_var """) self.assertEqual(expected, self.mod2.read()) self.assertTrue(self.pkg.get_child("mod1.py") is not None) def test_moving_resources_using_move_module_for_packages(self): self.mod1.write( dedent("""\ import pkg my_pkg = pkg""" ) ) pkg2 = testutils.create_package(self.project, "pkg2") mover = move.create_move(self.project, self.pkg) mover.get_changes(pkg2).do() expected = dedent("""\ import pkg2.pkg my_pkg = pkg2.pkg""") self.assertEqual(expected, self.mod1.read()) self.assertTrue(pkg2.get_child("pkg") is not None) def test_moving_resources_using_move_module_for_init_dot_py(self): self.mod1.write( dedent("""\ import pkg my_pkg = pkg""" ) ) pkg2 = testutils.create_package(self.project, "pkg2") init = self.pkg.get_child("__init__.py") mover = move.create_move(self.project, init) mover.get_changes(pkg2).do() self.assertEqual( dedent("""\ import pkg2.pkg my_pkg = pkg2.pkg""" ), self.mod1.read(), ) self.assertTrue(pkg2.get_child("pkg") is not None) def test_moving_module_and_star_imports(self): self.mod1.write("a_var = 1") self.mod2.write( dedent("""\ from mod1 import * a = a_var """) ) mover = move.create_move(self.project, self.mod1) mover.get_changes(self.pkg).do() self.assertEqual( dedent("""\ from pkg.mod1 import * a = a_var """), self.mod2.read(), ) def test_moving_module_and_not_removing_blanks_after_imports(self): self.mod4.write("a_var = 1") self.mod2.write( dedent("""\ from pkg import mod4 import os print(mod4.a_var) """) ) mover = move.create_move(self.project, self.mod4) mover.get_changes(self.project.root).do() self.assertEqual( dedent("""\ import os import mod4 print(mod4.a_var) """), self.mod2.read(), ) def test_moving_module_refactoring_and_nonexistent_destinations(self): self.mod4.write("a_var = 1") self.mod2.write( dedent("""\ from pkg import mod4 import os print(mod4.a_var) """) ) with self.assertRaises(exceptions.RefactoringError): mover = move.create_move(self.project, self.mod4) mover.get_changes(None).do() def test_moving_methods_choosing_the_correct_class(self): code = dedent("""\ class A(object): def a_method(self): pass """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) self.assertTrue(isinstance(mover, move.MoveMethod)) def test_moving_methods_getting_new_method_for_empty_methods(self): code = dedent("""\ class A(object): def a_method(self): pass """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) self.assertEqual( dedent("""\ def new_method(self): pass """), mover.get_new_method("new_method"), ) def test_moving_methods_getting_new_method_for_constant_methods(self): code = dedent("""\ class A(object): def a_method(self): return 1 """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) self.assertEqual( dedent("""\ def new_method(self): return 1 """), mover.get_new_method("new_method"), ) def test_moving_methods_getting_new_method_passing_simple_paremters(self): code = dedent("""\ class A(object): def a_method(self, p): return p """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) self.assertEqual( dedent("""\ def new_method(self, p): return p """), mover.get_new_method("new_method"), ) def test_moving_methods_getting_new_method_using_main_object(self): code = dedent("""\ class A(object): attr = 1 def a_method(host): return host.attr """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) self.assertEqual( dedent("""\ def new_method(self, host): return host.attr """), mover.get_new_method("new_method"), ) def test_moving_methods_getting_new_method_renaming_main_object(self): code = dedent("""\ class A(object): attr = 1 def a_method(self): return self.attr """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) self.assertEqual( dedent("""\ def new_method(self, host): return host.attr """), mover.get_new_method("new_method"), ) def test_moving_methods_gettin_new_method_with_keyword_arguments(self): code = dedent("""\ class A(object): attr = 1 def a_method(self, p=None): return p """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) self.assertEqual( dedent("""\ def new_method(self, p=None): return p """), mover.get_new_method("new_method"), ) def test_moving_methods_gettin_new_method_with_many_kinds_arguments(self): code = dedent("""\ class A(object): attr = 1 def a_method(self, p1, *args, **kwds): return self.attr """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) expected = dedent("""\ def new_method(self, host, p1, *args, **kwds): return host.attr """) self.assertEqual(expected, mover.get_new_method("new_method")) def test_moving_methods_getting_new_method_for_multi_line_methods(self): code = dedent("""\ class A(object): def a_method(self): a = 2 return a """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) self.assertEqual( dedent("""\ def new_method(self): a = 2 return a """), mover.get_new_method("new_method"), ) def test_moving_methods_getting_old_method_for_constant_methods(self): self.mod2.write("class B(object):\n pass\n") code = dedent("""\ import mod2 class A(object): attr = mod2.B() def a_method(self): return 1 """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) mover.get_changes("attr", "new_method").do() expected = dedent("""\ import mod2 class A(object): attr = mod2.B() def a_method(self): return self.attr.new_method() """) self.assertEqual(expected, self.mod1.read()) def test_moving_methods_getting_getting_changes_for_goal_class(self): self.mod2.write("class B(object):\n var = 1\n") code = dedent("""\ import mod2 class A(object): attr = mod2.B() def a_method(self): return 1 """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) mover.get_changes("attr", "new_method").do() expected = dedent("""\ class B(object): var = 1 def new_method(self): return 1 """) self.assertEqual(expected, self.mod2.read()) def test_moving_methods_getting_getting_changes_for_goal_class2(self): code = dedent("""\ class B(object): var = 1 class A(object): attr = B() def a_method(self): return 1 """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) mover.get_changes("attr", "new_method").do() self.assertEqual( dedent("""\ class B(object): var = 1 def new_method(self): return 1 class A(object): attr = B() def a_method(self): return self.attr.new_method() """), self.mod1.read(), ) def test_moving_methods_and_nonexistent_attributes(self): code = dedent("""\ class A(object): def a_method(self): return 1 """) self.mod1.write(code) with self.assertRaises(exceptions.RefactoringError): mover = move.create_move(self.project, self.mod1, code.index("a_method")) mover.get_changes("x", "new_method") def test_unknown_attribute_type(self): code = dedent("""\ class A(object): attr = 1 def a_method(self): return 1 """) self.mod1.write(code) with self.assertRaises(exceptions.RefactoringError): mover = move.create_move(self.project, self.mod1, code.index("a_method")) mover.get_changes("attr", "new_method") def test_moving_methods_and_moving_used_imports(self): self.mod2.write("class B(object):\n var = 1\n") code = dedent("""\ import sys import mod2 class A(object): attr = mod2.B() def a_method(self): return sys.version """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) mover.get_changes("attr", "new_method").do() code = dedent("""\ import sys class B(object): var = 1 def new_method(self): return sys.version """) self.assertEqual(code, self.mod2.read()) def test_moving_methods_getting_getting_changes_for_goal_class3(self): self.mod2.write( dedent("""\ class B(object): pass """) ) code = dedent("""\ import mod2 class A(object): attr = mod2.B() def a_method(self): return 1 """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) mover.get_changes("attr", "new_method").do() expected = dedent("""\ class B(object): def new_method(self): return 1 """) self.assertEqual(expected, self.mod2.read()) def test_moving_methods_and_source_class_with_parameters(self): self.mod2.write("class B(object):\n pass\n") code = dedent("""\ import mod2 class A(object): attr = mod2.B() def a_method(self, p): return p """) self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("a_method")) mover.get_changes("attr", "new_method").do() expected1 = dedent("""\ import mod2 class A(object): attr = mod2.B() def a_method(self, p): return self.attr.new_method(p) """) self.assertEqual(expected1, self.mod1.read()) expected2 = dedent("""\ class B(object): def new_method(self, p): return p """) self.assertEqual(expected2, self.mod2.read()) def test_moving_globals_to_a_module_with_only_docstrings(self): self.mod1.write( dedent("""\ import sys def f(): print(sys.version) """) ) self.mod2.write( dedent('''\ """doc More docs ... """ ''') ) mover = move.create_move( self.project, self.mod1, self.mod1.read().index("f()") + 1 ) self.project.do(mover.get_changes(self.mod2)) self.assertEqual( dedent('''\ """doc More docs ... """ import sys def f(): print(sys.version) '''), self.mod2.read(), ) def test_moving_globals_to_a_module_with_only_docstrings2(self): code = dedent("""\ import os import sys def f(): print(sys.version, os.path) """) self.mod1.write(code) self.mod2.write('"""doc\n\nMore docs ...\n\n"""\n') mover = move.create_move( self.project, self.mod1, self.mod1.read().index("f()") + 1 ) self.project.do(mover.get_changes(self.mod2)) expected = dedent('''\ """doc More docs ... """ import os import sys def f(): print(sys.version, os.path) ''') self.assertEqual(expected, self.mod2.read()) def test_moving_a_global_when_it_is_used_after_a_multiline_str(self): code = dedent('''\ def f(): pass s = """\\ """ r = f() ''') self.mod1.write(code) mover = move.create_move(self.project, self.mod1, code.index("f()") + 1) self.project.do(mover.get_changes(self.mod2)) expected = dedent('''\ import mod2 s = """\\ """ r = mod2.f() ''') self.assertEqual(expected, self.mod1.read()) def test_raising_an_exception_when_moving_non_package_folders(self): dir = self.project.root.create_folder("dir") with self.assertRaises(exceptions.RefactoringError): move.create_move(self.project, dir) def test_moving_to_a_module_with_encoding_cookie(self): code1 = "# -*- coding: utf-8 -*-" self.mod1.write(code1) code2 = dedent("""\ def f(): pass """) self.mod2.write(code2) mover = move.create_move(self.project, self.mod2, code2.index("f()") + 1) self.project.do(mover.get_changes(self.mod1)) expected = "%s\n%s" % (code1, code2) self.assertEqual(expected, self.mod1.read()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/multiprojecttest.py0000664000175000017500000000716200000000000022106 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.refactor import multiproject, rename, move from ropetest import testutils class MultiProjectRefactoringTest(unittest.TestCase): def setUp(self): super(MultiProjectRefactoringTest, self).setUp() self.project1 = testutils.sample_project(foldername="testproject1") self.project2 = testutils.sample_project(foldername="testproject2") self.mod1 = self.project1.root.create_file("mod1.py") self.other = self.project1.root.create_file("other.py") self.mod2 = self.project2.root.create_file("mod2.py") def tearDown(self): testutils.remove_project(self.project1) testutils.remove_project(self.project2) super(MultiProjectRefactoringTest, self).tearDown() def test_trivial_rename(self): self.mod1.write("var = 1\n") refactoring = multiproject.MultiProjectRefactoring(rename.Rename, []) renamer = refactoring(self.project1, self.mod1, 1) multiproject.perform(renamer.get_all_changes("newvar")) self.assertEqual("newvar = 1\n", self.mod1.read()) def test_rename(self): self.mod1.write("var = 1\n") self.mod2.write( dedent("""\ import mod1 myvar = mod1.var """) ) refactoring = multiproject.MultiProjectRefactoring( rename.Rename, [self.project2] ) renamer = refactoring(self.project1, self.mod1, 1) multiproject.perform(renamer.get_all_changes("newvar")) self.assertEqual("newvar = 1\n", self.mod1.read()) self.assertEqual( dedent("""\ import mod1 myvar = mod1.newvar """), self.mod2.read(), ) def test_move(self): self.mod1.write( dedent("""\ def a_func(): pass """) ) self.mod2.write( dedent("""\ import mod1 myvar = mod1.a_func() """) ) refactoring = multiproject.MultiProjectRefactoring( move.create_move, [self.project2] ) renamer = refactoring(self.project1, self.mod1, self.mod1.read().index("_func")) multiproject.perform(renamer.get_all_changes(self.other)) self.assertEqual("", self.mod1.read()) self.assertEqual( dedent("""\ def a_func(): pass """), self.other.read(), ) self.assertEqual( dedent("""\ import other myvar = other.a_func() """), self.mod2.read(), ) def test_rename_from_the_project_not_containing_the_change(self): self.project2.get_prefs().add("python_path", self.project1.address) self.mod1.write("var = 1\n") self.mod2.write( dedent("""\ import mod1 myvar = mod1.var """) ) refactoring = multiproject.MultiProjectRefactoring( rename.Rename, [self.project1] ) renamer = refactoring(self.project2, self.mod2, self.mod2.read().rindex("var")) multiproject.perform(renamer.get_all_changes("newvar")) self.assertEqual( dedent("""\ newvar = 1 """), self.mod1.read(), ) self.assertEqual( dedent("""\ import mod1 myvar = mod1.newvar """), self.mod2.read(), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637506004.0 rope-0.22.0/ropetest/refactor/patchedasttest.py0000664000175000017500000015525500000000000021514 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest import sys from textwrap import dedent from rope.base import ast from rope.base.utils import pycompat from rope.refactor import patchedast from ropetest import testutils try: basestring except NameError: basestring = (str, bytes) NameConstant = "Name" if sys.version_info <= (3, 8) else "NameConstant" Bytes = "Bytes" if (3, 0) <= sys.version_info <= (3, 8) else "Str" class PatchedASTTest(unittest.TestCase): def setUp(self): super(PatchedASTTest, self).setUp() def tearDown(self): super(PatchedASTTest, self).tearDown() def test_bytes_string(self): source = '1 + b"("\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) str_fragment = 'b"("' start = source.index(str_fragment) checker.check_region(Bytes, start, start + len(str_fragment)) checker.check_children(Bytes, [str_fragment]) def test_integer_literals_and_region(self): source = "a = 10\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("10") checker.check_region("Num", start, start + 2) def test_negative_integer_literals_and_region(self): source = "a = -10\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("-10") end = start + 3 # Python 3 parses as UnaryOp(op=USub(), operand=Num(n=10)) if pycompat.PY3: start += 1 checker.check_region("Num", start, end) def test_scientific_integer_literals_and_region(self): source = "a = -1.0e-3\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("-1.0e-3") end = start + 7 # Python 3 parses as UnaryOp(op=USub(), operand=Num(n=10)) if pycompat.PY3: start += 1 checker.check_region("Num", start, end) def test_hex_integer_literals_and_region(self): source = "a = 0x1\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("0x1") checker.check_region("Num", start, start + 3) @testutils.only_for_versions_lower("3") def test_long_literals_and_region(self): source = "a = 0x1L\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("0x1L") checker.check_region("Num", start, start + 4) def test_octal_integer_literals_and_region(self): source = "a = -0125e1\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("-0125e1") end = start + 7 # Python 3 parses as UnaryOp(op=USub(), operand=Num(n=10)) if pycompat.PY3: start += 1 checker.check_region("Num", start, end) def test_integer_literals_and_sorted_children(self): source = "a = 10\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) # start = source.index('10') checker.check_children("Num", ["10"]) def test_ellipsis(self): source = "a[...]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("...") checker.check_region("Ellipsis", start, start + len("...")) def test_ass_name_node(self): source = "a = 10\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("a") checker.check_region("Name", start, start + 1) checker.check_children("Name", ["a"]) def test_assign_node(self): source = "a = 10\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("a") # noqa checker.check_region("Assign", 0, len(source) - 1) checker.check_children("Assign", ["Name", " ", "=", " ", "Num"]) @testutils.only_for_versions_higher("3.6") def test_ann_assign_node_without_target(self): source = "a: List[int]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("a") # noqa checker.check_region("AnnAssign", 0, len(source) - 1) checker.check_children("AnnAssign", ["Name", "", ":", " ", "Subscript"]) @testutils.only_for_versions_higher("3.6") def test_ann_assign_node_with_target(self): source = "a: int = 10\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("a") # noqa checker.check_region("AnnAssign", 0, len(source) - 1) checker.check_children( "AnnAssign", ["Name", "", ":", " ", "Name", " ", "=", " ", "Num"] ) def test_add_node(self): source = "1 + 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BinOp", 0, len(source) - 1) checker.check_children("BinOp", ["Num", " ", "+", " ", "Num"]) def test_lshift_node(self): source = "1 << 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BinOp", 0, len(source) - 1) checker.check_children("BinOp", ["Num", " ", "<<", " ", "Num"]) def test_and_node(self): source = "True and True\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BoolOp", 0, len(source) - 1) checker.check_children("BoolOp", [NameConstant, " ", "and", " ", NameConstant]) def test_basic_closing_parens(self): source = "1 + (2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BinOp", 0, len(source) - 1) checker.check_children("BinOp", ["Num", " ", "+", " (", "Num", ")"]) def test_basic_opening_parens(self): source = "(1) + 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BinOp", 0, len(source) - 1) checker.check_children("BinOp", ["(", "Num", ") ", "+", " ", "Num"]) def test_basic_opening_biway(self): source = "(1) + (2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BinOp", 0, len(source) - 1) checker.check_children("BinOp", ["(", "Num", ") ", "+", " (", "Num", ")"]) def test_basic_opening_double(self): source = "1 + ((2))\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BinOp", 0, len(source) - 1) checker.check_children("BinOp", ["Num", " ", "+", " ((", "Num", "))"]) def test_handling_comments(self): source = "(1 + #(\n2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("BinOp", ["Num", " ", "+", " #(\n", "Num"]) def test_handling_parens_with_spaces(self): source = "1 + (2\n )\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("BinOp", ["Num", " ", "+", " (", "Num", "\n )"]) def test_handling_strings(self): source = '1 + "("\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("BinOp", ["Num", " ", "+", " ", "Str"]) def test_handling_implicit_string_concatenation(self): source = "a = '1''2'" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Assign", ["Name", " ", "=", " ", "Str"]) checker.check_children("Str", ["'1''2'"]) def test_handling_implicit_string_concatenation_line_breaks(self): source = dedent("""\ a = '1' \\ '2'""") ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Assign", ["Name", " ", "=", " ", "Str"]) checker.check_children("Str", ["'1' \\\n'2'"]) def test_handling_explicit_string_concatenation_line_breaks(self): source = "a = ('1' \n'2')" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Assign", ["Name", " ", "=", " (", "Str", ")"]) checker.check_children("Str", ["'1' \n'2'"]) def test_not_concatenating_strings_on_separate_lines(self): source = dedent("""\ '1' '2' """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Module", ["", "Expr", "\n", "Expr", "\n"]) def test_handling_raw_strings(self): source = 'r"abc"\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Str", ['r"abc"']) @testutils.only_for_versions_higher("3.6") def test_handling_format_strings_basic(self): source = '1 + f"abc{a}"\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("JoinedStr", ['f"', "abc", "FormattedValue", "", '"']) checker.check_children("FormattedValue", ["{", "", "Name", "", "}"]) @testutils.only_for_versions_higher("3.6") def test_handling_format_strings_with_implicit_join(self): source = '''"1" + rf'abc{a}' f"""xxx{b} """\n''' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "JoinedStr", ["rf'", "abc", "FormattedValue", '\' f"""xxx', "FormattedValue", " ", '"""',], ) checker.check_children("FormattedValue", ["{", "", "Name", "", "}"]) @testutils.only_for_versions_higher("3.6") def test_handling_format_strings_with_format_spec(self): source = 'f"abc{a:01}"\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("JoinedStr", ['f"', "abc", "FormattedValue", "", '"']) checker.check_children( "FormattedValue", ["{", "", "Name", "", ":", "", "01", "", "}"] ) @testutils.only_for_versions_higher("3.6") def test_handling_format_strings_with_inner_format_spec(self): source = 'f"abc{a:{length}01}"\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("JoinedStr", ['f"', "abc", "FormattedValue", "", '"']) checker.check_children( "FormattedValue", ["{", "", "Name", "", ":", "{", "Name", "}", "01", "", "}"], ) @testutils.only_for_versions_higher("3.6") def test_handling_format_strings_with_expression(self): source = 'f"abc{a + b}"\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("JoinedStr", ['f"', "abc", "FormattedValue", "", '"']) checker.check_children("FormattedValue", ["{", "", "BinOp", "", "}"]) @testutils.only_for_versions_lower("3") def test_long_integer_literals(self): source = "0x1L + a" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("BinOp", ["Num", " ", "+", " ", "Name"]) checker.check_children("Num", ["0x1L"]) def test_complex_number_literals(self): source = "1.0e2j + a" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("BinOp", ["Num", " ", "+", " ", "Name"]) checker.check_children("Num", ["1.0e2j"]) def test_ass_attr_node(self): source = "a.b = 1\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Attribute", 0, source.index("=") - 1) checker.check_children("Attribute", ["Name", "", ".", "", "b"]) def test_ass_list_node(self): source = "[a, b] = 1, 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("List", 0, source.index("]") + 1) checker.check_children("List", ["[", "", "Name", "", ",", " ", "Name", "", "]"]) def test_ass_tuple(self): source = "a, b = range(2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Tuple", 0, source.index("=") - 1) checker.check_children("Tuple", ["Name", "", ",", " ", "Name"]) def test_ass_tuple2(self): source = "(a, b) = range(2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Tuple", 0, source.index("=") - 1) checker.check_children( "Tuple", ["(", "", "Name", "", ",", " ", "Name", "", ")"] ) def test_assert(self): source = "assert True\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Assert", 0, len(source) - 1) checker.check_children("Assert", ["assert", " ", NameConstant]) def test_assert2(self): source = 'assert True, "error"\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Assert", 0, len(source) - 1) checker.check_children( "Assert", ["assert", " ", NameConstant, "", ",", " ", "Str"] ) def test_aug_assign_node(self): source = "a += 1\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("a") # noqa checker.check_region("AugAssign", 0, len(source) - 1) checker.check_children("AugAssign", ["Name", " ", "+", "", "=", " ", "Num"]) @testutils.only_for_versions_lower("3") def test_back_quotenode(self): source = "`1`\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Repr", 0, len(source) - 1) checker.check_children("Repr", ["`", "", "Num", "", "`"]) def test_bitand(self): source = "1 & 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BinOp", 0, len(source) - 1) checker.check_children("BinOp", ["Num", " ", "&", " ", "Num"]) def test_bitor(self): source = "1 | 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("BinOp", ["Num", " ", "|", " ", "Num"]) def test_call_func(self): source = "f(1, 2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Call", 0, len(source) - 1) checker.check_children( "Call", ["Name", "", "(", "", "Num", "", ",", " ", "Num", "", ")"] ) def test_call_func_and_keywords(self): source = "f(1, p=2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "Num", "", ",", " ", "keyword", "", ")"] ) @testutils.only_for_versions_lower("3.5") def test_call_func_and_star_args(self): source = "f(1, *args)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "Num", "", ",", " ", "*", "", "Name", "", ")"] ) @testutils.only_for("3.5") def test_call_func_and_star_argspython35(self): source = "f(1, *args)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "Num", "", ",", " *", "Starred", "", ")"] ) @testutils.only_for_versions_lower("3.5") def test_call_func_and_only_dstar_args(self): source = "f(**kwds)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Call", ["Name", "", "(", "", "**", "", "Name", "", ")"]) @testutils.only_for("3.5") def test_call_func_and_only_dstar_args_python35(self): source = "f(**kwds)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Call", ["Name", "", "(", "**", "keyword", "", ")"]) @testutils.only_for_versions_lower("3.5") def test_call_func_and_both_varargs_and_kwargs(self): source = "f(*args, **kwds)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "*", "", "Name", "", ",", " ", "**", "", "Name", "", ")"], ) @testutils.only_for("3.5") def test_call_func_and_both_varargs_and_kwargs_python35(self): source = "f(*args, **kwds)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "*", "Starred", "", ",", " **", "keyword", "", ")"], ) def test_class_node(self): source = dedent('''\ class A(object): """class docs""" pass ''') ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Class", 0, len(source) - 1) checker.check_children( "Class", ["class", " ", "A", "", "(", "", "Name", "", ")", "", ":", "\n ", "Expr", "\n ", "Pass"], ) def test_class_with_no_bases(self): source = dedent("""\ class A: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Class", 0, len(source) - 1) checker.check_children("Class", ["class", " ", "A", "", ":", "\n ", "Pass"]) def test_simple_compare(self): source = "1 < 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Compare", 0, len(source) - 1) checker.check_children("Compare", ["Num", " ", "<", " ", "Num"]) def test_multiple_compare(self): source = "1 < 2 <= 3\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Compare", 0, len(source) - 1) checker.check_children( "Compare", ["Num", " ", "<", " ", "Num", " ", "<=", " ", "Num"] ) def test_decorators_node(self): source = dedent("""\ @d def f(): pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("FunctionDef", 0, len(source) - 1) checker.check_children( "FunctionDef", ["@", "", "Name", "\n", "def", " ", "f", "", "(", "", "arguments", "", ")", "", ":", "\n ", "Pass"], ) @testutils.only_for("2.6") def test_decorators_for_classes(self): source = dedent("""\ @d class C(object): pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("ClassDef", 0, len(source) - 1) checker.check_children( "ClassDef", ["@", "", "Name", "\n", "class", " ", "C", "", "(", "", "Name", "", ")", "", ":", "\n ", "Pass"], ) def test_both_varargs_and_kwargs(self): source = dedent("""\ def f(*args, **kwds): pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "arguments", ["*", "", "args", "", ",", " ", "**", "", "kwds"] ) def test_function_node(self): source = dedent("""\ def f(): pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Function", 0, len(source) - 1) checker.check_children( "Function", ["def", " ", "f", "", "(", "", "arguments", "", ")", "", ":", "\n ", "Pass"], ) @testutils.only_for_versions_higher("3.5") def test_async_function_node(self): source = dedent("""\ async def f(): pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("AsyncFunction", 0, len(source) - 1) checker.check_children( "AsyncFunction", ["async", " ", "def", " ", "f", "", "(", "", "arguments", "", ")", "", ":", "\n ", "Pass"], ) def test_function_node2(self): source = dedent('''\ def f(p1, **p2): """docs""" pass ''') ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Function", 0, len(source) - 1) checker.check_children( "Function", ["def", " ", "f", "", "(", "", "arguments", "", ")", "", ":", "\n ", "Expr", "\n ", "Pass"], ) expected_child = pycompat.ast_arg_type.__name__ checker.check_children( "arguments", [expected_child, "", ",", " ", "**", "", "p2"] ) @testutils.only_for_versions_lower("3") def test_function_node_and_tuple_parameters(self): source = dedent("""\ def f(a, (b, c)): pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Function", 0, len(source) - 1) checker.check_children( "Function", ["def", " ", "f", "", "(", "", "arguments", "", ")", "", ":", "\n ", "Pass"], ) checker.check_children("arguments", ["Name", "", ",", " ", "Tuple"]) def test_dict_node(self): source = "{1: 2, 3: 4}\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Dict", 0, len(source) - 1) checker.check_children( "Dict", ["{", "", "Num", "", ":", " ", "Num", "", ",", " ", "Num", "", ":", " ", "Num", "", "}"], ) @testutils.only_for("3.5") def test_dict_node_with_unpacking(self): source = "{**dict1, **dict2}\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Dict", 0, len(source) - 1) checker.check_children( "Dict", ["{", "", "**", "", "Name", "", ",", " ", "**", "", "Name", "", "}"] ) def test_div_node(self): source = "1 / 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("BinOp", 0, len(source) - 1) checker.check_children("BinOp", ["Num", " ", "/", " ", "Num"]) @testutils.only_for_versions_lower("3") def test_simple_exec_node(self): source = 'exec ""\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Exec", 0, len(source) - 1) checker.check_children("Exec", ["exec", "", "", " ", "Str", "", ""]) @testutils.only_for_versions_lower("3") def test_exec_node(self): source = 'exec "" in locals(), globals()\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Exec", 0, len(source) - 1) checker.check_children( "Exec", ["exec", "", "", " ", "Str", " ", "in", " ", "Call", "", ",", " ", "Call", "", ""], ) @testutils.only_for_versions_lower("3") def test_exec_node_with_parens(self): source = 'exec("", locals(), globals())\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Exec", 0, len(source) - 1) checker.check_children( "Exec", ["exec", "", "(", "", "Str", "", ",", " ", "Call", "", ",", " ", "Call", "", ")"], ) def test_for_node(self): source = dedent("""\ for i in range(1): pass else: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("For", 0, len(source) - 1) checker.check_children( "For", ["for", " ", "Name", " ", "in", " ", "Call", "", ":", "\n ", "Pass", "\n", "else", "", ":", "\n ", "Pass"], ) @testutils.only_for_versions_higher("3.5") def test_async_for_node(self): source = dedent("""\ async def foo(): async for i in range(1): pass else: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("AsyncFor", source.index("async for"), len(source) - 1) checker.check_children( "AsyncFor", ["async", " ", "for", " ", "Name", " ", "in", " ", "Call", "", ":", "\n ", "Pass", "\n ", "else", "", ":", "\n ", "Pass"], ) @testutils.only_for_versions_higher("3.8") def test_named_expr_node(self): source = dedent("""\ if a := 10 == 10: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.index("a") checker.check_region("NamedExpr", start, start + 13) checker.check_children("NamedExpr", ["Name", " ", ":=", " ", "Compare"]) def test_normal_from_node(self): source = "from x import y\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("ImportFrom", 0, len(source) - 1) checker.check_children( "ImportFrom", ["from", " ", "x", " ", "import", " ", "alias"] ) checker.check_children("alias", ["y"]) @testutils.only_for("2.5") def test_from_node(self): source = "from ..x import y as z\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("ImportFrom", 0, len(source) - 1) checker.check_children( "ImportFrom", ["from", " ", "..", "", "x", " ", "import", " ", "alias"] ) checker.check_children("alias", ["y", " ", "as", " ", "z"]) @testutils.only_for("2.5") def test_from_node_relative_import(self): source = "from . import y as z\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("ImportFrom", 0, len(source) - 1) checker.check_children( "ImportFrom", ["from", " ", ".", "", "", " ", "import", " ", "alias"] ) checker.check_children("alias", ["y", " ", "as", " ", "z"]) def test_simple_gen_expr_node(self): source = "zip(i for i in x)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("GeneratorExp", 4, len(source) - 2) checker.check_children("GeneratorExp", ["Name", " ", "comprehension"]) checker.check_children( "comprehension", ["for", " ", "Name", " ", "in", " ", "Name"] ) def test_gen_expr_node_handling_surrounding_parens(self): source = "(i for i in x)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("GeneratorExp", 0, len(source) - 1) checker.check_children( "GeneratorExp", ["(", "", "Name", " ", "comprehension", "", ")"] ) def test_gen_expr_node2(self): source = "zip(i for i in range(1) if i == 1)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "comprehension", ["for", " ", "Name", " ", "in", " ", "Call", " ", "if", " ", "Compare"], ) def test_get_attr_node(self): source = "a.b\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Attribute", 0, len(source) - 1) checker.check_children("Attribute", ["Name", "", ".", "", "b"]) def test_global_node(self): source = "global a, b\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Global", 0, len(source) - 1) checker.check_children("Global", ["global", " ", "a", "", ",", " ", "b"]) def test_if_node(self): source = "if True:\n pass\nelse:\n pass\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("If", 0, len(source) - 1) checker.check_children( "If", ["if", " ", NameConstant, "", ":", "\n ", "Pass", "\n", "else", "", ":", "\n ", "Pass"], ) def test_if_node2(self): source = dedent("""\ if True: pass elif False: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("If", 0, len(source) - 1) checker.check_children( "If", ["if", " ", NameConstant, "", ":", "\n ", "Pass", "\n", "If"] ) def test_if_node3(self): source = dedent("""\ if True: pass else: if True: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("If", 0, len(source) - 1) checker.check_children( "If", ["if", " ", NameConstant, "", ":", "\n ", "Pass", "\n", "else", "", ":", "\n ", "If"], ) def test_import_node(self): source = "import a, b as c\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Import", 0, len(source) - 1) checker.check_children( "Import", ["import", " ", "alias", "", ",", " ", "alias"] ) def test_lambda_node(self): source = "lambda a, b=1, *z: None\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Lambda", 0, len(source) - 1) checker.check_children( "Lambda", ["lambda", " ", "arguments", "", ":", " ", NameConstant] ) expected_child = pycompat.ast_arg_type.__name__ checker.check_children( "arguments", [expected_child, "", ",", " ", expected_child, "", "=", "", "Num", "", ",", " ", "*", "", "z"], ) def test_list_node(self): source = "[1, 2]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("List", 0, len(source) - 1) checker.check_children("List", ["[", "", "Num", "", ",", " ", "Num", "", "]"]) def test_list_comp_node(self): source = "[i for i in range(1) if True]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("ListComp", 0, len(source) - 1) checker.check_children( "ListComp", ["[", "", "Name", " ", "comprehension", "", "]"] ) checker.check_children( "comprehension", ["for", " ", "Name", " ", "in", " ", "Call", " ", "if", " ", NameConstant], ) def test_list_comp_node_with_multiple_comprehensions(self): source = "[i for i in range(1) for j in range(1) if True]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("ListComp", 0, len(source) - 1) checker.check_children( "ListComp", ["[", "", "Name", " ", "comprehension", " ", "comprehension", "", "]"], ) checker.check_children( "comprehension", ["for", " ", "Name", " ", "in", " ", "Call", " ", "if", " ", NameConstant], ) def test_set_node(self): # make sure we are in a python version with set literals source = "{1, 2}\n" try: eval(source) except SyntaxError: return ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Set", 0, len(source) - 1) checker.check_children("Set", ["{", "", "Num", "", ",", " ", "Num", "", "}"]) def test_set_comp_node(self): # make sure we are in a python version with set comprehensions source = "{i for i in range(1) if True}\n" try: eval(source) except SyntaxError: return ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("SetComp", 0, len(source) - 1) checker.check_children( "SetComp", ["{", "", "Name", " ", "comprehension", "", "}"] ) checker.check_children( "comprehension", ["for", " ", "Name", " ", "in", " ", "Call", " ", "if", " ", NameConstant], ) def test_dict_comp_node(self): # make sure we are in a python version with dict comprehensions source = "{i:i for i in range(1) if True}\n" try: eval(source) except SyntaxError: return ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("DictComp", 0, len(source) - 1) checker.check_children( "DictComp", ["{", "", "Name", "", ":", "", "Name", " ", "comprehension", "", "}"], ) checker.check_children( "comprehension", ["for", " ", "Name", " ", "in", " ", "Call", " ", "if", " ", NameConstant], ) def test_ext_slice_node(self): source = "x = xs[0,:]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) if sys.version_info >= (3, 9): checker.check_region("Tuple", 7, len(source) - 2) checker.check_children("Tuple", ["Num", "", ",", "", "Slice"]) else: checker.check_region("ExtSlice", 7, len(source) - 2) checker.check_children("ExtSlice", ["Index", "", ",", "", "Slice"]) def test_simple_module_node(self): source = "pass\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Module", 0, len(source)) checker.check_children("Module", ["", "Pass", "\n"]) def test_module_node(self): source = dedent('''\ """docs""" pass ''') ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Module", 0, len(source)) checker.check_children("Module", ["", "Expr", "\n", "Pass", "\n"]) checker.check_children("Str", ['"""docs"""']) def test_not_and_or_nodes(self): source = "not True or False\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Expr", ["BoolOp"]) checker.check_children("BoolOp", ["UnaryOp", " ", "or", " ", NameConstant]) @testutils.only_for_versions_lower("3") def test_print_node(self): source = "print >>out, 1,\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Print", 0, len(source) - 1) checker.check_children( "Print", ["print", " ", ">>", "", "Name", "", ",", " ", "Num", "", ","] ) @testutils.only_for_versions_lower("3") def test_printnl_node(self): source = "print 1\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Print", 0, len(source) - 1) checker.check_children("Print", ["print", " ", "Num"]) @testutils.only_for_versions_lower("3") def test_raise_node_for_python2(self): source = "raise x, y, z\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Raise", 0, len(source) - 1) checker.check_children( "Raise", ["raise", " ", "Name", "", ",", " ", "Name", "", ",", " ", "Name"] ) # @#testutils.only_for('3') @unittest.skipIf(sys.version < "3", "This is wrong") def test_raise_node_for_python3(self): source = "raise x(y)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_region("Raise", 0, len(source) - 1) checker.check_children("Raise", ["raise", " ", "Call"]) def test_return_node(self): source = dedent("""\ def f(): return None """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Return", ["return", " ", NameConstant]) def test_empty_return_node(self): source = dedent("""\ def f(): return """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Return", ["return"]) def test_simple_slice_node(self): source = "a[1:2]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Subscript", ["Name", "", "[", "", "Slice", "", "]"]) checker.check_children("Slice", ["Num", "", ":", "", "Num"]) def test_slice_node2(self): source = "a[:]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Subscript", ["Name", "", "[", "", "Slice", "", "]"]) checker.check_children("Slice", [":"]) def test_simple_subscript(self): source = "a[1]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) if sys.version_info >= (3, 9): checker.check_children("Subscript", ["Name", "", "[", "", "Num", "", "]"]) else: checker.check_children("Subscript", ["Name", "", "[", "", "Index", "", "]"]) checker.check_children("Index", ["Num"]) def test_tuple_node(self): source = "(1, 2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Tuple", ["(", "", "Num", "", ",", " ", "Num", "", ")"]) def test_tuple_node2(self): source = "#(\n1, 2\n" ast_frag = patchedast.get_patched_ast(source, True) 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) checker = _ResultChecker(self, ast_frag) checker.check_children("Tuple", ["(", "", "Num", ",", ")"]) def test_empty_tuple_node(self): source = "()\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) 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("""\ def f(): yield None """) ast_frag = patchedast.get_patched_ast(source, True) 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: pass else: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "While", ["while", " ", NameConstant, "", ":", "\n ", "Pass", "\n", "else", "", ":", "\n ", "Pass"], ) @testutils.only_for("2.5") def test_with_node(self): source = dedent("""\ from __future__ import with_statement with a as b: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "With", ["with", " ", "Name", " ", "as", " ", "Name", "", ":", "\n ", "Pass"], ) @testutils.only_for("3.5") def test_async_with_node(self): source = dedent("""\ async def afunc(): async with a as b: pass\n """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "AsyncWith", ["async", " ", "with", " ", "Name", " ", "as", " ", "Name", "", ":", "\n ", "Pass"], ) def test_try_finally_node(self): source = dedent("""\ try: pass finally: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) node_to_test = "Try" if pycompat.PY3 else "TryFinally" if pycompat.PY3: expected_children = ["try", "", ":", "\n ", "Pass", "\n", "finally", "", ":", "\n ", "Pass"] else: expected_children = ["try", "", ":", "\n ", "Pass", "\n", "finally", "", ":", "\n ", "Pass"] checker.check_children(node_to_test, expected_children) @testutils.only_for_versions_lower("3") def test_try_except_node(self): source = dedent("""\ try: pass except Exception, e: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "TryExcept", ["try", "", ":", "\n ", "Pass", "\n", ("excepthandler", "ExceptHandler")], ) checker.check_children( ("excepthandler", "ExceptHandler"), ["except", " ", "Name", "", ",", " ", "Name", "", ":", "\n ", "Pass"], ) def test_try_except_node__with_as_syntax(self): source = dedent("""\ try: pass except Exception as e: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) node_to_test = "Try" if pycompat.PY3 else "TryExcept" checker.check_children( node_to_test, ["try", "", ":", "\n ", "Pass", "\n", ("excepthandler", "ExceptHandler")], ) expected_child = "e" if pycompat.PY3 else "Name" checker.check_children( ("excepthandler", "ExceptHandler"), ["except", " ", "Name", " ", "as", " ", expected_child, "", ":", "\n ", "Pass"], ) @testutils.only_for("2.5") def test_try_except_and_finally_node(self): source = dedent("""\ try: pass except: pass finally: pass """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) node_to_test = "Try" if pycompat.PY3 else "TryFinally" if pycompat.PY3: expected_children = ["try", "", ":", "\n ", "Pass", "\n", "ExceptHandler", "\n", "finally", "", ":", "\n ", "Pass"] else: expected_children = ["TryExcept", "\n", "finally", "", ":", "\n ", "Pass"] checker.check_children(node_to_test, expected_children) def test_ignoring_comments(self): source = "#1\n1\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) start = source.rindex("1") checker.check_region("Num", start, start + 1) def test_simple_sliceobj(self): source = "a[1::3]\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Slice", ["Num", "", ":", "", ":", "", "Num"]) def test_ignoring_strings_that_start_with_a_char(self): source = 'r"""("""\n1\n' ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Module", ["", "Expr", "\n", "Expr", "\n"]) @testutils.only_for_versions_lower("3") def test_how_to_handle_old_not_equals(self): source = "1 <> 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Compare", ["Num", " ", "<>", " ", "Num"]) def test_semicolon(self): source = "1;\n" patchedast.get_patched_ast(source, True) @testutils.only_for("2.5") def test_if_exp_node(self): source = "1 if True else 2\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "IfExp", ["Num", " ", "if", " ", NameConstant, " ", "else", " ", "Num"] ) def test_delete_node(self): source = "del a, b\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Delete", ["del", " ", "Name", "", ",", " ", "Name"]) @testutils.only_for_versions_lower("3.5") def test_starargs_before_keywords_legacy(self): source = "foo(*args, a=1)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "*", "", "Name", "", ",", " ", "keyword", "", ")"], ) @testutils.only_for_versions_lower("3.5") def test_starargs_in_keywords_legacy(self): source = "foo(a=1, *args, b=2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "keyword", "", ",", " ", "*", "", "Name", "", ",", " ", "keyword", "", ")"], ) @testutils.only_for_versions_lower("3.5") def test_starargs_after_keywords_legacy(self): source = "foo(a=1, *args)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "keyword", "", ",", " ", "*", "", "Name", "", ")"], ) @testutils.only_for("3.5") def test_starargs_before_keywords(self): source = "foo(*args, a=1)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "*", "Starred", "", ",", " ", "keyword", "", ")"] ) @testutils.only_for("3.5") def test_starargs_in_keywords(self): source = "foo(a=1, *args, b=2)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "keyword", "", ",", " *", "Starred", "", ",", " ", "keyword", "", ")"], ) @testutils.only_for("3.5") def test_starargs_after_keywords(self): source = "foo(a=1, *args)\n" ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children( "Call", ["Name", "", "(", "", "keyword", "", ",", " *", "Starred", "", ")"] ) @testutils.only_for_versions_higher("3.5") def test_await_node(self): source = dedent("""\ async def f(): await sleep() """) ast_frag = patchedast.get_patched_ast(source, True) checker = _ResultChecker(self, ast_frag) checker.check_children("Await", ["await", " ", "Call"]) class _ResultChecker(object): def __init__(self, test_case, ast): self.test_case = test_case self.ast = ast def check_region(self, text, start, end): node = self._find_node(text) if node is None: self.test_case.fail("Node <%s> cannot be found" % text) self.test_case.assertEqual((start, end), node.region) def _find_node(self, text): goal = text if not isinstance(text, (tuple, list)): goal = [text] class Search(object): result = None def __call__(self, node): for text in goal: if sys.version_info >= (3, 8) and text in [ "Num", "Str", "NameConstant", "Ellipsis", ]: text = "Constant" if str(node).startswith(text): self.result = node break if node.__class__.__name__.startswith(text): self.result = node break return self.result is not None search = Search() ast.call_for_nodes(self.ast, search, recursive=True) return search.result def check_children(self, text, children): node = self._find_node(text) if node is None: self.test_case.fail("Node <%s> cannot be found" % text) result = list(node.sorted_children) self.test_case.assertEqual(len(children), len(result)) for expected, child in zip(children, result): goals = expected if not isinstance(expected, (tuple, list)): goals = [expected] for goal in goals: if goal == "" or isinstance(child, basestring): self.test_case.assertEqual(goal, child) break else: self.test_case.assertNotEqual("", text, "probably ignoring some node") if sys.version_info >= (3, 8) and expected in [ "Num", "Str", "NameConstant", "Ellipsis", ]: expected = "Constant" self.test_case.assertTrue( child.__class__.__name__.startswith(expected), msg="Expected <%s> but was <%s>" % (expected, child.__class__.__name__), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/renametest.py0000664000175000017500000014541400000000000020637 0ustar00lieryanlieryanimport sys from textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest import rope.base.codeanalyze import rope.refactor.occurrences from rope.refactor import rename from rope.refactor.rename import Rename from ropetest import testutils class RenameRefactoringTest(unittest.TestCase): def setUp(self): super(RenameRefactoringTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(RenameRefactoringTest, self).tearDown() def _local_rename(self, source_code, offset, new_name): testmod = testutils.create_module(self.project, "testmod") testmod.write(source_code) changes = Rename(self.project, testmod, offset).get_changes( new_name, resources=[testmod] ) self.project.do(changes) return testmod.read() def _rename(self, resource, offset, new_name, **kwds): changes = Rename(self.project, resource, offset).get_changes(new_name, **kwds) self.project.do(changes) def test_simple_global_variable_renaming(self): refactored = self._local_rename("a_var = 20\n", 2, "new_var") self.assertEqual("new_var = 20\n", refactored) def test_variable_renaming_only_in_its_scope(self): refactored = self._local_rename( dedent("""\ a_var = 20 def a_func(): a_var = 10 """), 32, "new_var", ) self.assertEqual( dedent("""\ a_var = 20 def a_func(): new_var = 10 """), refactored, ) def test_not_renaming_dot_name(self): refactored = self._local_rename( dedent("""\ replace = True 'aaa'.replace('a', 'b') """), 1, "new_var", ) self.assertEqual( dedent("""\ new_var = True 'aaa'.replace('a', 'b') """), refactored, ) def test_renaming_multiple_names_in_the_same_line(self): refactored = self._local_rename( dedent("""\ a_var = 10 a_var = 10 + a_var / 2 """), 2, "new_var", ) self.assertEqual( dedent("""\ new_var = 10 new_var = 10 + new_var / 2 """), refactored, ) def test_renaming_names_when_getting_some_attribute(self): refactored = self._local_rename( dedent("""\ a_var = 'a b c' a_var.split('\\n') """), 2, "new_var", ) self.assertEqual( dedent("""\ new_var = 'a b c' new_var.split('\\n') """), refactored, ) def test_renaming_names_when_getting_some_attribute2(self): refactored = self._local_rename( dedent("""\ a_var = 'a b c' a_var.split('\\n') """), 20, "new_var", ) self.assertEqual( dedent("""\ new_var = 'a b c' new_var.split('\\n') """), refactored, ) def test_renaming_function_parameters1(self): refactored = self._local_rename( dedent("""\ def f(a_param): print(a_param) """), 8, "new_param", ) self.assertEqual( dedent("""\ def f(new_param): print(new_param) """), refactored, ) def test_renaming_function_parameters2(self): refactored = self._local_rename( dedent("""\ def f(a_param): print(a_param) """), 30, "new_param", ) self.assertEqual( dedent("""\ def f(new_param): print(new_param) """), refactored, ) def test_renaming_occurrences_inside_functions(self): code = dedent("""\ def a_func(p1): a = p1 a_func(1) """) refactored = self._local_rename(code, code.index("p1") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param): a = new_param a_func(1) """), refactored, ) def test_renaming_comprehension_loop_variables(self): code = "[b_var for b_var, c_var in d_var if b_var == c_var]" refactored = self._local_rename(code, code.index("b_var") + 1, "new_var") self.assertEqual( "[new_var for new_var, c_var in d_var if new_var == c_var]", refactored ) def test_renaming_list_comprehension_loop_variables_in_assignment(self): code = "a_var = [b_var for b_var, c_var in d_var if b_var == c_var]" refactored = self._local_rename(code, code.index("b_var") + 1, "new_var") self.assertEqual( "a_var = [new_var for new_var, c_var in d_var if new_var == c_var]", refactored, ) def test_renaming_generator_comprehension_loop_variables(self): code = "a_var = (b_var for b_var, c_var in d_var if b_var == c_var)" refactored = self._local_rename(code, code.index("b_var") + 1, "new_var") self.assertEqual( "a_var = (new_var for new_var, c_var in d_var if new_var == c_var)", refactored, ) def test_renaming_comprehension_loop_variables_scope(self): code = dedent("""\ [b_var for b_var, c_var in d_var if b_var == c_var] b_var = 10 """) refactored = self._local_rename(code, code.index("b_var") + 1, "new_var") self.assertEqual( dedent("""\ [new_var for new_var, c_var in d_var if new_var == c_var] b_var = 10 """), refactored, ) @testutils.only_for_versions_higher("3.8") def test_renaming_inline_assignment(self): code = dedent("""\ while a_var := next(foo): print(a_var) """) refactored = self._local_rename(code, code.index("a_var") + 1, "new_var") self.assertEqual( dedent("""\ while new_var := next(foo): print(new_var) """), refactored, ) def test_renaming_arguments_for_normal_args_changing_calls(self): code = dedent("""\ def a_func(p1=None, p2=None): pass a_func(p2=1) """) refactored = self._local_rename(code, code.index("p2") + 1, "p3") self.assertEqual( dedent("""\ def a_func(p1=None, p3=None): pass a_func(p3=1) """), refactored, ) def test_renaming_function_parameters_of_class_init(self): code = dedent("""\ class A(object): def __init__(self, a_param): pass a_var = A(a_param=1) """) refactored = self._local_rename(code, code.index("a_param") + 1, "new_param") expected = dedent("""\ class A(object): def __init__(self, new_param): pass a_var = A(new_param=1) """) self.assertEqual(expected, refactored) def test_rename_functions_parameters_and_occurences_in_other_modules(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write( dedent("""\ def a_func(a_param): print(a_param) """) ) mod2.write( dedent("""\ from mod1 import a_func a_func(a_param=10) """) ) self._rename(mod1, mod1.read().index("a_param") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param): print(new_param) """), mod1.read(), ) self.assertEqual( dedent("""\ from mod1 import a_func a_func(new_param=10) """), mod2.read(), ) def test_renaming_with_backslash_continued_names(self): refactored = self._local_rename( "replace = True\n'ali'.\\\nreplace\n", 2, "is_replace" ) self.assertEqual("is_replace = True\n'ali'.\\\nreplace\n", refactored) @testutils.only_for("3.6") def test_renaming_occurrence_in_f_string(self): code = dedent("""\ a_var = 20 a_string=f'value: {a_var}' """) expected = dedent("""\ new_var = 20 a_string=f'value: {new_var}' """) refactored = self._local_rename(code, 2, "new_var") self.assertEqual(expected, refactored) @testutils.only_for("3.6") def test_renaming_occurrence_in_nested_f_string(self): code = dedent("""\ a_var = 20 a_string=f'{f"{a_var}"}' """) expected = dedent("""\ new_var = 20 a_string=f'{f"{new_var}"}' """) refactored = self._local_rename(code, 2, "new_var") self.assertEqual(expected, refactored) @testutils.only_for("3.6") def test_not_renaming_string_contents_in_f_string(self): refactored = self._local_rename( "a_var = 20\na_string=f'{\"a_var\"}'\n", 2, "new_var" ) self.assertEqual( dedent("""\ new_var = 20 a_string=f'{"a_var"}' """), refactored, ) def test_not_renaming_string_contents(self): refactored = self._local_rename("a_var = 20\na_string='a_var'\n", 2, "new_var") self.assertEqual( dedent("""\ new_var = 20 a_string='a_var' """), refactored, ) def test_not_renaming_comment_contents(self): refactored = self._local_rename("a_var = 20\n# a_var\n", 2, "new_var") self.assertEqual( dedent("""\ new_var = 20 # a_var """), refactored, ) def test_renaming_all_occurrences_in_containing_scope(self): code = dedent("""\ if True: a_var = 1 else: a_var = 20 """) refactored = self._local_rename(code, 16, "new_var") self.assertEqual( dedent("""\ if True: new_var = 1 else: new_var = 20 """), refactored, ) def test_renaming_a_variable_with_arguement_name(self): code = dedent("""\ a_var = 10 def a_func(a_var): print(a_var) """) refactored = self._local_rename(code, 1, "new_var") self.assertEqual( dedent("""\ new_var = 10 def a_func(a_var): print(a_var) """), refactored, ) def test_renaming_an_arguement_with_variable_name(self): code = dedent("""\ a_var = 10 def a_func(a_var): print(a_var) """) refactored = self._local_rename(code, len(code) - 3, "new_var") self.assertEqual( dedent("""\ a_var = 10 def a_func(new_var): print(new_var) """), refactored, ) def test_renaming_function_with_local_variable_name(self): code = dedent("""\ def a_func(): a_func=20 a_func()""") refactored = self._local_rename(code, len(code) - 3, "new_func") self.assertEqual( dedent("""\ def new_func(): a_func=20 new_func()""" ), refactored, ) def test_renaming_functions(self): code = dedent("""\ def a_func(): pass a_func() """) refactored = self._local_rename(code, len(code) - 5, "new_func") self.assertEqual( dedent("""\ def new_func(): pass new_func() """), refactored, ) @testutils.only_for("3.5") def test_renaming_async_function(self): code = dedent("""\ async def a_func(): pass a_func()""") refactored = self._local_rename(code, len(code) - 5, "new_func") self.assertEqual( dedent("""\ async def new_func(): pass new_func()""" ), refactored, ) @testutils.only_for("3.5") def test_renaming_await(self): code = dedent("""\ async def b_func(): pass async def a_func(): await b_func()""") refactored = self._local_rename(code, len(code) - 5, "new_func") self.assertEqual( dedent("""\ async def new_func(): pass async def a_func(): await new_func()""" ), refactored, ) def test_renaming_functions_across_modules(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ def a_func(): pass a_func() """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ import mod1 mod1.a_func() """) ) self._rename(mod1, len(mod1.read()) - 5, "new_func") self.assertEqual( dedent("""\ def new_func(): pass new_func() """), mod1.read(), ) self.assertEqual( dedent("""\ import mod1 mod1.new_func() """), mod2.read(), ) def test_renaming_functions_across_modules_from_import(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ def a_func(): pass a_func() """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ from mod1 import a_func a_func() """) ) self._rename(mod1, len(mod1.read()) - 5, "new_func") self.assertEqual( dedent("""\ def new_func(): pass new_func() """), mod1.read(), ) self.assertEqual( dedent("""\ from mod1 import new_func new_func() """), mod2.read(), ) def test_renaming_functions_from_another_module(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ def a_func(): pass a_func() """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ import mod1 mod1.a_func() """) ) self._rename(mod2, len(mod2.read()) - 5, "new_func") self.assertEqual( dedent("""\ def new_func(): pass new_func() """), mod1.read(), ) self.assertEqual( dedent("""\ import mod1 mod1.new_func() """), mod2.read(), ) def test_applying_all_changes_together(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ import mod2 mod2.a_func() """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ def a_func(): pass a_func() """) ) self._rename(mod2, len(mod2.read()) - 5, "new_func") self.assertEqual( dedent("""\ import mod2 mod2.new_func() """), mod1.read(), ) self.assertEqual( dedent("""\ def new_func(): pass new_func() """), mod2.read(), ) def test_renaming_modules(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ def a_func(): pass """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write("from mod1 import a_func\n") self._rename(mod2, mod2.read().index("mod1") + 1, "newmod") self.assertTrue( not mod1.exists() and self.project.find_module("newmod") is not None ) self.assertEqual("from newmod import a_func\n", mod2.read()) def test_renaming_modules_aliased(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ def a_func(): pass """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ import mod1 as m m.a_func() """) ) self._rename(mod1, None, "newmod") self.assertTrue( not mod1.exists() and self.project.find_module("newmod") is not None ) self.assertEqual("import newmod as m\nm.a_func()\n", mod2.read()) def test_renaming_packages(self): pkg = testutils.create_package(self.project, "pkg") mod1 = testutils.create_module(self.project, "mod1", pkg) mod1.write( dedent("""\ def a_func(): pass """) ) mod2 = testutils.create_module(self.project, "mod2", pkg) mod2.write("from pkg.mod1 import a_func\n") self._rename(mod2, 6, "newpkg") self.assertTrue(self.project.find_module("newpkg.mod1") is not None) new_mod2 = self.project.find_module("newpkg.mod2") self.assertEqual("from newpkg.mod1 import a_func\n", new_mod2.read()) def test_module_dependencies(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ class AClass(object): pass """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ import mod1 a_var = mod1.AClass() """) ) self.project.get_pymodule(mod2).get_attributes()["mod1"] mod1.write( dedent("""\ def AClass(): return 0 """) ) self._rename(mod2, len(mod2.read()) - 3, "a_func") self.assertEqual( dedent("""\ def a_func(): return 0 """), mod1.read(), ) self.assertEqual( dedent("""\ import mod1 a_var = mod1.a_func() """), mod2.read(), ) def test_renaming_class_attributes(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ class AClass(object): def __init__(self): self.an_attr = 10 """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ import mod1 a_var = mod1.AClass() another_var = a_var.an_attr""" ) ) self._rename(mod1, mod1.read().index("an_attr"), "attr") self.assertEqual( dedent("""\ class AClass(object): def __init__(self): self.attr = 10 """), mod1.read(), ) self.assertEqual( dedent("""\ import mod1 a_var = mod1.AClass() another_var = a_var.attr""" ), mod2.read() ) def test_renaming_class_attributes2(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ class AClass(object): def __init__(self): an_attr = 10 self.an_attr = 10 """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ import mod1 a_var = mod1.AClass() another_var = a_var.an_attr""" ) ) self._rename(mod1, mod1.read().rindex("an_attr"), "attr") self.assertEqual( dedent("""\ class AClass(object): def __init__(self): an_attr = 10 self.attr = 10 """), mod1.read(), ) self.assertEqual( dedent("""\ import mod1 a_var = mod1.AClass() another_var = a_var.attr""" ), mod2.read(), ) def test_renaming_methods_in_subclasses(self): mod = testutils.create_module(self.project, "mod1") mod.write( dedent("""\ class A(object): def a_method(self): pass class B(A): def a_method(self): pass """) ) self._rename( mod, mod.read().rindex("a_method") + 1, "new_method", in_hierarchy=True ) self.assertEqual( dedent("""\ class A(object): def new_method(self): pass class B(A): def new_method(self): pass """), mod.read(), ) def test_renaming_methods_in_sibling_classes(self): mod = testutils.create_module(self.project, "mod1") mod.write( dedent("""\ class A(object): def a_method(self): pass class B(A): def a_method(self): pass class C(A): def a_method(self): pass """) ) self._rename( mod, mod.read().rindex("a_method") + 1, "new_method", in_hierarchy=True ) self.assertEqual( dedent("""\ class A(object): def new_method(self): pass class B(A): def new_method(self): pass class C(A): def new_method(self): pass """), mod.read(), ) def test_not_renaming_methods_in_hierarchies(self): mod = testutils.create_module(self.project, "mod1") mod.write( dedent("""\ class A(object): def a_method(self): pass class B(A): def a_method(self): pass """) ) self._rename( mod, mod.read().rindex("a_method") + 1, "new_method", in_hierarchy=False ) self.assertEqual( dedent("""\ class A(object): def a_method(self): pass class B(A): def new_method(self): pass """), mod.read(), ) def test_undoing_refactorings(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ def a_func(): pass a_func() """) ) self._rename(mod1, len(mod1.read()) - 5, "new_func") self.project.history.undo() self.assertEqual( dedent("""\ def a_func(): pass a_func() """), mod1.read(), ) def test_undoing_renaming_modules(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write( dedent("""\ def a_func(): pass """) ) mod2 = testutils.create_module(self.project, "mod2") mod2.write("from mod1 import a_func\n") self._rename(mod2, 6, "newmod") self.project.history.undo() self.assertEqual("mod1.py", mod1.path) self.assertEqual("from mod1 import a_func\n", mod2.read()) def test_rename_in_module_renaming_one_letter_names_for_expressions(self): mod1 = testutils.create_module(self.project, "mod1") mod1.write("a = 10\nprint(1+a)\n") pymod = self.project.get_module("mod1") old_pyname = pymod["a"] finder = rope.refactor.occurrences.create_finder(self.project, "a", old_pyname) refactored = rename.rename_in_module( finder, "new_var", pymodule=pymod, replace_primary=True ) self.assertEqual( dedent("""\ new_var = 10 print(1+new_var) """), refactored, ) def test_renaming_for_loop_variable(self): code = dedent("""\ for var in range(10): print(var) """) refactored = self._local_rename(code, code.find("var") + 1, "new_var") self.assertEqual( dedent("""\ for new_var in range(10): print(new_var) """), refactored, ) @testutils.only_for("3.5") def test_renaming_async_for_loop_variable(self): code = dedent("""\ async def func(): async for var in range(10): print(var) """) refactored = self._local_rename(code, code.find("var") + 1, "new_var") self.assertEqual( dedent("""\ async def func(): async for new_var in range(10): print(new_var) """), refactored, ) @testutils.only_for("3.5") def test_renaming_async_with_context_manager(self): code = dedent("""\ def a_cm(): pass async def a_func(): async with a_cm() as x: pass""") refactored = self._local_rename(code, code.find("a_cm") + 1, "another_cm") expected = dedent("""\ def another_cm(): pass async def a_func(): async with another_cm() as x: pass""") self.assertEqual(refactored, expected) @testutils.only_for("3.5") def test_renaming_async_with_as_variable(self): code = dedent("""\ async def func(): async with a_func() as var: print(var) """) refactored = self._local_rename(code, code.find("var") + 1, "new_var") self.assertEqual( dedent("""\ async def func(): async with a_func() as new_var: print(new_var) """), refactored, ) def test_renaming_parameters(self): code = dedent("""\ def a_func(param): print(param) a_func(param=hey) """) refactored = self._local_rename(code, code.find("param") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param): print(new_param) a_func(new_param=hey) """), refactored, ) def test_renaming_assigned_parameters(self): code = dedent("""\ def f(p): p = p + 1 return p f(p=1) """) refactored = self._local_rename(code, code.find("p"), "arg") self.assertEqual( dedent("""\ def f(arg): arg = arg + 1 return arg f(arg=1) """), refactored, ) def test_renaming_parameters_not_renaming_others(self): code = dedent("""\ def a_func(param): print(param) param=10 a_func(param) """) refactored = self._local_rename(code, code.find("param") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param): print(new_param) param=10 a_func(param) """), refactored, ) def test_renaming_parameters_not_renaming_others2(self): code = dedent("""\ def a_func(param): print(param) param=10 a_func(param=param)""") refactored = self._local_rename(code, code.find("param") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param): print(new_param) param=10 a_func(new_param=param)""" ), refactored, ) def test_renaming_parameters_with_multiple_params(self): code = dedent("""\ def a_func(param1, param2): print(param1) a_func(param1=1, param2=2) """) refactored = self._local_rename(code, code.find("param1") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param, param2): print(new_param) a_func(new_param=1, param2=2) """), refactored, ) def test_renaming_parameters_with_multiple_params2(self): code = dedent("""\ def a_func(param1, param2): print(param1) a_func(param1=1, param2=2) """) refactored = self._local_rename(code, code.rfind("param2") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(param1, new_param): print(param1) a_func(param1=1, new_param=2) """), refactored, ) def test_renaming_parameters_on_calls(self): code = dedent("""\ def a_func(param): print(param) a_func(param = hey) """) refactored = self._local_rename(code, code.rfind("param") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param): print(new_param) a_func(new_param = hey) """), refactored, ) def test_renaming_parameters_spaces_before_call(self): code = dedent("""\ def a_func(param): print(param) a_func (param=hey) """) refactored = self._local_rename(code, code.rfind("param") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param): print(new_param) a_func (new_param=hey) """), refactored, ) def test_renaming_parameter_like_objects_after_keywords(self): code = dedent("""\ def a_func(param): print(param) dict(param=hey) """) refactored = self._local_rename(code, code.find("param") + 1, "new_param") self.assertEqual( dedent("""\ def a_func(new_param): print(new_param) dict(param=hey) """), refactored, ) def test_renaming_variables_in_init_dot_pys(self): pkg = testutils.create_package(self.project, "pkg") init_dot_py = pkg.get_child("__init__.py") init_dot_py.write("a_var = 10\n") mod = testutils.create_module(self.project, "mod") mod.write("import pkg\nprint(pkg.a_var)\n") self._rename(mod, mod.read().index("a_var") + 1, "new_var") self.assertEqual("new_var = 10\n", init_dot_py.read()) self.assertEqual("import pkg\nprint(pkg.new_var)\n", mod.read()) def test_renaming_variables_in_init_dot_pys2(self): pkg = testutils.create_package(self.project, "pkg") init_dot_py = pkg.get_child("__init__.py") init_dot_py.write("a_var = 10\n") mod = testutils.create_module(self.project, "mod") mod.write("import pkg\nprint(pkg.a_var)\n") self._rename(init_dot_py, init_dot_py.read().index("a_var") + 1, "new_var") self.assertEqual("new_var = 10\n", init_dot_py.read()) self.assertEqual("import pkg\nprint(pkg.new_var)\n", mod.read()) def test_renaming_variables_in_init_dot_pys3(self): pkg = testutils.create_package(self.project, "pkg") init_dot_py = pkg.get_child("__init__.py") init_dot_py.write("a_var = 10\n") mod = testutils.create_module(self.project, "mod") mod.write("import pkg\nprint(pkg.a_var)\n") self._rename(mod, mod.read().index("a_var") + 1, "new_var") self.assertEqual("new_var = 10\n", init_dot_py.read()) self.assertEqual("import pkg\nprint(pkg.new_var)\n", mod.read()) def test_renaming_resources_using_rename_module_refactoring(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write("a_var = 1") mod2.write("import mod1\nmy_var = mod1.a_var\n") renamer = rename.Rename(self.project, mod1) renamer.get_changes("newmod").do() self.assertEqual("import newmod\nmy_var = newmod.a_var\n", mod2.read()) def test_renam_resources_using_rename_module_refactor_for_packages(self): mod1 = testutils.create_module(self.project, "mod1") pkg = testutils.create_package(self.project, "pkg") mod1.write("import pkg\nmy_pkg = pkg") renamer = rename.Rename(self.project, pkg) renamer.get_changes("newpkg").do() self.assertEqual("import newpkg\nmy_pkg = newpkg", mod1.read()) def test_renam_resources_use_rename_module_refactor_for_init_dot_py(self): mod1 = testutils.create_module(self.project, "mod1") pkg = testutils.create_package(self.project, "pkg") mod1.write("import pkg\nmy_pkg = pkg") renamer = rename.Rename(self.project, pkg.get_child("__init__.py")) renamer.get_changes("newpkg").do() self.assertEqual("import newpkg\nmy_pkg = newpkg", mod1.read()) def test_renaming_global_variables(self): code = dedent("""\ a_var = 1 def a_func(): global a_var var = a_var """) refactored = self._local_rename(code, code.index("a_var"), "new_var") self.assertEqual( dedent("""\ new_var = 1 def a_func(): global new_var var = new_var """), refactored, ) def test_renaming_global_variables2(self): code = dedent("""\ a_var = 1 def a_func(): global a_var var = a_var """) refactored = self._local_rename(code, code.rindex("a_var"), "new_var") self.assertEqual( dedent("""\ new_var = 1 def a_func(): global new_var var = new_var """), refactored, ) def test_renaming_when_unsure(self): code = dedent("""\ class C(object): def a_func(self): pass def f(arg): arg.a_func() """) mod1 = testutils.create_module(self.project, "mod1") mod1.write(code) self._rename(mod1, code.index("a_func"), "new_func", unsure=self._true) self.assertEqual( dedent("""\ class C(object): def new_func(self): pass def f(arg): arg.new_func() """), mod1.read(), ) def _true(self, *args): return True def test_renaming_when_unsure_with_confirmation(self): def confirm(occurrence): return False code = dedent("""\ class C(object): def a_func(self): pass def f(arg): arg.a_func() """) mod1 = testutils.create_module(self.project, "mod1") mod1.write(code) self._rename(mod1, code.index("a_func"), "new_func", unsure=confirm) self.assertEqual( dedent("""\ class C(object): def new_func(self): pass def f(arg): arg.a_func() """), mod1.read(), ) def test_renaming_when_unsure_not_renaming_knowns(self): code = dedent("""\ class C1(object): def a_func(self): pass class C2(object): def a_func(self): pass c1 = C1() c1.a_func() c2 = C2() c2.a_func() """) mod1 = testutils.create_module(self.project, "mod1") mod1.write(code) self._rename(mod1, code.index("a_func"), "new_func", unsure=self._true) self.assertEqual( dedent("""\ class C1(object): def new_func(self): pass class C2(object): def a_func(self): pass c1 = C1() c1.new_func() c2 = C2() c2.a_func() """), mod1.read(), ) def test_renaming_in_strings_and_comments(self): code = dedent("""\ a_var = 1 # a_var """) mod1 = testutils.create_module(self.project, "mod1") mod1.write(code) self._rename(mod1, code.index("a_var"), "new_var", docs=True) self.assertEqual( dedent("""\ new_var = 1 # new_var """), mod1.read(), ) def test_not_renaming_in_strings_and_comments_where_not_visible(self): code = dedent("""\ def f(): a_var = 1 # a_var """) mod1 = testutils.create_module(self.project, "mod1") mod1.write(code) self._rename(mod1, code.index("a_var"), "new_var", docs=True) self.assertEqual( dedent("""\ def f(): new_var = 1 # a_var """), mod1.read(), ) def test_not_renaming_all_text_occurrences_in_strings_and_comments(self): code = dedent("""\ a_var = 1 # a_vard _a_var """) mod1 = testutils.create_module(self.project, "mod1") mod1.write(code) self._rename(mod1, code.index("a_var"), "new_var", docs=True) self.assertEqual( dedent("""\ new_var = 1 # a_vard _a_var """), mod1.read(), ) def test_renaming_occurrences_in_overwritten_scopes(self): refactored = self._local_rename( dedent("""\ a_var = 20 def f(): print(a_var) def f(): print(a_var) """), 2, "new_var", ) self.assertEqual( dedent("""\ new_var = 20 def f(): print(new_var) def f(): print(new_var) """), refactored, ) def test_renaming_occurrences_in_overwritten_scopes2(self): code = dedent("""\ def f(): a_var = 1 print(a_var) def f(): a_var = 1 print(a_var) """) refactored = self._local_rename(code, code.index("a_var") + 1, "new_var") self.assertEqual(code.replace("a_var", "new_var", 2), refactored) @testutils.only_for_versions_higher("3.5") def test_renaming_in_generalized_dict_unpacking(self): code = dedent("""\ a_var = {**{'stuff': 'can'}, **{'stuff': 'crayon'}} if "stuff" in a_var: print("ya") """) mod1 = testutils.create_module(self.project, "mod1") mod1.write(code) refactored = self._local_rename(code, code.index("a_var") + 1, "new_var") expected = dedent("""\ new_var = {**{'stuff': 'can'}, **{'stuff': 'crayon'}} if "stuff" in new_var: print("ya") """) self.assertEqual(expected, refactored) def test_dos_line_ending_and_renaming(self): code = "\r\na = 1\r\n\r\nprint(2 + a + 2)\r\n" offset = code.replace("\r\n", "\n").rindex("a") refactored = self._local_rename(code, offset, "b") self.assertEqual( "\nb = 1\n\nprint(2 + b + 2)\n", refactored.replace("\r\n", "\n") ) def test_multi_byte_strs_and_renaming(self): s = u"{LATIN SMALL LETTER I WITH DIAERESIS}" * 4 code = u"# -*- coding: utf-8 -*-\n# " + s + "\na = 1\nprint(2 + a + 2)\n" refactored = self._local_rename(code, code.rindex("a"), "b") self.assertEqual( u"# -*- coding: utf-8 -*-\n# " + s + "\nb = 1\nprint(2 + b + 2)\n", refactored, ) def test_resources_parameter(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write( dedent("""\ def f(): pass """) ) mod2.write( dedent("""\ import mod1 mod1.f() """) ) self._rename(mod1, mod1.read().rindex("f"), "g", resources=[mod1]) self.assertEqual( dedent("""\ def g(): pass """), mod1.read(), ) self.assertEqual( dedent("""\ import mod1 mod1.f() """), mod2.read(), ) def test_resources_parameter_not_changing_defining_module(self): mod1 = testutils.create_module(self.project, "mod1") mod2 = testutils.create_module(self.project, "mod2") mod1.write( dedent("""\ def f(): pass """) ) mod2.write( dedent("""\ import mod1 mod1.f() """) ) self._rename(mod1, mod1.read().rindex("f"), "g", resources=[mod2]) self.assertEqual( dedent("""\ def f(): pass """), mod1.read(), ) self.assertEqual( dedent("""\ import mod1 mod1.g() """), mod2.read(), ) # XXX: with variables should not leak @testutils.only_for("2.5") def xxx_test_with_statement_variables_should_not_leak(self): code = dedent("""\ f = 1 with open("1.txt") as f: print(f) """) if sys.version_info < (2, 6, 0): code = "from __future__ import with_statement\n" + code mod1 = testutils.create_module(self.project, "mod1") mod1.write(code) self._rename(mod1, code.rindex("f"), "file") expected = dedent("""\ f = 1 with open("1.txt") as file: print(file) """) self.assertEqual(expected, mod1.read()) def test_rename_in_list_comprehension(self): code = dedent("""\ some_var = 1 compr = [some_var for some_var in range(10)] """) offset = code.index("some_var") refactored = self._local_rename(code, offset, "new_var") expected = dedent("""\ new_var = 1 compr = [some_var for some_var in range(10)] """) 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): self.project = testutils.sample_project() self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(ChangeOccurrencesTest, self).tearDown() def test_simple_case(self): self.mod.write( dedent("""\ a_var = 1 print(a_var) """) ) changer = rename.ChangeOccurrences( self.project, self.mod, self.mod.read().index("a_var") ) changer.get_changes("new_var").do() self.assertEqual( dedent("""\ new_var = 1 print(new_var) """), self.mod.read(), ) def test_only_performing_inside_scopes(self): self.mod.write( dedent("""\ a_var = 1 new_var = 2 def f(): print(a_var) """) ) changer = rename.ChangeOccurrences( self.project, self.mod, self.mod.read().rindex("a_var") ) changer.get_changes("new_var").do() self.assertEqual( dedent("""\ a_var = 1 new_var = 2 def f(): print(new_var) """), self.mod.read(), ) def test_only_performing_on_calls(self): self.mod.write( dedent("""\ def f1(): pass def f2(): pass g = f1 a = f1() """) ) changer = rename.ChangeOccurrences( self.project, self.mod, self.mod.read().rindex("f1") ) changer.get_changes("f2", only_calls=True).do() self.assertEqual( dedent("""\ def f1(): pass def f2(): pass g = f1 a = f2() """), self.mod.read(), ) def test_only_performing_on_reads(self): self.mod.write( dedent("""\ a = 1 b = 2 print(a) """) ) changer = rename.ChangeOccurrences( self.project, self.mod, self.mod.read().rindex("a") ) changer.get_changes("b", writes=False).do() self.assertEqual( dedent("""\ a = 1 b = 2 print(b) """), self.mod.read(), ) class ImplicitInterfacesTest(unittest.TestCase): def setUp(self): super(ImplicitInterfacesTest, self).setUp() self.project = testutils.sample_project(validate_objectdb=True) self.pycore = self.project.pycore self.mod1 = testutils.create_module(self.project, "mod1") self.mod2 = testutils.create_module(self.project, "mod2") def tearDown(self): testutils.remove_project(self.project) super(ImplicitInterfacesTest, self).tearDown() def _rename(self, resource, offset, new_name, **kwds): changes = Rename(self.project, resource, offset).get_changes(new_name, **kwds) self.project.do(changes) def test_performing_rename_on_parameters(self): self.mod1.write("def f(arg):\n arg.run()\n") self.mod2.write( dedent("""\ import mod1 class A(object): def run(self): pass class B(object): def run(self): pass mod1.f(A()) mod1.f(B()) """) ) self.pycore.analyze_module(self.mod2) self._rename(self.mod1, self.mod1.read().index("run"), "newrun") self.assertEqual("def f(arg):\n arg.newrun()\n", self.mod1.read()) self.assertEqual( dedent("""\ import mod1 class A(object): def newrun(self): pass class B(object): def newrun(self): pass mod1.f(A()) mod1.f(B()) """), self.mod2.read(), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1637501104.0 rope-0.22.0/ropetest/refactor/restructuretest.py0000664000175000017500000002332200000000000021750 0ustar00lieryanlieryanfrom textwrap import dedent from rope.refactor import restructure from ropetest import testutils try: import unittest2 as unittest except ImportError: import unittest class RestructureTest(unittest.TestCase): def setUp(self): super(RestructureTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(RestructureTest, self).tearDown() def test_trivial_case(self): refactoring = restructure.Restructure(self.project, "a = 1", "a = 0") self.mod.write("b = 1\n") self.project.do(refactoring.get_changes()) self.assertEqual("b = 1\n", self.mod.read()) def test_replacing_simple_patterns(self): refactoring = restructure.Restructure(self.project, "a = 1", "a = int(1)") self.mod.write("a = 1\nb = 1\n") self.project.do(refactoring.get_changes()) self.assertEqual("a = int(1)\nb = 1\n", self.mod.read()) def test_replacing_patterns_with_normal_names(self): refactoring = restructure.Restructure( self.project, "${a} = 1", "${a} = int(1)", args={"a": "exact"} ) self.mod.write("a = 1\nb = 1\n") self.project.do(refactoring.get_changes()) self.assertEqual("a = int(1)\nb = 1\n", self.mod.read()) def test_replacing_patterns_with_any_names(self): refactoring = restructure.Restructure(self.project, "${a} = 1", "${a} = int(1)") self.mod.write("a = 1\nb = 1\n") self.project.do(refactoring.get_changes()) self.assertEqual("a = int(1)\nb = int(1)\n", self.mod.read()) def test_replacing_patterns_with_any_names2(self): refactoring = restructure.Restructure(self.project, "${x} + ${x}", "${x} * 2") self.mod.write("a = 1 + 1\n") self.project.do(refactoring.get_changes()) self.assertEqual("a = 1 * 2\n", self.mod.read()) def test_replacing_patterns_with_checks(self): self.mod.write( dedent("""\ def f(p=1): return p g = f g() """) ) refactoring = restructure.Restructure( self.project, "${f}()", "${f}(2)", args={"f": "object=mod.f"} ) self.project.do(refactoring.get_changes()) self.assertEqual( dedent("""\ def f(p=1): return p g = f g(2) """), self.mod.read(), ) def test_replacing_assignments_with_sets(self): refactoring = restructure.Restructure( self.project, "${a} = ${b}", "${a}.set(${b})" ) self.mod.write("a = 1\nb = 1\n") self.project.do(refactoring.get_changes()) self.assertEqual("a.set(1)\nb.set(1)\n", self.mod.read()) def test_replacing_sets_with_assignments(self): refactoring = restructure.Restructure( self.project, "${a}.set(${b})", "${a} = ${b}" ) self.mod.write("a.set(1)\nb.set(1)\n") self.project.do(refactoring.get_changes()) self.assertEqual( dedent("""\ a = 1 b = 1 """), self.mod.read(), ) def test_using_make_checks(self): self.mod.write( dedent("""\ def f(p=1): return p g = f g() """) ) refactoring = restructure.Restructure( self.project, "${f}()", "${f}(2)", args={"f": "object=mod.f"} ) self.project.do(refactoring.get_changes()) self.assertEqual( dedent("""\ def f(p=1): return p g = f g(2) """), self.mod.read(), ) def test_using_make_checking_builtin_types(self): self.mod.write("a = 1 + 1\n") refactoring = restructure.Restructure( self.project, "${i} + ${i}", "${i} * 2", args={"i": "type=__builtin__.int"} ) self.project.do(refactoring.get_changes()) self.assertEqual("a = 1 * 2\n", self.mod.read()) def test_auto_indentation_when_no_indentation(self): self.mod.write("a = 2\n") refactoring = restructure.Restructure( self.project, "${a} = 2", "${a} = 1\n${a} += 1" ) self.project.do(refactoring.get_changes()) self.assertEqual( dedent("""\ a = 1 a += 1 """), self.mod.read(), ) def test_auto_indentation(self): self.mod.write( dedent("""\ def f(): a = 2 """) ) refactoring = restructure.Restructure( self.project, "${a} = 2", "${a} = 1\n${a} += 1" ) self.project.do(refactoring.get_changes()) self.assertEqual( dedent("""\ def f(): a = 1 a += 1 """), self.mod.read(), ) def test_auto_indentation_and_not_indenting_blanks(self): self.mod.write("def f():\n a = 2\n") refactoring = restructure.Restructure( self.project, "${a} = 2", "${a} = 1\n\n${a} += 1" ) self.project.do(refactoring.get_changes()) self.assertEqual("def f():\n a = 1\n\n a += 1\n", self.mod.read()) def test_importing_names(self): self.mod.write("a = 2\n") refactoring = restructure.Restructure( self.project, "${a} = 2", "${a} = myconsts.two", imports=["import myconsts"] ) self.project.do(refactoring.get_changes()) self.assertEqual( dedent("""\ import myconsts a = myconsts.two """), self.mod.read(), ) def test_not_importing_names_when_there_are_no_changes(self): self.mod.write("a = True\n") refactoring = restructure.Restructure( self.project, "${a} = 2", "${a} = myconsts.two", imports=["import myconsts"] ) self.project.do(refactoring.get_changes()) self.assertEqual("a = True\n", self.mod.read()) def test_handling_containing_matches(self): self.mod.write("a = 1 / 2 / 3\n") refactoring = restructure.Restructure( self.project, "${a} / ${b}", "${a} // ${b}" ) self.project.do(refactoring.get_changes()) self.assertEqual("a = 1 // 2 // 3\n", self.mod.read()) def test_handling_overlapping_matches(self): self.mod.write("a = 1\na = 1\na = 1\n") refactoring = restructure.Restructure(self.project, "a = 1\na = 1\n", "b = 1") self.project.do(refactoring.get_changes()) self.assertEqual("b = 1\na = 1\n", self.mod.read()) def test_preventing_stack_overflow_when_matching(self): self.mod.write("1\n") refactoring = restructure.Restructure(self.project, "${a}", "${a}") self.project.do(refactoring.get_changes()) self.assertEqual("1\n", self.mod.read()) def test_performing_a_restructuring_to_all_modules(self): mod2 = testutils.create_module(self.project, "mod2") self.mod.write("a = 1\n") mod2.write("b = 1\n") refactoring = restructure.Restructure(self.project, "1", "2 / 1") self.project.do(refactoring.get_changes()) self.assertEqual("a = 2 / 1\n", self.mod.read()) self.assertEqual("b = 2 / 1\n", mod2.read()) def test_performing_a_restructuring_to_selected_modules(self): mod2 = testutils.create_module(self.project, "mod2") self.mod.write("a = 1\n") mod2.write("b = 1\n") refactoring = restructure.Restructure(self.project, "1", "2 / 1") self.project.do(refactoring.get_changes(resources=[mod2])) self.assertEqual("a = 1\n", self.mod.read()) self.assertEqual("b = 2 / 1\n", mod2.read()) def test_unsure_argument_of_default_wildcard(self): self.mod.write( dedent("""\ def f(p): return p * 2 x = "" * 2 i = 1 * 2 """) ) refactoring = restructure.Restructure( self.project, "${s} * 2", "dup(${s})", args={"s": {"type": "__builtins__.str", "unsure": True}}, ) self.project.do(refactoring.get_changes()) self.assertEqual( dedent("""\ def f(p): return dup(p) x = dup("") i = 1 * 2 """), self.mod.read(), ) def test_statement_after_string_and_column(self): mod_text = dedent("""\ def f(x): if a == "a": raise Exception("test") """) self.mod.write(mod_text) 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(), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/similarfindertest.py0000664000175000017500000003031500000000000022211 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.refactor import similarfinder from ropetest import testutils class SimilarFinderTest(unittest.TestCase): def setUp(self): super(SimilarFinderTest, self).setUp() self.project = testutils.sample_project() self.mod = testutils.create_module(self.project, "mod") def tearDown(self): testutils.remove_project(self.project) super(SimilarFinderTest, self).tearDown() def _create_finder(self, source, **kwds): self.mod.write(source) pymodule = self.project.get_pymodule(self.mod) return similarfinder.SimilarFinder(pymodule, **kwds) def test_trivial_case(self): finder = self._create_finder("") self.assertEqual([], list(finder.get_match_regions("10"))) def test_constant_integer(self): source = "a = 10\n" finder = self._create_finder(source) result = [(source.index("10"), source.index("10") + 2)] self.assertEqual(result, list(finder.get_match_regions("10"))) def test_bool_is_not_similar_to_integer(self): source = dedent("""\ a = False b = 0""") finder = self._create_finder(source) result = [(source.index("False"), source.index("False") + len("False"))] self.assertEqual(result, list(finder.get_match_regions("False"))) def test_simple_addition(self): source = "a = 1 + 2\n" finder = self._create_finder(source) result = [(source.index("1"), source.index("2") + 1)] self.assertEqual(result, list(finder.get_match_regions("1 + 2"))) def test_simple_addition2(self): source = "a = 1 +2\n" finder = self._create_finder(source) result = [(source.index("1"), source.index("2") + 1)] self.assertEqual(result, list(finder.get_match_regions("1 + 2"))) def test_simple_assign_statements(self): source = "a = 1 + 2\n" finder = self._create_finder(source) self.assertEqual( [(0, len(source) - 1)], list(finder.get_match_regions("a = 1 + 2")) ) def test_simple_multiline_statements(self): source = dedent("""\ a = 1 b = 2 """) finder = self._create_finder(source) self.assertEqual( [(0, len(source) - 1)], list(finder.get_match_regions("a = 1\nb = 2")) ) def test_multiple_matches(self): source = "a = 1 + 1\n" finder = self._create_finder(source) result = list(finder.get_match_regions("1")) self.assertEqual(2, len(result)) start1 = source.index("1") self.assertEqual((start1, start1 + 1), result[0]) start2 = source.rindex("1") self.assertEqual((start2, start2 + 1), result[1]) def test_multiple_matches2(self): source = dedent("""\ a = 1 b = 2 a = 1 b = 2 """) finder = self._create_finder(source) self.assertEqual(2, len(list(finder.get_match_regions("a = 1\nb = 2")))) def test_restricting_the_region_to_search(self): source = "1\n\n1\n" finder = self._create_finder(source) result = list(finder.get_match_regions("1", start=2)) start = source.rfind("1") self.assertEqual([(start, start + 1)], result) def test_matching_basic_patterns(self): source = "b = a\n" finder = self._create_finder(source) result = list(finder.get_match_regions("${a}", args={"a": "exact"})) start = source.rfind("a") self.assertEqual([(start, start + 1)], result) def test_match_get_ast(self): source = "b = a\n" finder = self._create_finder(source) result = list(finder.get_matches("${a}", args={"a": "exact"})) self.assertEqual("a", result[0].get_ast("a").id) def test_match_get_ast_for_statements(self): source = "b = a\n" finder = self._create_finder(source) result = list(finder.get_matches("b = ${a}")) self.assertEqual("a", result[0].get_ast("a").id) def test_matching_multiple_patterns(self): source = "c = a + b\n" finder = self._create_finder(source) result = list(finder.get_matches("${a} + ${b}")) self.assertEqual("a", result[0].get_ast("a").id) self.assertEqual("b", result[0].get_ast("b").id) def test_matching_any_patterns(self): source = "b = a\n" finder = self._create_finder(source) result = list(finder.get_matches("b = ${x}")) self.assertEqual("a", result[0].get_ast("x").id) def test_matching_any_patterns_repeating(self): source = "b = 1 + 1\n" finder = self._create_finder(source) result = list(finder.get_matches("b = ${x} + ${x}")) self.assertEqual(1, result[0].get_ast("x").n) def test_matching_any_patterns_not_matching_different_nodes(self): source = "b = 1 + 2\n" finder = self._create_finder(source) result = list(finder.get_matches("b = ${x} + ${x}")) self.assertEqual(0, len(result)) def test_matching_normal_names_and_assname(self): source = "a = 1\n" finder = self._create_finder(source) result = list(finder.get_matches("${a} = 1")) self.assertEqual("a", result[0].get_ast("a").id) def test_matching_normal_names_and_assname2(self): source = "a = 1\n" finder = self._create_finder(source) result = list(finder.get_matches("${a}", args={"a": "exact"})) self.assertEqual(1, len(result)) def test_matching_normal_names_and_attributes(self): source = "x.a = 1\n" finder = self._create_finder(source) result = list(finder.get_matches("${a} = 1", args={"a": "exact"})) self.assertEqual(0, len(result)) def test_functions_not_matching_when_only_first_parameters(self): source = "f(1, 2)\n" finder = self._create_finder(source) self.assertEqual(0, len(list(finder.get_matches("f(1)")))) def test_matching_nested_try_finally(self): source = dedent("""\ if 1: try: pass except: pass """) pattern = dedent("""\ try: pass except: pass """) finder = self._create_finder(source) self.assertEqual(1, len(list(finder.get_matches(pattern)))) def test_matching_dicts_inside_functions(self): source = dedent("""\ def f(p): d = {1: p.x} """) pattern = "{1: ${a}.x}" finder = self._create_finder(source) self.assertEqual(1, len(list(finder.get_matches(pattern)))) class CheckingFinderTest(unittest.TestCase): def setUp(self): super(CheckingFinderTest, self).setUp() self.project = testutils.sample_project() self.mod1 = testutils.create_module(self.project, "mod1") def tearDown(self): testutils.remove_project(self.project) super(CheckingFinderTest, self).tearDown() def test_trivial_case(self): self.mod1.write("") pymodule = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymodule) self.assertEqual([], list(finder.get_matches("10", {}))) def test_simple_finding(self): self.mod1.write( dedent("""\ class A(object): pass a = A() """) ) pymodule = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymodule) result = list(finder.get_matches("${anything} = ${A}()", {})) self.assertEqual(1, len(result)) def test_not_matching_when_the_name_does_not_match(self): self.mod1.write( dedent("""\ class A(object): pass a = list() """) ) pymodule = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymodule) result = list(finder.get_matches("${anything} = ${C}()", {"C": "name=mod1.A"})) self.assertEqual(0, len(result)) def test_not_matching_unknowns_finding(self): self.mod1.write( dedent("""\ class A(object): pass a = unknown() """) ) pymodule = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymodule) result = list(finder.get_matches("${anything} = ${C}()", {"C": "name=mod1.A"})) self.assertEqual(0, len(result)) def test_finding_and_matching_pyobjects(self): source = dedent("""\ class A(object): pass NewA = A a = NewA() """) self.mod1.write(source) pymodule = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymodule) result = list( finder.get_matches("${anything} = ${A}()", {"A": "object=mod1.A"}) ) self.assertEqual(1, len(result)) start = source.rindex("a =") self.assertEqual((start, len(source) - 1), result[0].get_region()) def test_finding_and_matching_types(self): source = dedent("""\ class A(object): def f(self): pass a = A() b = a.f() """) self.mod1.write(source) pymodule = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymodule) result = list( finder.get_matches("${anything} = ${inst}.f()", {"inst": "type=mod1.A"}) ) self.assertEqual(1, len(result)) start = source.rindex("b") self.assertEqual((start, len(source) - 1), result[0].get_region()) def test_checking_the_type_of_an_ass_name_node(self): self.mod1.write( dedent("""\ class A(object): pass an_a = A() """) ) pymodule = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymodule) result = list(finder.get_matches("${a} = ${assigned}", {"a": "type=mod1.A"})) self.assertEqual(1, len(result)) def test_checking_instance_of_an_ass_name_node(self): self.mod1.write( dedent("""\ class A(object): pass class B(A): pass b = B() """) ) pymodule = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymodule) result = list( finder.get_matches("${a} = ${assigned}", {"a": "instance=mod1.A"}) ) self.assertEqual(1, len(result)) def test_checking_equality_of_imported_pynames(self): mod2 = testutils.create_module(self.project, "mod2") mod2.write( dedent("""\ class A(object): pass """) ) self.mod1.write( dedent("""\ from mod2 import A an_a = A() """) ) pymod1 = self.project.get_pymodule(self.mod1) finder = similarfinder.SimilarFinder(pymod1) result = list(finder.get_matches("${a_class}()", {"a_class": "name=mod2.A"})) self.assertEqual(1, len(result)) class TemplateTest(unittest.TestCase): def test_simple_templates(self): template = similarfinder.CodeTemplate("${a}\n") self.assertEqual(set(["a"]), set(template.get_names())) def test_ignoring_matches_in_comments(self): template = similarfinder.CodeTemplate("#${a}\n") self.assertEqual({}.keys(), template.get_names()) def test_ignoring_matches_in_strings(self): template = similarfinder.CodeTemplate("'${a}'\n") self.assertEqual({}.keys(), template.get_names()) def test_simple_substitution(self): template = similarfinder.CodeTemplate("${a}\n") self.assertEqual("b\n", template.substitute({"a": "b"})) def test_substituting_multiple_names(self): template = similarfinder.CodeTemplate("${a}, ${b}\n") self.assertEqual("1, 2\n", template.substitute({"a": "1", "b": "2"})) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636907704.0 rope-0.22.0/ropetest/refactor/suitestest.py0000664000175000017500000001557100000000000020704 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.base import ast from rope.refactor import suites class SuiteTest(unittest.TestCase): def setUp(self): super(SuiteTest, self).setUp() def tearDown(self): super(SuiteTest, self).tearDown() def test_trivial_case(self): root = source_suite_tree("") self.assertEqual(1, root.get_start()) self.assertEqual(0, len(root.get_children())) def test_simple_ifs(self): root = source_suite_tree( dedent("""\ if True: pass""" ) ) self.assertEqual(1, len(root.get_children())) def test_simple_else(self): root = source_suite_tree( dedent("""\ if True: pass else: pass """) ) self.assertEqual(2, len(root.get_children())) self.assertEqual(1, root.get_children()[1].get_start()) def test_for(self): root = source_suite_tree( dedent("""\ for i in range(10): pass else: pass """) ) self.assertEqual(2, len(root.get_children())) self.assertEqual(2, root.get_children()[1].get_start()) def test_while(self): root = source_suite_tree( dedent("""\ while True: pass """) ) self.assertEqual(1, len(root.get_children())) self.assertEqual(1, root.get_children()[0].get_start()) def test_with(self): root = source_suite_tree( dedent("""\ from __future__ import with_statement with file(x): pass """) ) self.assertEqual(1, len(root.get_children())) self.assertEqual(2, root.get_children()[0].get_start()) def test_try_finally(self): root = source_suite_tree( dedent("""\ try: pass finally: pass """) ) self.assertEqual(2, len(root.get_children())) self.assertEqual(1, root.get_children()[0].get_start()) def test_try_except(self): root = source_suite_tree( dedent("""\ try: pass except: pass else: pass """) ) self.assertEqual(3, len(root.get_children())) self.assertEqual(1, root.get_children()[2].get_start()) def test_try_except_finally(self): root = source_suite_tree( dedent("""\ try: pass except: pass finally: pass """) ) self.assertEqual(3, len(root.get_children())) self.assertEqual(1, root.get_children()[2].get_start()) def test_local_start_and_end(self): root = source_suite_tree( dedent("""\ if True: pass else: pass """) ) self.assertEqual(1, root.local_start()) self.assertEqual(4, root.local_end()) if_suite = root.get_children()[0] self.assertEqual(2, if_suite.local_start()) self.assertEqual(2, if_suite.local_end()) else_suite = root.get_children()[1] self.assertEqual(4, else_suite.local_start()) self.assertEqual(4, else_suite.local_end()) def test_find_suite(self): root = source_suite_tree("\n") self.assertEqual(root, root.find_suite(1)) def test_find_suite_for_ifs(self): root = source_suite_tree( dedent("""\ if True: pass """) ) if_suite = root.get_children()[0] self.assertEqual(if_suite, root.find_suite(2)) def test_find_suite_for_between_suites(self): root = source_suite_tree( dedent("""\ if True: pass print(1) if True: pass """) ) if_suite1 = root.get_children()[0] if_suite2 = root.get_children()[1] self.assertEqual(if_suite1, root.find_suite(2)) self.assertEqual(if_suite2, root.find_suite(5)) self.assertEqual(root, root.find_suite(3)) def test_simple_find_visible(self): root = source_suite_tree("a = 1\n") self.assertEqual(1, suites.find_visible_for_suite(root, [1])) def test_simple_find_visible_ifs(self): root = source_suite_tree( dedent("""\ if True: a = 1 b = 2 """) ) self.assertEqual(root.find_suite(3), root.find_suite(4)) self.assertEqual(3, suites.find_visible_for_suite(root, [3, 4])) def test_simple_find_visible_for_else(self): root = source_suite_tree( dedent("""\ if True: pass else: pass """) ) self.assertEqual(2, suites.find_visible_for_suite(root, [2, 4])) def test_simple_find_visible_for_different_suites(self): root = source_suite_tree( dedent("""\ if True: pass a = 1 if False: pass """) ) self.assertEqual(1, suites.find_visible_for_suite(root, [2, 3])) self.assertEqual(5, suites.find_visible_for_suite(root, [5])) self.assertEqual(1, suites.find_visible_for_suite(root, [2, 5])) def test_not_always_selecting_scope_start(self): root = source_suite_tree( dedent("""\ if True: a = 1 if True: pass else: pass """) ) self.assertEqual(3, suites.find_visible_for_suite(root, [4, 6])) self.assertEqual(3, suites.find_visible_for_suite(root, [3, 5])) self.assertEqual(3, suites.find_visible_for_suite(root, [4, 5])) def test_ignoring_functions(self): root = source_suite_tree( dedent("""\ def f(): pass a = 1 """) ) self.assertEqual(3, suites.find_visible_for_suite(root, [2, 3])) def test_ignoring_classes(self): root = source_suite_tree( dedent("""\ a = 1 class C(): pass """) ) self.assertEqual(1, suites.find_visible_for_suite(root, [1, 3])) def source_suite_tree(source): return suites.ast_suite_tree(ast.parse(source)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633441152.0 rope-0.22.0/ropetest/refactor/usefunctiontest.py0000664000175000017500000001455100000000000021727 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.base import exceptions from ropetest import testutils from rope.refactor.usefunction import UseFunction class UseFunctionTest(unittest.TestCase): def setUp(self): super(UseFunctionTest, self).setUp() self.project = testutils.sample_project() self.mod1 = testutils.create_module(self.project, "mod1") self.mod2 = testutils.create_module(self.project, "mod2") def tearDown(self): testutils.remove_project(self.project) super(UseFunctionTest, self).tearDown() def test_simple_case(self): code = dedent("""\ def f(): pass """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.project.do(user.get_changes()) self.assertEqual(code, self.mod1.read()) def test_simple_function(self): code = dedent("""\ def f(p): print(p) print(1) """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.project.do(user.get_changes()) self.assertEqual( dedent("""\ def f(p): print(p) f(1) """), self.mod1.read(), ) def test_simple_function2(self): code = dedent("""\ def f(p): print(p + 1) print(1 + 1) """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.project.do(user.get_changes()) self.assertEqual( dedent("""\ def f(p): print(p + 1) f(1) """), self.mod1.read(), ) def test_functions_with_multiple_statements(self): code = dedent("""\ def f(p): r = p + 1 print(r) r = 2 + 1 print(r) """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.project.do(user.get_changes()) self.assertEqual( dedent("""\ def f(p): r = p + 1 print(r) f(2) """), self.mod1.read(), ) def test_returning(self): code = dedent("""\ def f(p): return p + 1 r = 2 + 1 print(r) """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.project.do(user.get_changes()) self.assertEqual( dedent("""\ def f(p): return p + 1 r = f(2) print(r) """), self.mod1.read(), ) def test_returning_a_single_expression(self): code = dedent("""\ def f(p): return p + 1 print(2 + 1) """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.project.do(user.get_changes()) self.assertEqual( dedent("""\ def f(p): return p + 1 print(f(2)) """), self.mod1.read(), ) def test_occurrences_in_other_modules(self): code = dedent("""\ def f(p): return p + 1 """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.mod2.write("print(2 + 1)\n") self.project.do(user.get_changes()) self.assertEqual( dedent("""\ import mod1 print(mod1.f(2)) """), self.mod2.read(), ) def test_when_performing_on_non_functions(self): code = "var = 1\n" self.mod1.write(code) with self.assertRaises(exceptions.RefactoringError): UseFunction(self.project, self.mod1, code.rindex("var")) def test_differing_in_the_inner_temp_names(self): code = dedent("""\ def f(p): a = p + 1 print(a) b = 2 + 1 print(b) """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.project.do(user.get_changes()) self.assertEqual( dedent("""\ def f(p): a = p + 1 print(a) f(2) """), self.mod1.read(), ) # TODO: probably new options should be added to restructure def xxx_test_being_a_bit_more_intelligent_when_returning_assigneds(self): code = dedent("""\ def f(p): a = p + 1 return a var = 2 + 1 print(var) """) self.mod1.write(code) user = UseFunction(self.project, self.mod1, code.rindex("f")) self.project.do(user.get_changes()) self.assertEqual( dedent("""\ def f(p): a = p + 1 return a var = f(p) print(var) """), self.mod1.read(), ) def test_exception_when_performing_a_function_with_yield(self): code = dedent("""\ def func(): yield 1 """) self.mod1.write(code) with self.assertRaises(exceptions.RefactoringError): UseFunction(self.project, self.mod1, code.index("func")) def test_exception_when_performing_a_function_two_returns(self): code = dedent("""\ def func(): return 1 return 2 """) self.mod1.write(code) with self.assertRaises(exceptions.RefactoringError): UseFunction(self.project, self.mod1, code.index("func")) def test_exception_when_returns_is_not_the_last_statement(self): code = dedent("""\ def func(): return 2 a = 1 """) self.mod1.write(code) with self.assertRaises(exceptions.RefactoringError): UseFunction(self.project, self.mod1, code.index("func")) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633802493.0 rope-0.22.0/ropetest/runmodtest.py0000664000175000017500000001457500000000000017072 0ustar00lieryanlieryanimport os from textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.base import exceptions from ropetest import testutils class PythonFileRunnerTest(unittest.TestCase): def setUp(self): super(PythonFileRunnerTest, self).setUp() self.project = testutils.sample_project() self.pycore = self.project.pycore def tearDown(self): testutils.remove_project(self.project) super(PythonFileRunnerTest, self).tearDown() def make_sample_python_file(self, file_path, get_text_function_source=None): self.project.root.create_file(file_path) file = self.project.get_resource(file_path) if not get_text_function_source: get_text_function_source = "def get_text():\n return 'run'\n\n" file_content = ( get_text_function_source + "output = open('output.txt', 'w')\n" "output.write(get_text())\noutput.close()\n" ) file.write(file_content) def get_output_file_content(self, file_path): try: output_path = "" last_slash = file_path.rfind("/") if last_slash != -1: output_path = file_path[0 : last_slash + 1] file = self.project.get_resource(output_path + "output.txt") return file.read() except exceptions.ResourceNotFoundError: return "" def test_making_runner(self): file_path = "sample.py" self.make_sample_python_file(file_path) file_resource = self.project.get_resource(file_path) runner = self.pycore.run_module(file_resource) runner.wait_process() self.assertEqual("run", self.get_output_file_content(file_path)) def test_passing_arguments(self): file_path = "sample.py" function_source = dedent("""\ import sys def get_text(): return str(sys.argv[1:]) """) self.make_sample_python_file(file_path, function_source) file_resource = self.project.get_resource(file_path) runner = self.pycore.run_module(file_resource, args=["hello", "world"]) runner.wait_process() self.assertTrue( self.get_output_file_content(file_path).endswith("['hello', 'world']") ) def test_passing_arguments_with_spaces(self): file_path = "sample.py" function_source = dedent("""\ import sys def get_text(): return str(sys.argv[1:]) """) self.make_sample_python_file(file_path, function_source) file_resource = self.project.get_resource(file_path) runner = self.pycore.run_module(file_resource, args=["hello world"]) runner.wait_process() self.assertTrue( self.get_output_file_content(file_path).endswith("['hello world']") ) def test_killing_runner(self): file_path = "sample.py" code = dedent("""\ def get_text(): import time time.sleep(1) return 'run' """) self.make_sample_python_file( file_path, code, ) file_resource = self.project.get_resource(file_path) runner = self.pycore.run_module(file_resource) runner.kill_process() self.assertEqual("", self.get_output_file_content(file_path)) def test_running_nested_files(self): self.project.root.create_folder("src") file_path = "src/sample.py" self.make_sample_python_file(file_path) file_resource = self.project.get_resource(file_path) runner = self.pycore.run_module(file_resource) runner.wait_process() self.assertEqual("run", self.get_output_file_content(file_path)) def test_setting_process_input(self): file_path = "sample.py" code = dedent("""\ def get_text(): import sys return sys.stdin.readline() """) self.make_sample_python_file(file_path, code) temp_file_name = "processtest.tmp" try: temp_file = open(temp_file_name, "w") temp_file.write("input text\n") temp_file.close() file_resource = self.project.get_resource(file_path) stdin = open(temp_file_name) runner = self.pycore.run_module(file_resource, stdin=stdin) runner.wait_process() stdin.close() self.assertEqual("input text\n", self.get_output_file_content(file_path)) finally: os.remove(temp_file_name) def test_setting_process_output(self): file_path = "sample.py" code = dedent("""\ def get_text(): print('output text') return 'run' """) self.make_sample_python_file(file_path, code) temp_file_name = "processtest.tmp" try: file_resource = self.project.get_resource(file_path) stdout = open(temp_file_name, "w") runner = self.pycore.run_module(file_resource, stdout=stdout) runner.wait_process() stdout.close() temp_file = open(temp_file_name, "r") self.assertEqual("output text\n", temp_file.read()) temp_file.close() finally: os.remove(temp_file_name) def test_setting_pythonpath(self): src = self.project.root.create_folder("src") src.create_file("sample.py") src.get_child("sample.py").write("def f():\n pass\n") self.project.root.create_folder("test") file_path = "test/test.py" code = dedent("""\ def get_text(): import sample sample.f() return'run' """) self.make_sample_python_file(file_path, code) file_resource = self.project.get_resource(file_path) runner = self.pycore.run_module(file_resource) runner.wait_process() self.assertEqual("run", self.get_output_file_content(file_path)) def test_making_runner_when_doi_is_disabled(self): self.project.set("enable_doi", False) file_path = "sample.py" self.make_sample_python_file(file_path) file_resource = self.project.get_resource(file_path) runner = self.pycore.run_module(file_resource) runner.wait_process() self.assertEqual("run", self.get_output_file_content(file_path)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633432139.0 rope-0.22.0/ropetest/simplifytest.py0000664000175000017500000000433100000000000017407 0ustar00lieryanlieryantry: import unittest2 as unittest except ImportError: import unittest from rope.base import simplify class SimplifyTest(unittest.TestCase): def test_trivial_case(self): self.assertEqual("", simplify.real_code("")) def test_empty_strs(self): code = 's = ""\n' self.assertEqual(code, simplify.real_code(code)) def test_blanking_strs(self): code = 's = "..."\n' self.assertEqual('s = " "\n', simplify.real_code(code)) def test_changing_to_double_quotes(self): code = "s = ''\n" self.assertEqual('s = ""\n', simplify.real_code(code)) def test_changing_to_double_quotes2(self): code = 's = """\n"""\n' self.assertEqual('s = " "\n', simplify.real_code(code)) def test_removing_comments(self): code = "# c\n" self.assertEqual(" \n", simplify.real_code(code)) def test_removing_comments_that_contain_strings(self): code = '# "c"\n' self.assertEqual(" \n", simplify.real_code(code)) def test_removing_strings_containing_comments(self): code = '"#c"\n' self.assertEqual('" "\n', simplify.real_code(code)) def test_joining_implicit_continuations(self): code = "(\n)\n" self.assertEqual("( )\n", simplify.real_code(code)) def test_joining_explicit_continuations(self): code = "1 + \\\n 2\n" self.assertEqual("1 + 2\n", simplify.real_code(code)) def test_replacing_tabs(self): code = "1\t+\t2\n" self.assertEqual("1 + 2\n", simplify.real_code(code)) def test_replacing_semicolons(self): code = "a = 1;b = 2\n" self.assertEqual("a = 1\nb = 2\n", simplify.real_code(code)) def test_simplifying_f_string(self): code = 's = f"..{hello}.."\n' self.assertEqual('s = f"..{hello}.."\n', simplify.real_code(code)) def test_simplifying_f_string_containing_quotes(self): code = """s = f"..'{hello}'.."\n""" self.assertEqual("""s = f"..'{hello}'.."\n""", simplify.real_code(code)) def test_simplifying_uppercase_f_string_containing_quotes(self): code = """s = Fr"..'{hello}'.."\n""" self.assertEqual("""s = Fr"..'{hello}'.."\n""", simplify.real_code(code)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1632648447.0 rope-0.22.0/ropetest/testutils.py0000664000175000017500000000642600000000000016722 0ustar00lieryanlieryanimport os.path import shutil import sys import logging logging.basicConfig(format="%(levelname)s:%(funcName)s:%(message)s", level=logging.INFO) try: import unittest2 as unittest except ImportError: import unittest import rope.base.project from rope.contrib import generate def sample_project(root=None, foldername=None, **kwds): if root is None: root = "sample_project" if foldername: root = foldername # HACK: Using ``/dev/shm/`` for faster tests if os.name == "posix": if os.path.isdir("/dev/shm") and os.access("/dev/shm", os.W_OK): root = "/dev/shm/" + root elif os.path.isdir("/tmp") and os.access("/tmp", os.W_OK): root = "/tmp/" + root logging.debug("Using %s as root of the project.", root) # Using these prefs for faster tests prefs = { "save_objectdb": False, "save_history": False, "validate_objectdb": False, "automatic_soa": False, "ignored_resources": [".ropeproject", "*.pyc"], "import_dynload_stdmods": False, } prefs.update(kwds) remove_recursively(root) project = rope.base.project.Project(root, **prefs) return project create_module = generate.create_module create_package = generate.create_package def remove_project(project): project.close() remove_recursively(project.address) def remove_recursively(path): import time # windows sometimes raises exceptions instead of removing files if os.name == "nt" or sys.platform == "cygwin": for i in range(12): try: _remove_recursively(path) except OSError as e: if e.errno not in (13, 16, 32): raise time.sleep(0.3) else: break else: _remove_recursively(path) def _remove_recursively(path): if not os.path.exists(path): return if os.path.isfile(path): os.remove(path) else: shutil.rmtree(path) def only_for(version): """Should be used as a decorator for a unittest.TestCase test method""" return unittest.skipIf( sys.version < version, "This test requires at least {0} version of Python.".format(version), ) def only_for_versions_lower(version): """Should be used as a decorator for a unittest.TestCase test method""" return unittest.skipIf( sys.version > version, "This test requires version of Python lower than {0}".format(version), ) def only_for_versions_higher(version): """Should be used as a decorator for a unittest.TestCase test method""" return unittest.skipIf( sys.version < version, "This test requires version of Python higher than {0}".format(version), ) def skipNotPOSIX(): return unittest.skipIf(os.name != "posix", "This test works only on POSIX") def time_limit(timeout): if not any(procname in sys.argv[0] for procname in {"pytest", "py.test"}): # no-op when running tests without pytest return lambda *args, **kwargs: lambda func: func # do a local import so we don't import pytest when running without pytest import pytest # this prevents infinite loop/recursion from taking forever in CI return pytest.mark.time_limit(timeout) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1633802493.0 rope-0.22.0/ropetest/type_hinting_test.py0000664000175000017500000003642500000000000020424 0ustar00lieryanlieryanfrom textwrap import dedent try: import unittest2 as unittest except ImportError: import unittest from rope.contrib.codeassist import code_assist from rope.base.oi.type_hinting import evaluate from ropetest import testutils class AbstractHintingTest(unittest.TestCase): def setUp(self): super(AbstractHintingTest, self).setUp() self.project = testutils.sample_project() def tearDown(self): testutils.remove_project(self.project) super(AbstractHintingTest, self).tearDown() def _assist(self, code, offset=None, resource=None, **kwds): if offset is None: offset = len(code) return code_assist(self.project, code, offset, resource, **kwds) def assert_completion_in_result(self, name, scope, result): for proposal in result: if proposal.name == name and proposal.scope == scope: return self.fail( "completion <%s> in scope %r not proposed, available names: %r" % (name, scope, [(i.name, i.scope) for i in result]) ) def assert_completion_not_in_result(self, name, scope, result): for proposal in result: if proposal.name == name and proposal.scope == scope: self.fail("completion <%s> was proposed" % name) def run(self, result=None): if self.__class__.__name__.startswith("Abstract"): return super(AbstractHintingTest, self).run(result) class DocstringParamHintingTest(AbstractHintingTest): def test_hint_param(self): code = dedent('''\ class Sample(object): def a_method(self, a_arg): """:type a_arg: threading.Thread""" a_arg.is_a''') result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hierarchical_hint_param(self): code = dedent('''\ class ISample(object): def a_method(self, a_arg): """:type a_arg: threading.Thread""" class Sample(ISample): def a_method(self, a_arg): a_arg.is_a''') result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) class DocstringReturnHintingTest(AbstractHintingTest): def test_hierarchical_hint_rtype(self): code = dedent('''\ class ISample(object): def b_method(self): """:rtype: threading.Thread""" class Sample(ISample): def b_method(self): pass def a_method(self): self.b_method().is_a''') result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) class AbstractAssignmentHintingTest(AbstractHintingTest): def _make_class_hint(self, type_str): raise NotImplementedError def _make_constructor_hint(self, type_str): raise NotImplementedError def test_hint_attr(self): code = ( "class Sample(object):\n" + self._make_class_hint("threading.Thread") + " def a_method(self):\n" " self.a_attr.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hierarchical_hint_attr(self): code = ( "class ISample(object):\n" + self._make_class_hint("threading.Thread") + "\n\n" "class Sample(ISample):\n" " a_attr = None\n" " def a_method(self):\n" " self.a_attr.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_defined_by_constructor(self): code = ( "class Sample(object):\n" " def __init__(self, arg):\n" + self._make_constructor_hint("threading.Thread") + " def a_method(self):\n" " self.a_attr.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_attr_redefined_by_constructor(self): code = ( "class Sample(object):\n" + self._make_class_hint("threading.Thread") + " def __init__(self):\n" " self.a_attr = None\n" " def a_method(self):\n" " self.a_attr.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hierarchical_hint_attr_redefined_by_constructor(self): code = ( "class ISample(object):\n" + self._make_class_hint("threading.Thread") + "\n\n" "class Sample(ISample):\n" " def __init__(self):\n" " self.a_attr = None\n" " def a_method(self):\n" " self.a_attr.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_attr_for_pre_defined_type(self): code = ( "class Other(object):\n" " def is_alive(self):\n" " pass\n" "\n\n" "class Sample(object):\n" + self._make_class_hint("Other") + " def a_method(self):\n" " self.a_attr.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_attr_for_post_defined_type(self): code = ( "class Sample(object):\n" + self._make_class_hint("Other") + " def a_method(self):\n" " self.a_attr.is_a" ) offset = len(code) code += ( "\n\n" "class Other(object):\n" " def is_alive(self):\n" " pass\n" ) result = self._assist(code, offset) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_parametrized_list(self): code = ( "class Sample(object):\n" + self._make_class_hint("list[threading.Thread]") + " def a_method(self):\n" " for i in self.a_attr:\n" " i.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_parametrized_tuple(self): code = ( "class Sample(object):\n" + self._make_class_hint("tuple[threading.Thread]") + " def a_method(self):\n" " for i in self.a_attr:\n" " i.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_parametrized_set(self): code = ( "class Sample(object):\n" + self._make_class_hint("set[threading.Thread]") + " def a_method(self):\n" " for i in self.a_attr:\n" " i.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_parametrized_iterable(self): code = ( "class Sample(object):\n" + self._make_class_hint("collections.Iterable[threading.Thread]") + " def a_method(self):\n" " for i in self.a_attr:\n" " i.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_parametrized_iterator(self): code = ( "class Sample(object):\n" + self._make_class_hint("collections.Iterator[threading.Thread]") + " def a_method(self):\n" " for i in self.a_attr:\n" " i.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_parametrized_dict_key(self): code = ( "class Sample(object):\n" + self._make_class_hint("dict[str, threading.Thread]") + " def a_method(self):\n" " for i in self.a_attr.keys():\n" " i.sta" ) result = self._assist(code) self.assert_completion_in_result("startswith", "builtin", result) def test_hint_parametrized_dict_value(self): code = ( "class Sample(object):\n" + self._make_class_hint("dict[str, threading.Thread]") + " def a_method(self):\n" " for i in self.a_attr.values():\n" " i.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_parametrized_nested_tuple_list(self): code = ( "class Sample(object):\n" + self._make_class_hint("tuple[list[threading.Thread]]") + " def a_method(self):\n" " for j in self.a_attr:\n" " for i in j:\n" " i.is_a" ) result = self._assist(code) self.assert_completion_in_result("is_alive", "attribute", result) def test_hint_or(self): code = ( "class Sample(object):\n" + self._make_class_hint("str | threading.Thread") + " def a_method(self):\n" " for i in self.a_attr.values():\n" " i.is_a" ) result = self._assist(code) # Be sure, there isn't errors currently # self.assert_completion_in_result('is_alive', 'attribute', result) def test_hint_nonexistent(self): code = ( "class Sample(object):\n" + self._make_class_hint("sdfdsf.asdfasdf.sdfasdf.Dffg") + " def a_method(self):\n" " for i in self.a_attr.values():\n" " i.is_a" ) self._assist(code) def test_hint_invalid_syntax(self): code = ( "class Sample(object):\n" + self._make_class_hint("sdf | & # &*") + " def a_method(self):\n" " for i in self.a_attr.values():\n" " i.is_a" ) self._assist(code) class DocstringNoneAssignmentHintingTest(AbstractAssignmentHintingTest): def _make_class_hint(self, type_str): return ' """:type a_attr: ' + type_str + '"""\n' " a_attr = None\n" def _make_constructor_hint(self, type_str): return ( ' """:type arg: ' + type_str + '"""\n' " self.a_attr = arg\n" ) class DocstringNotImplementedAssignmentHintingTest(AbstractAssignmentHintingTest): def _make_class_hint(self, type_str): return ( ' """:type a_attr: ' + type_str + '"""\n' " a_attr = NotImplemented\n" ) def _make_constructor_hint(self, type_str): return ( ' """:type arg: ' + type_str + '"""\n' " self.a_attr = arg\n" ) class PEP0484CommentNoneAssignmentHintingTest(AbstractAssignmentHintingTest): def _make_class_hint(self, type_str): return " a_attr = None # type: " + type_str + "\n" def _make_constructor_hint(self, type_str): return " self.a_attr = None # type: " + type_str + "\n" class PEP0484CommentNotImplementedAssignmentHintingTest(AbstractAssignmentHintingTest): def _make_class_hint(self, type_str): return " a_attr = NotImplemented # type: " + type_str + "\n" def _make_constructor_hint(self, type_str): return " self.a_attr = NotImplemented # type: " + type_str + "\n" class EvaluateTest(unittest.TestCase): def test_parser(self): tests = [ ("Foo", "(name Foo)"), ("mod1.Foo", "(name mod1.Foo)"), ("mod1.mod2.Foo", "(name mod1.mod2.Foo)"), ("Foo[Bar]", "('[' (name Foo) [(name Bar)])"), ( "Foo[Bar1, Bar2, Bar3]", "('[' (name Foo) [(name Bar1), (name Bar2), (name Bar3)])", ), ("Foo[Bar[Baz]]", "('[' (name Foo) [('[' (name Bar) [(name Baz)])])"), ( "Foo[Bar1[Baz1], Bar2[Baz2]]", "('[' (name Foo) [('[' (name Bar1) [(name Baz1)]), ('[' (name Bar2) [(name Baz2)])])", ), ("mod1.mod2.Foo[Bar]", "('[' (name mod1.mod2.Foo) [(name Bar)])"), ( "mod1.mod2.Foo[mod1.mod2.Bar]", "('[' (name mod1.mod2.Foo) [(name mod1.mod2.Bar)])", ), ( "mod1.mod2.Foo[Bar1, Bar2, Bar3]", "('[' (name mod1.mod2.Foo) [(name Bar1), (name Bar2), (name Bar3)])", ), ( "mod1.mod2.Foo[mod1.mod2.Bar[mod1.mod2.Baz]]", "('[' (name mod1.mod2.Foo) [('[' (name mod1.mod2.Bar) [(name mod1.mod2.Baz)])])", ), ( "mod1.mod2.Foo[mod1.mod2.Bar1[mod1.mod2.Baz1], mod1.mod2.Bar2[mod1.mod2.Baz2]]", "('[' (name mod1.mod2.Foo) [('[' (name mod1.mod2.Bar1) [(name mod1.mod2.Baz1)]), ('[' (name mod1.mod2.Bar2) [(name mod1.mod2.Baz2)])])", ), ("(Foo, Bar) -> Baz", "('(' [(name Foo), (name Bar)] (name Baz))"), ( "(mod1.mod2.Foo[mod1.mod2.Bar1[mod1.mod2.Baz1], mod1.mod2.Bar2[mod1.mod2.Baz2]], mod1.mod2.Bar[mod1.mod2.Bar1[mod1.mod2.Baz1], mod1.mod2.Bar2[mod1.mod2.Baz2]]) -> mod1.mod2.Baz[mod1.mod2.Bar1[mod1.mod2.Baz1], mod1.mod2.Bar2[mod1.mod2.Baz2]]", "('(' [('[' (name mod1.mod2.Foo) [('[' (name mod1.mod2.Bar1) [(name mod1.mod2.Baz1)]), ('[' (name mod1.mod2.Bar2) [(name mod1.mod2.Baz2)])]), ('[' (name mod1.mod2.Bar) [('[' (name mod1.mod2.Bar1) [(name mod1.mod2.Baz1)]), ('[' (name mod1.mod2.Bar2) [(name mod1.mod2.Baz2)])])] ('[' (name mod1.mod2.Baz) [('[' (name mod1.mod2.Bar1) [(name mod1.mod2.Baz1)]), ('[' (name mod1.mod2.Bar2) [(name mod1.mod2.Baz2)])]))", ), ( "(Foo, Bar) -> Baz | Foo[Bar[Baz]]", "('|' ('(' [(name Foo), (name Bar)] (name Baz)) ('[' (name Foo) [('[' (name Bar) [(name Baz)])]))", ), ( "Foo[Bar[Baz | (Foo, Bar) -> Baz]]", "('[' (name Foo) [('[' (name Bar) [('|' (name Baz) ('(' [(name Foo), (name Bar)] (name Baz)))])])", ), ] for t, expected in tests: result = repr(evaluate.compile(t)) self.assertEqual(expected, result) class RegressionHintingTest(AbstractHintingTest): def test_hierarchical_hint_for_mutable_attr_type(self): """Test for #157, AttributeError: 'PyObject' object has no attribute 'get_doc'""" code = dedent("""\ class SuperClass(object): def __init__(self): self.foo = None class SubClass(SuperClass): def __init__(self): super(SubClass, self).__init__() self.bar = 3 def foo(self): return self.bar""") result = self._assist(code) self.assert_completion_in_result("bar", "attribute", result) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1637593692.3753905 rope-0.22.0/setup.cfg0000664000175000017500000000004600000000000014254 0ustar00lieryanlieryan[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1636636936.0 rope-0.22.0/setup.py0000664000175000017500000000443500000000000014153 0ustar00lieryanlieryanimport io import os.path try: from setuptools import setup except ImportError: from distutils.core import setup classifiers = [ "Development Status :: 4 - Beta", "Operating System :: OS Independent", "Environment :: X11 Applications", "Environment :: Win32 (MS Windows)", "Environment :: MacOS X", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Software Development", ] def get_long_description(): lines = io.open("README.rst", "r", encoding="utf8").read().splitlines(False) end = lines.index("Getting Started") return "\n" + "\n".join(lines[:end]) + "\n" def get_version(): version = None with io.open( os.path.join(os.path.dirname(__file__), "rope", "__init__.py") ) as inif: for line in inif: if line.startswith("VERSION"): version = line.split("=")[1].strip(" \t\"'\n") break return version setup( name="rope", version=get_version(), description="a python refactoring library...", long_description=get_long_description(), long_description_content_type="text/x-rst", author="Ali Gholami Rudi", author_email="aligrudi@users.sourceforge.net", url="https://github.com/python-rope/rope", packages=[ "rope", "rope.base", "rope.base.oi", "rope.base.oi.type_hinting", "rope.base.oi.type_hinting.providers", "rope.base.oi.type_hinting.resolvers", "rope.base.utils", "rope.contrib", "rope.refactor", "rope.refactor.importutils", ], license="LGPL-3.0-or-later", classifiers=classifiers, extras_require={ "dev": [ "build", "pytest", "pytest-timeout", ] }, )