Mercurial > hg > dotemacs
changeset 152:55ceabc58fcc
elpy: upgrade to 1.12
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/NEWS.rst @@ -0,0 +1,464 @@ +New in Elpy 1.12.0 +================== + +- Some symbols can now be completed with parentheses after them, + useful for functions. The heuristic for when to add parentheses and + not is not optimal, so this is not enabled by default—customize + ``elpy-company-post-completion-function`` to enable this feature. +- Support dedicated Python shells (one per Python file). You can + customized ``elpy-dedicated-shells`` to make this the default. +- Elpy now uses ``python -m pip`` instead of pip directly to improve + interaction with virtualenvs. +- Support for Python 2.6 has been officially dropped. +- Display for backend errors has been disabled by default. Jedi has + not had a release in a long while, and many bugs have been unfixed. + These errors have only caused added workload for Elpy maintainers. + The option will be reverted once Jedi has had a new release. + + +New in Elpy 1.11.0 +================== + +- Elpy now supports yapf to format your code. +- You can now adjust whether Elpy should hide modes from the mode line + or not using ``elpy-remove-modeline-lighter`` +- When the new option ``elpy-disable-backend-error-display`` is set, + Elpy will not show its error pop-up anymore. This can be useful if + you run into an annoying bug in Jedi, for example. +- New command ``elpy-goto-definition-other-window`` on ``C-x 4 M-.``. +- Expanding ``super`` now gives the short form supported in Python 3. +- All Rope errors are now caught, as the upstream maintainers did not + show interest in distinguishing between malformed input and bugs in + their library. + + +New in Elpy 1.10.0 +================== + +- Marking the current indentation level at the top level will now mark + the whole buffer. +- The region will be normalized before re-indenting it, making the + behavior more predictable for partially marked lines. +- Using autopep8 on the whole buffer will now keep point (roughly) at + the same location as it was. +- The autopep8 code now also uses the same configuration options as + the command line tool. +- Malformed JSON data from the backend is now handled better. +- RPC processes are restarted when the current virtualenv changes. +- Python 3.5 is now officially supported. +- Flymake will now not be enabled in buffers without file name, where + it can't run anyhow, or when the checker program does not exist in + the first place. +- It is now possible to ask Elpy not to remove the mode line lighter + of company mode, which can be useful if you use company in other + modes. +- Test discovery now also allows file names without "test" in them to + be tested. Classes and methods still require the substring, though. +- Spurious equals signs at the end of completions from Jedi will now + be ignored. +- Various other bug fixes. + + +New in Elpy 1.9.0 +================= + +- Elpy now supports the ``autopep8`` library for automatically + formatting Python code. All refactoring-related code is now grouped + under ``C-c C-r``. Use ``C-c C-r i`` to fix up imports using + importmagic, ``C-c C-r p`` to fix up Python code with autopep8, and + ``C-c C-r r`` to bring up the old Rope refactoring menu. +- ``C-c C-b`` will now select a region containing surrounding lines of + the current indentation or more. +- ``C-c C-z`` in a Python shell will now switch back to the last + Python buffer, allowing to use the key to cycle back and forth + between the Python buffer and shell. +- The pattern used for ``C-c C-s`` is now customizeable in + ``elpy-rgrep-file-pattern``. +- ``<C-return>`` now can be used to send the current statement to the + Python shell. Be careful, this can break with nested statements. +- The Elpy minor mode now also works in modes derived from + ``python-mode``, not just in the mode itself. + + +New in Elpy 1.8.1 +================= + +- Ignore a ``KeyError`` from Jedi 0.9.0 which occurs a lot in normal + code. + + +New in Elpy 1.8.0 +================= + +- Emacs 24.5 is now officially supported +- The new configuration option ``elpy-rpc-ignored-buffer-size`` defines a maximum buffer size to be handle completion in, to avoid laggy interaction in unusually large files +- Indentation block movement was replaced with code that just moves the marked block or the current line; this should be a lot less magical and more predictable +- Running the test at point now correctly ignores any inner methods +- Jedi docstrings now show the full name of the object +- The RPC interpreter is now chosen correctly on cygwin +- ``elpy-shell-send-region-or-buffer`` now warns of tabs in the data being sent +- Elpy now binds stdout and stderr to ``/dev/null`` to avoid being confused by spurious output from other libraries +- RPC buffers (and processes) are removed after some time to avoid them piling up endlessly +- It is not possibly anymore to use customize alone to use ipython, because of some bad interaction between custom options in Elpy and python.el +- And lots of bugfixes (50 issues closed!) + + +New in Elpy 1.7.1 +================= + +- Do not fail on errors from importmagic. +- Handle new minor mode behavior of new versions of yasnippet. +- Do use the argument to ``elpy-use-ipython`` correctly. +- Handle unexpected data from the backend more gracefully. + + +New in Elpy 1.7.0 +================= + +- Elpy now can add missing import directives automatically, by using + Alec Thomas' excellent importmagic_ library. Use ``C-c C-m`` to add + a single import statement, or ``C-c C-S-m`` to include all missing + import statements. Many thanks to Georg Brandl for doing a lot of + work to bring this feature to Elpy! +- The Jedi backend now also supports ``C-c C-d`` to display a + docstring. Thanks again to Georg Brandl for the patch. +- It is now possible to disable the display of the current function in + the echo area by setting ``elpy-eldoc-show-current-function`` to + ``nil``. +- idomenu was removed. +- Twisted's Trial test runner is now supported. Thanks to Elric Milon + for the patch! +- All test runners now use a variable to decide which command to run, + which for example allows using ``manage.py`` for the Django test + runner, or your own test script which sets up the environment + correctly. +- Emacs 24.4 is now officially supported. +- Various bugfixes. + +.. _importmagic: https://github.com/alecthomas/importmagic + +New in Elpy 1.6.0 +================= + +- When point is on a line with a flymake error, Elpy will now show the + error in the echo area. +- The movement commands (``C-<cursor>``) have been reworked again. + Going left and right will now move by indentation levels left of the + current indentation, i.e. jump four spaces, and by words right of + the current indentation. Going up and down will go to the previous + or next line with the indentation level point is at, not the + indentation the line has. Try it, it's more difficult to explain + than to use. +- Completion results are now sorted more sensibly, with + single-underscore symbols at the end, and double-underscore symbols + after normal symbols, but before single-underscore ones. +- ``M-x elpy-config`` will now point out if there are newer versions + available for packages used by Elpy. +- ``M-x elpy-config`` will now warn if ``~/.local/bin`` is not in + ``PATH`` while there is no virtualenv active. +- The ``M-x elpy-version`` command is back by popular demand. +- RPC buffers used by Elpy are now hidden by default, having a space + at the beginning of the name. +- When the Rope library throws an error, Elpy will now also attempt to + provide reproduction steps. This used to only happen for Jedi. +- Various bug fixes. + + +New in Elpy 1.5.1 +================= + +- Fix a bug where company-mode might get confused about the current + backend, leading to an error about ``Symbol's function definition is + void: nil`` +- Fix Rope so it won’t search the whole project directory. This was an + intended feature in v1.5 which did not work originally. +- Use ``yas-text`` instead of ``text`` in snippets for compatibility + with the unreleased yasnippet from MELPA (thanks to Daniel Wu!) + +New in Elpy 1.5.0 +================= + +- Elpy now has a `manual`_. Additionally, there's a menu bar now which + should make it easier to discover Elpy features. +- The Elpy Python package now ships with the Emacs Lisp package, + removing the need to install Elpy via pip. +- Python 3.4 is now officially supported. +- The new command ``elpy-config`` can be used to configure Elpy using + Emacs' built-in customize system. Elpy has been changed to make the + most of this. +- Elpy now uses company-mode instead of auto-complete for on-the-fly + auto completion. This changes a few things. There is no automatic + documentation popup anymore. Instead, you can type ``C-d`` and get + the documentation buffer. In addition, you can type ``C-w`` to see + the source of the current candidate in context. +- Elpy now uses pyvenv as the virtualenv module, enabling + virtualenvwrapper hooks. +- We now ship with a large number of YASnippet snippets. Try ``M-x + yas-insert-snippet``. +- The new unified test running interface on ``C-c C-t`` will try to + determine the current test and run it, or, failing that, run all + tests. Provide a prefix argument to just run all tests no matter + what. You can change the test runner to be used using + ``elpy-set-test-runner``. Elpy supports the default unittest + discover runner, the Django discover runner, nosetests and py.test + by default. New test runners can easily be defined. +- There's a new multi-edit functionality. ``C-c C-e`` will edit all + occurrences of the symbol under point. When using Jedi, this is + using semantic information as opposed to just syntactic one. When a + region is active, edit all occurrences of the text in region in the + current buffer. +- When sending Python code to the interactive interpreter using ``C-c + C-c``, Elpy will now not automatically pop to the interpreter + anymore. Use ``C-c C-z`` to switch to the interpreter. +- Elpy will now display the current class and function if there is no + call tip to be displayed. Removes the ``C-c C-q`` binding. +- If there is a call tip, highlight the current argument (requires Jedi). +- The documentation interface using ``C-c C-d`` is much smarter now, + falling back to pydoc when necessary and providing sensible + completion for that, too. Provide a prefix argument if you want no + smarts, just pydoc. +- ``<S-return>`` and ``<C-S-return>`` now open a line below or above + the current one. +- ``<C-cursor>`` will now navigate between Python blocks of the same + indentation level. ``<M-cursor>`` will move the current block. Try + it, it's easier to understand when you see it than to explain it. +- There's a new concept of modules. The variable + ``elpy-default-minor-modes`` is gone (use ``elpy-mode-hook`` for + minor modes). Instead, there's now ``elpy-modules`` which can be + used to enable or disable certain features of Elpy. +- ``elpy-clean-modeline`` is gone, modules now clean themselves up. +- Elpy now distinguishes between the project root, where project files + are located, and the library root, which should be part of + ``sys.path`` to import the module under development. +- ``elpy-project-ignored-directories`` replaces the old + ``elpy-rgrep-ignored-directories`` and is used by more features. +- ``elpy-doc-websearch`` has been removed as it was barely useable + as is. +- Elpy now tries to be more helpful when errors in the backend happen. + This removes ``elpy-rpc-traceback``, as that will be displayed by + default. +- Optimizations were added to handle large files, making general + interaction a lot faster. +- When Rope is being used, do not search through unusually large + directories. This should speed up interaction in those cases, + especially when editing a file in the home directory. +- And a whole lot of minor bug fixes and little improvements. + +.. _manual: https://elpy.readthedocs.io/ + + +New in Elpy 1.4.2 +================== + +- Minor bugfix to prevent an error from projectile-project-root to + interfere with Elpy’s project finding strategy. + +New in Elpy 1.4.1 +================= + +- Elpy now sets project-wide preferences for Rope, enabling completion + in the sys package, among others. +- An error is avoided in the Jedi backend when trying to go to symbols + in compiled packages. +- A compatibility alias was added for nose.el, which insists on + breaking backwards compatibility with Emacs 24.x. + +New in Elpy 1.4.0 +================= + +- Elpy has moved to its own ELPA. Make sure to update your + package-archives (as described above). +- For a file in a Projectile-managed project is opened, Elpy will now + use Projectile’s project root. +- When the user has set a valid python-check-command, elpy will now + refrain from overriding it. +- On Windows, elpy is now using the pythonw.exe interpreter for the + RPC process, as that seems to be causing fewer issues. +- And various smaller bugfixes. + +New in Elpy 1.3.0 +================= + +- virtualenv.el has been replaced by pyvenv.el, as that library offers + more features. +- elpy-rpc-restart now works globally, not just in Elpy buffers. +- Elpy does not try to complete in comments anymore. +- The new command elpy-rpc-traceback gives access to the last stack + trace in the Elpy backend, helping with debugging problems. +- The flymake check function is now run with the current directory as + / to avoid accidental imports. +- Ensure correct handling of yas-snippet-dirs by Elpy. This variable + can be a string, so ensure it’s a list before adding to it. +- The new variable elpy-show-installation-instructions can be used to + disable the installation screen. +- Fix a very nasty bug causing spurious empty lines in a buffer or + consume 100% CPU in certain situations when using the Jedi backend. + Thanks to Matthias Dahl for finding this bug. +- Various other bugfixes. + +New in Elpy 1.2.1 +================= + +Bugfix release. + +- The refactoring was not ported to the new asynchronous API, + resulting in an error when refactoring was attempted. +- The project root now always returns a directory. Too many parts of + elpy relies on this. If the project root turns out to be your home + directory, elpy will warn you about it. +- Elpy now works correctly with Emacs 24.2. There were some + compatibility functions missing. +- Blocking RPC calls now do not block for one second even if there is + process output. + +New in Elpy 1.2 +=============== + +- Elpy now uses asynchronous RPC. This means that Emacs should not + freeze anymore while eldoc or auto-complete functions run. +- ``elpy-shell-send-region-or-buffer`` will now remove common + indentation of the region, making it possible to easily send parts + of an if statement or function body without manually adjusting the + indentation. +- The Python package depends on ``flake8``, and will also try to be + smarter when detecting ``flake8`` for on-the-fly checking. +- ``elpy-check`` can be run with a prefix argument to check the whole + project, instead of only the current file. +- ``elpy-rgrep-symbol`` now ignores a few common directories + (``.tox``, ``build``, ``dist``). +- When using the rope backend, Elpy will not create the + ``.ropeproject`` folders anymore. This should keep projects a lot + cleaner. + +New in Elpy 1.1 +=============== + +- Elpy now always uses the root directory of the package as the + project root; this should avoid some confusion and improve + auto-completion suggestions +- ``elpy-shell-send-region-or-buffer`` now accepts a prefix argument + to run code wrapped behind ``if __name__ == '__main__'``, which is + ignored by default +- ``elpy-project-root`` is now a safe local variable and can be set + from file variables +- Elpy now supports project-specific RPC processes, see + ``elpy-rpc-project-specific`` for how to use this +- ``M-*`` now works to go back where you came from after a ``M-.`` +- Elpy now ships with a few dedicated snippets for YASnippet +- Support and require Jedi 0.6.0 + +New in Elpy 1.0 +=============== + +- Version 0.9 was a release candidate, so this release focused on bug + fixes instead of new features. +- ``elpy-enable`` now takes an optional argument that skips variable + initialization for those users who prefer their own defaults for + other modes. +- ``python-check.sh`` has been removed from Elpy, as the flake8 tool + from pypi does everything it does, only better. +- Elpy will now start the helper subprocess in the root directory, + avoiding accidental Python path clobbering. + +New in Elpy 0.9 +=============== + +- Elpy now officially support Python 2.6, 2.7 and 3.3 on Emacs 24.2 + and 24.3, with continuous integration tests thanks to + `Travis CI`_. +- Extended support for Pydoc. ``C-u C-c C-d`` will now prompt for an + auto-completed symbol to run Pydoc on. The pydoc output will be + formatted and placed in a help buffer for easy review. +- Refactoring support is back. ``C-c C-r`` will pop up a refactoring + wizard offering various refactoring options. Most of them depend on + the presence of Rope, though, even if Jedi is used as a completion + backend. +- The Rope backend has been extended to provide completions for + modules in an import clause. +- New refactoring option: Add missing imports. This will search for + undefined symbols in the current file and automatically add + appropriate imports. +- ``C-c C-c (elpy-rgrep-symbol)`` now prompts for a regexp when a prefix + argument is given instead of using the symbol at point. + +.. _Travis CI: https://travis-ci.org/ + +New in Elpy 0.8 +=============== + +Python Backend Rewrite +---------------------- + +- Elpy does not use Pymacs, Ropemacs and Ropemode anymore, but instead + provides its own Python interface with the elpy package on PyPI. +- This not only should improve performance, but also enables using + Jedi as an alternative backend for completion. Use ``M-x + elpy-set-backend`` to change between rope and jedi. For now, this + does disable all refactoring support, though. + +Project Support +--------------- + +- Elpy now has built-in project support. The interface is rather + simple: You can set ``elpy-project-root`` to the correct value in + ``.dir-locals.el``, or just rely on the automatic detection. If you + change your mind, you can always just ``elpy-set-project-root``. +- New dependency: Find File in Project (ffip), bound to ``C-c C-f`` by + default. This will allow you to find files anywhere in your project + using a search-as-you-type interface like ido. +- New dependency: nose, bound to ``C-c C-t`` by default. This will run + the nosetests binary in the root of your current library directory. + You can restrict the tests being run to the current test or the + current module by adding prefix arguments. +- New function: Recursive grep for symbol, bound to ``C-c C-s`` by + default. This will search for the symbol at point in the whole + project. + +New dependencies +---------------- + +- idomenu, bound to ``C-c C-j`` by default. This replaces the standard + imenu interface with an ido-based search-as-you-type interface for + definitions in the current buffer. +- virtualenv.el, replacing pyvirtualenv.el). Use ``M-x + virtualenv-workon`` to enable a virtualenv. +- iedit.el, bound to ``M-,`` by default. This highlights all occurrences + of the symbol at point or the active region in the current buffer or + narrowing. When you edit any of them, all others will be edited the + same. This allows some basic and very quick refactoring. +- New variable ``elpy-default-minor-modes`` which is run by ``elpy-mode`` + on startup. If you don’t want to use some modes, remove them from + here. + +Key Bindings and Functions +-------------------------- + +- The key bindings have been reworked and cleaned up. Sorry, this + might cause confusion. +- Yasnippet is now on its own keybinding, ``C-c C-i``, instead of + sharing the auto-complete interface. This was done because some + snippets conflicted with legitimate, unsnippy completions. +- New function: Occur Definitions, bound to ``C-c C-o`` by default. This + will run the standard occur command to show definitions (classes and + functions) in your current buffer, giving you a very quick outline + and the ability to jump to different definitions quickly. +- New function: Show Defun, bound to ``C-c C-q`` by default. This will + show the current method and possibly class in the mode line, which + is helpful in long functions. +- New functions: Forward/backward definition, bound to ``M-n`` and ``M-p`` + as well as ``<M-down>`` and ``<M-up>`` by default. These will jump to + the next or previous definition (class or function), helping with + quick navigation through a file. + +Miscellaneous +------------- + +- The documentation function (``C-c C-d``) now uses pydoc when a prefix + arg is given. +- The web search function (``C-c C-w``) now searches for the current + symbol by default. The tab-completing web documentation interface + was removed and is scheduled to be replaced with a new pydoc + interface in future versions. +- The ``python-check.sh`` is now shipped with elpy. If you load elpy.el + before you load python.el, it should be the default + ``python-check-command``.
rename from elpa/elpy-1.8.0/elpy-autoloads.el rename to elpa/elpy-1.12.0/elpy-autoloads.el --- a/elpa/elpy-1.8.0/elpy-autoloads.el +++ b/elpa/elpy-1.12.0/elpy-autoloads.el @@ -3,7 +3,7 @@ ;;; Code: (add-to-list 'load-path (or (file-name-directory #$) (car load-path))) -;;;### (autoloads nil "elpy" "elpy.el" (21954 12867 40604 476000)) +;;;### (autoloads nil "elpy" "elpy.el" (22528 61683 884152 80000)) ;;; Generated autoloads from elpy.el (autoload 'elpy-enable "elpy" "\ @@ -38,8 +38,8 @@ ;;;*** -;;;### (autoloads nil nil ("elpy-pkg.el" "elpy-refactor.el") (21954 -;;;;;; 12867 294239 466000)) +;;;### (autoloads nil nil ("elpy-pkg.el" "elpy-refactor.el") (22528 +;;;;;; 61684 201051 64000)) ;;;***
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy-pkg.el @@ -0,0 +1,9 @@ +(define-package "elpy" "1.12.0" "Emacs Python Development Environment" + '((company "0.8.2") + (find-file-in-project "3.3") + (highlight-indentation "0.5.0") + (pyvenv "1.3") + (yasnippet "0.8.0"))) +;; Local Variables: +;; no-byte-compile: t +;; End:
rename from elpa/elpy-1.8.0/elpy-refactor.el rename to elpa/elpy-1.12.0/elpy-refactor.el --- a/elpa/elpy-1.8.0/elpy-refactor.el +++ b/elpa/elpy-1.12.0/elpy-refactor.el @@ -1,6 +1,6 @@ ;;; elpy-refactor.el --- Refactoring mode for Elpy -;; Copyright (C) 2013 Jorgen Schaefer +;; Copyright (C) 2013-2016 Jorgen Schaefer ;; Author: Jorgen Schaefer <contact@jorgenschaefer.de> ;; URL: https://github.com/jorgenschaefer/elpy @@ -281,5 +281,17 @@ (list (buffer-file-name) method args))) +(defun elpy-refactor-options (option) + "Show available refactor options and let user choose one." + (interactive "c[i]: importmagic-fixup [p]: autopep8-fix-code [r]: refactor") + (let ((choice (char-to-string option))) + (cond + ((string-equal choice "i") + (elpy-importmagic-fixup)) + ((string-equal choice "p") + (elpy-autopep8-fix-code)) + ((string-equal choice "r") + (elpy-refactor))))) + (provide 'elpy-refactor) ;;; elpy-refactor.el ends here
rename from elpa/elpy-1.8.0/elpy.el rename to elpa/elpy-1.12.0/elpy.el --- a/elpa/elpy-1.8.0/elpy.el +++ b/elpa/elpy-1.12.0/elpy.el @@ -1,10 +1,10 @@ ;;; elpy.el --- Emacs Python Development Environment -*- lexical-binding: t -*- -;; Copyright (C) 2012-2014 Jorgen Schaefer +;; Copyright (C) 2012-2016 Jorgen Schaefer ;; Author: Jorgen Schaefer <contact@jorgenschaefer.de> ;; URL: https://github.com/jorgenschaefer/elpy -;; Version: 1.8.0 +;; Version: 1.12.0 ;; Keywords: Python, IDE, Languages, Tools ;; Package-Requires: ((company "0.8.2") (find-file-in-project "3.3") (highlight-indentation "0.5.0") (pyvenv "1.3") (yasnippet "0.8.0")) @@ -31,7 +31,7 @@ ;; For more information, read the Elpy manual: -;; http://elpy.readthedocs.org/en/latest/index.html +;; https://elpy.readthedocs.io/en/latest/index.html ;;; Code: @@ -46,7 +46,7 @@ (require 'elpy-refactor) (require 'pyvenv) -(defconst elpy-version "1.8.0" +(defconst elpy-version "1.12.0" "The version of the Elpy lisp code.") ;;;;;;;;;;;;;;;;;;;;;; @@ -144,6 +144,25 @@ elpy-project-find-svn-root)) :group 'elpy) +(make-obsolete-variable 'elpy-company-hide-modeline 'elpy-remove-modeline-lighter) +(defcustom elpy-remove-modeline-lighter t + "Non-nil if Elpy should remove most mode line display. + +Modeline shows many minor modes currently active. For Elpy, this is mostly +uninteresting information, but if you rely on your modeline in other modes, +you might want to keep it." + :type 'boolean + :group 'elpy) + +(defcustom elpy-dedicated-shells nil + "Non-nil if Elpy should use dedicated shells. + +Elpy can use a unique python shell for all buffers and support +manually started dedicated shells. Setting this option to non-nil +force the creation of dedicated shells for each buffers." + :type 'boolean + :group 'elpy) + (defcustom elpy-rpc-backend nil "Your preferred backend. @@ -195,7 +214,7 @@ :safe #'integerp :group 'elpy) -(defcustom elpy-rpc-python-command (if (equal system-type "windows-nt") +(defcustom elpy-rpc-python-command (if (equal system-type 'windows-nt) "pythonw" "python") "The Python interpreter for the RPC backend. @@ -250,6 +269,22 @@ :type 'integer :group 'elpy) +(defcustom elpy-company-post-completion-function 'ignore + "Your preferred Company post completion function. + +Elpy can automatically insert parentheses after completing +callable objects. + +The heuristic on when to insert these parentheses can easily be +wrong, though, so this is disabled by default. Set this variable +to the function `elpy-company-post-complete-parens' to enable +this feature." + :type '(choice (const :tag "Ignore post complete" ignore) + (const :tag "Complete callables with parens" + elpy-company-post-complete-parens) + (function :tag "Other function")) + :group 'elpy) + (defcustom elpy-eldoc-show-current-function t "If true, show the current function if no calltip is available. @@ -295,9 +330,33 @@ :type '(repeat string) :group 'elpy) +(defcustom elpy-rgrep-file-pattern "*.py" + "FILES to use for `elpy-rgrep-symbol'." + :type 'string + :group 'elpy) + +(defcustom elpy-disable-backend-error-display t + "Non-nil if Elpy should disable backed error display." + :type 'boolean + :group 'elpy) + ;;;;;;;;;;;;; ;;; Elpy Mode +(defvar elpy-refactor-map + (let ((map (make-sparse-keymap "Refactor"))) + (define-key map (kbd "i") (cons (format "%smport fixup" + (propertize "i" 'face 'bold)) + 'elpy-importmagic-fixup)) + (define-key map (kbd "f") (cons (format "%sormat code" + (propertize "f" 'face 'bold)) + 'elpy-format-code)) + (define-key map (kbd "r") (cons (format "%sefactor" + (propertize "r" 'face 'bold)) + 'elpy-refactor)) + map) + "Key map for the refactor command") + (defvar elpy-mode-map (let ((map (make-sparse-keymap))) ;; Alphabetical order to make it easier to find free C-c C-X @@ -309,25 +368,26 @@ ;; (define-key map (kbd "C-M-x") 'python-shell-send-defun) ;; (define-key map (kbd "C-c <") 'python-indent-shift-left) ;; (define-key map (kbd "C-c >") 'python-indent-shift-right) + (define-key map (kbd "C-c RET") 'elpy-importmagic-add-import) + (define-key map (kbd "C-c C-b") 'elpy-nav-expand-to-indentation) (define-key map (kbd "C-c C-c") 'elpy-shell-send-region-or-buffer) - (define-key map (kbd "C-c C-z") 'elpy-shell-switch-to-shell) (define-key map (kbd "C-c C-d") 'elpy-doc) (define-key map (kbd "C-c C-e") 'elpy-multiedit-python-symbol-at-point) (define-key map (kbd "C-c C-f") 'elpy-find-file) - (define-key map (kbd "C-c RET") 'elpy-importmagic-add-import) - (define-key map (kbd "C-c <S-return>") 'elpy-importmagic-fixup) (define-key map (kbd "C-c C-n") 'elpy-flymake-next-error) (define-key map (kbd "C-c C-o") 'elpy-occur-definitions) (define-key map (kbd "C-c C-p") 'elpy-flymake-previous-error) - (define-key map (kbd "C-c C-r") 'elpy-refactor) (define-key map (kbd "C-c C-s") 'elpy-rgrep-symbol) (define-key map (kbd "C-c C-t") 'elpy-test) (define-key map (kbd "C-c C-v") 'elpy-check) - ;; (define-key map (kbd "C-c C-z") 'python-shell-switch-to-shell) + (define-key map (kbd "C-c C-z") 'elpy-shell-switch-to-shell) + (define-key map (kbd "C-c C-r") elpy-refactor-map) (define-key map (kbd "<S-return>") 'elpy-open-and-indent-line-below) (define-key map (kbd "<C-S-return>") 'elpy-open-and-indent-line-above) + (define-key map (kbd "<C-return>") 'elpy-shell-send-current-statement) + (define-key map (kbd "<C-down>") 'elpy-nav-forward-block) (define-key map (kbd "<C-up>") 'elpy-nav-backward-block) (define-key map (kbd "<C-left>") 'elpy-nav-backward-indent) @@ -335,10 +395,11 @@ (define-key map (kbd "<M-down>") 'elpy-nav-move-line-or-region-down) (define-key map (kbd "<M-up>") 'elpy-nav-move-line-or-region-up) - (define-key map (kbd "<M-left>") 'elpy-nav-move-region-or-line-left) - (define-key map (kbd "<M-right>") 'elpy-nav-move-region-or-line-right) + (define-key map (kbd "<M-left>") 'elpy-nav-indent-shift-left) + (define-key map (kbd "<M-right>") 'elpy-nav-indent-shift-right) (define-key map (kbd "M-.") 'elpy-goto-definition) + (define-key map (kbd "C-x 4 M-.") 'elpy-goto-definition-other-window) (define-key map (kbd "M-TAB") 'elpy-company-backend) map) @@ -389,10 +450,10 @@ ["Previous Error" elpy-flymake-previous-error :help "Go to the previous inline error, if any"]) ("Indentation Blocks" - ["Dedent" elpy-nav-move-region-or-line-left + ["Dedent" python-indent-shift-left :help "Dedent current block or region" :suffix (if (use-region-p) "Region" "Block")] - ["Indent" elpy-nav-move-region-or-line-right + ["Indent" python-indent-shift-right :help "Indent current block or region" :suffix (if (use-region-p) "Region" "Block")] ["Up" elpy-nav-move-line-or-region-up @@ -422,7 +483,9 @@ "Elpy only works with python.el from " "Emacs 24 and above")))) (elpy-modules-global-init) - (add-hook 'python-mode-hook 'elpy-mode)) + (define-key inferior-python-mode-map (kbd "C-c C-z") 'elpy-shell-switch-to-buffer) + (add-hook 'python-mode-hook 'elpy-mode) + (add-hook 'pyvenv-post-activate-hooks 'elpy-rpc--disconnect)) (defun elpy-disable () "Disable Elpy in all future Python buffers." @@ -441,7 +504,7 @@ \\{elpy-mode-map}" :lighter " Elpy" - (when (not (eq major-mode 'python-mode)) + (when (not (derived-mode-p 'python-mode)) (error "Elpy only works with `python-mode'")) (cond (elpy-mode @@ -458,8 +521,8 @@ (with-current-buffer (get-buffer-create "*Elpy News*") (let ((inhibit-read-only t)) (erase-buffer) - (insert-file (concat (file-name-directory (locate-library "elpy")) - "NEWS.rst")) + (insert-file-contents (concat (file-name-directory (locate-library "elpy")) + "NEWS.rst")) (help-mode)) (pop-to-buffer (current-buffer)))) @@ -546,6 +609,22 @@ config['importmagic_version'] = None config['importmagic_latest'] = latest('importmagic') +try: + import autopep8 + config['autopep8_version'] = autopep8.__version__ + config['autopep8_latest'] = latest('autopep8', config['autopep8_version']) +except: + config['autopep8_version'] = None + config['autopep8_latest'] = latest('autopep8') + +try: + import yapf + config['yapf_version'] = yapf.__version__ + config['yapf_latest'] = latest('yapf', config['yapf_version']) +except: + config['yapf_version'] = None + config['yapf_latest'] = latest('yapf') + json.dump(config, sys.stdout) ") @@ -666,11 +745,11 @@ (member (expand-file-name "~/.local/bin/") exec-path)))) (elpy-insert--para - "The directory ~/.local/bin/ is not in your PATH, even though you " - "do not have an active virtualenv. Installing Python packages " - "locally will create executables in that directory, so Emacs " - "won't find them. If you are missing some commands, do add this " - "directory to your PATH.\n\n")) + "The directory ~/.local/bin/ is not in your PATH. As there is " + "no active virtualenv, installing Python packages locally will " + "place executables in that directory, so Emacs won't find them. " + "If you are missing some commands, do add this directory to your " + "PATH.\n\n")) ;; Python found, but can't find the elpy module (when (and (gethash "python_rpc_executable" config) @@ -779,8 +858,48 @@ :package "importmagic" :upgrade t) (insert "\n\n")) + ;; No autopep8 available + (when (not (gethash "autopep8_version" config)) + (elpy-insert--para + "The autopep8 package is not available. Commands using this will " + "not work.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package "autopep8") + (insert "\n\n")) + + ;; Newer version of autopep8 available + (when (and (gethash "autopep8_version" config) + (gethash "autopep8_latest" config)) + (elpy-insert--para + "There is a newer version of the autopep8 package available.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package "autopep8" :upgrade t) + (insert "\n\n")) + + ;; No yapf available + (when (not (gethash "yapf_version" config)) + (elpy-insert--para + "The yapf package is not available. Commands using this will " + "not work.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package "yapf") + (insert "\n\n")) + + ;; Newer version of yapf available + (when (and (gethash "yapf_version" config) + (gethash "yapf_latest" config)) + (elpy-insert--para + "There is a newer version of the yapf package available.\n") + (insert "\n") + (widget-create 'elpy-insert--pip-button + :package "yapf" :upgrade t) + (insert "\n\n")) + ;; flake8, the default syntax checker, not found - (when (not (executable-find "flake8")) + (when (not (executable-find python-check-command)) (elpy-insert--para "The configured syntax checker could not be found. Elpy uses this " "program to provide syntax checks of your programs, so you might " @@ -867,6 +986,10 @@ (rope-latest (gethash "rope_latest" config)) (importmagic-version (gethash "importmagic_version" config)) (importmagic-latest (gethash "importmagic_latest" config)) + (autopep8-version (gethash "autopep8_version" config)) + (autopep8-latest (gethash "autopep8_latest" config)) + (yapf-version (gethash "yapf_version" config)) + (yapf-latest (gethash "yapf_latest" config)) (virtual-env (gethash "virtual_env" config)) (virtual-env-short (gethash "virtual_env_short" config)) table maxwidth) @@ -918,6 +1041,12 @@ ("Importmagic" . ,(elpy-config--package-link "importmagic" importmagic-version importmagic-latest)) + ("Autopep8" . ,(elpy-config--package-link "autopep8" + autopep8-version + autopep8-latest)) + ("Yapf" . ,(elpy-config--package-link "yapf" + yapf-version + yapf-latest)) ("Syntax checker" . ,(let ((syntax-checker (executable-find python-check-command))) @@ -1008,9 +1137,15 @@ "")) (command (cond - ((executable-find "pip") - (format "pip install %s%s%s" - user-option upgrade-option python-package)) + ((= (call-process elpy-rpc-python-command + nil nil nil + "-m" "pip" "--help") + 0) + (format "%s -m pip install %s%s%s" + elpy-rpc-python-command + user-option + upgrade-option + python-package)) ((executable-find "easy_install") (format "easy_install %s%s" user-option python-package)) @@ -1178,7 +1313,7 @@ (let ((grep-find-ignored-directories (append elpy-project-ignored-directories grep-find-ignored-directories))) (rgrep regexp - "*.py" + elpy-rgrep-file-pattern (or (elpy-project-root) default-directory))) (with-current-buffer next-error-last-buffer @@ -1227,7 +1362,8 @@ (elpy-find-file nil)))) (t (let ((ffip-prune-patterns elpy-project-ignored-directories) - (ffip-project-root (elpy-project-root)) + (ffip-project-root (or (elpy-project-root) + default-directory)) ;; Set up ido to use vertical file lists. (ido-decorations '("\n" "" "\n" "\n..." "[" "]" " [No match]" " [Matched]" @@ -1415,6 +1551,12 @@ (t (error "I don't know how to set ipython settings for this Emacs")))) +(defun elpy-shell-display-buffer () + "Display inferior Python process buffer." + (display-buffer (process-buffer (elpy-shell-get-or-create-process)) + nil + 'visible)) + (defun elpy-shell-send-region-or-buffer (&optional arg) "Send the active region or the buffer to the Python shell. @@ -1441,33 +1583,56 @@ (goto-char (point-min)) (setq has-if-main (re-search-forward if-main-regex nil t))) (python-shell-send-buffer arg)) - (display-buffer (process-buffer (elpy-shell-get-or-create-process)) - nil - 'visible) + (elpy-shell-display-buffer) (when has-if-main (message (concat "Removed if __main__ == '__main__' construct, " "use a prefix argument to evaluate."))))) +(defun elpy-shell-send-current-statement () + "Send current statement to Python shell." + (interactive) + (let ((beg (python-nav-beginning-of-statement)) + (end (python-nav-end-of-statement))) + (elpy-shell-get-or-create-process) + (python-shell-send-string (buffer-substring beg end))) + (elpy-shell-display-buffer) + (python-nav-forward-statement)) + (defun elpy-shell-switch-to-shell () "Switch to inferior Python process buffer." (interactive) (pop-to-buffer (process-buffer (elpy-shell-get-or-create-process)))) +(defun elpy-shell-switch-to-buffer () + "Switch from inferior Python process buffer to recent Python buffer." + (interactive) + (pop-to-buffer (other-buffer (current-buffer) 1))) + (defun elpy-shell-get-or-create-process () "Get or create an inferior Python process for current buffer and return it." (let* ((bufname (format "*%s*" (python-shell-get-process-name nil))) - (proc (get-buffer-process bufname))) - (if proc - proc - (run-python (python-shell-parse-command)) - (get-buffer-process bufname)))) + (dedbufname (format "*%s*" (python-shell-get-process-name t))) + (proc (get-buffer-process bufname)) + (dedproc (get-buffer-process dedbufname))) + (if elpy-dedicated-shells + (if dedproc + dedproc + (run-python (python-shell-parse-command) t) + (get-buffer-process dedbufname)) + (if dedproc + dedproc + (if proc + proc + (run-python (python-shell-parse-command)) + (get-buffer-process bufname)))))) (defun elpy-shell--region-without-indentation (beg end) "Return the current region as a string, but without indentation." (if (= beg end) "" (let ((region (buffer-substring beg end)) - (indent-level nil)) + (indent-level nil) + (indent-tabs-mode nil)) (with-temp-buffer (insert region) (goto-char (point-min)) @@ -1523,6 +1688,10 @@ ;;;;;;;;;;;;;; ;;; Navigation +(defvar elpy-nav-expand--initial-position nil + "Initial position before expanding to indentation.") +(make-variable-buffer-local 'elpy-nav-expand--initial-position) + (defun elpy-goto-definition () "Go to the definition of the symbol at point, if found." (interactive) @@ -1531,23 +1700,35 @@ (elpy-goto-location (car location) (cadr location)) (error "No definition found")))) -(defun elpy-goto-location (filename offset) - "Show FILENAME at OFFSET to the user." +(defun elpy-goto-definition-other-window () + "Go to the definition of the symbol at point in other window, if found." + (interactive) + (let ((location (elpy-rpc-get-definition))) + (if location + (elpy-goto-location (car location) (cadr location) 'other-window) + (error "No definition found")))) + +(defun elpy-goto-location (filename offset &optional other-window-p) + "Show FILENAME at OFFSET to the user. + +If other-window-p is non-nil, show the same in other window." (ring-insert find-tag-marker-ring (point-marker)) - (let ((buffer (find-file filename))) - (with-current-buffer buffer - (with-selected-window (get-buffer-window buffer) - (goto-char (1+ offset)) - (recenter 0))))) + (let ((buffer (find-file-noselect filename))) + (if other-window-p + (pop-to-buffer buffer t) + (switch-to-buffer buffer)) + (goto-char (1+ offset)) + (recenter 0))) (defun elpy-nav-forward-block () "Move to the next line indented like point. This will skip over lines and statements with different indentation levels." - (interactive) + (interactive "^") (let ((indent (current-column)) - (start (point))) + (start (point)) + (cur nil)) (when (/= (% indent python-indent-offset) 0) (setq indent (* (1+ (/ indent python-indent-offset)) @@ -1555,6 +1736,9 @@ (python-nav-forward-statement) (while (and (< indent (current-indentation)) (not (eobp))) + (when (equal (point) cur) + (error "Statement does not finish")) + (setq cur (point)) (python-nav-forward-statement)) (when (< (current-indentation) indent) @@ -1565,9 +1749,10 @@ This will skip over lines and statements with different indentation levels." - (interactive) + (interactive "^") (let ((indent (current-column)) - (start (point))) + (start (point)) + (cur nil)) (when (/= (% indent python-indent-offset) 0) (setq indent (* (1+ (/ indent python-indent-offset)) @@ -1575,6 +1760,9 @@ (python-nav-backward-statement) (while (and (< indent (current-indentation)) (not (bobp))) + (when (equal (point) cur) + (error "Statement does not start")) + (setq cur (point)) (python-nav-backward-statement)) (when (< (current-indentation) indent) @@ -1653,53 +1841,6 @@ (length region)))) (setq deactivate-mark nil))) -(defun elpy-nav-move-region-or-line-left () - "Dedent the current indentation block, or the active region." - (interactive) - (if (use-region-p) - (elpy--nav-move-region-left) - (elpy--nav-move-line-left))) - -(defun elpy-nav-move-region-or-line-right () - "Indent the current indentation block, or the active region." - (interactive) - (if (use-region-p) - (elpy--nav-move-region-right) - (elpy--nav-move-line-right ))) - -(defun elpy--nav-move-line-left () - (save-excursion - (goto-char (point-at-bol)) - (when (looking-at (format "^ \\{%i\\}" python-indent)) - (replace-match "")))) - -(defun elpy--nav-move-line-right () - (save-excursion - (goto-char (point-at-bol)) - (insert (make-string python-indent ?\s)))) - -(defun elpy--nav-move-region-left () - (save-excursion - (let ((beg (region-beginning)) - (end (region-end))) - (goto-char beg) - (goto-char (point-at-bol)) - (while (< (point) end) - (elpy--nav-move-line-left) - (forward-line 1))) - (setq deactivate-mark nil))) - -(defun elpy--nav-move-region-right () - (save-excursion - (let ((beg (region-beginning)) - (end (region-end))) - (goto-char beg) - (goto-char (point-at-bol)) - (while (< (point) end) - (elpy--nav-move-line-right) - (forward-line 1))) - (setq deactivate-mark nil))) - (defun elpy-open-and-indent-line-below () "Open a line below the current one, move there, and indent." (interactive) @@ -1714,6 +1855,61 @@ (insert "\n")) (indent-according-to-mode)) +(defun elpy-nav-expand-to-indentation () + "Select surrounding lines with current indentation." + (interactive) + (setq elpy-nav-expand--initial-position (point)) + (let ((indentation (current-indentation))) + (if (= indentation 0) + (mark-whole-buffer) + (while (<= indentation (current-indentation)) + (forward-line -1)) + (forward-line 1) + (push-mark (point) nil t) + (while (<= indentation (current-indentation)) + (forward-line 1)) + (backward-char)))) + +(defadvice keyboard-quit (before collapse-region activate) + (when (eq last-command 'elpy-nav-expand-to-indentation) + (goto-char elpy-nav-expand--initial-position))) + +(defun elpy-nav-normalize-region () + "If the first or last line are not fully selected, select them completely." + (let ((beg (region-beginning)) + (end (region-end))) + (goto-char beg) + (beginning-of-line) + (push-mark (point) nil t) + (goto-char end) + (when (not (= (point) (line-beginning-position))) + (end-of-line)))) + +(defun elpy-nav-indent-shift-right (&optional count) + "Shift current line by COUNT columns to the right. + +COUNT defaults to `python-indent-offset'. +If region is active, normalize the region and shift." + (interactive) + (if (use-region-p) + (progn + (elpy-nav-normalize-region) + (python-indent-shift-right (region-beginning) (region-end) current-prefix-arg)) + (python-indent-shift-right (line-beginning-position) (line-end-position) current-prefix-arg))) + +(defun elpy-nav-indent-shift-left (&optional count) + "Shift current line by COUNT columns to the left. + +COUNT defaults to `python-indent-offset'. +If region is active, normalize the region and shift." + (interactive) + (if (use-region-p) + (progn + (elpy-nav-normalize-region) + (python-indent-shift-left (region-beginning) (region-end) current-prefix-arg)) + (python-indent-shift-left (line-beginning-position) (line-end-position) current-prefix-arg))) + + ;;;;;;;;;;;;;;;; ;;; Test running @@ -1788,7 +1984,7 @@ (file buffer-file-name) (module (elpy-test--module-name-for-file top file)) (test (elpy-test--current-test-name))) - (if (and file (string-match "/test[^/]*$" file)) + (if (and file (string-match "test" (or module test ""))) (progn (save-buffer) (list top file module test)) @@ -1977,10 +2173,10 @@ nil)))) ;;;;;;;;;;;;;; -;;; Import manipulation - -(defun elpy-importmagic--replace-block (spec) - "Replace an imports block. SPEC is (startline endline newblock)." +;;; Buffer manipulation + +(defun elpy-buffer--replace-block (spec) + "Replace a block. SPEC is (startline endline newblock)." (let ((start-line (nth 0 spec)) (end-line (nth 1 spec)) (new-block (nth 2 spec))) @@ -1996,6 +2192,18 @@ (delete-region beg end) (insert new-block))))))) +(defun elpy-buffer--replace-region (beg end rep) + "Replace text in BUFFER in region (BEG END) with REP." + (unless (string-equal (buffer-substring beg end) rep) + (save-excursion + (goto-char end) + (insert rep) + (delete-region beg end)))) + + +;;;;;;;;;;;;;;;;;;;;;;; +;;; Import manipulation + (defun elpy-importmagic--add-import-read-args (&optional object prompt) (let* ((default-object (save-excursion (let ((bounds (with-syntax-table python-dotty-syntax-table @@ -2021,12 +2229,13 @@ (list (if (equal user-choice "") first-choice user-choice))))))) (defun elpy-importmagic-add-import (statement) + "Prompt to import thing at point, show possbile imports and add selected import." (interactive (elpy-importmagic--add-import-read-args)) (unless (equal statement "") (let* ((res (elpy-rpc "add_import" (list buffer-file-name (elpy-rpc--buffer-contents) statement)))) - (elpy-importmagic--replace-block res)))) + (elpy-buffer--replace-block res)))) (defun elpy-importmagic-fixup () "Query for new imports of unresolved symbols, and remove unreferenced imports. @@ -2046,7 +2255,47 @@ (let* ((res (elpy-rpc "remove_unreferenced_imports" (list buffer-file-name (elpy-rpc--buffer-contents))))) (unless (stringp res) - (elpy-importmagic--replace-block res)))) + (elpy-buffer--replace-block res)))) + +;;;;;;;;;;;;;;;;;;;;; +;;; Code reformatting + +(defun elpy-format-code () + "Format code using the available formatter." + (interactive) + (cond + ((executable-find "yapf") + (elpy-yapf-fix-code)) + ((executable-find "autopep8") + (elpy-autopep8-fix-code)) + (t + (message "Install yapf/autopep8 to format code.")))) + +(defun elpy-yapf-fix-code () + "Automatically formats Python code with yapf." + (interactive) + (elpy--fix-code-with-formatter "fix_code_with_yapf")) + +(defun elpy-autopep8-fix-code () + "Automatically formats Python code to conform to the PEP 8 style guide." + (interactive) + (elpy--fix-code-with-formatter "fix_code")) + +(defun elpy--fix-code-with-formatter (method) + "Common routine for formatting python code." + (let ((line (line-number-at-pos)) + (col (current-column))) + (if (use-region-p) + (let ((new-block (elpy-rpc method (list (elpy-rpc--region-contents)))) + (beg (region-beginning)) (end (region-end))) + (elpy-buffer--replace-region beg end new-block)) + ;; Vector instead of list, json.el in Emacs 24.3 and before + ;; breaks for single-element lists of alists. + (let ((new-block (elpy-rpc method (vector (elpy-rpc--buffer-contents)))) + (beg (point-min)) (end (point-max))) + (elpy-buffer--replace-region beg end new-block))) + (forward-line (1- line)) + (forward-char col))) ;;;;;;;;;;;;;; ;;; Multi-Edit @@ -2164,7 +2413,9 @@ "symbol at point were found by the backend.")) (message "No occurrences of the symbol at point found"))) (t - (elpy-multiedit--usages usages)))))) + (save-restriction + (widen) + (elpy-multiedit--usages usages))))))) (defun elpy-multiedit--usages (usages) "Mark the usages in USAGES for editing." @@ -2510,6 +2761,8 @@ full-python-command)) (new-elpy-rpc-buffer (generate-new-buffer name)) (proc nil)) + (when (not full-python-command) + (error "Can't find Python command, configure `elpy-rpc-python-command'")) (with-current-buffer new-elpy-rpc-buffer (setq elpy-rpc--buffer-p t elpy-rpc--buffer (current-buffer) @@ -2590,10 +2843,13 @@ (did-read-json nil)) (goto-char (point-min)) (condition-case err - (setq json (let ((json-array-type 'list)) - (json-read)) - line-end (1+ (point)) - did-read-json t) + (progn + (setq json (let ((json-array-type 'list)) + (json-read))) + (if (listp json) + (setq line-end (1+ (point)) + did-read-json t) + (goto-char (point-min)))) (error (goto-char (point-min)))) (cond @@ -2622,8 +2878,8 @@ (elpy-insert--popup "*Elpy Version Mismatch*" (elpy-insert--header "Elpy Version Mismatch") (elpy-insert--para - "You are not using the same version of Elpy in Emacs Lisp" - "compared to Python. This can cause random problems. Please" + "You are not using the same version of Elpy in Emacs Lisp " + "compared to Python. This can cause random problems. Please " "do make sure to use compatible versions.\n") (insert "\n" @@ -2685,7 +2941,7 @@ elpy-rpc-error-timeout))) (message "Elpy error popup ignored, see `elpy-rpc-error-timeout': %s" message)) - (t + ((not elpy-disable-backend-error-display) (let ((config (elpy-config--get-config))) (elpy-insert--popup "*Elpy Error*" (elpy-insert--header "Elpy Error") @@ -2804,6 +3060,18 @@ `((filename . ,file-name) (delete_after_use . t))))) +(defun elpy-rpc--region-contents () + "Return the selected region as a string." + (if (use-region-p) + (buffer-substring (region-beginning) (region-end)))) + +(defun elpy-rpc--disconnect () + "Disconnect rpc process from elpy buffers." + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (memq 'elpy-mode minor-mode-list) + (setq elpy-rpc--buffer nil))))) + ;; RPC API functions (defun elpy-rpc-restart () @@ -2897,8 +3165,8 @@ "Get the Pydoc documentation for SYMBOL. Returns a possible multi-line docstring." - (elpy-rpc "get_pydoc_documentation" (list symbol) - success error)) + (elpy-rpc "get_pydoc_documentation" (list symbol) + success error)) (defun elpy-rpc-get-usages (&optional success error) (elpy-rpc "get_usages" @@ -2949,17 +3217,22 @@ (defun elpy-modules-remove-modeline-lighter (mode-name) "Remove the lighter for MODE-NAME. -It's not necessary to see (Python Elpy yas company ElDoc) all the -time. Honestly." +It should not be necessary to see (Python Elpy yas company ElDoc) all the +time. + +If you need your modeline, you can set the variable `elpy-remove-modeline-lighter' to nil +" + (interactive) - (cond - ((eq mode-name 'eldoc-minor-mode) - (setq eldoc-minor-mode-string nil)) - (t - (let ((cell (assq mode-name minor-mode-alist))) - (when cell - (setcdr cell - (list ""))))))) + (when elpy-remove-modeline-lighter + (cond + ((eq mode-name 'eldoc-minor-mode) + (setq eldoc-minor-mode-string nil)) + (t + (let ((cell (assq mode-name minor-mode-alist))) + (when cell + (setcdr cell + (list "")))))))) ;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Module: Sane Defaults @@ -2990,7 +3263,7 @@ (`buffer-init ;; We want immediate completions from company. (set (make-local-variable 'company-idle-delay) - 0) + 0.01) ;; And annotations should be right-aligned. (set (make-local-variable 'company-tooltip-align-annotations) t) @@ -3052,6 +3325,105 @@ name)) result)) +(defun elpy-company--python-exception-p (name) + "Check whether NAME is a Python exception." + (member name '("ArithmeticError" + "AssertionError" + "AttributeError" + "BlockingIOError" + "BrokenPipeError" + "BufferError" + "BytesWarning" + "ChildProcessError" + "ConnectionAbortedError" + "ConnectionError" + "ConnectionRefusedError" + "ConnectionResetError" + "DeprecationWarning" + "EOFError" + "EnvironmentError" + "Exception" + "FileExistsError" + "FileNotFoundError" + "FloatingPointError" + "FutureWarning" + "IOError" + "ImportError" + "ImportWarning" + "IndentationError" + "IndexError" + "InterruptedError" + "IsADirectoryError" + "KeyError" + "LookupError" + "MemoryError" + "NameError" + "NotADirectoryError" + "NotImplementedError" + "OSError" + "OverflowError" + "PendingDeprecationWarning" + "PermissionError" + "ProcessLookupError" + "RecursionError" + "ReferenceError" + "ResourceWarning" + "RuntimeError" + "RuntimeWarning" + "StandardError" + "StopAsyncIteration" + "StopIteration" + "SyntaxError" + "SyntaxWarning" + "SystemError" + "TabError" + "TimeoutError" + "TypeError" + "UnboundLocalError" + "UnicodeDecodeError" + "UnicodeEncodeError" + "UnicodeError" + "UnicodeTranslateError" + "UnicodeWarning" + "UserWarning" + "ValueError" + "Warning" + "ZeroDivisionError"))) + +(defun elpy-company-post-complete-parens (annotation name) + "Complete functions, classes, and callable instances with parentheses. + +Add parentheses in case ANNOTATION is \"class\", \"function\", or \"instance\", +unless the completion is already looking at a left parenthesis, +or unless NAME is a Python exception outside a reasonably formed raise statement, +or unless NAME is no callable instance." + (when (not (looking-at-p "\(")) + (cond ((string= annotation "function") + (insert "()") + (backward-char 1)) + ((string= annotation "class") + (cond ((elpy-company--python-exception-p name) + (when (save-excursion + (backward-word 2) + (looking-at "\\_<raise\\_>")) + (insert "()") + (backward-char 1))) + (t + (insert "()") + (backward-char 1)))) + ((string= annotation "instance") + ;; The jedi backend annotates some callables as instances (e.g. numpy + ;; and scipy) and `elpy-company--cache' does not allow to identify + ;; callable instances. + ;; It looks easy to modify `elpy-company--cache' cheaply for the jedi + ;; backend to eliminate the `elpy-rpc-get-calltip' call below, but + ;; not for the rope backend. + (insert "()") + (backward-char 1) + (when (not (elpy-rpc-get-calltip)) + (backward-char 1) + (delete-char 2)))))) + (defun elpy-company-backend (command &optional arg &rest ignored) "A company-mode backend for Elpy." (interactive (list 'interactive)) @@ -3104,7 +3476,8 @@ ;; doc-buffer <candidate> => put doc buffer in `company-doc-buffer' (`doc-buffer (let* ((name (elpy-company--cache-name arg)) - (doc (elpy-rpc-get-completion-docstring name))) + (doc (when name + (elpy-rpc-get-completion-docstring name)))) (when doc (company-doc-buffer doc)))) ;; require-match => Never require a match, even if the user @@ -3115,13 +3488,17 @@ ;; line-number) (`location (let* ((name (elpy-company--cache-name arg)) - (loc (elpy-rpc-get-completion-location name))) + (loc (when name + (elpy-rpc-get-completion-location name)))) (when loc (cons (car loc) (cadr loc))))) ;; match <candidate> => for non-prefix based backends ;; post-completion <candidate> => after insertion, for snippets - )) + (`post-completion + (funcall elpy-company-post-completion-function + (elpy-company--cache-annotation arg) + (elpy-company--cache-name arg))))) (defun elpy--sort-and-strip-duplicates (seq) "Sort SEQ and remove any duplicates." @@ -3224,7 +3601,9 @@ (set (make-local-variable 'flymake-warning-predicate) "^W[0-9]") (set (make-local-variable 'flymake-warning-re) "^W[0-9]")) - (flymake-mode 1)) + (when (and (buffer-file-name) + (executable-find python-check-command)) + (flymake-mode 1))) (`buffer-stop (flymake-mode -1) (kill-local-variable 'flymake-no-changes-timeout) @@ -3385,6 +3764,12 @@ (when (not (fboundp 'python-shell-send-string)) (defalias 'python-shell-send-string 'python-send-string)) +(when (not (fboundp 'python-indent-shift-right)) + (defalias 'python-indent-shift-right 'python-shift-right)) + +(when (not (fboundp 'python-indent-shift-left)) + (defalias 'python-indent-shift-left 'python-shift-left)) + ;; Emacs 24.2 made `locate-dominating-file' accept a predicate instead ;; of a string. Simply overwrite the current one, it's ;; backwards-compatible. The code below is taken from Emacs 24.3. @@ -3445,5 +3830,6 @@ highlight-indent-active) (highlight-indentation))))) + (provide 'elpy) ;;; elpy.el ends here
rename from elpa/elpy-1.8.0/elpy/__init__.py rename to elpa/elpy-1.12.0/elpy/__init__.py --- a/elpa/elpy-1.8.0/elpy/__init__.py +++ b/elpa/elpy-1.12.0/elpy/__init__.py @@ -1,6 +1,6 @@ # Elpy, the Emacs Lisp Python Environment -# Copyright (C) 2013 Jorgen Schaefer +# Copyright (C) 2013-2016 Jorgen Schaefer # Author: Jorgen Schaefer <contact@jorgenschaefer.de> # URL: http://github.com/jorgenschaefer/elpy @@ -37,5 +37,5 @@ """ __author__ = "Jorgen Schaefer" -__version__ = "1.8.0" +__version__ = "1.12.0" __license__ = "GPL"
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/auto_pep8.py @@ -0,0 +1,21 @@ +"""Glue for the "autopep8" library. + +""" + +from elpy.rpc import Fault + + +try: + import autopep8 +except ImportError: # pragma: no cover + autopep8 = None + + +def fix_code(code): + """Formats Python code to conform to the PEP 8 style guide. + + """ + if not autopep8: + raise Fault('autopep8 not installed, cannot fix code.', + code=400) + return autopep8.fix_code(code, apply_config=True)
rename from elpa/elpy-1.8.0/elpy/impmagic.py rename to elpa/elpy-1.12.0/elpy/impmagic.py --- a/elpa/elpy-1.8.0/elpy/impmagic.py +++ b/elpa/elpy-1.12.0/elpy/impmagic.py @@ -15,6 +15,10 @@ importmagic = None +class ImportMagicError(Exception): + """Used to pass defined errors from importmagic to the RPC layer.""" + + class ImportMagic(object): def __init__(self): @@ -80,12 +84,20 @@ return start_line, end_line, import_block def get_unresolved_symbols(self, source): - scope = importmagic.symbols.Scope.from_source(source) + try: + scope = importmagic.symbols.Scope.from_source(source) + except SyntaxError: + raise ImportMagicError('cannot find unresolved names in ' + 'incomplete file') unres, unref = scope.find_unresolved_and_unreferenced_symbols() return list(unres) def remove_unreferenced_imports(self, source): - scope = importmagic.symbols.Scope.from_source(source) + try: + scope = importmagic.symbols.Scope.from_source(source) + except SyntaxError: + raise ImportMagicError('cannot find unreferenced imports in ' + 'incomplete file') unres, unref = scope.find_unresolved_and_unreferenced_symbols() # Note: we do not supply "unres" to the call below, since we do # not want to add imports without querying the user from which
rename from elpa/elpy-1.8.0/elpy/jedibackend.py rename to elpa/elpy-1.12.0/elpy/jedibackend.py --- a/elpa/elpy-1.8.0/elpy/jedibackend.py +++ b/elpa/elpy-1.12.0/elpy/jedibackend.py @@ -38,8 +38,8 @@ return [] self.completions = dict((proposal.name, proposal) for proposal in proposals) - return [{'name': proposal.name, - 'suffix': proposal.complete, + return [{'name': proposal.name.rstrip("="), + 'suffix': proposal.complete.rstrip("="), 'annotation': proposal.type, 'meta': proposal.description} for proposal in proposals] @@ -125,6 +125,17 @@ call = None if not call: return None + try: + call.index + except AttributeError as e: + if "get_definition" in str(e): + # Bug #627 / jedi#573 + return None + elif "get_subscope_by_name" in str(e): + # Bug #677 / jedi#628 + return None + else: + raise return {"name": call.name, "index": call.index, "params": [param.description for param in call.params]} @@ -283,6 +294,33 @@ "No such file or directory" in str(e) ): return None + # Bug #561, #564, #570, #588, #593, #599 / jedi#572, jedi#579, jedi#590 + if isinstance(e, KeyError): + return None + # Bug #519 / jedi#610 + if ( + isinstance(e, RuntimeError) and + "maximum recursion depth exceeded" in str(e) + ): + return None + # Bug #563 / jedi#589 + if ( + isinstance(e, AttributeError) and + "MergedNamesDict" in str(e) + ): + return None + # Bug #615 / jedi#592 + if ( + isinstance(e, AttributeError) and + "ListComprehension" in str(e) + ): + return None + # Bug #569 / jedi#593 + if ( + isinstance(e, AttributeError) and + "names_dict" in str(e) + ): + return None from jedi import debug
rename from elpa/elpy-1.8.0/elpy/refactor.py rename to elpa/elpy-1.12.0/elpy/refactor.py --- a/elpa/elpy-1.8.0/elpy/refactor.py +++ b/elpa/elpy-1.12.0/elpy/refactor.py @@ -50,6 +50,8 @@ """ +import os + from elpy.rpc import Fault try: @@ -97,12 +99,16 @@ """ def __init__(self, project_root, filename): self.project_root = project_root - if ROPE_AVAILABLE: - self.project = Project(project_root, ropefolder=None) - self.resource = path_to_resource(self.project, filename) - else: - self.project = None - self.resource = FakeResource(filename) + if not ROPE_AVAILABLE: + raise Fault('rope not installed, cannot refactor code.', + code=400) + if not os.path.exists(project_root): + raise Fault( + "cannot do refactoring without a local project root", + code=400 + ) + self.project = Project(project_root, ropefolder=None) + self.resource = path_to_resource(self.project, filename) def get_refactor_options(self, start, end=None): """Return a list of options for refactoring at the given position. @@ -232,8 +238,7 @@ def refactor_module_to_package(self): """Convert the current module into a package.""" refactor = ModuleToPackage(self.project, self.resource) - changes = refactor.get_changes() - return translate_changes(changes) + return self._get_changes(refactor) @options("Rename symbol at point", category="Symbol", args=[("offset", "offset", None), @@ -250,9 +255,8 @@ refactor = Rename(self.project, self.resource, offset) except RefactoringError as e: raise Fault(str(e), code=400) - changes = refactor.get_changes(new_name, in_hierarchy=in_hierarchy, - docs=docs) - return translate_changes(changes) + return self._get_changes(refactor, new_name, + in_hierarchy=in_hierarchy, docs=docs) @options("Rename current module", category="Module", args=[("new_name", "string", "Rename to: ")], @@ -260,8 +264,7 @@ def refactor_rename_current_module(self, new_name): """Rename the current module.""" refactor = Rename(self.project, self.resource, None) - changes = refactor.get_changes(new_name) - return translate_changes(changes) + return self._get_changes(refactor, new_name) @options("Move the current module to a different package", category="Module", @@ -271,8 +274,7 @@ """Move the current module.""" refactor = create_move(self.project, self.resource) resource = path_to_resource(self.project, new_name) - changes = refactor.get_changes(resource) - return translate_changes(changes) + return self._get_changes(refactor, resource) @options("Inline function call at point", category="Symbol", args=[("offset", "offset", None), @@ -282,10 +284,9 @@ """Inline the function call at point.""" refactor = create_inline(self.project, self.resource, offset) if only_this: - changes = refactor.get_changes(remove=False, only_current=True) + return self._get_changes(refactor, remove=False, only_current=True) else: - changes = refactor.get_changes(remove=True, only_current=False) - return translate_changes(changes) + return self._get_changes(refactor, remove=True, only_current=False) @options("Extract current region as a method", category="Region", args=[("start", "start_offset", None), @@ -297,16 +298,30 @@ make_global): """Extract region as a method.""" refactor = ExtractMethod(self.project, self.resource, start, end) - changes = refactor.get_changes(name, similar=True, global_=make_global) - return translate_changes(changes) + return self._get_changes( + refactor, name, similar=True, global_=make_global + ) @options("Use the function at point wherever possible", category="Method", args=[("offset", "offset", None)], available=ROPE_AVAILABLE) def refactor_use_function(self, offset): """Use the function at point wherever possible.""" - refactor = UseFunction(self.project, self.resource, offset) - changes = refactor.get_changes() + try: + refactor = UseFunction(self.project, self.resource, offset) + except RefactoringError as e: + raise Fault( + 'Refactoring error: {}'.format(e), + code=400 + ) + return self._get_changes(refactor) + + def _get_changes(self, refactor, *args, **kwargs): + try: + changes = refactor.get_changes(*args, **kwargs) + except Exception as e: + raise Fault("Error during refactoring: {}".format(e), + code=400) return translate_changes(changes)
rename from elpa/elpy-1.8.0/elpy/ropebackend.py rename to elpa/elpy-1.12.0/elpy/ropebackend.py --- a/elpa/elpy-1.8.0/elpy/ropebackend.py +++ b/elpa/elpy-1.12.0/elpy/ropebackend.py @@ -8,7 +8,6 @@ import os import time -import traceback import rope.contrib.codeassist import rope.base.project @@ -97,38 +96,8 @@ resource, maxfixes=MAXFIXES, **kwargs) - except (rope.base.exceptions.BadIdentifierError, - rope.base.exceptions.ModuleSyntaxError, - rope.base.exceptions.ResourceNotFoundError, - rope.base.exceptions.NameNotFoundError, - IndentationError, - LookupError, - AttributeError): + except Exception: return None - except Exception as e: - data = { - "traceback": traceback.format_exc(), - "rope_debug_info": { - "project_root": self.project_root, - "filename": filename, - "source": source, - "function_name": (rope_function.__module__ + - "." + - rope_function.__name__), - "function_args": ", ".join([ - "project", "source", str(offset), "resource", - "maxfixes={0}".format(MAXFIXES) - ] + [ - u"{}={}".format(k, v) - for (k, v) in kwargs.items() - ]) - } - } - raise rpc.Fault( - code=500, - message=str(e), - data=data - ) def rpc_get_completions(self, filename, source, offset): proposals = self.call_rope( @@ -140,23 +109,18 @@ try: starting_offset = rope.contrib.codeassist.starting_offset(source, offset) - except (rope.base.exceptions.BadIdentifierError, - rope.base.exceptions.ModuleSyntaxError, - IndentationError, - LookupError, - AttributeError): + except Exception: return [] prefixlen = offset - starting_offset - self.completions = dict((proposal.name, proposal) - for proposal in proposals) try: + self.completions = dict((proposal.name, proposal) + for proposal in proposals) return [{'name': proposal.name, 'suffix': proposal.name[prefixlen:], 'annotation': proposal.type, 'meta': str(proposal)} for proposal in proposals] - except rope.base.exceptions.ModuleSyntaxError: - # Bug#406 + except Exception: return [] def rpc_get_completion_docstring(self, completion):
rename from elpa/elpy-1.8.0/elpy/server.py rename to elpa/elpy-1.12.0/elpy/server.py --- a/elpa/elpy-1.8.0/elpy/server.py +++ b/elpa/elpy-1.12.0/elpy/server.py @@ -11,7 +11,9 @@ from elpy.pydocutils import get_pydoc_completions from elpy.rpc import JSONRPCServer, Fault -from elpy.impmagic import ImportMagic +from elpy.impmagic import ImportMagic, ImportMagicError +from elpy.auto_pep8 import fix_code +from elpy.yapfutil import fix_code as fix_code_with_yapf try: @@ -208,7 +210,10 @@ """ self._ensure_import_magic() - return self.import_magic.get_import_symbols(symbol) + try: + return self.import_magic.get_import_symbols(symbol) + except ImportMagicError as err: + raise Fault(str(err), code=200) def rpc_add_import(self, filename, source, statement): """Add an import statement to the module. @@ -216,7 +221,10 @@ """ self._ensure_import_magic() source = get_source(source) - return self.import_magic.add_import(source, statement) + try: + return self.import_magic.add_import(source, statement) + except ImportMagicError as err: + raise Fault(str(err), code=200) def rpc_get_unresolved_symbols(self, filename, source): """Return a list of unreferenced symbols in the module. @@ -224,7 +232,10 @@ """ self._ensure_import_magic() source = get_source(source) - return self.import_magic.get_unresolved_symbols(source) + try: + return self.import_magic.get_unresolved_symbols(source) + except ImportMagicError as err: + raise Fault(str(err), code=200) def rpc_remove_unreferenced_imports(self, filename, source): """Remove unused import statements. @@ -232,7 +243,24 @@ """ self._ensure_import_magic() source = get_source(source) - return self.import_magic.remove_unreferenced_imports(source) + try: + return self.import_magic.remove_unreferenced_imports(source) + except ImportMagicError as err: + raise Fault(str(err), code=200) + + def rpc_fix_code(self, source): + """Formats Python code to conform to the PEP 8 style guide. + + """ + source = get_source(source) + return fix_code(source) + + def rpc_fix_code_with_yapf(self, source): + """Formats Python code to conform to the PEP 8 style guide. + + """ + source = get_source(source) + return fix_code_with_yapf(source) def get_source(fileobj): @@ -250,7 +278,8 @@ return fileobj else: try: - with io.open(fileobj["filename"], encoding="utf-8") as f: + with io.open(fileobj["filename"], encoding="utf-8", + errors="ignore") as f: return f.read() finally: if fileobj.get('delete_after_use'):
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/__init__.py @@ -0,0 +1,8 @@ +"""Unit tests for elpy.""" + +try: + import unittest2 + import sys + sys.modules['unittest'] = unittest2 +except: + pass
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/compat.py @@ -0,0 +1,18 @@ +"""Python 2/3 compatibility definitions. + +These are used by the rest of Elpy to keep compatibility definitions +in one place. + +""" + +import sys + + +if sys.version_info >= (3, 0): + PYTHON3 = True + import builtins + from io import StringIO +else: + PYTHON3 = False + import __builtin__ as builtins # noqa + from StringIO import StringIO # noqa
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/support.py @@ -0,0 +1,806 @@ +# coding: utf-8 + +"""Support classes and functions for the elpy test code. + +Elpy uses a bit of a peculiar test setup to avoid redundancy. For the +tests of the two backends, we provide generic test cases for generic +tests and for specific callback tests. + +These mixins can be included in the actual test classes. We can't add +these tests to a BackendTestCase subclass directly because the test +discovery would find them there and try to run them, which would fail. + +""" + +import os +import shutil +import sys +import tempfile +import unittest + +from elpy.tests import compat + + +class BackendTestCase(unittest.TestCase): + """Base class for backend tests. + + This class sets up a project root directory and provides an easy + way to create files within the project root. + + """ + + def setUp(self): + """Create the project root and make sure it gets cleaned up.""" + super(BackendTestCase, self).setUp() + self.project_root = tempfile.mkdtemp(prefix="elpy-test") + self.addCleanup(shutil.rmtree, self.project_root, True) + + def project_file(self, relname, contents): + """Create a file named relname within the project root. + + Write contents into that file. + + """ + full_name = os.path.join(self.project_root, relname) + try: + os.makedirs(os.path.dirname(full_name)) + except OSError: + pass + if compat.PYTHON3: + fobj = open(full_name, "w", encoding="utf-8") + else: + fobj = open(full_name, "w") + with fobj as f: + f.write(contents) + return full_name + + +class GenericRPCTests(object): + """Generic RPC test methods. + + This is a mixin to add tests that should be run for all RPC + methods that follow the generic (filename, source, offset) calling + conventions. + + """ + METHOD = None + + def rpc(self, filename, source, offset): + method = getattr(self.backend, self.METHOD) + return method(filename, source, offset) + + def test_should_not_fail_on_inexisting_file(self): + filename = self.project_root + "/doesnotexist.py" + self.rpc(filename, "", 0) + + def test_should_not_fail_on_empty_file(self): + filename = self.project_file("test.py", "") + self.rpc(filename, "", 0) + + def test_should_not_fail_if_file_is_none(self): + self.rpc(None, "", 0) + + def test_should_not_fail_for_module_syntax_errors(self): + source, offset = source_and_offset( + "class Foo(object):\n" + " def bar(self):\n" + " foo(_|_" + " bar(" + "\n" + " def a(self):\n" + " pass\n" + "\n" + " def b(self):\n" + " pass\n" + "\n" + " def b(self):\n" + " pass\n" + "\n" + " def b(self):\n" + " pass\n" + "\n" + " def b(self):\n" + " pass\n" + "\n" + " def b(self):\n" + " pass\n" + ) + filename = self.project_file("test.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_for_bad_indentation(self): + # Bug in Rope: rope#80 + source, offset = source_and_offset( + "def foo():\n" + " print(23)_|_\n" + " print(17)\n") + filename = self.project_file("test.py", source) + + self.rpc(filename, source, offset) + + @unittest.skipIf((3, 3) <= sys.version_info < (3, 4), + "Bug in jedi for Python 3.3") + def test_should_not_fail_for_relative_import(self): + # Bug in Rope: rope#81 and rope#82 + source, offset = source_and_offset( + "from .. import foo_|_" + ) + filename = self.project_file("test.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_on_keyword(self): + source, offset = source_and_offset( + "_|_try:\n" + " pass\n" + "except:\n" + " pass\n") + filename = self.project_file("test.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_with_bad_encoding(self): + # Bug in Rope: rope#83 + source, offset = source_and_offset( + u'# coding: utf-8X_|_\n' + ) + filename = self.project_file("test.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_with_form_feed_characters(self): + # Bug in Jedi: jedi#424 + source, offset = source_and_offset( + "\f\n" + "class Test(object):_|_\n" + " pass" + ) + filename = self.project_file("test.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_for_dictionaries_in_weird_places(self): + # Bug in Jedi: jedi#417 + source, offset = source_and_offset( + "import json\n" + "\n" + "def foo():\n" + " json.loads(_|_\n" + "\n" + " json.load.return_value = {'foo': [],\n" + " 'bar': True}\n" + "\n" + " c = Foo()\n" + ) + filename = self.project_file("test.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_break_with_binary_characters_in_docstring(self): + # Bug in Jedi: jedi#427 + template = '''\ +class Foo(object): + def __init__(self): + """ + COMMUNITY instance that this conversion belongs to. + DISPERSY_VERSION is the dispersy conversion identifier (on the wire version; must be one byte). + COMMUNIY_VERSION is the community conversion identifier (on the wire version; must be one byte). + + COMMUNIY_VERSION may not be '\\x00' or '\\xff'. '\\x00' is used by the DefaultConversion until + a proper conversion instance can be made for the Community. '\\xff' is reserved for when + more than one byte is needed as a version indicator. + """ + pass + + +x = Foo() +x._|_ +''' + source, offset = source_and_offset(template) + filename = self.project_file("test.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_for_def_without_name(self): + # Bug jedi#429 + source, offset = source_and_offset( + "def_|_():\n" + " if True:\n" + " return True\n" + " else:\n" + " return False\n" + ) + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_on_lambda(self): + # Bug #272 / jedi#431, jedi#572 + source, offset = source_and_offset( + "map(lambda_|_" + ) + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_on_literals(self): + # Bug #314, #344 / jedi#466 + source = u'lit = u"""\\\n# -*- coding: utf-8 -*-\n"""\n' + offset = 0 + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_with_args_as_args(self): + # Bug #347 in rope_py3k + source, offset = source_and_offset( + "def my_function(*args):\n" + " ret_|_" + ) + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_for_unicode_chars_in_string(self): + # Bug #358 / jedi#482 + source = '''\ +# coding: utf-8 + +logging.info(u"Saving «{}»...".format(title)) +requests.get(u"https://web.archive.org/save/{}".format(url)) +''' + offset = 57 + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_for_bad_escape_sequence(self): + # Bug #360 / jedi#485 + source = r"v = '\x'" + offset = 8 + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_for_coding_declarations_in_strings(self): + # Bug #314 / jedi#465 / python#22221 + source = u'lit = """\\\n# -*- coding: utf-8 -*-\n"""' + offset = 8 + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_if_root_vanishes(self): + # Bug #353 + source, offset = source_and_offset( + "import foo\n" + "foo._|_" + ) + filename = self.project_file("project.py", source) + shutil.rmtree(self.project_root) + + self.rpc(filename, source, offset) + + # For some reason, this breaks a lot of other tests. Couldn't + # figure out why. + # + # def test_should_not_fail_for_sys_path(self): + # # Bug #365 / jedi#486 + # source, offset = source_and_offset( + # "import sys\n" + # "\n" + # "sys.path.index(_|_\n" + # ) + # filename = self.project_file("project.py", source) + # + # self.rpc(filename, source, offset) + + def test_should_not_fail_for_key_error(self): + # Bug #561, #564, #570, #588, #593, #599 / jedi#572, jedi#579, + # jedi#590 + source, offset = source_and_offset( + "map(lambda_|_" + ) + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_for_badly_defined_global_variable(self): + # Bug #519 / jedi#610 + source, offset = source_and_offset( + """\ +def funct1(): + global global_dict_var + global_dict_var = dict() + +def funct2(): + global global_dict_var + q = global_dict_var.copy_|_() + print(q)""") + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + def test_should_not_fail_with_mergednamesdict(self): + # Bug #563 / jedi#589 + source, offset = source_and_offset( + u'from email import message_|_' + ) + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + +class RPCGetCompletionsTests(GenericRPCTests): + METHOD = "rpc_get_completions" + + def test_should_complete_builtin(self): + source, offset = source_and_offset("o_|_") + + expected = ["object", "oct", "open", "or", "ord"] + actual = [cand['name'] for cand in + self.backend.rpc_get_completions("test.py", + source, offset)] + + for candidate in expected: + self.assertIn(candidate, actual) + + if sys.version_info >= (3, 5): + JSON_COMPLETIONS = ["SONDecoder", "SONEncoder", "SONDecodeError"] + else: + JSON_COMPLETIONS = ["SONDecoder", "SONEncoder"] + + def test_should_complete_imports(self): + source, offset = source_and_offset("import json\n" + "json.J_|_") + filename = self.project_file("test.py", source) + completions = self.backend.rpc_get_completions(filename, + source, + offset) + self.assertEqual( + sorted([cand['suffix'] for cand in completions]), + sorted(self.JSON_COMPLETIONS)) + + def test_should_complete_top_level_modules_for_import(self): + source, offset = source_and_offset("import multi_|_") + filename = self.project_file("test.py", source) + completions = self.backend.rpc_get_completions(filename, + source, + offset) + if compat.PYTHON3: + expected = ["processing"] + else: + expected = ["file", "processing"] + self.assertEqual(sorted([cand['suffix'] for cand in completions]), + sorted(expected)) + + def test_should_complete_packages_for_import(self): + source, offset = source_and_offset("import email.mi_|_") + filename = self.project_file("test.py", source) + completions = self.backend.rpc_get_completions(filename, + source, + offset) + self.assertEqual([cand['suffix'] for cand in completions], + ["me"]) + + def test_should_not_complete_for_import(self): + source, offset = source_and_offset("import foo.Conf_|_") + filename = self.project_file("test.py", source) + completions = self.backend.rpc_get_completions(filename, + source, + offset) + self.assertEqual([cand['suffix'] for cand in completions], + []) + + @unittest.skipIf((3, 3) <= sys.version_info < (3, 4), + "Bug in jedi for Python 3.3") + def test_should_not_fail_for_short_module(self): + source, offset = source_and_offset("from .. import foo_|_") + filename = self.project_file("test.py", source) + completions = self.backend.rpc_get_completions(filename, + source, + offset) + self.assertIsNotNone(completions) + + def test_should_complete_sys(self): + source, offset = source_and_offset("import sys\nsys._|_") + filename = self.project_file("test.py", source) + completions = self.backend.rpc_get_completions(filename, + source, + offset) + self.assertIn('path', [cand['suffix'] for cand in completions]) + + def test_should_find_with_trailing_text(self): + source, offset = source_and_offset( + "import threading\nthreading.T_|_mumble mumble") + + expected = ["Thread", "ThreadError", "Timer"] + actual = [cand['name'] for cand in + self.backend.rpc_get_completions("test.py", source, offset)] + + for candidate in expected: + self.assertIn(candidate, actual) + + def test_should_find_completion_different_package(self): + # See issue #74 + self.project_file("project/__init__.py", "") + source1 = ("class Add:\n" + " def add(self, a, b):\n" + " return a + b\n") + self.project_file("project/add.py", source1) + source2, offset = source_and_offset( + "from project.add import Add\n" + "class Calculator:\n" + " def add(self, a, b):\n" + " c = Add()\n" + " c.ad_|_\n") + file2 = self.project_file("project/calculator.py", source2) + proposals = self.backend.rpc_get_completions(file2, + source2, + offset) + self.assertEqual(["add"], + [proposal["name"] for proposal in proposals]) + + +class RPCGetCompletionDocstringTests(object): + def test_should_return_docstring(self): + source, offset = source_and_offset("import json\n" + "json.JSONEnc_|_") + filename = self.project_file("test.py", source) + completions = self.backend.rpc_get_completions(filename, + source, + offset) + completions.sort(key=lambda p: p["name"]) + prop = completions[0] + self.assertEqual(prop["name"], "JSONEncoder") + + docs = self.backend.rpc_get_completion_docstring("JSONEncoder") + + self.assertIn("Extensible JSON", docs) + + def test_should_return_none_if_unknown(self): + docs = self.backend.rpc_get_completion_docstring("Foo") + + self.assertIsNone(docs) + + +class RPCGetCompletionLocationTests(object): + def test_should_return_location(self): + source, offset = source_and_offset("donaudampfschiff = 1\n" + "donau_|_") + filename = self.project_file("test.py", source) + completions = self.backend.rpc_get_completions(filename, + source, + offset) + prop = completions[0] + self.assertEqual(prop["name"], "donaudampfschiff") + + loc = self.backend.rpc_get_completion_location("donaudampfschiff") + + self.assertEqual((filename, 1), loc) + + def test_should_return_none_if_unknown(self): + docs = self.backend.rpc_get_completion_location("Foo") + + self.assertIsNone(docs) + + +class RPCGetDefinitionTests(GenericRPCTests): + METHOD = "rpc_get_definition" + + def test_should_return_definition_location_same_file(self): + source, offset = source_and_offset("import threading\n" + "def test_function(a, b):\n" + " return a + b\n" + "\n" + "test_func_|_tion(\n") + filename = self.project_file("test.py", source) + + location = self.backend.rpc_get_definition(filename, + source, + offset) + + self.assertEqual(location[0], filename) + # On def or on the function name + self.assertIn(location[1], (17, 21)) + + def test_should_return_location_in_same_file_if_not_saved(self): + source, offset = source_and_offset( + "import threading\n" + "\n" + "\n" + "def other_function():\n" + " test_f_|_unction(1, 2)\n" + "\n" + "\n" + "def test_function(a, b):\n" + " return a + b\n") + filename = self.project_file("test.py", "") + + location = self.backend.rpc_get_definition(filename, + source, + offset) + + self.assertEqual(location[0], filename) + # def or function name + self.assertIn(location[1], (67, 71)) + + def test_should_return_location_in_different_file(self): + source1 = ("def test_function(a, b):\n" + " return a + b\n") + file1 = self.project_file("test1.py", source1) + source2, offset = source_and_offset("from test1 import test_function\n" + "test_funct_|_ion(1, 2)\n") + file2 = self.project_file("test2.py", source2) + + definition = self.backend.rpc_get_definition(file2, + source2, + offset) + + self.assertEqual(definition[0], file1) + # Either on the def or on the function name + self.assertIn(definition[1], (0, 4)) + + def test_should_return_none_if_location_not_found(self): + source, offset = source_and_offset("test_f_|_unction()\n") + filename = self.project_file("test.py", source) + + definition = self.backend.rpc_get_definition(filename, + source, + offset) + + self.assertIsNone(definition) + + def test_should_return_none_if_outside_of_symbol(self): + source, offset = source_and_offset("test_function(_|_)\n") + filename = self.project_file("test.py", source) + + definition = self.backend.rpc_get_definition(filename, + source, + offset) + + self.assertIsNone(definition) + + def test_should_return_definition_location_different_package(self): + # See issue #74 + self.project_file("project/__init__.py", "") + source1 = ("class Add:\n" + " def add(self, a, b):\n" + " return a + b\n") + file1 = self.project_file("project/add.py", source1) + source2, offset = source_and_offset( + "from project.add import Add\n" + "class Calculator:\n" + " def add(self, a, b):\n" + " return Add_|_().add(a, b)\n") + file2 = self.project_file("project/calculator.py", source2) + + location = self.backend.rpc_get_definition(file2, + source2, + offset) + + self.assertEqual(location[0], file1) + # class or class name + self.assertIn(location[1], (0, 6)) + + def test_should_find_variable_definition(self): + source, offset = source_and_offset("SOME_VALUE = 1\n" + "\n" + "variable = _|_SOME_VALUE\n") + filename = self.project_file("test.py", source) + self.assertEqual(self.backend.rpc_get_definition(filename, + source, + offset), + (filename, 0)) + + +class RPCGetCalltipTests(GenericRPCTests): + METHOD = "rpc_get_calltip" + + @unittest.skipIf(sys.version_info >= (3, 0), + "Bug in Jedi 0.9.0") + def test_should_get_calltip(self): + source, offset = source_and_offset( + "import threading\nthreading.Thread(_|_") + filename = self.project_file("test.py", source) + calltip = self.backend.rpc_get_calltip(filename, + source, + offset) + + expected = self.THREAD_CALLTIP + + self.assertEqual(calltip, expected) + + @unittest.skipIf(sys.version_info >= (3, 0), + "Bug in Jedi 0.9.0") + def test_should_get_calltip_even_after_parens(self): + source, offset = source_and_offset( + "import threading\nthreading.Thread(foo()_|_") + filename = self.project_file("test.py", source) + + actual = self.backend.rpc_get_calltip(filename, + source, + offset) + + self.assertEqual(self.THREAD_CALLTIP, actual) + + @unittest.skipIf(sys.version_info >= (3, 0), + "Bug in Jedi 0.9.0") + def test_should_get_calltip_at_closing_paren(self): + source, offset = source_and_offset( + "import threading\nthreading.Thread(_|_)") + filename = self.project_file("test.py", source) + + actual = self.backend.rpc_get_calltip(filename, + source, + offset) + + self.assertEqual(self.THREAD_CALLTIP, actual) + + def test_should_not_missing_attribute_get_definition(self): + # Bug #627 / jedi#573 + source, offset = source_and_offset( + "import threading\nthreading.Thread(_|_)") + filename = self.project_file("test.py", source) + + self.backend.rpc_get_calltip(filename, source, offset) + + def test_should_return_none_for_bad_identifier(self): + source, offset = source_and_offset( + "froblgoo(_|_") + filename = self.project_file("test.py", source) + calltip = self.backend.rpc_get_calltip(filename, + source, + offset) + self.assertIsNone(calltip) + + def test_should_remove_self_argument(self): + source, offset = source_and_offset( + "d = dict()\n" + "d.keys(_|_") + filename = self.project_file("test.py", source) + + actual = self.backend.rpc_get_calltip(filename, + source, + offset) + + self.assertEqual(self.KEYS_CALLTIP, actual) + + def test_should_remove_package_prefix(self): + source, offset = source_and_offset( + "import decimal\n" + "d = decimal.Decimal('1.5')\n" + "d.radix(_|_") + filename = self.project_file("test.py", source) + + actual = self.backend.rpc_get_calltip(filename, + source, + offset) + + self.assertEqual(self.RADIX_CALLTIP, actual) + + def test_should_return_none_outside_of_all(self): + filename = self.project_file("test.py", "") + source, offset = source_and_offset("import thr_|_eading\n") + calltip = self.backend.rpc_get_calltip(filename, + source, offset) + self.assertIsNone(calltip) + + def test_should_find_calltip_different_package(self): + # See issue #74 + self.project_file("project/__init__.py", "") + source1 = ("class Add:\n" + " def add(self, a, b):\n" + " return a + b\n") + self.project_file("project/add.py", source1) + source2, offset = source_and_offset( + "from project.add import Add\n" + "class Calculator:\n" + " def add(self, a, b):\n" + " c = Add()\n" + " c.add(_|_\n") + file2 = self.project_file("project/calculator.py", source2) + + actual = self.backend.rpc_get_calltip(file2, + source2, + offset) + + self.assertEqual(self.ADD_CALLTIP, actual) + + +class RPCGetDocstringTests(GenericRPCTests): + METHOD = "rpc_get_docstring" + + def check_docstring(self, docstring): + + def first_line(s): + return s[:s.index("\n")] + + self.assertEqual(first_line(docstring), + self.JSON_LOADS_DOCSTRING) + + def test_should_get_docstring(self): + source, offset = source_and_offset( + "import json\njson.loads_|_(") + filename = self.project_file("test.py", source) + docstring = self.backend.rpc_get_docstring(filename, + source, + offset) + self.check_docstring(docstring) + + def test_should_return_none_for_bad_identifier(self): + source, offset = source_and_offset( + "froblgoo_|_(\n") + filename = self.project_file("test.py", source) + docstring = self.backend.rpc_get_docstring(filename, + source, + offset) + self.assertIsNone(docstring) + + +class RPCGetUsagesTests(GenericRPCTests): + METHOD = "rpc_get_usages" + + def test_should_return_uses_in_same_file(self): + filename = self.project_file("test.py", "") + source, offset = source_and_offset( + "def foo(x):\n" + " return _|_x + x\n") + + usages = self.backend.rpc_get_usages(filename, + source, + offset) + + self.assertEqual(usages, + [{'name': 'x', + 'offset': 8, + 'filename': filename}, + {'name': 'x', + 'filename': filename, + 'offset': 23}, + {'name': u'x', + 'filename': filename, + 'offset': 27}]) + + def test_should_return_uses_in_other_file(self): + file1 = self.project_file("file1.py", "") + file2 = self.project_file("file2.py", "\n\n\n\n\nx = 5") + source, offset = source_and_offset( + "import file2\n" + "file2._|_x\n") + + usages = self.backend.rpc_get_usages(file1, + source, + offset) + + self.assertEqual(usages, + [{'name': 'x', + 'filename': file1, + 'offset': 19}, + {'name': 'x', + 'filename': file2, + 'offset': 5}]) + + def test_should_not_fail_without_symbol(self): + filename = self.project_file("file.py", "") + + usages = self.backend.rpc_get_usages(filename, + "", + 0) + + self.assertEqual(usages, []) + + +def source_and_offset(source): + """Return a source and offset from a source description. + + >>> source_and_offset("hello, _|_world") + ("hello, world", 7) + >>> source_and_offset("_|_hello, world") + ("hello, world", 0) + >>> source_and_offset("hello, world_|_") + ("hello, world", 12) + """ + offset = source.index("_|_") + return source[:offset] + source[offset + 3:], offset
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_auto_pep8.py @@ -0,0 +1,20 @@ +# coding: utf-8 + +"""Tests for the elpy.autopep8 module""" + +import unittest + +from elpy import auto_pep8 +from elpy.tests.support import BackendTestCase + + +class Autopep8TestCase(BackendTestCase): + + def setUp(self): + if not auto_pep8.autopep8: + raise unittest.SkipTest + + def test_fix_code(self): + code_block = 'x= 123\n' + new_block = auto_pep8.fix_code(code_block) + self.assertEqual(new_block, 'x = 123\n')
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_impmagic.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +"""Tests for the elpy.impmagic module""" + +import re +import sys +import unittest + +from elpy import impmagic +from elpy.tests.support import BackendTestCase + +TEST_SOURCE = '''# test file + +import time +import logging + +os.getcwd() +time.sleep(1) +''' + + +@unittest.skipIf(sys.version_info >= (3, 5), "importmagic fails in 3.5") +class ImportMagicTestCase(BackendTestCase): + def setUp(self): + if not impmagic.importmagic: + raise unittest.SkipTest + self.importmagic = impmagic.ImportMagic() + super(ImportMagicTestCase, self).setUp() + + def build_index(self): + self.project_file('mymod.py', 'class AnUncommonName:\n pass\n') + self.importmagic.build_index(self.project_root, + custom_path=[self.project_root], + blacklist_re=re.compile('^$')) + self.importmagic._thread.join() + + def test_get_symbols(self): + self.build_index() + candidates = self.importmagic.get_import_symbols('AnUncommonName') + self.assertEqual(candidates, ['from mymod import AnUncommonName']) + candidates = self.importmagic.get_import_symbols('mymod') + self.assertEqual(candidates, ['import mymod']) + + def test_add_import(self): + self.build_index() + start, end, newblock = self.importmagic.add_import( + TEST_SOURCE, 'from mymod import AnUncommonName') + self.assertEqual(start, 2) + self.assertEqual(end, 5) + self.assertEqual(newblock.strip(), + 'import logging\n' + 'import time\n' + 'from mymod import AnUncommonName') + + start, end, newblock = self.importmagic.add_import( + TEST_SOURCE, 'import mymod') + self.assertEqual(start, 2) + self.assertEqual(end, 5) + self.assertEqual(newblock.strip(), + 'import logging\nimport mymod\nimport time') + + def test_get_unresolved_symbols(self): + self.build_index() + symbols = self.importmagic.get_unresolved_symbols('x = a + b\ny = c.d') + self.assertEqual(sorted(symbols), ['a', 'b', 'c.d']) + + def test_remove_unreferenced_imports(self): + self.build_index() + start, end, newblock = \ + self.importmagic.remove_unreferenced_imports(TEST_SOURCE) + self.assertEqual(start, 2) + self.assertEqual(end, 5) + self.assertEqual(newblock.strip(), 'import time')
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_jedibackend.py @@ -0,0 +1,287 @@ +"""Tests for the elpy.jedibackend module.""" + +import sys +import unittest + +import jedi +import mock + +from elpy import jedibackend +from elpy import rpc +from elpy.tests import compat +from elpy.tests.support import BackendTestCase +from elpy.tests.support import RPCGetCompletionsTests +from elpy.tests.support import RPCGetCompletionDocstringTests +from elpy.tests.support import RPCGetCompletionLocationTests +from elpy.tests.support import RPCGetDocstringTests +from elpy.tests.support import RPCGetDefinitionTests +from elpy.tests.support import RPCGetCalltipTests +from elpy.tests.support import RPCGetUsagesTests + + +class JediBackendTestCase(BackendTestCase): + def setUp(self): + super(JediBackendTestCase, self).setUp() + self.backend = jedibackend.JediBackend(self.project_root) + + +class TestInit(JediBackendTestCase): + def test_should_have_jedi_as_name(self): + self.assertEqual(self.backend.name, "jedi") + + +class TestRPCGetCompletions(RPCGetCompletionsTests, + JediBackendTestCase): + pass + + +class TestRPCGetCompletionDocstring(RPCGetCompletionDocstringTests, + JediBackendTestCase): + pass + + +class TestRPCGetCompletionLocation(RPCGetCompletionLocationTests, + JediBackendTestCase): + pass + + +class TestRPCGetDocstring(RPCGetDocstringTests, + JediBackendTestCase): + JSON_LOADS_DOCSTRING = ( + 'loads(s, encoding=None, cls=None, ' + 'object_hook=None, parse_float=None,' + ) + + def check_docstring(self, docstring): + lines = docstring.splitlines() + self.assertEqual(lines[0], 'Documentation for json.loads:') + self.assertEqual(lines[2], self.JSON_LOADS_DOCSTRING) + + +class TestRPCGetDefinition(RPCGetDefinitionTests, + JediBackendTestCase): + @mock.patch("jedi.Script") + def test_should_not_fail_if_module_path_is_none(self, Script): + """Do not fail if loc.module_path is None. + + This can happen under some circumstances I am unsure about. + See #537 for the issue that reported this. + + """ + locations = [ + mock.Mock(module_path=None) + ] + script = Script.return_value + script.goto_definitions.return_value = locations + script.goto_assignments.return_value = locations + + location = self.rpc("", "", 0) + + self.assertIsNone(location) + + +class TestRPCGetCalltip(RPCGetCalltipTests, + JediBackendTestCase): + KEYS_CALLTIP = {'index': 0, + 'params': [''], + 'name': u'keys'} + if sys.version_info >= (3, 5): + RADIX_CALLTIP = {'index': 0, + 'params': ['10'], + 'name': u'radix'} + else: + RADIX_CALLTIP = {'index': None, + 'params': [], + 'name': u'radix'} + ADD_CALLTIP = {'index': 0, + 'params': [u'a', u'b'], + 'name': u'add'} + if compat.PYTHON3: + THREAD_CALLTIP = {"name": "Thread", + "params": ["group=None", + "target=None", + "name=None", + "args=()", + "kwargs=None", + "daemon=None"], + "index": 0} + else: + THREAD_CALLTIP = {"name": "Thread", + "params": ["group=None", + "target=None", + "name=None", + "args=()", + "kwargs=None", + "verbose=None"], + "index": 0} + + def test_should_not_fail_with_get_subscope_by_name(self): + # Bug #677 / jedi#628 + source = ( + u"my_lambda = lambda x: x+1\n" + u"my_lambda(1)" + ) + filename = self.project_file("project.py", source) + offset = 37 + + sigs = self.backend.rpc_get_calltip(filename, source, offset) + if sigs is not None: + sigs[0].index + + +class TestRPCGetUsages(RPCGetUsagesTests, + JediBackendTestCase): + def test_should_not_fail_for_missing_module(self): + # This causes use.module_path to be None + source = "import sys\n\nsys.path.\n" # insert()" + offset = 21 + filename = self.project_file("project.py", source) + + self.rpc(filename, source, offset) + + +class TestPosToLinecol(unittest.TestCase): + def test_should_handle_beginning_of_string(self): + self.assertEqual(jedibackend.pos_to_linecol("foo", 0), + (1, 0)) + + def test_should_handle_end_of_line(self): + self.assertEqual(jedibackend.pos_to_linecol("foo\nbar\nbaz\nqux", 9), + (3, 1)) + + def test_should_handle_end_of_string(self): + self.assertEqual(jedibackend.pos_to_linecol("foo\nbar\nbaz\nqux", 14), + (4, 2)) + + +class TestLinecolToPos(unittest.TestCase): + def test_should_handle_beginning_of_string(self): + self.assertEqual(jedibackend.linecol_to_pos("foo", 1, 0), + 0) + + def test_should_handle_end_of_string(self): + self.assertEqual(jedibackend.linecol_to_pos("foo\nbar\nbaz\nqux", + 3, 1), + 9) + + def test_should_return_offset(self): + self.assertEqual(jedibackend.linecol_to_pos("foo\nbar\nbaz\nqux", + 4, 2), + 14) + + def test_should_fail_for_line_past_text(self): + self.assertRaises(ValueError, + jedibackend.linecol_to_pos, "foo\n", 3, 1) + + def test_should_fail_for_column_past_text(self): + self.assertRaises(ValueError, + jedibackend.linecol_to_pos, "foo\n", 1, 10) + + +class TestRunWithDebug(unittest.TestCase): + @mock.patch('jedi.Script') + def test_should_call_method(self, Script): + Script.return_value.test_method.return_value = "test-result" + + result = jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3) + + Script.assert_called_with(1, 2, arg=3) + self.assertEqual(result, 'test-result') + + @mock.patch('jedi.Script') + def test_should_re_raise(self, Script): + Script.side_effect = RuntimeError + + with self.assertRaises(RuntimeError): + jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3, + re_raise=(RuntimeError,)) + + @mock.patch('jedi.Script') + @mock.patch('jedi.set_debug_function') + def test_should_keep_debug_info(self, set_debug_function, Script): + Script.side_effect = RuntimeError + + try: + jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3) + except rpc.Fault as e: + self.assertGreaterEqual(e.code, 400) + self.assertIsNotNone(e.data) + self.assertIn("traceback", e.data) + jedi_debug_info = e.data["jedi_debug_info"] + self.assertIsNotNone(jedi_debug_info) + self.assertEqual(jedi_debug_info["script_args"], + "1, 2, arg=3") + self.assertEqual(jedi_debug_info["source"], None) + self.assertEqual(jedi_debug_info["method"], "test_method") + self.assertEqual(jedi_debug_info["debug_info"], []) + else: + self.fail("Fault not thrown") + + @mock.patch('jedi.Script') + @mock.patch('jedi.set_debug_function') + def test_should_keep_error_text(self, set_debug_function, Script): + Script.side_effect = RuntimeError + + try: + jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3) + except rpc.Fault as e: + self.assertEqual(str(e), str(RuntimeError())) + self.assertEqual(e.message, str(RuntimeError())) + else: + self.fail("Fault not thrown") + + @mock.patch('jedi.Script') + @mock.patch('jedi.set_debug_function') + def test_should_handle_source_special(self, set_debug_function, Script): + Script.side_effect = RuntimeError + + try: + jedibackend.run_with_debug(jedi, 'test_method', source="foo") + except rpc.Fault as e: + self.assertEqual(e.data["jedi_debug_info"]["script_args"], + "source=source") + self.assertEqual(e.data["jedi_debug_info"]["source"], "foo") + else: + self.fail("Fault not thrown") + + @mock.patch('jedi.Script') + @mock.patch('jedi.set_debug_function') + def test_should_set_debug_info(self, set_debug_function, Script): + the_debug_function = [None] + + def my_set_debug_function(debug_function, **kwargs): + the_debug_function[0] = debug_function + + def my_script(*args, **kwargs): + the_debug_function[0](jedi.debug.NOTICE, "Notice") + the_debug_function[0](jedi.debug.WARNING, "Warning") + the_debug_function[0]("other", "Other") + raise RuntimeError + + set_debug_function.side_effect = my_set_debug_function + Script.return_value.test_method = my_script + + try: + jedibackend.run_with_debug(jedi, 'test_method', source="foo") + except rpc.Fault as e: + self.assertEqual(e.data["jedi_debug_info"]["debug_info"], + ["[N] Notice", + "[W] Warning", + "[?] Other"]) + else: + self.fail("Fault not thrown") + + @mock.patch('jedi.set_debug_function') + @mock.patch('jedi.Script') + def test_should_not_fail_with_bad_data(self, Script, set_debug_function): + import jedi.debug + + def set_debug(function, speed=True): + if function is not None: + function(jedi.debug.NOTICE, u"\xab") + + set_debug_function.side_effect = set_debug + Script.return_value.test_method.side_effect = Exception + + with self.assertRaises(rpc.Fault): + jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_pydocutils.py @@ -0,0 +1,88 @@ +import os +import unittest +import shutil +import sys +import tempfile + +import mock + +import elpy.pydocutils + + +class TestGetPydocCompletions(unittest.TestCase): + def test_should_return_top_level_modules(self): + modules = elpy.pydocutils.get_pydoc_completions("") + self.assertIn('sys', modules) + self.assertIn('json', modules) + + def test_should_return_submodules(self): + modules = elpy.pydocutils.get_pydoc_completions("elpy") + self.assertIn("elpy.rpc", modules) + self.assertIn("elpy.server", modules) + modules = elpy.pydocutils.get_pydoc_completions("os") + self.assertIn("os.path", modules) + + def test_should_find_objects_in_module(self): + self.assertIn("elpy.tests.test_pydocutils.TestGetPydocCompletions", + elpy.pydocutils.get_pydoc_completions + ("elpy.tests.test_pydocutils")) + + def test_should_find_attributes_of_objects(self): + attribs = elpy.pydocutils.get_pydoc_completions( + "elpy.tests.test_pydocutils.TestGetPydocCompletions") + self.assertIn("elpy.tests.test_pydocutils.TestGetPydocCompletions." + "test_should_find_attributes_of_objects", + attribs) + + def test_should_return_none_for_inexisting_module(self): + self.assertEqual([], + elpy.pydocutils.get_pydoc_completions + ("does_not_exist")) + + def test_should_work_for_unicode_strings(self): + self.assertIsNotNone(elpy.pydocutils.get_pydoc_completions + (u"sys")) + + def test_should_find_partial_completions(self): + self.assertIn("multiprocessing", + elpy.pydocutils.get_pydoc_completions + ("multiprocess")) + self.assertIn("multiprocessing.util", + elpy.pydocutils.get_pydoc_completions + ("multiprocessing.ut")) + + def test_should_ignore_trailing_dot(self): + self.assertIn("elpy.pydocutils", + elpy.pydocutils.get_pydoc_completions + ("elpy.")) + + +class TestGetModules(unittest.TestCase): + def test_should_return_top_level_modules(self): + modules = elpy.pydocutils.get_modules() + self.assertIn('sys', modules) + self.assertIn('json', modules) + + def test_should_return_submodules(self): + modules = elpy.pydocutils.get_modules("elpy") + self.assertIn("rpc", modules) + self.assertIn("server", modules) + + @mock.patch.object(elpy.pydocutils, 'safeimport') + def test_should_catch_import_errors(self, safeimport): + def raise_function(message): + raise elpy.pydocutils.ErrorDuringImport(message, + (None, None, None)) + safeimport.side_effect = raise_function + self.assertEqual([], elpy.pydocutils.get_modules("foo.bar")) + + def test_should_not_fail_for_permission_denied(self): + tmpdir = tempfile.mkdtemp(prefix="test-elpy-get-modules-") + sys.path.append(tmpdir) + os.chmod(tmpdir, 0o000) + try: + elpy.pydocutils.get_modules() + finally: + os.chmod(tmpdir, 0o755) + shutil.rmtree(tmpdir) + sys.path.remove(tmpdir)
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_refactor.py @@ -0,0 +1,545 @@ +import unittest +import tempfile +import shutil +import os +import mock +import sys + +from elpy import refactor +from textwrap import dedent + + +class RefactorTestCase(unittest.TestCase): + def setUp(self): + self.project_root = tempfile.mkdtemp(prefix="test-refactor-root") + self.addCleanup(shutil.rmtree, self.project_root, + ignore_errors=True) + + def create_file(self, name, contents=""): + filename = os.path.join(self.project_root, name) + contents = dedent(contents) + offset = contents.find("_|_") + if offset > -1: + contents = contents[:offset] + contents[offset + 3:] + with open(filename, "w") as f: + f.write(contents) + return filename, offset + + def assertSourceEqual(self, first, second, msg=None): + """Fail if the two objects are unequal, ignoring indentation.""" + self.assertEqual(dedent(first), dedent(second), msg=msg) + + +class TestGetRefactorOptions(RefactorTestCase): + def test_should_only_return_importsmodule_if_not_on_symbol(self): + filename, offset = self.create_file("foo.py", + """\ + import foo + _|_""") + ref = refactor.Refactor(self.project_root, filename) + options = ref.get_refactor_options(offset) + self.assertTrue(all(opt['category'] in ('Imports', + 'Module') + for opt in options)) + filename, offset = self.create_file("foo.py", + """\ + _|_ + import foo""") + ref = refactor.Refactor(self.project_root, filename) + options = ref.get_refactor_options(offset) + self.assertTrue(all(opt['category'] in ('Imports', + 'Module') + for opt in options)) + + def test_should_return_all_if_on_symbol(self): + filename, offset = self.create_file("foo.py", + "import _|_foo") + ref = refactor.Refactor(self.project_root, filename) + options = ref.get_refactor_options(offset) + self.assertTrue(all(opt['category'] in ('Imports', + 'Method', + 'Module', + 'Symbol') + for opt in options)) + + def test_should_return_only_region_if_endoffset(self): + filename, offset = self.create_file("foo.py", + "import foo") + ref = refactor.Refactor(self.project_root, filename) + options = ref.get_refactor_options(offset, 5) + self.assertTrue(all(opt['category'] == 'Region' + for opt in options)) + + @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") + def test_should_treat_from_import_special(self): + filename, offset = self.create_file("foo.py", + """\ + import foo + _|_""") + ref = refactor.Refactor(self.project_root, filename) + options = ref.get_refactor_options(offset) + self.assertFalse(any(opt['name'] == "refactor_froms_to_imports" + for opt in options)) + filename, offset = self.create_file("foo.py", + "imp_|_ort foo") + ref = refactor.Refactor(self.project_root, filename) + options = ref.get_refactor_options(offset) + self.assertTrue(any(opt['name'] == "refactor_froms_to_imports" + for opt in options)) + + +class TestGetChanges(RefactorTestCase): + def test_should_fail_if_method_is_not_refactoring(self): + filename, offset = self.create_file("foo.py") + ref = refactor.Refactor(self.project_root, filename) + self.assertRaises(ValueError, ref.get_changes, "bad_name") + + def test_should_return_method_results(self): + filename, offset = self.create_file("foo.py") + ref = refactor.Refactor(self.project_root, filename) + with mock.patch.object(ref, 'refactor_extract_method') as test: + test.return_value = "Meep!" + self.assertEqual(ref.get_changes("refactor_extract_method", + 1, 2), + "Meep!") + test.assert_called_with(1, 2) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestIsOnSymbol(RefactorTestCase): + def test_should_find_symbol(self): + filename, offset = self.create_file("test.py", "__B_|_AR = 100") + r = refactor.Refactor(self.project_root, filename) + self.assertTrue(r._is_on_symbol(offset)) + + # Issue #111 + def test_should_find_symbol_with_underscores(self): + filename, offset = self.create_file("test.py", "_|___BAR = 100") + r = refactor.Refactor(self.project_root, filename) + self.assertTrue(r._is_on_symbol(offset)) + + def test_should_not_find_weird_places(self): + filename, offset = self.create_file("test.py", "hello = _|_ 1 + 1") + r = refactor.Refactor(self.project_root, filename) + self.assertFalse(r._is_on_symbol(offset)) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestFromsToImports(RefactorTestCase): + def test_should_refactor(self): + filename, offset = self.create_file( + "foo.py", + """\ + _|_from datetime import datetime + + d = datetime(2013, 4, 7) + """) + ref = refactor.Refactor(self.project_root, filename) + (change,) = ref.get_changes("refactor_froms_to_imports", offset) + self.assertEqual(change['action'], 'change') + self.assertEqual(change['file'], filename) + self.assertSourceEqual(change['contents'], + """\ + import datetime + + d = datetime.datetime(2013, 4, 7) + """) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestOrganizeImports(RefactorTestCase): + def test_should_refactor(self): + filename, offset = self.create_file( + "foo.py", + """\ + import unittest, base64 + import datetime, json + + obj = json.dumps(23) + unittest.TestCase() + """) + ref = refactor.Refactor(self.project_root, filename) + (change,) = ref.get_changes("refactor_organize_imports") + self.assertEqual(change['action'], 'change') + self.assertEqual(change['file'], filename) + self.assertSourceEqual(change['contents'], + """\ + import json + import unittest + + + obj = json.dumps(23) + unittest.TestCase() + """) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestModuleToPackage(RefactorTestCase): + def test_should_refactor(self): + filename, offset = self.create_file( + "foo.py", + "_|_import os\n") + ref = refactor.Refactor(self.project_root, filename) + changes = ref.refactor_module_to_package() + a, b, c = changes + # Not sure why the a change is there. It's a CHANGE that + # changes nothing... + self.assertEqual(a['diff'], '') + + self.assertEqual(b['action'], 'create') + self.assertEqual(b['type'], 'directory') + self.assertEqual(b['path'], os.path.join(self.project_root, "foo")) + + self.assertEqual(c['action'], 'move') + self.assertEqual(c['type'], 'file') + self.assertEqual(c['source'], os.path.join(self.project_root, + "foo.py")) + self.assertEqual(c['destination'], os.path.join(self.project_root, + "foo/__init__.py")) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestRenameAtPoint(RefactorTestCase): + def test_should_refactor(self): + filename, offset = self.create_file( + "foo.py", + """\ + class Foo(object): + def _|_foo(self): + return 5 + + def bar(self): + return self.foo() + """) + file2, offset2 = self.create_file( + "bar.py", + """\ + import foo + + + x = foo.Foo() + x.foo()""") + ref = refactor.Refactor(self.project_root, filename) + first, second = ref.refactor_rename_at_point(offset, "frob", + in_hierarchy=False, + docs=False) + if first['file'] == filename: + a, b = first, second + else: + a, b = second, first + self.assertEqual(a['action'], 'change') + self.assertEqual(a['file'], filename) + self.assertSourceEqual(a['contents'], + """\ + class Foo(object): + def frob(self): + return 5 + + def bar(self): + return self.frob() + """) + self.assertEqual(b['action'], 'change') + self.assertEqual(b['file'], file2) + self.assertSourceEqual(b['contents'], + """\ + import foo + + + x = foo.Foo() + x.frob()""") + + def test_should_refactor_in_hierarchy(self): + filename, offset = self.create_file( + "foo.py", + """\ + class Foo(object): + def _|_foo(self): + return 5 + + def bar(self): + return self.foo() + + class Bar(Foo): + def foo(self): + return 42 + + class Baz(object): + def foo(self): + return 42 + """) + file2, offset2 = self.create_file( + "bar.py", + """\ + import foo + + + x, y, z = foo.Foo(), foo.Bar(), foo.Baz() + x.foo() + y.foo() + z.foo()""") + ref = refactor.Refactor(self.project_root, filename) + first, second = ref.refactor_rename_at_point(offset, "frob", + in_hierarchy=True, + docs=False) + if first['file'] == filename: + a, b = first, second + else: + a, b = second, first + self.assertEqual(a['action'], 'change') + self.assertEqual(a['file'], filename) + self.assertSourceEqual(a['contents'], + """\ + class Foo(object): + def frob(self): + return 5 + + def bar(self): + return self.frob() + + class Bar(Foo): + def frob(self): + return 42 + + class Baz(object): + def foo(self): + return 42 + """) + self.assertEqual(b['action'], 'change') + self.assertEqual(b['file'], file2) + self.assertSourceEqual(b['contents'], + """\ + import foo + + + x, y, z = foo.Foo(), foo.Bar(), foo.Baz() + x.frob() + y.frob() + z.foo()""") + + def test_should_refactor_in_docstrings(self): + filename, offset = self.create_file( + "foo.py", + """\ + class Foo(object): + "Frobnicate the foo" + def _|_foo(self): + return 5 + + print("I'm an unrelated foo") + """) + ref = refactor.Refactor(self.project_root, filename) + (change,) = ref.refactor_rename_at_point(offset, "frob", + in_hierarchy=False, + docs=True) + self.assertEqual(change['action'], 'change') + self.assertEqual(change['file'], filename) + self.assertSourceEqual(change['contents'], + """\ + class Foo(object): + "Frobnicate the frob" + def frob(self): + return 5 + + print("I'm an unrelated foo") + """) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestRenameCurrentModule(RefactorTestCase): + def test_should_refactor(self): + filename, offset = self.create_file( + "foo.py", + "_|_import os\n") + file2, offset = self.create_file( + "bar.py", + """\ + _|_import foo + foo.os + """) + dest = os.path.join(self.project_root, "frob.py") + ref = refactor.Refactor(self.project_root, filename) + a, b = ref.refactor_rename_current_module("frob") + + self.assertEqual(a['action'], 'change') + self.assertEqual(a['file'], file2) + self.assertEqual(a['contents'], + "import frob\n" + "frob.os\n") + + self.assertEqual(b['action'], 'move') + self.assertEqual(b['type'], 'file') + self.assertEqual(b['source'], filename) + self.assertEqual(b['destination'], dest) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestMoveModule(RefactorTestCase): + def test_should_refactor(self): + filename, offset = self.create_file( + "foo.py", + "_|_import os\n") + file2, offset = self.create_file( + "bar.py", + """\ + _|_import foo + foo.os + """) + dest = os.path.join(self.project_root, "frob") + os.mkdir(dest) + with open(os.path.join(dest, "__init__.py"), "w") as f: + f.write("") + ref = refactor.Refactor(self.project_root, filename) + a, b = ref.refactor_move_module(dest) + + self.assertEqual(a['action'], 'change') + self.assertEqual(a['file'], file2) + self.assertSourceEqual(a['contents'], + """\ + import frob.foo + frob.foo.os + """) + + self.assertEqual(b['action'], 'move') + self.assertEqual(b['type'], 'file') + self.assertEqual(b['source'], filename) + self.assertEqual(b['destination'], + os.path.join(dest, "foo.py")) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestCreateInline(RefactorTestCase): + def setUp(self): + super(TestCreateInline, self).setUp() + self.filename, self.offset = self.create_file( + "foo.py", + """\ + def add(a, b): + return a + b + + x = _|_add(2, 3) + y = add(17, 4) + """) + + def test_should_refactor_single_occurrenc(self): + ref = refactor.Refactor(self.project_root, self.filename) + (change,) = ref.refactor_create_inline(self.offset, True) + + self.assertEqual(change['action'], 'change') + self.assertEqual(change['file'], self.filename) + self.assertSourceEqual(change['contents'], + """\ + def add(a, b): + return a + b + + x = 2 + 3 + y = add(17, 4) + """) + + def test_should_refactor_all_occurrencs(self): + ref = refactor.Refactor(self.project_root, self.filename) + (change,) = ref.refactor_create_inline(self.offset, False) + + self.assertEqual(change['action'], 'change') + self.assertEqual(change['file'], self.filename) + self.assertSourceEqual(change['contents'], + """\ + x = 2 + 3 + y = 17 + 4 + """) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestExtractMethod(RefactorTestCase): + def setUp(self): + super(TestExtractMethod, self).setUp() + self.filename, self.offset = self.create_file( + "foo.py", + """\ + class Foo(object): + def spaghetti(self, a, b): + _|_x = a + 5 + y = b + 23 + return y + """) + + @unittest.skipIf(sys.version_info >= (3, 5), "Python 3.5 not supported") + def test_should_refactor_local(self): + ref = refactor.Refactor(self.project_root, self.filename) + (change,) = ref.refactor_extract_method(self.offset, 104, + "calc", False) + self.assertEqual(change['action'], 'change') + self.assertEqual(change['file'], self.filename) + expected = """\ + class Foo(object): + def spaghetti(self, a, b): + return self.calc(a, b) + + def calc(self, a, b): + x = a + 5 + y = b + 23 + return y + """ + expected2 = expected.replace("return self.calc(a, b)", + "return self.calc(b, a)") + expected2 = expected2.replace("def calc(self, a, b)", + "def calc(self, b, a)") + # This is silly, but it's what we got. + if change['contents'] == dedent(expected2): + self.assertSourceEqual(change['contents'], expected2) + else: + self.assertSourceEqual(change['contents'], expected) + + @unittest.skipIf(sys.version_info >= (3, 5), "Python 3.5 not supported") + def test_should_refactor_global(self): + ref = refactor.Refactor(self.project_root, self.filename) + (change,) = ref.refactor_extract_method(self.offset, 104, + "calc", True) + self.assertEqual(change['action'], 'change') + self.assertEqual(change['file'], self.filename) + expected = """\ + class Foo(object): + def spaghetti(self, a, b): + return calc(a, b) + + def calc(a, b): + x = a + 5 + y = b + 23 + return y + """ + expected2 = expected.replace("return calc(a, b)", + "return calc(b, a)") + expected2 = expected2.replace("def calc(a, b)", + "def calc(b, a)") + if change['contents'] == dedent(expected2): + self.assertSourceEqual(change['contents'], expected2) + else: + self.assertSourceEqual(change['contents'], expected) + + +@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") +class TestUseFunction(RefactorTestCase): + def test_should_refactor(self): + filename, offset = self.create_file( + "foo.py", + """\ + def _|_add_and_multiply(a, b, c): + temp = a + b + return temp * c + + f = 1 + 2 + g = f * 3 + """) + + ref = refactor.Refactor(self.project_root, filename) + (change,) = ref.refactor_use_function(offset) + + self.assertEqual(change['action'], 'change') + self.assertEqual(change['file'], filename) + self.assertSourceEqual(change['contents'], + """\ + def add_and_multiply(a, b, c): + temp = a + b + return temp * c + + g = add_and_multiply(1, 2, 3) + """)
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_ropebackend.py @@ -0,0 +1,205 @@ +"""Tests for elpy.ropebackend.""" + +import os +import shutil +import sys +import tempfile + +import mock + +from elpy import ropebackend +from elpy import rpc +from elpy.tests import compat +from elpy.tests.support import BackendTestCase +from elpy.tests.support import RPCGetCompletionsTests +from elpy.tests.support import RPCGetCompletionDocstringTests +from elpy.tests.support import RPCGetCompletionLocationTests +from elpy.tests.support import RPCGetDefinitionTests +from elpy.tests.support import RPCGetCalltipTests +from elpy.tests.support import RPCGetDocstringTests + + +class RopeBackendTestCase(BackendTestCase): + def setUp(self): + super(RopeBackendTestCase, self).setUp() + self.backend = ropebackend.RopeBackend(self.project_root) + + +class ShouldCallValidateTest(object): + def test_should_call_validate(self): + with mock.patch.object(self.backend, 'validate') as validate: + self.rpc(None, "", 0) + + self.assertTrue(validate.called) + + +class TestInit(RopeBackendTestCase): + def test_should_have_rope_as_name(self): + self.assertEqual(self.backend.name, "rope") + + def test_should_patch_project_files(self): + self.project_file("foo.txt", "") + self.project_file("baddir/file.py", "") + + self.backend.project.validate() + + actual = [f.real_path for f in + self.backend.project.file_list.get_files()] + self.assertEqual([os.path.join(self.project_root, "foo.txt")], + actual) + + def test_should_fail_for_inexisting_project_root(self): + with self.assertRaises(rpc.Fault): + ropebackend.RopeBackend("/does/not/exist/") + + +class TestValidate(RopeBackendTestCase): + def test_should_call_validate_after_timeout(self): + with mock.patch("time.time") as t: + t.return_value = 10 + self.backend.validate() + with mock.patch.object(self.backend, 'project') as project: + t.return_value = 10 + ropebackend.VALIDATE_EVERY_SECONDS + 1 + self.backend.validate() + + self.assertTrue(project.validate.called) + + def test_should_not_call_validate_before_timeout(self): + with mock.patch("time.time") as t: + t.return_value = 10 + self.backend.validate() + with mock.patch.object(self.backend, 'project') as project: + t.return_value = 10 + ropebackend.VALIDATE_EVERY_SECONDS - 1 + self.backend.validate() + + self.assertFalse(project.validate.called) + + def test_should_not_fail_if_root_vanishes(self): + # Bug #353 + tmpdir = tempfile.mkdtemp(prefix="elpy-test-validate-") + try: + backend = ropebackend.RopeBackend(tmpdir) + finally: + shutil.rmtree(tmpdir) + backend.validate() + + +class TestRPCGetCompletions(RPCGetCompletionsTests, + RopeBackendTestCase): + pass + + +class TestRPCGetCompletionDocstring(RPCGetCompletionDocstringTests, + RopeBackendTestCase): + pass + + +class TestRPCGetCompletionLocation(RPCGetCompletionLocationTests, + RopeBackendTestCase): + pass + + +class TestRPCGetDefinition(RPCGetDefinitionTests, + ShouldCallValidateTest, + RopeBackendTestCase): + pass + + +class TestRPCGetCalltip(RPCGetCalltipTests, + ShouldCallValidateTest, + RopeBackendTestCase): + ADD_CALLTIP = 'Add.add(a, b)' + RADIX_CALLTIP = "Decimal.radix()" + if compat.PYTHON3: + THREAD_CALLTIP = ( + "threading.Thread(group=None, target=None, " + "name=None, args=(), kwargs=None, daemon=None, *)" + ) + KEYS_CALLTIP = "builtins.keys()" + else: + THREAD_CALLTIP = ( + "threading.Thread(group=None, target=None, " + "name=None, args=(), kwargs=None, verbose=None)" + ) + KEYS_CALLTIP = "__builtin__.keys()" + + +class TestRPCGetDocstring(RPCGetDocstringTests, + ShouldCallValidateTest, + RopeBackendTestCase): + if sys.version_info < (2, 7): + JSON_LOADS_DOCSTRING = ( + 'loads(s, encoding=None, cls=None, object_hook=None, ' + 'parse_float=None, parse_int=None, parse_constant=None, ' + '**kw):' + ) + else: + JSON_LOADS_DOCSTRING = ( + 'loads(s, encoding=None, cls=None, object_hook=None, ' + 'parse_float=None, parse_int=None, parse_constant=None, ' + 'object_pairs_hook=None, **kw):' + ) + + +class TestGetPythonProjectFiles(RopeBackendTestCase): + def test(self): + self.project_file("foo.txt", "") + self.project_file("gooddir/__init__.py", "") + self.project_file("gooddir/file.py", "") + self.project_file("baddir/file.py", "") + expected = set(os.path.join(self.project_root, name) + for name in ["foo.txt", "gooddir/__init__.py", + "gooddir/file.py"]) + project = self.backend.project + + actual = set(resource.real_path + for resource + in ropebackend.get_python_project_files(project)) + + self.assertEqual(expected, actual) + + +class TestPatchProjectFiles(RopeBackendTestCase): + def test(self): + self.project_file("foo.txt", "") + self.project_file("gooddir/__init__.py", "") + self.project_file("gooddir/file.py", "") + self.project_file("baddir/file.py", "") + expected = set(os.path.join(self.project_root, name) + for name in ["foo.txt", "gooddir/__init__.py", + "gooddir/file.py"]) + + actual = set(resource.real_path + for resource + in self.backend.project.get_files()) + self.assertEqual(expected, actual) + + +class TestCallRope(RopeBackendTestCase): + def test_should_return_value(self): + func = mock.MagicMock() + func.return_value = 23 + + actual = self.backend.call_rope( + func, "foo.py", "", 0 + ) + + self.assertEqual(23, actual) + + def test_should_raise_fault_with_data_on_exception(self): + func = mock.MagicMock() + func.side_effect = RuntimeError("Stuff!") + func.__module__ = "rope.test" + func.__name__ = "test_function" + + try: + self.backend.call_rope( + func, "foo.py", "", 0 + ) + except rpc.Fault as e: + self.assertEqual(500, e.code) + self.assertEqual("Stuff!", e.message) + self.assertIn("traceback", e.data) + self.assertIn("rope_debug_info", e.data) + self.assertEqual("rope.test.test_function", + e.data["rope_debug_info"]["function_name"])
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_rpc.py @@ -0,0 +1,209 @@ +"""Tests for elpy.rpc.""" + +import json +import unittest +import sys + +from elpy import rpc +from elpy.tests.compat import StringIO + + +class TestFault(unittest.TestCase): + def test_should_have_code_and_data(self): + fault = rpc.Fault("Hello", code=250, data="Fnord") + self.assertEqual(str(fault), "Hello") + self.assertEqual(fault.code, 250) + self.assertEqual(fault.data, "Fnord") + + def test_should_have_defaults_for_code_and_data(self): + fault = rpc.Fault("Hello") + self.assertEqual(str(fault), "Hello") + self.assertEqual(fault.code, 500) + self.assertIsNone(fault.data) + + +class TestJSONRPCServer(unittest.TestCase): + def setUp(self): + self.stdin = StringIO() + self.stdout = StringIO() + self.rpc = rpc.JSONRPCServer(self.stdin, self.stdout) + + def write(self, s): + self.stdin.seek(0) + self.stdin.truncate() + self.stdout.seek(0) + self.stdout.truncate() + self.stdin.write(s) + self.stdin.seek(0) + + def read(self): + value = self.stdout.getvalue() + self.stdin.seek(0) + self.stdin.truncate() + self.stdout.seek(0) + self.stdout.truncate() + return value + + +class TestInit(TestJSONRPCServer): + def test_should_use_arguments(self): + self.assertEqual(self.rpc.stdin, self.stdin) + self.assertEqual(self.rpc.stdout, self.stdout) + + def test_should_default_to_sys(self): + testrpc = rpc.JSONRPCServer() + self.assertEqual(sys.stdin, testrpc.stdin) + self.assertEqual(sys.stdout, testrpc.stdout) + + +class TestReadJson(TestJSONRPCServer): + def test_should_read_json(self): + objlist = [{'foo': 'bar'}, + {'baz': 'qux', 'fnord': 'argl\nbargl'}, + "beep\r\nbeep\r\nbeep"] + self.write("".join([(json.dumps(obj) + "\n") + for obj in objlist])) + for obj in objlist: + self.assertEqual(self.rpc.read_json(), + obj) + + def test_should_raise_eof_on_eof(self): + self.assertRaises(EOFError, self.rpc.read_json) + + def test_should_fail_on_malformed_json(self): + self.write("malformed json\n") + self.assertRaises(ValueError, + self.rpc.read_json) + + +class TestWriteJson(TestJSONRPCServer): + def test_should_write_json_line(self): + objlist = [{'foo': 'bar'}, + {'baz': 'qux', 'fnord': 'argl\nbargl'}, + ] + for obj in objlist: + self.rpc.write_json(**obj) + self.assertEqual(json.loads(self.read()), + obj) + + +class TestHandleRequest(TestJSONRPCServer): + def test_should_fail_if_json_does_not_contain_a_method(self): + self.write(json.dumps(dict(params=[], + id=23))) + self.assertRaises(ValueError, + self.rpc.handle_request) + + def test_should_call_right_method(self): + self.write(json.dumps(dict(method='foo', + params=[1, 2, 3], + id=23))) + self.rpc.rpc_foo = lambda *params: params + self.rpc.handle_request() + self.assertEqual(json.loads(self.read()), + dict(id=23, + result=[1, 2, 3])) + + def test_should_pass_defaults_for_missing_parameters(self): + def test_method(*params): + self.args = params + + self.write(json.dumps(dict(method='foo'))) + self.rpc.rpc_foo = test_method + self.rpc.handle_request() + self.assertEqual(self.args, ()) + self.assertEqual(self.read(), "") + + def test_should_return_error_for_missing_method(self): + self.write(json.dumps(dict(method='foo', + id=23))) + self.rpc.handle_request() + result = json.loads(self.read()) + + self.assertEqual(result["id"], 23) + self.assertEqual(result["error"]["message"], + "Unknown method foo") + + def test_should_return_error_for_exception_in_method(self): + def test_method(): + raise ValueError("An error was raised") + + self.write(json.dumps(dict(method='foo', + id=23))) + self.rpc.rpc_foo = test_method + + self.rpc.handle_request() + result = json.loads(self.read()) + + self.assertEqual(result["id"], 23) + self.assertEqual(result["error"]["message"], "An error was raised") + self.assertIn("traceback", result["error"]["data"]) + + def test_should_not_include_traceback_for_faults(self): + def test_method(): + raise rpc.Fault("This is a fault") + + self.write(json.dumps(dict(method="foo", + id=23))) + self.rpc.rpc_foo = test_method + + self.rpc.handle_request() + result = json.loads(self.read()) + + self.assertEqual(result["id"], 23) + self.assertEqual(result["error"]["message"], "This is a fault") + self.assertNotIn("traceback", result["error"]) + + def test_should_add_data_for_faults(self): + def test_method(): + raise rpc.Fault("St. Andreas' Fault", + code=12345, data="Yippieh") + + self.write(json.dumps(dict(method="foo", id=23))) + self.rpc.rpc_foo = test_method + + self.rpc.handle_request() + result = json.loads(self.read()) + + self.assertEqual(result["error"]["data"], "Yippieh") + + def test_should_call_handle_for_unknown_method(self): + def test_handle(method_name, args): + return "It works" + self.write(json.dumps(dict(method="doesnotexist", + id=23))) + self.rpc.handle = test_handle + self.rpc.handle_request() + self.assertEqual(json.loads(self.read()), + dict(id=23, + result="It works")) + + +class TestServeForever(TestJSONRPCServer): + def handle_request(self): + self.hr_called += 1 + if self.hr_called > 10: + raise self.error() + + def setUp(self): + super(TestServeForever, self).setUp() + self.hr_called = 0 + self.error = KeyboardInterrupt + self.rpc.handle_request = self.handle_request + + def test_should_call_handle_request_repeatedly(self): + self.rpc.serve_forever() + self.assertEqual(self.hr_called, 11) + + def test_should_return_on_some_errors(self): + self.error = KeyboardInterrupt + self.rpc.serve_forever() + self.error = EOFError + self.rpc.serve_forever() + self.error = SystemExit + self.rpc.serve_forever() + + def test_should_fail_on_most_errors(self): + self.error = RuntimeError + self.assertRaises(RuntimeError, + self.rpc.serve_forever)
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_server.py @@ -0,0 +1,421 @@ +# coding: utf-8 + +"""Tests for the elpy.server module""" + +import os +import tempfile +import unittest + +import mock + +from elpy import rpc +from elpy import server +from elpy.tests import compat +from elpy.tests.support import BackendTestCase +import elpy.refactor + + +class ServerTestCase(unittest.TestCase): + def setUp(self): + self.srv = server.ElpyRPCServer() + + +class BackendCallTestCase(ServerTestCase): + def assert_calls_backend(self, method): + with mock.patch("elpy.server.get_source") as get_source: + with mock.patch.object(self.srv, "backend") as backend: + get_source.return_value = "transformed source" + + getattr(self.srv, method)("filename", "source", "offset") + + get_source.assert_called_with("source") + getattr(backend, method).assert_called_with( + "filename", "transformed source", "offset" + ) + + +class TestInit(ServerTestCase): + def test_should_not_select_a_backend_by_default(self): + self.assertIsNone(self.srv.backend) + + +class TestRPCEcho(ServerTestCase): + def test_should_return_arguments(self): + self.assertEqual(("hello", "world"), + self.srv.rpc_echo("hello", "world")) + + +class TestRPCInit(ServerTestCase): + @mock.patch("elpy.jedibackend.JediBackend") + @mock.patch("elpy.ropebackend.RopeBackend") + def test_should_set_project_root(self, RopeBackend, JediBackend): + self.srv.rpc_init({"project_root": "/project/root", + "backend": "rope"}) + + self.assertEqual("/project/root", self.srv.project_root) + + @mock.patch("elpy.jedibackend.JediBackend") + @mock.patch("elpy.ropebackend.RopeBackend") + def test_should_initialize_rope(self, RopeBackend, JediBackend): + self.srv.rpc_init({"project_root": "/project/root", + "backend": "rope"}) + + RopeBackend.assert_called_with("/project/root") + + @mock.patch("elpy.jedibackend.JediBackend") + @mock.patch("elpy.ropebackend.RopeBackend") + def test_should_initialize_jedi(self, RopeBackend, JediBackend): + self.srv.rpc_init({"project_root": "/project/root", + "backend": "jedi"}) + + JediBackend.assert_called_with("/project/root") + + @mock.patch("elpy.jedibackend.JediBackend") + @mock.patch("elpy.ropebackend.RopeBackend") + def test_should_use_rope_if_available_and_requested( + self, RopeBackend, JediBackend): + RopeBackend.return_value.name = "rope" + JediBackend.return_value.name = "jedi" + + self.srv.rpc_init({"project_root": "/project/root", + "backend": "rope"}) + + self.assertEqual("rope", self.srv.backend.name) + + @mock.patch("elpy.jedibackend.JediBackend") + @mock.patch("elpy.ropebackend.RopeBackend") + def test_should_use_jedi_if_available_and_requested( + self, RopeBackend, JediBackend): + RopeBackend.return_value.name = "rope" + JediBackend.return_value.name = "jedi" + + self.srv.rpc_init({"project_root": "/project/root", + "backend": "jedi"}) + + self.assertEqual("jedi", self.srv.backend.name) + + @mock.patch("elpy.jedibackend.JediBackend") + @mock.patch("elpy.ropebackend.RopeBackend") + def test_should_use_rope_if_available_and_nothing_requested( + self, RopeBackend, JediBackend): + RopeBackend.return_value.name = "rope" + JediBackend.return_value.name = "jedi" + + self.srv.rpc_init({"project_root": "/project/root", + "backend": None}) + + self.assertEqual("rope", self.srv.backend.name) + + @mock.patch("elpy.jedibackend.JediBackend") + @mock.patch("elpy.ropebackend.RopeBackend") + def test_should_use_jedi_if_rope_not_available_and_nothing_requested( + self, RopeBackend, JediBackend): + RopeBackend.return_value.name = "rope" + JediBackend.return_value.name = "jedi" + old_rope = server.ropebackend + server.ropebackend = None + + try: + self.srv.rpc_init({"project_root": "/project/root", + "backend": None}) + finally: + server.ropebackend = old_rope + + self.assertEqual("jedi", self.srv.backend.name) + + @mock.patch("elpy.jedibackend.JediBackend") + @mock.patch("elpy.ropebackend.RopeBackend") + def test_should_use_none_if_nothing_available( + self, RopeBackend, JediBackend): + RopeBackend.return_value.name = "rope" + JediBackend.return_value.name = "jedi" + old_rope = server.ropebackend + old_jedi = server.jedibackend + server.ropebackend = None + server.jedibackend = None + + try: + self.srv.rpc_init({"project_root": "/project/root", + "backend": None}) + finally: + server.ropebackend = old_rope + server.jedibackend = old_jedi + + self.assertIsNone(self.srv.backend) + + +class TestRPCGetCalltip(BackendCallTestCase): + def test_should_call_backend(self): + self.assert_calls_backend("rpc_get_calltip") + + def test_should_handle_no_backend(self): + self.srv.backend = None + self.assertIsNone(self.srv.rpc_get_calltip("filname", "source", + "offset")) + + +class TestRPCGetCompletions(BackendCallTestCase): + def test_should_call_backend(self): + self.assert_calls_backend("rpc_get_completions") + + def test_should_handle_no_backend(self): + self.srv.backend = None + self.assertEqual([], + self.srv.rpc_get_completions("filname", "source", + "offset")) + + def test_should_sort_results(self): + with mock.patch.object(self.srv, 'backend') as backend: + backend.rpc_get_completions.return_value = [ + {'name': '_e'}, + {'name': '__d'}, + {'name': 'c'}, + {'name': 'B'}, + {'name': 'a'}, + ] + expected = list(reversed(backend.rpc_get_completions.return_value)) + + actual = self.srv.rpc_get_completions("filename", "source", + "offset") + + self.assertEqual(expected, actual) + + def test_should_uniquify_results(self): + with mock.patch.object(self.srv, 'backend') as backend: + backend.rpc_get_completions.return_value = [ + {'name': 'a'}, + {'name': 'a'}, + ] + expected = [{'name': 'a'}] + + actual = self.srv.rpc_get_completions("filename", "source", + "offset") + + self.assertEqual(expected, actual) + + +class TestRPCGetCompletionDocs(ServerTestCase): + def test_should_call_backend(self): + with mock.patch.object(self.srv, "backend") as backend: + self.srv.rpc_get_completion_docstring("completion") + + (backend.rpc_get_completion_docstring + .assert_called_with("completion")) + + def test_should_handle_no_backend(self): + self.srv.backend = None + self.assertIsNone(self.srv.rpc_get_completion_docstring("foo")) + + +class TestRPCGetCompletionLocation(ServerTestCase): + def test_should_call_backend(self): + with mock.patch.object(self.srv, "backend") as backend: + self.srv.rpc_get_completion_location("completion") + + (backend.rpc_get_completion_location + .assert_called_with("completion")) + + def test_should_handle_no_backend(self): + self.srv.backend = None + self.assertIsNone(self.srv.rpc_get_completion_location("foo")) + + +class TestRPCGetDefinition(BackendCallTestCase): + def test_should_call_backend(self): + self.assert_calls_backend("rpc_get_definition") + + def test_should_handle_no_backend(self): + self.srv.backend = None + self.assertIsNone(self.srv.rpc_get_definition("filname", "source", + "offset")) + + +class TestRPCGetDocstring(BackendCallTestCase): + def test_should_call_backend(self): + self.assert_calls_backend("rpc_get_docstring") + + def test_should_handle_no_backend(self): + self.srv.backend = None + self.assertIsNone(self.srv.rpc_get_docstring("filname", "source", + "offset")) + + +class TestRPCGetPydocCompletions(ServerTestCase): + @mock.patch.object(server, 'get_pydoc_completions') + def test_should_call_pydoc_completions(self, get_pydoc_completions): + srv = server.ElpyRPCServer() + srv.rpc_get_pydoc_completions() + get_pydoc_completions.assert_called_with(None) + srv.rpc_get_pydoc_completions("foo") + get_pydoc_completions.assert_called_with("foo") + + +class TestGetPydocDocumentation(ServerTestCase): + @mock.patch("pydoc.render_doc") + def test_should_find_documentation(self, render_doc): + render_doc.return_value = "expected" + + actual = self.srv.rpc_get_pydoc_documentation("open") + + render_doc.assert_called_with("open", + "Elpy Pydoc Documentation for %s", + False) + self.assertEqual("expected", actual) + + def test_should_return_none_for_unknown_module(self): + actual = self.srv.rpc_get_pydoc_documentation("frob.open") + + self.assertIsNone(actual) + + def test_should_return_valid_unicode(self): + import json + + docstring = self.srv.rpc_get_pydoc_documentation("tarfile") + + json.dumps(docstring) + + +class TestRPCGetRefactorOptions(BackendTestCase): + @mock.patch.object(compat.builtins, '__import__') + def test_should_fail_if_rope_is_not_available(self, import_): + import_.side_effect = ImportError + filename = self.project_file("foo.py", "") + srv = server.ElpyRPCServer() + self.assertRaises(ImportError, srv.rpc_get_refactor_options, + filename, 0) + + @mock.patch.object(elpy.refactor, 'Refactor') + def test_should_initialize_and_call_refactor_object(self, Refactor): + filename = self.project_file("foo.py", "import foo") + srv = server.ElpyRPCServer() + srv.project_root = self.project_root + + srv.rpc_get_refactor_options(filename, 5) + + Refactor.assert_called_with(self.project_root, filename) + Refactor.return_value.get_refactor_options.assert_called_with(5, None) + + +class TestRPCRefactor(BackendTestCase): + @mock.patch.object(compat.builtins, '__import__') + def test_should_fail_if_rope_is_not_available(self, import_): + import_.side_effect = ImportError + filename = self.project_file("foo.py", "") + srv = server.ElpyRPCServer() + self.assertRaises(ImportError, srv.rpc_refactor, + filename, 'foo', ()) + + @mock.patch.object(elpy.refactor, 'Refactor') + def test_should_initialize_and_call_refactor_object_with_args( + self, Refactor): + filename = self.project_file("foo.py", "import foo") + srv = server.ElpyRPCServer() + srv.project_root = self.project_root + + srv.rpc_refactor(filename, 'foo', (1, 2, 3)) + + Refactor.assert_called_with(self.project_root, filename) + Refactor.return_value.get_changes.assert_called_with('foo', 1, 2, 3) + + @mock.patch.object(elpy.refactor, 'Refactor') + def test_should_initialize_and_call_refactor_object_without_args( + self, Refactor): + filename = self.project_file("foo.py", "import foo") + srv = server.ElpyRPCServer() + srv.project_root = self.project_root + + srv.rpc_refactor(filename, 'foo', None) + + Refactor.assert_called_with(self.project_root, filename) + Refactor.return_value.get_changes.assert_called_with('foo') + + +class TestRPCGetUsages(BackendCallTestCase): + def test_should_call_backend(self): + self.assert_calls_backend("rpc_get_usages") + + def test_should_handle_no_backend(self): + self.srv.backend = None + with self.assertRaises(rpc.Fault): + self.assertIsNone(self.srv.rpc_get_usages("filname", "source", + "offset")) + + +class TestRPCImportMagic(ServerTestCase): + def test_should_call_importmagic(self): + with mock.patch.object(self.srv, "import_magic") as impmagic: + self.srv.rpc_get_import_symbols("filename", "source", "os") + impmagic.get_import_symbols.assert_called_with("os") + self.srv.rpc_add_import("filename", "source", "import os") + impmagic.add_import.assert_called_with("source", "import os") + self.srv.rpc_get_unresolved_symbols("filename", "source") + impmagic.get_unresolved_symbols.assert_called_with("source") + self.srv.rpc_remove_unreferenced_imports("filename", "source") + impmagic.remove_unreferenced_imports.assert_called_with("source") + + +class TestGetSource(unittest.TestCase): + def test_should_return_string_by_default(self): + self.assertEqual(server.get_source("foo"), + "foo") + + def test_should_return_file_contents(self): + fd, filename = tempfile.mkstemp(prefix="elpy-test-") + self.addCleanup(os.remove, filename) + with open(filename, "w") as f: + f.write("file contents") + + fileobj = {'filename': filename} + + self.assertEqual(server.get_source(fileobj), + "file contents") + + def test_should_clean_up_tempfile(self): + fd, filename = tempfile.mkstemp(prefix="elpy-test-") + with open(filename, "w") as f: + f.write("file contents") + + fileobj = {'filename': filename, + 'delete_after_use': True} + + self.assertEqual(server.get_source(fileobj), + "file contents") + self.assertFalse(os.path.exists(filename)) + + def test_should_support_utf8(self): + fd, filename = tempfile.mkstemp(prefix="elpy-test-") + self.addCleanup(os.remove, filename) + with open(filename, "wb") as f: + f.write(u"möp".encode("utf-8")) + + source = server.get_source({'filename': filename}) + + self.assertEqual(source, u"möp") + + +class TestPysymbolKey(BackendTestCase): + def keyLess(self, a, b): + self.assertLess(b, a) + self.assertLess(server._pysymbol_key(a), + server._pysymbol_key(b)) + + def test_should_be_case_insensitive(self): + self.keyLess("bar", "Foo") + + def test_should_sort_private_symbols_after_public_symbols(self): + self.keyLess("foo", "_bar") + + def test_should_sort_private_symbols_after_dunder_symbols(self): + self.assertLess(server._pysymbol_key("__foo__"), + server._pysymbol_key("_bar")) + + def test_should_sort_dunder_symbols_after_public_symbols(self): + self.keyLess("bar", "__foo") + + +class Autopep8TestCase(ServerTestCase): + + def test_rpc_fix_code_should_return_formatted_string(self): + code_block = 'x= 123\n' + new_block = self.srv.rpc_fix_code(code_block) + self.assertEqual(new_block, 'x = 123\n')
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_support.py @@ -0,0 +1,19 @@ +"""Tests for elpy.tests.support. Yep, we test test code.""" + +import unittest + +from elpy.tests.support import source_and_offset + + +class TestSourceAndOffset(unittest.TestCase): + def test_should_return_source_and_offset(self): + self.assertEqual(source_and_offset("hello, _|_world"), + ("hello, world", 7)) + + def test_should_handle_beginning_of_string(self): + self.assertEqual(source_and_offset("_|_hello, world"), + ("hello, world", 0)) + + def test_should_handle_end_of_string(self): + self.assertEqual(source_and_offset("hello, world_|_"), + ("hello, world", 12))
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/tests/test_yapf.py @@ -0,0 +1,28 @@ +# coding: utf-8 +"""Tests for the elpy.yapf module""" + +import sys +import unittest + +from elpy import yapfutil +from elpy.tests.support import BackendTestCase + + +@unittest.skipIf(yapfutil.YAPF_NOT_SUPPORTED, + 'yapf not supported for current python version') +class YAPFTestCase(BackendTestCase): + def setUp(self): + if not yapfutil.YAPF_NOT_SUPPORTED: + raise unittest.SkipTest + + def test_fix_code(self): + testdata = [ + ('x= 123\n', 'x = 123\n'), + ('x=1; \ny=2 \n', 'x = 1\ny = 2\n'), + ] + for src, expected in testdata: + self._assert_format(src, expected) + + def _assert_format(self, src, expected): + new_block = yapfutil.fix_code(src) + self.assertEqual(new_block, expected)
new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.12.0/elpy/yapfutil.py @@ -0,0 +1,33 @@ +"""Glue for the "yapf" library. + +""" + +import os +import sys +from elpy.rpc import Fault + +YAPF_NOT_SUPPORTED = sys.version_info < (2, 7) or ( + sys.version_info >= (3, 0) and sys.version_info <= (3, 3)) + +try: + if YAPF_NOT_SUPPORTED: + yapf_api = None + else: + from yapf.yapflib import yapf_api + from yapf.yapflib import file_resources +except ImportError: # pragma: no cover + yapf_api = None + + +def fix_code(code): + """Formats Python code to conform to the PEP 8 style guide. + + """ + if not yapf_api: + raise Fault('yapf not installed', code=400) + style_config = file_resources.GetDefaultStyleForDir(os.getcwd()) + reformatted_source, _ = yapf_api.FormatCode(code, + filename='<stdin>', + style_config=style_config, + verify=False) + return reformatted_source
rename from elpa/elpy-1.8.0/snippets/python-mode/.yas-setup.el rename to elpa/elpy-1.12.0/snippets/python-mode/.yas-setup.el --- a/elpa/elpy-1.8.0/snippets/python-mode/.yas-setup.el +++ b/elpa/elpy-1.12.0/snippets/python-mode/.yas-setup.el @@ -47,13 +47,19 @@ ""))) (defun elpy-snippet-super-form () - "Return (Class, first-arg).method" + "Return (Class, first-arg).method if Py2. +Else return ().method for Py3." (let* ((defun-info (elpy-snippet-current-method-and-args)) (class (nth 0 defun-info)) (method (nth 1 defun-info)) (args (nth 2 defun-info)) - (first-arg (nth 0 args))) - (format "(%s, %s).%s" class first-arg method))) + (first-arg (nth 0 args)) + (py-version-command " -c 'import sys ; print(sys.version_info.major)'") + ;; Get the python version. Either 2 or 3 + (py-version-num (substring (shell-command-to-string (concat elpy-rpc-python-command py-version-command))0 1))) + (if (string-match py-version-num "2") + (format "(%s, %s).%s" class first-arg method) + (format "().%s" method)))) (defun elpy-snippet-super-arguments () "Return the argument list for the current method."
rename from elpa/elpy-1.8.0/snippets/python-mode/__abs__ rename to elpa/elpy-1.12.0/snippets/python-mode/__abs__
rename from elpa/elpy-1.8.0/snippets/python-mode/__add__ rename to elpa/elpy-1.12.0/snippets/python-mode/__add__
rename from elpa/elpy-1.8.0/snippets/python-mode/__and__ rename to elpa/elpy-1.12.0/snippets/python-mode/__and__
rename from elpa/elpy-1.8.0/snippets/python-mode/__bool__ rename to elpa/elpy-1.12.0/snippets/python-mode/__bool__
rename from elpa/elpy-1.8.0/snippets/python-mode/__call__ rename to elpa/elpy-1.12.0/snippets/python-mode/__call__
rename from elpa/elpy-1.8.0/snippets/python-mode/__cmp__ rename to elpa/elpy-1.12.0/snippets/python-mode/__cmp__
rename from elpa/elpy-1.8.0/snippets/python-mode/__coerce__ rename to elpa/elpy-1.12.0/snippets/python-mode/__coerce__
rename from elpa/elpy-1.8.0/snippets/python-mode/__complex__ rename to elpa/elpy-1.12.0/snippets/python-mode/__complex__
rename from elpa/elpy-1.8.0/snippets/python-mode/__contains__ rename to elpa/elpy-1.12.0/snippets/python-mode/__contains__
rename from elpa/elpy-1.8.0/snippets/python-mode/__del__ rename to elpa/elpy-1.12.0/snippets/python-mode/__del__
rename from elpa/elpy-1.8.0/snippets/python-mode/__delattr__ rename to elpa/elpy-1.12.0/snippets/python-mode/__delattr__
rename from elpa/elpy-1.8.0/snippets/python-mode/__delete__ rename to elpa/elpy-1.12.0/snippets/python-mode/__delete__
rename from elpa/elpy-1.8.0/snippets/python-mode/__delitem__ rename to elpa/elpy-1.12.0/snippets/python-mode/__delitem__
rename from elpa/elpy-1.8.0/snippets/python-mode/__div__ rename to elpa/elpy-1.12.0/snippets/python-mode/__div__
rename from elpa/elpy-1.8.0/snippets/python-mode/__divmod__ rename to elpa/elpy-1.12.0/snippets/python-mode/__divmod__
rename from elpa/elpy-1.8.0/snippets/python-mode/__enter__ rename to elpa/elpy-1.12.0/snippets/python-mode/__enter__
rename from elpa/elpy-1.8.0/snippets/python-mode/__eq__ rename to elpa/elpy-1.12.0/snippets/python-mode/__eq__
rename from elpa/elpy-1.8.0/snippets/python-mode/__exit__ rename to elpa/elpy-1.12.0/snippets/python-mode/__exit__
rename from elpa/elpy-1.8.0/snippets/python-mode/__float__ rename to elpa/elpy-1.12.0/snippets/python-mode/__float__
rename from elpa/elpy-1.8.0/snippets/python-mode/__floordiv__ rename to elpa/elpy-1.12.0/snippets/python-mode/__floordiv__
rename from elpa/elpy-1.8.0/snippets/python-mode/__ge__ rename to elpa/elpy-1.12.0/snippets/python-mode/__ge__
rename from elpa/elpy-1.8.0/snippets/python-mode/__get__ rename to elpa/elpy-1.12.0/snippets/python-mode/__get__
rename from elpa/elpy-1.8.0/snippets/python-mode/__getattr__ rename to elpa/elpy-1.12.0/snippets/python-mode/__getattr__
rename from elpa/elpy-1.8.0/snippets/python-mode/__getattribute__ rename to elpa/elpy-1.12.0/snippets/python-mode/__getattribute__
rename from elpa/elpy-1.8.0/snippets/python-mode/__getitem__ rename to elpa/elpy-1.12.0/snippets/python-mode/__getitem__
rename from elpa/elpy-1.8.0/snippets/python-mode/__gt__ rename to elpa/elpy-1.12.0/snippets/python-mode/__gt__
rename from elpa/elpy-1.8.0/snippets/python-mode/__hash__ rename to elpa/elpy-1.12.0/snippets/python-mode/__hash__
rename from elpa/elpy-1.8.0/snippets/python-mode/__hex__ rename to elpa/elpy-1.12.0/snippets/python-mode/__hex__
rename from elpa/elpy-1.8.0/snippets/python-mode/__iadd__ rename to elpa/elpy-1.12.0/snippets/python-mode/__iadd__
rename from elpa/elpy-1.8.0/snippets/python-mode/__iand__ rename to elpa/elpy-1.12.0/snippets/python-mode/__iand__
rename from elpa/elpy-1.8.0/snippets/python-mode/__idiv__ rename to elpa/elpy-1.12.0/snippets/python-mode/__idiv__
rename from elpa/elpy-1.8.0/snippets/python-mode/__ifloordiv__ rename to elpa/elpy-1.12.0/snippets/python-mode/__ifloordiv__
rename from elpa/elpy-1.8.0/snippets/python-mode/__ilshift__ rename to elpa/elpy-1.12.0/snippets/python-mode/__ilshift__
rename from elpa/elpy-1.8.0/snippets/python-mode/__imod__ rename to elpa/elpy-1.12.0/snippets/python-mode/__imod__
rename from elpa/elpy-1.8.0/snippets/python-mode/__imul__ rename to elpa/elpy-1.12.0/snippets/python-mode/__imul__
rename from elpa/elpy-1.8.0/snippets/python-mode/__index__ rename to elpa/elpy-1.12.0/snippets/python-mode/__index__
rename from elpa/elpy-1.8.0/snippets/python-mode/__init__ rename to elpa/elpy-1.12.0/snippets/python-mode/__init__
rename from elpa/elpy-1.8.0/snippets/python-mode/__instancecheck__ rename to elpa/elpy-1.12.0/snippets/python-mode/__instancecheck__
rename from elpa/elpy-1.8.0/snippets/python-mode/__int__ rename to elpa/elpy-1.12.0/snippets/python-mode/__int__
rename from elpa/elpy-1.8.0/snippets/python-mode/__invert__ rename to elpa/elpy-1.12.0/snippets/python-mode/__invert__
rename from elpa/elpy-1.8.0/snippets/python-mode/__ior__ rename to elpa/elpy-1.12.0/snippets/python-mode/__ior__
rename from elpa/elpy-1.8.0/snippets/python-mode/__ipow__ rename to elpa/elpy-1.12.0/snippets/python-mode/__ipow__
rename from elpa/elpy-1.8.0/snippets/python-mode/__irshift__ rename to elpa/elpy-1.12.0/snippets/python-mode/__irshift__
rename from elpa/elpy-1.8.0/snippets/python-mode/__isub__ rename to elpa/elpy-1.12.0/snippets/python-mode/__isub__
rename from elpa/elpy-1.8.0/snippets/python-mode/__iter__ rename to elpa/elpy-1.12.0/snippets/python-mode/__iter__
rename from elpa/elpy-1.8.0/snippets/python-mode/__itruediv__ rename to elpa/elpy-1.12.0/snippets/python-mode/__itruediv__
rename from elpa/elpy-1.8.0/snippets/python-mode/__ixor__ rename to elpa/elpy-1.12.0/snippets/python-mode/__ixor__
rename from elpa/elpy-1.8.0/snippets/python-mode/__le__ rename to elpa/elpy-1.12.0/snippets/python-mode/__le__
rename from elpa/elpy-1.8.0/snippets/python-mode/__len__ rename to elpa/elpy-1.12.0/snippets/python-mode/__len__
rename from elpa/elpy-1.8.0/snippets/python-mode/__long__ rename to elpa/elpy-1.12.0/snippets/python-mode/__long__
rename from elpa/elpy-1.8.0/snippets/python-mode/__lshift__ rename to elpa/elpy-1.12.0/snippets/python-mode/__lshift__
rename from elpa/elpy-1.8.0/snippets/python-mode/__lt__ rename to elpa/elpy-1.12.0/snippets/python-mode/__lt__
rename from elpa/elpy-1.8.0/snippets/python-mode/__mod__ rename to elpa/elpy-1.12.0/snippets/python-mode/__mod__
rename from elpa/elpy-1.8.0/snippets/python-mode/__mul__ rename to elpa/elpy-1.12.0/snippets/python-mode/__mul__
rename from elpa/elpy-1.8.0/snippets/python-mode/__ne__ rename to elpa/elpy-1.12.0/snippets/python-mode/__ne__
rename from elpa/elpy-1.8.0/snippets/python-mode/__neg__ rename to elpa/elpy-1.12.0/snippets/python-mode/__neg__
rename from elpa/elpy-1.8.0/snippets/python-mode/__new__ rename to elpa/elpy-1.12.0/snippets/python-mode/__new__
rename from elpa/elpy-1.8.0/snippets/python-mode/__nonzero__ rename to elpa/elpy-1.12.0/snippets/python-mode/__nonzero__
rename from elpa/elpy-1.8.0/snippets/python-mode/__oct__ rename to elpa/elpy-1.12.0/snippets/python-mode/__oct__
rename from elpa/elpy-1.8.0/snippets/python-mode/__or__ rename to elpa/elpy-1.12.0/snippets/python-mode/__or__
rename from elpa/elpy-1.8.0/snippets/python-mode/__pos__ rename to elpa/elpy-1.12.0/snippets/python-mode/__pos__
rename from elpa/elpy-1.8.0/snippets/python-mode/__pow__ rename to elpa/elpy-1.12.0/snippets/python-mode/__pow__
rename from elpa/elpy-1.8.0/snippets/python-mode/__radd__ rename to elpa/elpy-1.12.0/snippets/python-mode/__radd__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rand__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rand__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rdivmod__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rdivmod__
rename from elpa/elpy-1.8.0/snippets/python-mode/__repr__ rename to elpa/elpy-1.12.0/snippets/python-mode/__repr__
rename from elpa/elpy-1.8.0/snippets/python-mode/__reversed__ rename to elpa/elpy-1.12.0/snippets/python-mode/__reversed__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rfloordiv__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rfloordiv__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rlshift__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rlshift__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rmod__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rmod__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rmul__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rmul__
rename from elpa/elpy-1.8.0/snippets/python-mode/__ror__ rename to elpa/elpy-1.12.0/snippets/python-mode/__ror__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rpow__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rpow__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rrshift__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rrshift__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rshift__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rshift__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rsub__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rsub__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rtruediv__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rtruediv__
rename from elpa/elpy-1.8.0/snippets/python-mode/__rxor__ rename to elpa/elpy-1.12.0/snippets/python-mode/__rxor__
rename from elpa/elpy-1.8.0/snippets/python-mode/__set__ rename to elpa/elpy-1.12.0/snippets/python-mode/__set__
rename from elpa/elpy-1.8.0/snippets/python-mode/__setattr__ rename to elpa/elpy-1.12.0/snippets/python-mode/__setattr__
rename from elpa/elpy-1.8.0/snippets/python-mode/__setitem__ rename to elpa/elpy-1.12.0/snippets/python-mode/__setitem__
rename from elpa/elpy-1.8.0/snippets/python-mode/__slots__ rename to elpa/elpy-1.12.0/snippets/python-mode/__slots__
rename from elpa/elpy-1.8.0/snippets/python-mode/__str__ rename to elpa/elpy-1.12.0/snippets/python-mode/__str__
rename from elpa/elpy-1.8.0/snippets/python-mode/__sub__ rename to elpa/elpy-1.12.0/snippets/python-mode/__sub__
rename from elpa/elpy-1.8.0/snippets/python-mode/__subclasscheck__ rename to elpa/elpy-1.12.0/snippets/python-mode/__subclasscheck__
rename from elpa/elpy-1.8.0/snippets/python-mode/__truediv__ rename to elpa/elpy-1.12.0/snippets/python-mode/__truediv__
rename from elpa/elpy-1.8.0/snippets/python-mode/__unicode__ rename to elpa/elpy-1.12.0/snippets/python-mode/__unicode__
rename from elpa/elpy-1.8.0/snippets/python-mode/__xor__ rename to elpa/elpy-1.12.0/snippets/python-mode/__xor__
rename from elpa/elpy-1.8.0/snippets/python-mode/ase rename to elpa/elpy-1.12.0/snippets/python-mode/ase
rename from elpa/elpy-1.8.0/snippets/python-mode/asne rename to elpa/elpy-1.12.0/snippets/python-mode/asne
rename from elpa/elpy-1.8.0/snippets/python-mode/asr rename to elpa/elpy-1.12.0/snippets/python-mode/asr
rename from elpa/elpy-1.8.0/snippets/python-mode/class rename to elpa/elpy-1.12.0/snippets/python-mode/class
rename from elpa/elpy-1.8.0/snippets/python-mode/defs rename to elpa/elpy-1.12.0/snippets/python-mode/defs
rename from elpa/elpy-1.8.0/snippets/python-mode/enc rename to elpa/elpy-1.12.0/snippets/python-mode/enc
rename from elpa/elpy-1.8.0/snippets/python-mode/env rename to elpa/elpy-1.12.0/snippets/python-mode/env
rename from elpa/elpy-1.8.0/snippets/python-mode/from rename to elpa/elpy-1.12.0/snippets/python-mode/from
rename from elpa/elpy-1.8.0/snippets/python-mode/pdb rename to elpa/elpy-1.12.0/snippets/python-mode/pdb
rename from elpa/elpy-1.8.0/snippets/python-mode/py3 rename to elpa/elpy-1.12.0/snippets/python-mode/py3
rename from elpa/elpy-1.8.0/snippets/python-mode/super rename to elpa/elpy-1.12.0/snippets/python-mode/super
deleted file mode 100644 --- a/elpa/elpy-1.8.0/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU 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 -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - <program> Copyright (C) <year> <name of author> - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -<http://www.gnu.org/licenses/>. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -<http://www.gnu.org/philosophy/why-not-lgpl.html>.
deleted file mode 100644 --- a/elpa/elpy-1.8.0/README.rst +++ /dev/null @@ -1,74 +0,0 @@ -======================================= -Elpy, the Emacs Lisp Python Environment -======================================= - -Elpy is an Emacs package to bring powerful Python editing to Emacs. It -combines a number of other packages, both written in Emacs Lisp as -well as Python. - -.. image:: https://secure.travis-ci.org/jorgenschaefer/elpy.png?branch=master - :target: http://travis-ci.org/jorgenschaefer/elpy?branch=master - -.. image:: https://coveralls.io/repos/jorgenschaefer/elpy/badge.png?branch=master - :target: https://coveralls.io/r/jorgenschaefer/elpy?branch=master - -Documentation -============= - -Elpy is fully documented at readthedocs.org: - -http://elpy.readthedocs.org/en/latest/index.html - -Quick Installation -================== - -First, install the required Python packages::: - - # Either of these - pip install rope - pip install jedi - # flake8 for code checks - pip install flake8 - # and importmagic for automatic imports - pip install importmagic - - -Evaluate this in your ``*scratch*`` buffer: - -.. code-block:: lisp - - (require 'package) - (add-to-list 'package-archives - '("elpy" . "http://jorgenschaefer.github.io/packages/")) - - -Then run ``M-x package-refresh-contents`` to load the contents of the -new repository, and ``M-x package-install RET elpy RET`` to install -elpy. - -Finally, add the following to your ``.emacs``: - -.. code-block:: lisp - - (package-initialize) - (elpy-enable) - -Done. - -Contact -======= - -For questions regarding Elpy, do not hesitate to open an `issue on -github`_ or visit us on IRC, channel ``#emacs-elpy`` on -``irc.freenode.net``. - -.. _issue on github: https://github.com/jorgenschaefer/elpy/issues/new - -License -======= - -This project is free software: You can redistribute it and/or modify -it under the terms of the `GNU General Public License`__, either -version 3 of the License, or (at your option) any later version. - -.. __: LICENSE