| @ -1,152 +0,0 @@ | |||
| ;;; color-theme-autoloads.el --- automatically extracted autoloads | |||
| ;; | |||
| ;;; Code: | |||
| (add-to-list 'load-path (or (file-name-directory #$) (car load-path))) | |||
| ;;;### (autoloads nil "color-theme" "color-theme.el" (21569 47711 | |||
| ;;;;;; 0 0)) | |||
| ;;; Generated autoloads from color-theme.el | |||
| (autoload 'color-theme-select "color-theme" "\ | |||
| Displays a special buffer for selecting and installing a color theme. | |||
| With optional prefix ARG, this buffer will include color theme libraries | |||
| as well. A color theme library is in itself not complete, it must be | |||
| used as part of another color theme to be useful. Thus, color theme | |||
| libraries are mainly useful for color theme authors. | |||
| \(fn &optional ARG)" t nil) | |||
| (autoload 'color-theme-describe "color-theme" "\ | |||
| Describe color theme listed at point. | |||
| This shows the documentation of the value of text-property color-theme | |||
| at point. The text-property color-theme should be a color theme | |||
| function. See `color-themes'. | |||
| \(fn)" t nil) | |||
| (autoload 'color-theme-install-at-mouse "color-theme" "\ | |||
| Install color theme clicked upon using the mouse. | |||
| First argument EVENT is used to set point. Then | |||
| `color-theme-install-at-point' is called. | |||
| \(fn EVENT)" t nil) | |||
| (autoload 'color-theme-install-at-point-for-current-frame "color-theme" "\ | |||
| Install color theme at point for current frame only. | |||
| Binds `color-theme-is-global' to nil and calls | |||
| `color-theme-install-at-point'. | |||
| \(fn)" t nil) | |||
| (autoload 'color-theme-print "color-theme" "\ | |||
| Print the current color theme function. | |||
| You can contribute this function to <URL:news:gnu.emacs.sources> or | |||
| paste it into your .emacs file and call it. That should recreate all | |||
| the settings necessary for your color theme. | |||
| Example: | |||
| (require 'color-theme) | |||
| (defun my-color-theme () | |||
| \"Color theme by Alex Schroeder, created 2000-05-17.\" | |||
| (interactive) | |||
| (color-theme-install | |||
| '(... | |||
| ... | |||
| ...))) | |||
| (my-color-theme) | |||
| If you want to use a specific color theme function, you can call the | |||
| color theme function in your .emacs directly. | |||
| Example: | |||
| (require 'color-theme) | |||
| (color-theme-gnome2) | |||
| \(fn &optional BUF)" t nil) | |||
| (autoload 'color-theme-analyze-defun "color-theme" "\ | |||
| Once you have a color-theme printed, check for missing faces. | |||
| This is used by maintainers who receive a color-theme submission | |||
| and want to make sure it follows the guidelines by the color-theme | |||
| author. | |||
| \(fn)" t nil) | |||
| (autoload 'color-theme-make-snapshot "color-theme" "\ | |||
| Return the definition of the current color-theme. | |||
| The function returned will recreate the color-theme in use at the moment. | |||
| \(fn)" nil nil) | |||
| (autoload 'color-theme-compare "color-theme" "\ | |||
| Compare two color themes. | |||
| This will print the differences between installing THEME-A and | |||
| installing THEME-B. Note that the order is important: If a face is | |||
| defined in THEME-A and not in THEME-B, then this will not show up as a | |||
| difference, because there is no reset before installing THEME-B. If a | |||
| face is defined in THEME-B and not in THEME-A, then this will show up as | |||
| a difference. | |||
| \(fn THEME-A THEME-B)" t nil) | |||
| (autoload 'color-theme-install "color-theme" "\ | |||
| Install a color theme defined by frame parameters, variables and faces. | |||
| The theme is installed for all present and future frames; any missing | |||
| faces are created. See `color-theme-install-faces'. | |||
| THEME is a color theme definition. See below for more information. | |||
| If you want to install a color theme from your .emacs, use the output | |||
| generated by `color-theme-print'. This produces color theme function | |||
| which you can copy to your .emacs. | |||
| A color theme definition is a list: | |||
| \([FUNCTION] FRAME-PARAMETERS VARIABLE-SETTINGS FACE-DEFINITIONS) | |||
| FUNCTION is the color theme function which called `color-theme-install'. | |||
| This is no longer used. There was a time when this package supported | |||
| automatic factoring of color themes. This has been abandoned. | |||
| FRAME-PARAMETERS is an alist of frame parameters. These are installed | |||
| with `color-theme-install-frame-params'. These are installed last such | |||
| that any changes to the default face can be changed by the frame | |||
| parameters. | |||
| VARIABLE-DEFINITIONS is an alist of variable settings. These are | |||
| installed with `color-theme-install-variables'. | |||
| FACE-DEFINITIONS is an alist of face definitions. These are installed | |||
| with `color-theme-install-faces'. | |||
| If `color-theme-is-cumulative' is nil, a color theme will undo face and | |||
| frame-parameter settings of previous color themes. | |||
| \(fn THEME)" nil nil) | |||
| (autoload 'color-theme-submit "color-theme" "\ | |||
| Submit your color-theme to the maintainer. | |||
| \(fn)" t nil) | |||
| (autoload 'color-theme-initialize "color-theme" "\ | |||
| Initialize the color theme package by loading color-theme-libraries. | |||
| \(fn)" t nil) | |||
| ;;;*** | |||
| ;;;### (autoloads nil nil ("color-theme-pkg.el") (21569 47711 845247 | |||
| ;;;;;; 0)) | |||
| ;;;*** | |||
| ;; Local Variables: | |||
| ;; version-control: never | |||
| ;; no-byte-compile: t | |||
| ;; no-update-autoloads: t | |||
| ;; End: | |||
| ;;; color-theme-autoloads.el ends here | |||
| @ -1,5 +0,0 @@ | |||
| (define-package "color-theme" "20080305.34" "install color themes" 'nil :url "http://www.emacswiki.org/cgi-bin/wiki.pl?ColorTheme" :keywords | |||
| '("faces")) | |||
| ;; Local Variables: | |||
| ;; no-byte-compile: t | |||
| ;; End: | |||
| @ -1,22 +0,0 @@ | |||
| (eval-when-compile | |||
| (require 'color-theme)) | |||
| (defun color-theme-example () | |||
| "Example theme. Carbon copy of color-theme-gnome contributed by Jonadab." | |||
| (interactive) | |||
| (color-theme-install | |||
| '(color-theme-example | |||
| ((foreground-color . "wheat") | |||
| (background-color . "darkslategrey") | |||
| (background-mode . dark)) | |||
| (default ((t (nil)))) | |||
| (region ((t (:foreground "cyan" :background "dark cyan")))) | |||
| (underline ((t (:foreground "yellow" :underline t)))) | |||
| (modeline ((t (:foreground "dark cyan" :background "wheat")))) | |||
| (modeline-buffer-id ((t (:foreground "dark cyan" :background "wheat")))) | |||
| (modeline-mousable ((t (:foreground "dark cyan" :background "wheat")))) | |||
| (modeline-mousable-minor-mode ((t (:foreground "dark cyan" :background "wheat")))) | |||
| (italic ((t (:foreground "dark red" :italic t)))) | |||
| (bold-italic ((t (:foreground "dark red" :bold t :italic t)))) | |||
| (font-lock-comment-face ((t (:foreground "Firebrick")))) | |||
| (bold ((t (:bold))))))) | |||
| @ -1,28 +0,0 @@ | |||
| ;;; color-theme-monokai-autoloads.el --- automatically extracted autoloads | |||
| ;; | |||
| ;;; Code: | |||
| (add-to-list 'load-path (or (file-name-directory #$) (car load-path))) | |||
| ;;;### (autoloads nil "color-theme-monokai" "../../../../.emacs.d/elpa/color-theme-monokai-0.0.5/color-theme-monokai.el" | |||
| ;;;;;; "5e588ec55086e698d854b204864f3032") | |||
| ;;; Generated autoloads from ../../../../.emacs.d/elpa/color-theme-monokai-0.0.5/color-theme-monokai.el | |||
| (autoload 'color-theme-monokai "color-theme-monokai" "\ | |||
| \(fn)" t nil) | |||
| ;;;*** | |||
| ;;;### (autoloads nil nil ("../../../../.emacs.d/elpa/color-theme-monokai-0.0.5/color-theme-monokai-autoloads.el" | |||
| ;;;;;; "../../../../.emacs.d/elpa/color-theme-monokai-0.0.5/color-theme-monokai.el") | |||
| ;;;;;; (21569 47878 908014 0)) | |||
| ;;;*** | |||
| ;; Local Variables: | |||
| ;; version-control: never | |||
| ;; no-byte-compile: t | |||
| ;; no-update-autoloads: t | |||
| ;; End: | |||
| ;;; color-theme-monokai-autoloads.el ends here | |||
| @ -1 +0,0 @@ | |||
| (define-package "color-theme-monokai" "0.0.5" "Monokai Color Theme for Emacs." '((color-theme "6.5.5"))) | |||
| @ -1,45 +0,0 @@ | |||
| ;;; color-theme-monokai.el --- Monokai Color Theme for Emacs. | |||
| ;; Author: Lorenzo Villani <lorenzo@villani.me> | |||
| ;; URL: https://github.com/lvillani/dotfiles/blob/master/emacs.d/projects/color-theme-monokai.el | |||
| ;; Version: 0.0.5 | |||
| ;; Package-Requires: ((color-theme "6.5.5")) | |||
| ;;;###autoload | |||
| (defun color-theme-monokai () | |||
| (interactive) | |||
| (color-theme-install | |||
| '(color-theme-monokai | |||
| ;; Frame Parameters | |||
| ((background-color . "#272822") | |||
| (foreground-color . "#F8F8F2") | |||
| (cursor-color . "#F8F8F0")) | |||
| ;; Main | |||
| (font-lock-builtin-face ((t (:foreground "#A6E22A")))) | |||
| (font-lock-comment-face ((t (:foreground "#75715E")))) | |||
| (font-lock-constant-face ((t (:foreground "#AE81FF")))) | |||
| (font-lock-doc-string-face ((t (:foreground "#E6DB74")))) | |||
| (font-lock-function-name-face ((t (:foreground "#A6E22E")))) | |||
| (font-lock-keyword-face ((t (:foreground "#F92672")))) | |||
| (font-lock-string-face ((t (:foreground "#E6DB74")))) | |||
| (font-lock-type-face ((t (:foreground "#89BDFF")))) | |||
| (font-lock-variable-name-face ((t (:foreground "#F92672")))) | |||
| (font-lock-warning-face ((t (:bold t :foreground "#FD5FF1")))) | |||
| ;; Main - #2 | |||
| (hl-line ((t (:background "#141411")))) | |||
| (minibuffer-prompt ((t (:foreground "#75715E")))) | |||
| (modeline ((t (:background "#595959" :foreground "#E6E6E6")))) | |||
| (region ((t (:background "#383830")))) | |||
| (show-paren-match-face ((t (:background "#383830")))) | |||
| ;; CUA | |||
| (cua-rectangle ((t (:background "#141411")))) | |||
| ;; IDO | |||
| (ido-first-match ((t (:foreground "#AE81FF")))) | |||
| (ido-only-match ((t (:foreground "#A6E22A")))) | |||
| (ido-subdir ((t (:foreground "#89BDFF")))) | |||
| ;; Misc | |||
| (yas/field-highlight-face ((t (:background "#383830"))))))) | |||
| (provide 'color-theme-monokai) | |||
| ;;; color-theme-monokai.el ends here | |||
| @ -1,49 +0,0 @@ | |||
| ;;; elpy-autoloads.el --- automatically extracted autoloads | |||
| ;; | |||
| ;;; Code: | |||
| (add-to-list 'load-path (or (file-name-directory #$) (car load-path))) | |||
| ;;;### (autoloads nil "elpy" "elpy.el" (21570 28048 0 0)) | |||
| ;;; Generated autoloads from elpy.el | |||
| (autoload 'elpy-enable "elpy" "\ | |||
| Enable Elpy in all future Python buffers. | |||
| \(fn &optional IGNORED)" t nil) | |||
| (autoload 'elpy-mode "elpy" "\ | |||
| Minor mode in Python buffers for the Emacs Lisp Python Environment. | |||
| This mode fully supports virtualenvs. Once you switch a | |||
| virtualenv using \\[pyvenv-workon], you can use | |||
| \\[elpy-rpc-restart] to make the elpy Python process use your | |||
| virtualenv. | |||
| See https://github.com/jorgenschaefer/elpy/wiki/Keybindings for a | |||
| more structured list. | |||
| \\{elpy-mode-map} | |||
| \(fn &optional ARG)" t nil) | |||
| (autoload 'elpy-config "elpy" "\ | |||
| Configure Elpy. | |||
| This function will pop up a configuration buffer, which is mostly | |||
| a customize buffer, but has some more options. | |||
| \(fn)" t nil) | |||
| ;;;*** | |||
| ;;;### (autoloads nil nil ("elpy-pkg.el" "elpy-refactor.el") (21570 | |||
| ;;;;;; 28048 583398 0)) | |||
| ;;;*** | |||
| ;; Local Variables: | |||
| ;; version-control: never | |||
| ;; no-byte-compile: t | |||
| ;; no-update-autoloads: t | |||
| ;; End: | |||
| ;;; elpy-autoloads.el ends here | |||
| @ -1,10 +0,0 @@ | |||
| (define-package "elpy" "20140810.7" "Emacs Python Development Environment" | |||
| '((company "0.8.2") | |||
| (find-file-in-project "3.3") | |||
| (highlight-indentation "0.5.0") | |||
| (idomenu "0.1") | |||
| (pyvenv "1.3") | |||
| (yasnippet "0.8.0"))) | |||
| ;; Local Variables: | |||
| ;; no-byte-compile: t | |||
| ;; End: | |||
| @ -1,285 +0,0 @@ | |||
| ;;; elpy-refactor.el --- Refactoring mode for Elpy | |||
| ;; Copyright (C) 2013 Jorgen Schaefer | |||
| ;; Author: Jorgen Schaefer <contact@jorgenschaefer.de> | |||
| ;; URL: https://github.com/jorgenschaefer/elpy | |||
| ;; 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/>. | |||
| ;;; Commentary: | |||
| ;; This file provides an interface, including a major mode, to use | |||
| ;; refactoring options provided by the Rope library. | |||
| ;;; Code: | |||
| ;; We require elpy, but elpy loads us, so we shouldn't load it back. | |||
| ;; (require 'elpy) | |||
| (defvar elpy-refactor-changes nil | |||
| "Changes that will be commited on \\[elpy-refactor-commit].") | |||
| (make-variable-buffer-local 'elpy-refactor-current-changes) | |||
| (defvar elpy-refactor-window-configuration nil | |||
| "The old window configuration. Will be restored after commit.") | |||
| (make-variable-buffer-local 'elpy-refactor-window-configuration) | |||
| (make-obsolete | |||
| 'elpy-refactor | |||
| "Refactoring has been unstable and flakey, support will be dropped in the future." | |||
| "elpy 1.5.0") | |||
| (defun elpy-refactor () | |||
| "Run the Elpy refactoring interface for Python code." | |||
| (interactive) | |||
| (save-some-buffers) | |||
| (let* ((selection (elpy-refactor-select | |||
| (elpy-refactor-rpc-get-options))) | |||
| (method (car selection)) | |||
| (args (cdr selection))) | |||
| (when method | |||
| (elpy-refactor-create-change-buffer | |||
| (elpy-refactor-rpc-get-changes method args))))) | |||
| (defun elpy-refactor-select (options) | |||
| "Show the user the refactoring options and let her choose one. | |||
| Depending on the chosen option, ask the user for further | |||
| arguments and build the argument. | |||
| Return a cons cell of the name of the option and the arg list | |||
| created." | |||
| (let ((buf (get-buffer-create "*Elpy Refactor*")) | |||
| (pos (vector (1- (point)) | |||
| (ignore-errors | |||
| (1- (region-beginning))) | |||
| (ignore-errors | |||
| (1- (region-end))))) | |||
| (inhibit-read-only t) | |||
| (options (sort options | |||
| (lambda (a b) | |||
| (let ((cata (cdr (assq 'category a))) | |||
| (catb (cdr (assq 'category b)))) | |||
| (if (equal cata catb) | |||
| (string< (cdr (assq 'description a)) | |||
| (cdr (assq 'description b))) | |||
| (string< cata catb)))))) | |||
| (key ?a) | |||
| last-category | |||
| option-alist) | |||
| (with-current-buffer buf | |||
| (erase-buffer) | |||
| (dolist (option options) | |||
| (let ((category (cdr (assq 'category option))) | |||
| (description (cdr (assq 'description option))) | |||
| (name (cdr (assq 'name option))) | |||
| (args (cdr (assq 'args option)))) | |||
| (when (not (equal category last-category)) | |||
| (when last-category | |||
| (insert "\n")) | |||
| (insert (propertize category 'face 'bold) "\n") | |||
| (setq last-category category)) | |||
| (insert " (" key ") " description "\n") | |||
| (setq option-alist (cons (list key name args) | |||
| option-alist)) | |||
| (setq key (1+ key)))) | |||
| (let ((window-conf (current-window-configuration))) | |||
| (unwind-protect | |||
| (progn | |||
| (with-selected-window (display-buffer buf) | |||
| (goto-char (point-min))) | |||
| (fit-window-to-buffer (get-buffer-window buf)) | |||
| (let* ((key (read-key "Refactoring action? ")) | |||
| (entry (cdr (assoc key option-alist)))) | |||
| (kill-buffer buf) | |||
| (cons (car entry) ; name | |||
| (elpy-refactor-build-arguments (cadr entry) | |||
| pos)))) | |||
| (set-window-configuration window-conf)))))) | |||
| (defun elpy-refactor-build-arguments (args pos) | |||
| "Translate an argument list specification to an argument list. | |||
| POS is a vector of three elements, the current offset, the offset | |||
| of the beginning of the region, and the offset of the end of the | |||
| region. | |||
| ARGS is a list of triples, each triple containing the name of an | |||
| argument (ignored), the type of the argument, and a possible | |||
| prompt string. | |||
| Available types: | |||
| offset - The offset in the buffer, (1- (point)) | |||
| start_offset - Offset of the beginning of the region | |||
| end_offset - Offset of the end of the region | |||
| string - A free-form string | |||
| filename - A non-existing file name | |||
| directory - An existing directory name | |||
| boolean - A boolean question" | |||
| (mapcar (lambda (arg) | |||
| (let ((type (cadr arg)) | |||
| (prompt (caddr arg))) | |||
| (cond | |||
| ((equal type "offset") | |||
| (aref pos 0)) | |||
| ((equal type "start_offset") | |||
| (aref pos 1)) | |||
| ((equal type "end_offset") | |||
| (aref pos 2)) | |||
| ((equal type "string") | |||
| (read-from-minibuffer prompt)) | |||
| ((equal type "filename") | |||
| (expand-file-name | |||
| (read-file-name prompt))) | |||
| ((equal type "directory") | |||
| (expand-file-name | |||
| (read-directory-name prompt))) | |||
| ((equal type "boolean") | |||
| (y-or-n-p prompt))))) | |||
| args)) | |||
| (defun elpy-refactor-create-change-buffer (changes) | |||
| "Show the user a buffer of changes. | |||
| The user can review the changes and confirm them with | |||
| \\[elpy-refactor-commit]." | |||
| (when (not changes) | |||
| (error "No changes for this refactoring action.")) | |||
| (with-current-buffer (get-buffer-create "*Elpy Refactor*") | |||
| (elpy-refactor-mode) | |||
| (setq elpy-refactor-changes changes | |||
| elpy-refactor-window-configuration (current-window-configuration)) | |||
| (let ((inhibit-read-only t)) | |||
| (erase-buffer) | |||
| (elpy-refactor-insert-changes changes)) | |||
| (select-window (display-buffer (current-buffer))) | |||
| (goto-char (point-min)))) | |||
| (defun elpy-refactor-insert-changes (changes) | |||
| "Format and display the changes described in CHANGES." | |||
| (insert (propertize "Use C-c C-c to apply the following changes." | |||
| 'face 'bold) | |||
| "\n\n") | |||
| (dolist (change changes) | |||
| (let ((action (cdr (assq 'action change)))) | |||
| (cond | |||
| ((equal action "change") | |||
| (insert (cdr (assq 'diff change)) | |||
| "\n")) | |||
| ((equal action "create") | |||
| (let ((type (cdr (assq 'type change)))) | |||
| (if (equal type "file") | |||
| (insert "+++ " (cdr (assq 'file change)) "\n" | |||
| "Create file " (cdr (assq 'file change)) "\n" | |||
| "\n") | |||
| (insert "+++ " (cdr (assq 'path change)) "\n" | |||
| "Create directory " (cdr (assq 'path change)) "\n" | |||
| "\n")))) | |||
| ((equal action "move") | |||
| (insert "--- " (cdr (assq 'source change)) "\n" | |||
| "+++ " (cdr (assq 'destination change)) "\n" | |||
| "Rename " (cdr (assq 'type change)) "\n" | |||
| "\n")) | |||
| ((equal action "delete") | |||
| (let ((type (cdr (assq 'type change)))) | |||
| (if (equal type "file") | |||
| (insert "--- " (cdr (assq 'file change)) "\n" | |||
| "Delete file " (cdr (assq 'file change)) "\n" | |||
| "\n") | |||
| (insert "--- " (cdr (assq 'path change)) "\n" | |||
| "Delete directory " (cdr (assq 'path change)) "\n" | |||
| "\n")))))))) | |||
| (defvar elpy-refactor-mode-map | |||
| (let ((map (make-sparse-keymap))) | |||
| (define-key map (kbd "C-c C-c") 'elpy-refactor-commit) | |||
| (define-key map (kbd "q") 'bury-buffer) | |||
| (define-key map (kbd "h") 'describe-mode) | |||
| (define-key map (kbd "?") 'describe-mode) | |||
| map) | |||
| "The key map for `elpy-refactor-mode'.") | |||
| (define-derived-mode elpy-refactor-mode diff-mode "Elpy Refactor" | |||
| "Mode to display refactoring actions and ask confirmation from the user. | |||
| \\{elpy-refactor-mode-map}" | |||
| :group 'elpy | |||
| (view-mode 1)) | |||
| (defun elpy-refactor-commit () | |||
| "Commit the changes in the current buffer." | |||
| (interactive) | |||
| (when (not elpy-refactor-changes) | |||
| (error "No changes to commit.")) | |||
| ;; Restore the window configuration as the first thing so that | |||
| ;; changes below are visible to the user. Especially the point | |||
| ;; change in possible buffer changes. | |||
| (set-window-configuration elpy-refactor-window-configuration) | |||
| (dolist (change elpy-refactor-changes) | |||
| (let ((action (cdr (assq 'action change)))) | |||
| (cond | |||
| ((equal action "change") | |||
| (with-current-buffer (find-file-noselect (cdr (assq 'file change))) | |||
| ;; This would break for save-excursion as the buffer is | |||
| ;; truncated, so all markets now point to position 1. | |||
| (let ((old-point (point))) | |||
| (undo-boundary) | |||
| (erase-buffer) | |||
| (insert (cdr (assq 'contents change))) | |||
| (undo-boundary) | |||
| (goto-char old-point)))) | |||
| ((equal action "create") | |||
| (if (equal (cdr (assq 'type change)) | |||
| "file") | |||
| (find-file-noselect (cdr (assq 'file change))) | |||
| (make-directory (cdr (assq 'path change))))) | |||
| ((equal action "move") | |||
| (let* ((source (cdr (assq 'source change))) | |||
| (dest (cdr (assq 'destination change))) | |||
| (buf (get-file-buffer source))) | |||
| (when buf | |||
| (with-current-buffer buf | |||
| (setq buffer-file-name dest) | |||
| (rename-buffer (file-name-nondirectory dest) t))) | |||
| (rename-file source dest))) | |||
| ((equal action "delete") | |||
| (if (equal (cdr (assq 'type change)) "file") | |||
| (let ((name (cdr (assq 'file change)))) | |||
| (when (y-or-n-p (format "Really delete %s? " name)) | |||
| (delete-file name t))) | |||
| (let ((name (cdr (assq 'directory change)))) | |||
| (when (y-or-n-p (format "Really delete %s? " name)) | |||
| (delete-directory name nil t)))))))) | |||
| (kill-buffer (current-buffer))) | |||
| (defun elpy-refactor-rpc-get-options () | |||
| "Get a list of refactoring options from the Elpy RPC." | |||
| (if (use-region-p) | |||
| (elpy-rpc "get_refactor_options" | |||
| (list (buffer-file-name) | |||
| (1- (region-beginning)) | |||
| (1- (region-end)))) | |||
| (elpy-rpc "get_refactor_options" | |||
| (list (buffer-file-name) | |||
| (1- (point)))))) | |||
| (defun elpy-refactor-rpc-get-changes (method args) | |||
| "Get a list of changes from the Elpy RPC after applying METHOD with ARGS." | |||
| (elpy-rpc "refactor" | |||
| (list (buffer-file-name) | |||
| method args))) | |||
| (provide 'elpy-refactor) | |||
| ;;; elpy-refactor.el ends here | |||
| @ -1,41 +0,0 @@ | |||
| # Elpy, the Emacs Lisp Python Environment | |||
| # Copyright (C) 2013 Jorgen Schaefer | |||
| # Author: Jorgen Schaefer <contact@jorgenschaefer.de> | |||
| # URL: http://github.com/jorgenschaefer/elpy | |||
| # 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/>. | |||
| """The Emacs Lisp Python Environment. | |||
| Elpy is a mode for Emacs to support writing Python code. This package | |||
| provides the backend within Python to support auto-completion, | |||
| documentation extraction, and navigation. | |||
| Emacs will start the protocol by running the module itself, like so: | |||
| python -m elpy | |||
| This will emit a greeting string on a single line, and then wait for | |||
| the protocol to start. Details of the protocol can be found in | |||
| elpy.rpc. | |||
| This package is unlikely to be useful on its own. | |||
| """ | |||
| __author__ = "Jorgen Schaefer" | |||
| __version__ = "1.5.1" | |||
| __license__ = "GPL" | |||
| @ -1,21 +0,0 @@ | |||
| """Main interface to the RPC server. | |||
| You should be able to just run the following to use this module: | |||
| python -m elpy | |||
| The first line should be "elpy-rpc ready". If it isn't, something | |||
| broke. | |||
| """ | |||
| import sys | |||
| import elpy | |||
| from elpy.server import ElpyRPCServer | |||
| if __name__ == '__main__': | |||
| sys.stdout.write('elpy-rpc ready ({0})\n' | |||
| .format(elpy.__version__)) | |||
| sys.stdout.flush() | |||
| ElpyRPCServer().serve_forever() | |||
| @ -1,33 +0,0 @@ | |||
| """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 | |||
| from io import StringIO | |||
| def ensure_not_unicode(obj): | |||
| return obj | |||
| else: | |||
| PYTHON3 = False | |||
| from StringIO import StringIO | |||
| def ensure_not_unicode(obj): | |||
| """Return obj. If it's a unicode string, convert it to str first. | |||
| Pydoc functions simply don't find anything for unicode | |||
| strings. No idea why. | |||
| """ | |||
| if isinstance(obj, unicode): | |||
| return obj.encode("utf-8") | |||
| else: | |||
| return obj | |||
| @ -1,255 +0,0 @@ | |||
| """Elpy backend using the Jedi library. | |||
| This backend uses the Jedi library: | |||
| https://github.com/davidhalter/jedi | |||
| """ | |||
| import sys | |||
| import traceback | |||
| import jedi | |||
| from elpy import rpc | |||
| class JediBackend(object): | |||
| """The Jedi backend class. | |||
| Implements the RPC calls we can pass on to Jedi. | |||
| Documentation: http://jedi.jedidjah.ch/en/latest/docs/plugin-api.html | |||
| """ | |||
| name = "jedi" | |||
| def __init__(self, project_root): | |||
| self.project_root = project_root | |||
| self.completions = {} | |||
| sys.path.append(project_root) | |||
| def rpc_get_completions(self, filename, source, offset): | |||
| line, column = pos_to_linecol(source, offset) | |||
| proposals = run_with_debug(jedi, 'completions', | |||
| source=source, line=line, column=column, | |||
| path=filename, encoding='utf-8') | |||
| if proposals is None: | |||
| return [] | |||
| self.completions = dict((proposal.name, proposal) | |||
| for proposal in proposals) | |||
| return [{'name': proposal.name, | |||
| 'suffix': proposal.complete, | |||
| 'annotation': proposal.type, | |||
| 'meta': proposal.description} | |||
| for proposal in proposals] | |||
| def rpc_get_completion_docstring(self, completion): | |||
| proposal = self.completions.get(completion) | |||
| if proposal is None: | |||
| return None | |||
| else: | |||
| return proposal.docstring(fast=False) | |||
| def rpc_get_completion_location(self, completion): | |||
| proposal = self.completions.get(completion) | |||
| if proposal is None: | |||
| return None | |||
| else: | |||
| return (proposal.module_path, proposal.line) | |||
| def rpc_get_definition(self, filename, source, offset): | |||
| line, column = pos_to_linecol(source, offset) | |||
| try: | |||
| locations = run_with_debug(jedi, 'goto_definitions', | |||
| source=source, line=line, column=column, | |||
| path=filename, encoding='utf-8', | |||
| re_raise=jedi.NotFoundError) | |||
| except jedi.NotFoundError: | |||
| return None | |||
| # goto_definitions() can return silly stuff like __builtin__ | |||
| # for int variables, so we fall back on goto() in those | |||
| # cases. See issue #76. | |||
| if ( | |||
| locations and | |||
| locations[0].module_path is None | |||
| ): | |||
| locations = run_with_debug(jedi, 'goto_assignments', | |||
| source=source, line=line, | |||
| column=column, | |||
| path=filename, encoding='utf-8') | |||
| if not locations: | |||
| return None | |||
| else: | |||
| loc = locations[-1] | |||
| try: | |||
| if loc.module_path: | |||
| if loc.module_path == filename: | |||
| offset = linecol_to_pos(source, | |||
| loc.line, | |||
| loc.column) | |||
| else: | |||
| with open(loc.module_path) as f: | |||
| offset = linecol_to_pos(f.read(), | |||
| loc.line, | |||
| loc.column) | |||
| except IOError: | |||
| return None | |||
| return (loc.module_path, offset) | |||
| def rpc_get_calltip(self, filename, source, offset): | |||
| line, column = pos_to_linecol(source, offset) | |||
| calls = run_with_debug(jedi, 'call_signatures', | |||
| source=source, line=line, column=column, | |||
| path=filename, encoding='utf-8') | |||
| if calls: | |||
| call = calls[0] | |||
| else: | |||
| call = None | |||
| if not call: | |||
| return None | |||
| return {"name": call.name, | |||
| "index": call.index, | |||
| "params": [param.description for param in call.params]} | |||
| def rpc_get_usages(self, filename, source, offset): | |||
| """Return the uses of the symbol at offset. | |||
| Returns a list of occurrences of the symbol, as dicts with the | |||
| fields name, filename, and offset. | |||
| """ | |||
| line, column = pos_to_linecol(source, offset) | |||
| try: | |||
| uses = run_with_debug(jedi, 'usages', | |||
| source=source, line=line, column=column, | |||
| path=filename, encoding='utf-8', | |||
| re_raise=(jedi.NotFoundError,)) | |||
| except jedi.NotFoundError: | |||
| return [] | |||
| if uses is None: | |||
| return None | |||
| result = [] | |||
| for use in uses: | |||
| if use.module_path == filename: | |||
| offset = linecol_to_pos(source, use.line, use.column) | |||
| else: | |||
| with open(use.module_path) as f: | |||
| text = f.read() | |||
| offset = linecol_to_pos(text, use.line, use.column) | |||
| result.append({"name": use.name, | |||
| "filename": use.module_path, | |||
| "offset": offset}) | |||
| return result | |||
| # From the Jedi documentation: | |||
| # | |||
| # line is the current line you want to perform actions on (starting | |||
| # with line #1 as the first line). column represents the current | |||
| # column/indent of the cursor (starting with zero). source_path | |||
| # should be the path of your file in the file system. | |||
| def pos_to_linecol(text, pos): | |||
| """Return a tuple of line and column for offset pos in text. | |||
| Lines are one-based, columns zero-based. | |||
| This is how Jedi wants it. Don't ask me why. | |||
| """ | |||
| line_start = text.rfind("\n", 0, pos) + 1 | |||
| line = text.count("\n", 0, line_start) + 1 | |||
| col = pos - line_start | |||
| return line, col | |||
| def linecol_to_pos(text, line, col): | |||
| """Return the offset of this line and column in text. | |||
| Lines are one-based, columns zero-based. | |||
| This is how Jedi wants it. Don't ask me why. | |||
| """ | |||
| nth_newline_offset = 0 | |||
| for i in range(line - 1): | |||
| new_offset = text.find("\n", nth_newline_offset) | |||
| if new_offset < 0: | |||
| raise ValueError("Text does not have {0} lines." | |||
| .format(line)) | |||
| nth_newline_offset = new_offset + 1 | |||
| offset = nth_newline_offset + col | |||
| if offset > len(text): | |||
| raise ValueError("Line {0} column {1} is not within the text" | |||
| .format(line, col)) | |||
| return offset | |||
| def run_with_debug(jedi, name, *args, **kwargs): | |||
| re_raise = kwargs.pop('re_raise', ()) | |||
| # Remove form feed characters, they confuse Jedi (jedi#424) | |||
| if 'source' in kwargs: | |||
| kwargs['source'] = kwargs['source'].replace("\f", " ") | |||
| try: | |||
| script = jedi.Script(*args, **kwargs) | |||
| return getattr(script, name)() | |||
| except Exception as e: | |||
| if isinstance(e, re_raise): | |||
| raise | |||
| # Bug jedi#417 | |||
| if isinstance(e, TypeError) and str(e) == 'no dicts allowed': | |||
| return None | |||
| # Bug jedi#427 | |||
| if isinstance(e, UnicodeDecodeError): | |||
| return None | |||
| # Bug jedi#429 | |||
| if isinstance(e, IndexError): | |||
| return None | |||
| # Bug jedi#431 | |||
| if isinstance(e, AttributeError) and str(e).endswith("'end_pos'"): | |||
| return None | |||
| # Bug in Python 2.6, see #275 | |||
| if isinstance(e, OSError) and e.errno == 13: | |||
| return None | |||
| from jedi import debug | |||
| debug_info = [] | |||
| def _debug(level, str_out): | |||
| if level == debug.NOTICE: | |||
| prefix = "[N]" | |||
| elif level == debug.WARNING: | |||
| prefix = "[W]" | |||
| else: | |||
| prefix = "[?]" | |||
| debug_info.append("{0} {1}".format(prefix, str_out)) | |||
| jedi.set_debug_function(_debug, speed=False) | |||
| try: | |||
| script = jedi.Script(*args, **kwargs) | |||
| return getattr(script, name)() | |||
| except Exception as e: | |||
| source = kwargs.get('source') | |||
| sc_args = [] | |||
| sc_args.extend(repr(arg) for arg in args) | |||
| sc_args.extend("{0}={1}".format(k, "source" if k == "source" | |||
| else repr(v)) | |||
| for (k, v) in kwargs.items()) | |||
| data = { | |||
| "traceback": traceback.format_exc(), | |||
| "jedi_debug_info": {'script_args': ", ".join(sc_args), | |||
| 'source': source, | |||
| 'method': name, | |||
| 'debug_info': debug_info} | |||
| } | |||
| raise rpc.Fault(message=str(e), | |||
| code=500, | |||
| data=data) | |||
| finally: | |||
| jedi.set_debug_function(None) | |||
| @ -1,91 +0,0 @@ | |||
| import sys | |||
| import types | |||
| from pydoc import safeimport, resolve, ErrorDuringImport | |||
| from pkgutil import iter_modules | |||
| from elpy import compat | |||
| # Types we want to recurse into (nodes). | |||
| CONTAINER_TYPES = (type, types.ModuleType) | |||
| # Types of attributes we can get documentation for (leaves). | |||
| PYDOC_TYPES = (type, | |||
| types.FunctionType, | |||
| types.BuiltinFunctionType, | |||
| types.BuiltinMethodType, | |||
| types.MethodType, | |||
| types.ModuleType) | |||
| if not compat.PYTHON3: # pragma: nocover | |||
| # Python 2 old style classes | |||
| CONTAINER_TYPES = tuple(list(CONTAINER_TYPES) + [types.ClassType]) | |||
| PYDOC_TYPES = tuple(list(PYDOC_TYPES) + [types.ClassType]) | |||
| def get_pydoc_completions(modulename): | |||
| """Get possible completions for modulename for pydoc. | |||
| Returns a list of possible values to be passed to pydoc. | |||
| """ | |||
| modulename = compat.ensure_not_unicode(modulename) | |||
| modulename = modulename.rstrip(".") | |||
| if modulename == "": | |||
| return sorted(get_modules()) | |||
| candidates = get_completions(modulename) | |||
| if candidates: | |||
| return sorted(candidates) | |||
| needle = modulename | |||
| if "." in needle: | |||
| modulename, part = needle.rsplit(".", 1) | |||
| candidates = get_completions(modulename) | |||
| else: | |||
| candidates = get_modules() | |||
| return sorted(candidate for candidate in candidates | |||
| if candidate.startswith(needle)) | |||
| def get_completions(modulename): | |||
| modules = set("{0}.{1}".format(modulename, module) | |||
| for module in get_modules(modulename)) | |||
| try: | |||
| module, name = resolve(modulename) | |||
| except ImportError: | |||
| return modules | |||
| if isinstance(module, CONTAINER_TYPES): | |||
| modules.update("{0}.{1}".format(modulename, name) | |||
| for name in dir(module) | |||
| if not name.startswith("_") and | |||
| isinstance(getattr(module, name), | |||
| PYDOC_TYPES)) | |||
| return modules | |||
| def get_modules(modulename=None): | |||
| """Return a list of modules and packages under modulename. | |||
| If modulename is not given, return a list of all top level modules | |||
| and packages. | |||
| """ | |||
| modulename = compat.ensure_not_unicode(modulename) | |||
| if not modulename: | |||
| try: | |||
| return ([modname for (importer, modname, ispkg) | |||
| in iter_modules() | |||
| if not modname.startswith("_")] + | |||
| list(sys.builtin_module_names)) | |||
| except OSError: | |||
| # Bug in Python 2.6, see #275 | |||
| return list(sys.builtin_module_names) | |||
| try: | |||
| module = safeimport(modulename) | |||
| except ErrorDuringImport: | |||
| return [] | |||
| if module is None: | |||
| return [] | |||
| if hasattr(module, "__path__"): | |||
| return [modname for (importer, modname, ispkg) | |||
| in iter_modules(module.__path__) | |||
| if not modname.startswith("_")] | |||
| return [] | |||
| @ -1,354 +0,0 @@ | |||
| """Refactoring methods for elpy. | |||
| This interfaces directly with rope, regardless of the backend used, | |||
| because the other backends don't really offer refactoring choices. | |||
| Once Jedi is similarly featureful as Rope we can try and offer both. | |||
| # Too complex: | |||
| - Restructure: Interesting, but too complex, and needs deep Rope | |||
| knowledge to do well. | |||
| - ChangeSignature: Slightly less complex interface, but still to | |||
| complex, requiring a large effort for the benefit. | |||
| # Too useless: | |||
| I could not get these to work in any useful fashion. I might be doing | |||
| something wrong. | |||
| - ExtractVariable does not replace the code extracted with the | |||
| variable, making it a glorified copy&paste function. Emacs can do | |||
| better than this interface by itself. | |||
| - EncapsulateField: Getter/setter methods are outdated, this should be | |||
| using properties. | |||
| - IntroduceFactory: Inserts a trivial method to the current class. | |||
| Cute. | |||
| - IntroduceParameter: Introduces a parameter correctly, but does not | |||
| replace the old code with the parameter. So it just edits the | |||
| argument list and adds a shiny default. | |||
| - LocalToField: Seems to just add "self." in front of all occurrences | |||
| of a variable in the local scope. | |||
| - MethodObject: This turns the current method into a callable | |||
| class/object. Not sure what that would be good for. | |||
| # Can't even get to work: | |||
| - ImportOrganizer expand_star_imports, handle_long_imports, | |||
| relatives_to_absolutes: Seem not to do anything. | |||
| - create_move: I was not able to figure out what it would like to see | |||
| as its attrib argument. | |||
| """ | |||
| try: | |||
| from rope.base.project import Project | |||
| from rope.base.libutils import path_to_resource | |||
| from rope.base import change as rope_change | |||
| from rope.base import worder | |||
| from rope.refactor.importutils import ImportOrganizer | |||
| from rope.refactor.topackage import ModuleToPackage | |||
| from rope.refactor.rename import Rename | |||
| from rope.refactor.move import create_move | |||
| from rope.refactor.inline import create_inline | |||
| from rope.refactor.extract import ExtractMethod | |||
| from rope.refactor.usefunction import UseFunction | |||
| ROPE_AVAILABLE = True | |||
| except ImportError: | |||
| ROPE_AVAILABLE = False | |||
| def options(description, **kwargs): | |||
| """Decorator to set some options on a method.""" | |||
| def set_notes(function): | |||
| function.refactor_notes = {'name': function.__name__, | |||
| 'category': "Miscellaneous", | |||
| 'description': description, | |||
| 'doc': getattr(function, '__doc__', | |||
| ''), | |||
| 'args': []} | |||
| function.refactor_notes.update(kwargs) | |||
| return function | |||
| return set_notes | |||
| class Refactor(object): | |||
| """The main refactoring interface. | |||
| Once initialized, the first call should be to get_refactor_options | |||
| to get a list of refactoring options at a given position. The | |||
| returned value will also list any additional options required. | |||
| Once you picked one, you can call get_changes to get the actual | |||
| refactoring changes. | |||
| """ | |||
| 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) | |||
| def get_refactor_options(self, start, end=None): | |||
| """Return a list of options for refactoring at the given position. | |||
| If `end` is also given, refactoring on a region is assumed. | |||
| Each option is a dictionary of key/value pairs. The value of | |||
| the key 'name' is the one to be used for get_changes. | |||
| The key 'args' contains a list of additional arguments | |||
| required for get_changes. | |||
| """ | |||
| result = [] | |||
| for symbol in dir(self): | |||
| if not symbol.startswith("refactor_"): | |||
| continue | |||
| method = getattr(self, symbol) | |||
| if not method.refactor_notes.get('available', True): | |||
| continue | |||
| category = method.refactor_notes['category'] | |||
| if end is not None and category != 'Region': | |||
| continue | |||
| if end is None and category == 'Region': | |||
| continue | |||
| is_on_symbol = self._is_on_symbol(start) | |||
| if not is_on_symbol and category in ('Symbol', 'Method'): | |||
| continue | |||
| requires_import = method.refactor_notes.get('only_on_imports', | |||
| False) | |||
| if requires_import and not self._is_on_import_statement(start): | |||
| continue | |||
| result.append(method.refactor_notes) | |||
| return result | |||
| def _is_on_import_statement(self, offset): | |||
| "Does this offset point to an import statement?" | |||
| data = self.resource.read() | |||
| bol = data.rfind("\n", 0, offset) + 1 | |||
| eol = data.find("\n", 0, bol) | |||
| if eol == -1: | |||
| eol = len(data) | |||
| line = data[bol:eol] | |||
| line = line.strip() | |||
| if line.startswith("import ") or line.startswith("from "): | |||
| return True | |||
| else: | |||
| return False | |||
| def _is_on_symbol(self, offset): | |||
| "Is this offset on a symbol?" | |||
| if not ROPE_AVAILABLE: | |||
| return False | |||
| data = self.resource.read() | |||
| if offset >= len(data): | |||
| return False | |||
| if data[offset] != '_' and not data[offset].isalnum(): | |||
| return False | |||
| word = worder.get_name_at(self.resource, offset) | |||
| if word: | |||
| return True | |||
| else: | |||
| return False | |||
| def get_changes(self, name, *args): | |||
| """Return a list of changes for the named refactoring action. | |||
| Changes are dictionaries describing a single action to be | |||
| taken for the refactoring to be successful. | |||
| A change has an action and possibly a type. In the description | |||
| below, the action is before the slash and the type after it. | |||
| change: Change file contents | |||
| - file: The path to the file to change | |||
| - contents: The new contents for the file | |||
| - Diff: A unified diff showing the changes introduced | |||
| create/file: Create a new file | |||
| - file: The file to create | |||
| create/directory: Create a new directory | |||
| - path: The directory to create | |||
| move/file: Rename a file | |||
| - source: The path to the source file | |||
| - destination: The path to the destination file name | |||
| move/directory: Rename a directory | |||
| - source: The path to the source directory | |||
| - destination: The path to the destination directory name | |||
| delete/file: Delete a file | |||
| - file: The file to delete | |||
| delete/directory: Delete a directory | |||
| - path: The directory to delete | |||
| """ | |||
| if not name.startswith("refactor_"): | |||
| raise ValueError("Bad refactoring name {0}".format(name)) | |||
| method = getattr(self, name) | |||
| if not method.refactor_notes.get('available', True): | |||
| raise RuntimeError("Method not available") | |||
| return method(*args) | |||
| @options("Convert from x import y to import x.y as y", category="Imports", | |||
| args=[("offset", "offset", None)], | |||
| only_on_imports=True, | |||
| available=ROPE_AVAILABLE) | |||
| def refactor_froms_to_imports(self, offset): | |||
| """Converting imports of the form "from ..." to "import ...".""" | |||
| refactor = ImportOrganizer(self.project) | |||
| changes = refactor.froms_to_imports(self.resource, offset) | |||
| return translate_changes(changes) | |||
| @options("Reorganize and clean up", category="Imports", | |||
| available=ROPE_AVAILABLE) | |||
| def refactor_organize_imports(self): | |||
| """Clean up and organize imports.""" | |||
| refactor = ImportOrganizer(self.project) | |||
| changes = refactor.organize_imports(self.resource) | |||
| return translate_changes(changes) | |||
| @options("Convert the current module into a package", category="Module", | |||
| available=ROPE_AVAILABLE) | |||
| 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) | |||
| @options("Rename symbol at point", category="Symbol", | |||
| args=[("offset", "offset", None), | |||
| ("new_name", "string", "Rename to: ")], | |||
| available=ROPE_AVAILABLE) | |||
| def refactor_rename_at_point(self, offset, new_name): | |||
| """Rename the symbol at point.""" | |||
| refactor = Rename(self.project, self.resource, offset) | |||
| changes = refactor.get_changes(new_name) | |||
| return translate_changes(changes) | |||
| @options("Rename current module", category="Module", | |||
| args=[("new_name", "string", "Rename to: ")], | |||
| available=ROPE_AVAILABLE) | |||
| 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) | |||
| @options("Move the current module to a different package", | |||
| category="Module", | |||
| args=[("new_name", "directory", "Destination package: ")], | |||
| available=ROPE_AVAILABLE) | |||
| def refactor_move_module(self, new_name): | |||
| """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) | |||
| @options("Inline function call at point", category="Symbol", | |||
| args=[("offset", "offset", None), | |||
| ("only_this", "boolean", "Only this occurrence? ")], | |||
| available=ROPE_AVAILABLE) | |||
| def refactor_create_inline(self, offset, only_this): | |||
| """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) | |||
| else: | |||
| changes = refactor.get_changes(remove=True, only_current=False) | |||
| return translate_changes(changes) | |||
| @options("Extract current region as a method", category="Region", | |||
| args=[("start", "start_offset", None), | |||
| ("end", "end_offset", None), | |||
| ("name", "string", "Method name: "), | |||
| ("make_global", "boolean", "Create global method? ")], | |||
| available=ROPE_AVAILABLE) | |||
| def refactor_extract_method(self, start, end, name, | |||
| 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) | |||
| @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() | |||
| return translate_changes(changes) | |||
| def translate_changes(initial_change): | |||
| """Translate rope.base.change.Change instances to dictionaries. | |||
| See Refactor.get_changes for an explanation of the resulting | |||
| dictionary. | |||
| """ | |||
| agenda = [initial_change] | |||
| result = [] | |||
| while agenda: | |||
| change = agenda.pop(0) | |||
| if isinstance(change, rope_change.ChangeSet): | |||
| agenda.extend(change.changes) | |||
| elif isinstance(change, rope_change.ChangeContents): | |||
| result.append({'action': 'change', | |||
| 'file': change.resource.real_path, | |||
| 'contents': change.new_contents, | |||
| 'diff': change.get_description()}) | |||
| elif isinstance(change, rope_change.CreateFile): | |||
| result.append({'action': 'create', | |||
| 'type': 'file', | |||
| 'file': change.resource.real_path}) | |||
| elif isinstance(change, rope_change.CreateFolder): | |||
| result.append({'action': 'create', | |||
| 'type': 'directory', | |||
| 'path': change.resource.real_path}) | |||
| elif isinstance(change, rope_change.MoveResource): | |||
| result.append({'action': 'move', | |||
| 'type': ('directory' | |||
| if change.new_resource.is_folder() | |||
| else 'file'), | |||
| 'source': change.resource.real_path, | |||
| 'destination': change.new_resource.real_path}) | |||
| elif isinstance(change, rope_change.RemoveResource): | |||
| if change.resource.is_folder(): | |||
| result.append({'action': 'delete', | |||
| 'type': 'directory', | |||
| 'path': change.resource.real_path}) | |||
| else: | |||
| result.append({'action': 'delete', | |||
| 'type': 'file', | |||
| 'file': change.resource.real_path}) | |||
| return result | |||
| class FakeResource(object): | |||
| """A fake resource in case Rope is absence.""" | |||
| def __init__(self, filename): | |||
| self.real_path = filename | |||
| def read(self): | |||
| with open(self.real_path) as f: | |||
| return f.read() | |||
| @ -1,299 +0,0 @@ | |||
| """Elpy backend using the Rope library. | |||
| This backend uses the Rope library: | |||
| http://rope.sourceforge.net/ | |||
| """ | |||
| import os | |||
| import time | |||
| import rope.contrib.codeassist | |||
| import rope.base.project | |||
| import rope.base.libutils | |||
| import rope.base.exceptions | |||
| import rope.contrib.findit | |||
| import elpy.pydocutils | |||
| VALIDATE_EVERY_SECONDS = 5 | |||
| MAXFIXES = 5 | |||
| class RopeBackend(object): | |||
| """The Rope backend class. | |||
| Implements the RPC calls we can pass on to Rope. Also subclasses | |||
| the native backend to provide methods Rope does not provide, if | |||
| any. | |||
| """ | |||
| name = "rope" | |||
| def __init__(self, project_root): | |||
| super(RopeBackend, self).__init__() | |||
| self.last_validation = 0 | |||
| self.project_root = project_root | |||
| self.completions = {} | |||
| prefs = dict(ignored_resources=['*.pyc', '*~', '.ropeproject', | |||
| '.hg', '.svn', '_svn', '.git'], | |||
| python_files=['*.py'], | |||
| save_objectdb=False, | |||
| compress_objectdb=False, | |||
| automatic_soa=True, | |||
| soa_followed_calls=0, | |||
| perform_doa=True, | |||
| validate_objectdb=True, | |||
| max_history_items=32, | |||
| save_history=False, | |||
| compress_history=False, | |||
| indent_size=4, | |||
| extension_modules=[], | |||
| import_dynload_stdmods=True, | |||
| ignore_syntax_errors=False, | |||
| ignore_bad_imports=False) | |||
| self.project = rope.base.project.Project(self.project_root, | |||
| ropefolder=None, | |||
| **prefs) | |||
| def get_resource(self, filename): | |||
| if filename is not None and os.path.exists(filename): | |||
| return rope.base.libutils.path_to_resource(self.project, | |||
| filename, | |||
| 'file') | |||
| else: | |||
| return None | |||
| def validate(self): | |||
| """Validate the stored project. | |||
| This should be called before every use of Rope. It will | |||
| revalidate the project, but do some call throttling. | |||
| """ | |||
| now = time.time() | |||
| if now > self.last_validation + VALIDATE_EVERY_SECONDS: | |||
| self.project.validate() | |||
| self.last_validation = now | |||
| def rpc_get_completions(self, filename, source, offset): | |||
| self.validate() | |||
| resource = self.get_resource(filename) | |||
| try: | |||
| proposals = rope.contrib.codeassist.code_assist(self.project, | |||
| source, offset, | |||
| resource, | |||
| maxfixes=MAXFIXES) | |||
| starting_offset = rope.contrib.codeassist.starting_offset(source, | |||
| offset) | |||
| except (rope.base.exceptions.BadIdentifierError, | |||
| rope.base.exceptions.ModuleSyntaxError, | |||
| IndentationError, | |||
| IndexError, | |||
| LookupError): | |||
| # Rope can't parse this file | |||
| return [] | |||
| prefixlen = offset - starting_offset | |||
| 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] | |||
| def rpc_get_completion_docstring(self, completion): | |||
| proposal = self.completions.get(completion) | |||
| if proposal is None: | |||
| return None | |||
| else: | |||
| return proposal.get_doc() | |||
| def rpc_get_completion_location(self, completion): | |||
| proposal = self.completions.get(completion) | |||
| if proposal is None: | |||
| return None | |||
| else: | |||
| if not proposal.pyname: | |||
| return None | |||
| module, lineno = proposal.pyname.get_definition_location() | |||
| if module is None: | |||
| return None | |||
| resource = module.get_module().get_resource() | |||
| return (resource.real_path, lineno) | |||
| def rpc_get_definition(self, filename, source, offset): | |||
| self.validate() | |||
| # The find_definition call fails on an empty strings | |||
| if source == '': | |||
| return None | |||
| resource = self.get_resource(filename) | |||
| try: | |||
| location = rope.contrib.findit.find_definition(self.project, | |||
| source, offset, | |||
| resource, MAXFIXES) | |||
| except (rope.base.exceptions.BadIdentifierError, | |||
| rope.base.exceptions.ModuleSyntaxError, | |||
| IndentationError, | |||
| LookupError): | |||
| # Rope can't parse this file | |||
| return None | |||
| if location is None: | |||
| return None | |||
| else: | |||
| return (location.resource.real_path, location.offset) | |||
| def rpc_get_calltip(self, filename, source, offset): | |||
| self.validate() | |||
| offset = find_called_name_offset(source, offset) | |||
| resource = self.get_resource(filename) | |||
| if 0 < offset < len(source) and source[offset] == ')': | |||
| offset -= 1 | |||
| try: | |||
| calltip = rope.contrib.codeassist.get_calltip( | |||
| self.project, source, offset, resource, MAXFIXES, | |||
| remove_self=True) | |||
| if calltip: | |||
| calltip = calltip.replace(".__init__(", "(") | |||
| calltip = calltip.replace("(self)", "()") | |||
| calltip = calltip.replace("(self, ", "(") | |||
| # "elpy.tests.support.source_and_offset(source)" | |||
| # => | |||
| # "support.source_and_offset(source)" | |||
| try: | |||
| openpos = calltip.index("(") | |||
| period2 = calltip.rindex(".", 0, openpos) | |||
| period1 = calltip.rindex(".", 0, period2) | |||
| calltip = calltip[period1 + 1:] | |||
| except ValueError: | |||
| pass | |||
| return calltip | |||
| except (rope.base.exceptions.BadIdentifierError, | |||
| rope.base.exceptions.ModuleSyntaxError, | |||
| IndentationError, | |||
| IndexError, | |||
| LookupError): | |||
| # Rope can't parse this file | |||
| return None | |||
| def rpc_get_docstring(self, filename, source, offset): | |||
| self.validate() | |||
| resource = self.get_resource(filename) | |||
| try: | |||
| docstring = rope.contrib.codeassist.get_doc(self.project, | |||
| source, offset, | |||
| resource, MAXFIXES) | |||
| except (rope.base.exceptions.BadIdentifierError, | |||
| rope.base.exceptions.ModuleSyntaxError, | |||
| IndentationError, | |||
| IndexError, | |||
| LookupError): | |||
| # Rope can't parse this file | |||
| docstring = None | |||
| return docstring | |||
| def find_called_name_offset(source, orig_offset): | |||
| """Return the offset of a calling function. | |||
| This only approximates movement. | |||
| """ | |||
| offset = min(orig_offset, len(source) - 1) | |||
| paren_count = 0 | |||
| while True: | |||
| if offset <= 1: | |||
| return orig_offset | |||
| elif source[offset] == '(': | |||
| if paren_count == 0: | |||
| return offset - 1 | |||
| else: | |||
| paren_count -= 1 | |||
| elif source[offset] == ')': | |||
| paren_count += 1 | |||
| offset -= 1 | |||
| ################################################################## | |||
| # A recurring problem in Rope for Elpy is that it searches the whole | |||
| # project root for Python files. If the user edits a file in their | |||
| # home directory, this can easily read a whole lot of files, making | |||
| # Rope practically useless. We change the file finding algorithm here | |||
| # to only recurse into directories with an __init__.py file in them. | |||
| def find_source_folders(self, folder): | |||
| for resource in folder.get_folders(): | |||
| if self._is_package(resource): | |||
| return [folder] | |||
| result = [] | |||
| for resource in folder.get_files(): | |||
| if resource.name.endswith('.py'): | |||
| result.append(folder) | |||
| break | |||
| for resource in folder.get_folders(): | |||
| if self._is_package(resource): | |||
| result.append(resource) | |||
| return result | |||
| import rope.base.pycore | |||
| rope.base.pycore.PyCore._find_source_folders = find_source_folders | |||
| def get_files(self): | |||
| if self.files is None: | |||
| self.files = get_python_project_files(self.project) | |||
| return self.files | |||
| rope.base.project._FileListCacher.get_files = get_files | |||
| def get_python_project_files(project): | |||
| for dirname, subdirs, files in os.walk(project.root.real_path): | |||
| for filename in files: | |||
| yield rope.base.libutils.path_to_resource( | |||
| project, os.path.join(dirname, filename), 'file') | |||
| subdirs[:] = [subdir for subdir in subdirs | |||
| if os.path.exists(os.path.join(dirname, subdir, | |||
| "__init__.py"))] | |||
| ################################################################## | |||
| # Monkey patching a method in rope because it doesn't complete import | |||
| # statements. | |||
| orig_code_completions = (rope.contrib.codeassist. | |||
| _PythonCodeAssist._code_completions) | |||
| def code_completions(self): | |||
| proposals = get_import_completions(self) | |||
| if proposals: | |||
| return proposals | |||
| else: | |||
| return orig_code_completions(self) | |||
| def get_import_completions(self): | |||
| if not self.word_finder.is_import_statement(self.offset): | |||
| return [] | |||
| modulename = self.word_finder.get_primary_at(self.offset) | |||
| # Rope can handle modules in packages | |||
| if "." in modulename: | |||
| return [] | |||
| return dict((name, FakeProposal(name)) | |||
| for name in elpy.pydocutils.get_modules() | |||
| if name.startswith(modulename)) | |||
| class FakeProposal(object): | |||
| def __init__(self, name): | |||
| self.name = name | |||
| self.type = "mock" | |||
| def get_doc(self): | |||
| return None | |||
| rope.contrib.codeassist._PythonCodeAssist._code_completions = code_completions | |||
| @ -1,151 +0,0 @@ | |||
| """A simple JSON-RPC-like server. | |||
| The server will read and write lines of JSON-encoded method calls and | |||
| responses. | |||
| See the documentation of the JSONRPCServer class for further details. | |||
| """ | |||
| import json | |||
| import sys | |||
| import traceback | |||
| class JSONRPCServer(object): | |||
| """Simple JSON-RPC-like server. | |||
| This class will read single-line JSON expressions from stdin, | |||
| decode them, and pass them to a handler. Return values from the | |||
| handler will be JSON-encoded and written to stdout. | |||
| To implement a handler, you need to subclass this class and add | |||
| methods starting with "rpc_". Methods then will be found. | |||
| Method calls should be encoded like this: | |||
| {"id": 23, "method": "method_name", "params": ["foo", "bar"]} | |||
| This will call self.rpc_method("foo", "bar"). | |||
| Responses will be encoded like this: | |||
| {"id": 23, "result": "foo"} | |||
| Errors will be encoded like this: | |||
| {"id": 23, "error": "Simple error message"} | |||
| See http://www.jsonrpc.org/ for the inspiration of the protocol. | |||
| """ | |||
| def __init__(self, stdin=None, stdout=None): | |||
| """Return a new JSON-RPC server object. | |||
| It will read lines of JSON data from stdin, and write the | |||
| responses to stdout. | |||
| """ | |||
| if stdin is None: | |||
| self.stdin = sys.stdin | |||
| else: | |||
| self.stdin = stdin | |||
| if stdout is None: | |||
| self.stdout = sys.stdout | |||
| else: | |||
| self.stdout = stdout | |||
| def read_json(self): | |||
| """Read a single line and decode it as JSON. | |||
| Can raise an EOFError() when the input source was closed. | |||
| """ | |||
| line = self.stdin.readline() | |||
| if line == '': | |||
| raise EOFError() | |||
| return json.loads(line) | |||
| def write_json(self, **kwargs): | |||
| """Write an JSON object on a single line. | |||
| The keyword arguments are interpreted as a single JSON object. | |||
| It's not possible with this method to write non-objects. | |||
| """ | |||
| self.stdout.write(json.dumps(kwargs) + "\n") | |||
| self.stdout.flush() | |||
| def handle_request(self): | |||
| """Handle a single JSON-RPC request. | |||
| Read a request, call the appropriate handler method, and | |||
| return the encoded result. Errors in the handler method are | |||
| caught and encoded as error objects. Errors in the decoding | |||
| phase are not caught, as we can not respond with an error | |||
| response to them. | |||
| """ | |||
| request = self.read_json() | |||
| if 'method' not in request: | |||
| raise ValueError("Received a bad request: {0}" | |||
| .format(request)) | |||
| method_name = request['method'] | |||
| request_id = request.get('id', None) | |||
| params = request.get('params') or [] | |||
| try: | |||
| method = getattr(self, "rpc_" + method_name, None) | |||
| if method is not None: | |||
| result = method(*params) | |||
| else: | |||
| result = self.handle(method_name, params) | |||
| if request_id is not None: | |||
| self.write_json(result=result, | |||
| id=request_id) | |||
| except Fault as fault: | |||
| error = {"message": fault.message, | |||
| "code": fault.code} | |||
| if fault.data is not None: | |||
| error["data"] = fault.data | |||
| self.write_json(error=error, id=request_id) | |||
| except Exception as e: | |||
| error = {"message": str(e), | |||
| "code": 500, | |||
| "data": {"traceback": traceback.format_exc()}} | |||
| self.write_json(error=error, id=request_id) | |||
| def handle(self, method_name, args): | |||
| """Handle the call to method_name. | |||
| You should overwrite this method in a subclass. | |||
| """ | |||
| raise Fault("Unknown method {0}".format(method_name)) | |||
| def serve_forever(self): | |||
| """Serve requests forever. | |||
| Errors are not caught, so this is a slight misnomer. | |||
| """ | |||
| while True: | |||
| try: | |||
| self.handle_request() | |||
| except (KeyboardInterrupt, EOFError, SystemExit): | |||
| break | |||
| class Fault(Exception): | |||
| """RPC Fault instances. | |||
| code defines the severity of the warning. | |||
| 2xx: Normal behavior lead to end of operation, i.e. a warning | |||
| 4xx: An expected error occurred | |||
| 5xx: An unexpected error occurred (usually includes a traceback) | |||
| """ | |||
| def __init__(self, message, code=500, data=None): | |||
| super(Fault, self).__init__(message) | |||
| self.message = message | |||
| self.code = code | |||
| self.data = data | |||
| @ -1,212 +0,0 @@ | |||
| """Method implementations for the Elpy JSON-RPC server. | |||
| This file implements the methods exported by the JSON-RPC server. It | |||
| handles backend selection and passes methods on to the selected | |||
| backend. | |||
| """ | |||
| import io | |||
| import os | |||
| import pydoc | |||
| from elpy.pydocutils import get_pydoc_completions | |||
| from elpy.rpc import JSONRPCServer, Fault | |||
| from elpy import compat | |||
| try: | |||
| from elpy import jedibackend | |||
| except ImportError: # pragma: no cover | |||
| jedibackend = None | |||
| try: | |||
| from elpy import ropebackend | |||
| except ImportError: # pragma: no cover | |||
| ropebackend = None | |||
| class ElpyRPCServer(JSONRPCServer): | |||
| """The RPC server for elpy. | |||
| See the rpc_* methods for exported method documentation. | |||
| """ | |||
| def __init__(self): | |||
| super(ElpyRPCServer, self).__init__() | |||
| self.backend = None | |||
| self.project_root = None | |||
| def _call_backend(self, method, default, *args, **kwargs): | |||
| """Call the backend method with args. | |||
| If there is currently no backend, return default.""" | |||
| meth = getattr(self.backend, method, None) | |||
| if meth is None: | |||
| return default | |||
| else: | |||
| return meth(*args, **kwargs) | |||
| def rpc_echo(self, *args): | |||
| """Return the arguments. | |||
| This is a simple test method to see if the protocol is | |||
| working. | |||
| """ | |||
| return args | |||
| def rpc_init(self, options): | |||
| self.project_root = options["project_root"] | |||
| if ropebackend and options["backend"] == "rope": | |||
| self.backend = ropebackend.RopeBackend(self.project_root) | |||
| elif jedibackend and options["backend"] == "jedi": | |||
| self.backend = jedibackend.JediBackend(self.project_root) | |||
| elif ropebackend: | |||
| self.backend = ropebackend.RopeBackend(self.project_root) | |||
| elif jedibackend: | |||
| self.backend = jedibackend.JediBackend(self.project_root) | |||
| else: | |||
| self.backend = None | |||
| return { | |||
| 'backend': (self.backend.name if self.backend is not None | |||
| else None) | |||
| } | |||
| def rpc_get_calltip(self, filename, source, offset): | |||
| """Get the calltip for the function at the offset. | |||
| """ | |||
| return self._call_backend("rpc_get_calltip", None, filename, | |||
| get_source(source), offset) | |||
| def rpc_get_completions(self, filename, source, offset): | |||
| """Get a list of completion candidates for the symbol at offset. | |||
| """ | |||
| return self._call_backend("rpc_get_completions", [], filename, | |||
| get_source(source), offset) | |||
| def rpc_get_completion_docstring(self, completion): | |||
| """Return documentation for a previously returned completion. | |||
| """ | |||
| return self._call_backend("rpc_get_completion_docstring", | |||
| None, completion) | |||
| def rpc_get_completion_location(self, completion): | |||
| """Return the location for a previously returned completion. | |||
| This returns a list of [file name, line number]. | |||
| """ | |||
| return self._call_backend("rpc_get_completion_location", None, | |||
| completion) | |||
| def rpc_get_definition(self, filename, source, offset): | |||
| """Get the location of the definition for the symbol at the offset. | |||
| """ | |||
| return self._call_backend("rpc_get_definition", None, filename, | |||
| get_source(source), offset) | |||
| def rpc_get_docstring(self, filename, source, offset): | |||
| """Get the docstring for the symbol at the offset. | |||
| """ | |||
| return self._call_backend("rpc_get_docstring", None, filename, | |||
| get_source(source), offset) | |||
| def rpc_get_pydoc_completions(self, name=None): | |||
| """Return a list of possible strings to pass to pydoc. | |||
| If name is given, the strings are under name. If not, top | |||
| level modules are returned. | |||
| """ | |||
| return get_pydoc_completions(name) | |||
| def rpc_get_pydoc_documentation(self, symbol): | |||
| """Get the Pydoc documentation for the given symbol. | |||
| Uses pydoc and can return a string with backspace characters | |||
| for bold highlighting. | |||
| """ | |||
| try: | |||
| docstring = pydoc.render_doc(str(symbol), | |||
| "Elpy Pydoc Documentation for %s", | |||
| False) | |||
| except (ImportError, pydoc.ErrorDuringImport): | |||
| return None | |||
| else: | |||
| if isinstance(docstring, bytes): | |||
| docstring = docstring.decode("utf-8", "replace") | |||
| return docstring | |||
| def rpc_get_refactor_options(self, filename, start, end=None): | |||
| """Return a list of possible refactoring options. | |||
| This list will be filtered depending on whether it's | |||
| applicable at the point START and possibly the region between | |||
| START and END. | |||
| """ | |||
| try: | |||
| from elpy import refactor | |||
| except: | |||
| raise ImportError("Rope not installed, refactorings unavailable") | |||
| ref = refactor.Refactor(self.project_root, filename) | |||
| return ref.get_refactor_options(start, end) | |||
| def rpc_refactor(self, filename, method, args): | |||
| """Return a list of changes from the refactoring action. | |||
| A change is a dictionary describing the change. See | |||
| elpy.refactor.translate_changes for a description. | |||
| """ | |||
| try: | |||
| from elpy import refactor | |||
| except: | |||
| raise ImportError("Rope not installed, refactorings unavailable") | |||
| if args is None: | |||
| args = () | |||
| ref = refactor.Refactor(self.project_root, filename) | |||
| return ref.get_changes(method, *args) | |||
| def rpc_get_usages(self, filename, source, offset): | |||
| """Get usages for the symbol at point. | |||
| """ | |||
| source = get_source(source) | |||
| if hasattr(self.backend, "rpc_get_usages"): | |||
| return self.backend.rpc_get_usages(filename, source, offset) | |||
| else: | |||
| raise Fault("get_usages not implemented by current backend", | |||
| code=400) | |||
| def get_source(fileobj): | |||
| """Translate fileobj into file contents. | |||
| fileobj is either a string or a dict. If it's a string, that's the | |||
| file contents. If it's a string, then the filename key contains | |||
| the name of the file whose contents we are to use. | |||
| If the dict contains a true value for the key delete_after_use, | |||
| the file should be deleted once read. | |||
| """ | |||
| if not isinstance(fileobj, dict): | |||
| return fileobj | |||
| else: | |||
| try: | |||
| with io.open(fileobj["filename"], encoding="utf-8") as f: | |||
| return f.read() | |||
| finally: | |||
| if fileobj.get('delete_after_use'): | |||
| try: | |||
| os.remove(fileobj["filename"]) | |||
| except: # pragma: no cover | |||
| pass | |||
| @ -1,8 +0,0 @@ | |||
| """Unit tests for elpy.""" | |||
| try: | |||
| import unittest2 | |||
| import sys | |||
| sys.modules['unittest'] = unittest2 | |||
| except: | |||
| pass | |||
| @ -1,18 +0,0 @@ | |||
| """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 | |||
| from StringIO import StringIO | |||
| @ -1,653 +0,0 @@ | |||
| """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 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 | |||
| with open(full_name, "w") 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): | |||
| 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) | |||
| 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): | |||
| source = u'# coding: utf-8X\n' | |||
| filename = self.project_file("test.py", source) | |||
| self.rpc(filename, source, 16) | |||
| 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 | |||
| source, offset = source_and_offset( | |||
| "map(lambda_|_" | |||
| ) | |||
| 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) | |||
| 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(["SONDecoder", "SONEncoder"])) | |||
| 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 elpy.tes_|_") | |||
| filename = self.project_file("test.py", source) | |||
| completions = self.backend.rpc_get_completions(filename, | |||
| source, | |||
| offset) | |||
| self.assertEqual([cand['suffix'] for cand in completions], | |||
| ["ts"]) | |||
| 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], | |||
| []) | |||
| 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.J_|_") | |||
| 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"], "JSONDecoder") | |||
| docs = self.backend.rpc_get_completion_docstring("JSONDecoder") | |||
| self.assertIn("Simple 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" | |||
| 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) | |||
| 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) | |||
| 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_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 test_should_get_docstring(self): | |||
| source, offset = source_and_offset( | |||
| "import threading\nthreading.Thread.join_|_(") | |||
| filename = self.project_file("test.py", source) | |||
| docstring = self.backend.rpc_get_docstring(filename, | |||
| source, | |||
| offset) | |||
| def first_line(s): | |||
| return s[:s.index("\n")] | |||
| self.assertEqual(first_line(docstring), | |||
| 'Thread.join(self, timeout=None):') | |||
| 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 | |||
| @ -1,208 +0,0 @@ | |||
| """Tests for the elpy.jedibackend module.""" | |||
| 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 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 TestRPCGetDefinition(RPCGetDefinitionTests, | |||
| JediBackendTestCase): | |||
| pass | |||
| class TestRPCGetCalltip(RPCGetCalltipTests, | |||
| JediBackendTestCase): | |||
| KEYS_CALLTIP = {'index': 0, | |||
| 'params': [''], | |||
| 'name': u'keys'} | |||
| 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} | |||
| class TestRPCGetUsages(RPCGetUsagesTests, | |||
| JediBackendTestCase): | |||
| pass | |||
| 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"], []) | |||
| @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())) | |||
| @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") | |||
| @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"]) | |||
| @ -1,90 +0,0 @@ | |||
| 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) | |||
| self.assertIn('elpy', 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) | |||
| self.assertIn('elpy', 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) | |||
| @ -1,399 +0,0 @@ | |||
| import unittest | |||
| import tempfile | |||
| import shutil | |||
| import os | |||
| import mock | |||
| from elpy import refactor | |||
| 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) | |||
| offset = contents.find("_|_") | |||
| if offset > -1: | |||
| contents = contents[:offset] + contents[offset + 3:] | |||
| with open(filename, "w") as f: | |||
| f.write(contents) | |||
| return filename, offset | |||
| class TestGetRefactorOptions(RefactorTestCase): | |||
| def test_should_only_return_importsmodule_if_not_on_symbol(self): | |||
| filename, offset = self.create_file("foo.py", | |||
| "import foo\n" | |||
| "_|_") | |||
| 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", | |||
| "_|_\n" | |||
| "import foo\n") | |||
| 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\n" | |||
| "_|_") | |||
| 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\n" | |||
| "\n" | |||
| "d = datetime(2013, 4, 7)\n") | |||
| 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.assertEqual(change['contents'], | |||
| "import datetime\n" | |||
| "\n" | |||
| "d = datetime.datetime(2013, 4, 7)\n") | |||
| @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\n" | |||
| "import datetime, json\n" | |||
| "\n" | |||
| "obj = json.dumps(23)\n" | |||
| "unittest.TestCase()\n") | |||
| 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.assertEqual(change['contents'], | |||
| "import json\n" | |||
| "import unittest\n" | |||
| "\n" | |||
| "\n" | |||
| "obj = json.dumps(23)\n" | |||
| "unittest.TestCase()\n") | |||
| @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):\n" | |||
| " def _|_foo(self):\n" | |||
| " return 5\n" | |||
| "\n" | |||
| " def bar(self):\n" | |||
| " return self.foo()\n") | |||
| file2, offset2 = self.create_file( | |||
| "bar.py", | |||
| "import foo\n" | |||
| "\n" | |||
| "\n" | |||
| "x = foo.Foo()\n" | |||
| "x.foo()") | |||
| ref = refactor.Refactor(self.project_root, filename) | |||
| first, second = ref.refactor_rename_at_point(offset, "frob") | |||
| if first['file'] == filename: | |||
| a, b = first, second | |||
| else: | |||
| a, b = second, first | |||
| self.assertEqual(a['action'], 'change') | |||
| self.assertEqual(a['file'], filename) | |||
| self.assertEqual(a['contents'], | |||
| "class Foo(object):\n" | |||
| " def frob(self):\n" | |||
| " return 5\n" | |||
| "\n" | |||
| " def bar(self):\n" | |||
| " return self.frob()\n") | |||
| self.assertEqual(b['action'], 'change') | |||
| self.assertEqual(b['file'], file2) | |||
| self.assertEqual(b['contents'], | |||
| "import foo\n" | |||
| "\n" | |||
| "\n" | |||
| "x = foo.Foo()\n" | |||
| "x.frob()") | |||
| @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\n" | |||
| "foo.os\n") | |||
| 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\n" | |||
| "foo.os\n") | |||
| 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.assertEqual(a['contents'], | |||
| "import frob.foo\n" | |||
| "frob.foo.os\n") | |||
| 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):\n" | |||
| " return a + b\n" | |||
| "\n" | |||
| "x = _|_add(2, 3)\n" | |||
| "y = add(17, 4)\n") | |||
| 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.assertEqual(change['contents'], | |||
| "def add(a, b):\n" | |||
| " return a + b\n" | |||
| "\n" | |||
| "x = 2 + 3\n" | |||
| "y = add(17, 4)\n") | |||
| 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.assertEqual(change['contents'], | |||
| "x = 2 + 3\n" | |||
| "y = 17 + 4\n") | |||
| @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):\n" | |||
| " def spaghetti(self, a, b):\n" | |||
| " _|_x = a + 5\n" | |||
| " y = b + 23\n" | |||
| " return y\n") | |||
| 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):\n" | |||
| " def spaghetti(self, a, b):\n" | |||
| " return self.calc(a, b)\n" | |||
| "\n" | |||
| " def calc(self, a, b):\n" | |||
| " x = a + 5\n" | |||
| " y = b + 23\n" | |||
| " return y\n") | |||
| 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)") | |||
| if change['contents'] == expected2: | |||
| self.assertEqual(change['contents'], expected2) | |||
| else: | |||
| self.assertEqual(change['contents'], expected) | |||
| 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):\n" | |||
| " def spaghetti(self, a, b):\n" | |||
| " return calc(a, b)\n" | |||
| "\n" | |||
| "def calc(a, b):\n" | |||
| " x = a + 5\n" | |||
| " y = b + 23\n" | |||
| " return y\n") | |||
| 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'] == expected2: | |||
| self.assertEqual(change['contents'], expected2) | |||
| else: | |||
| self.assertEqual(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):\n" | |||
| " temp = a + b\n" | |||
| " return temp * c\n" | |||
| "\n" | |||
| "f = 1 + 2\n" | |||
| "g = f * 3\n") | |||
| ref = refactor.Refactor(self.project_root, filename) | |||
| (change,) = ref.refactor_use_function(offset) | |||
| self.assertEqual(change['action'], 'change') | |||
| self.assertEqual(change['file'], filename) | |||
| self.assertEqual(change['contents'], | |||
| "def add_and_multiply(a, b, c):\n" | |||
| " temp = a + b\n" | |||
| " return temp * c\n" | |||
| "\n" | |||
| "g = add_and_multiply(1, 2, 3)\n") | |||
| @ -1,147 +0,0 @@ | |||
| """Tests for elpy.ropebackend.""" | |||
| import os | |||
| import mock | |||
| from elpy import ropebackend | |||
| 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) | |||
| 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) | |||
| 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): | |||
| pass | |||
| 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) | |||
| @ -1,209 +0,0 @@ | |||
| """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) | |||
| @ -1,351 +0,0 @@ | |||
| # 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")) | |||
| 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 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") | |||
| @ -1,19 +0,0 @@ | |||
| """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)) | |||
| @ -1,62 +0,0 @@ | |||
| (defun elpy-snippet-split-args (arg-string) | |||
| "Split a python argument string into ((name, default)..) tuples" | |||
| (mapcar (lambda (x) | |||
| (split-string x "[[:blank:]]*=[[:blank:]]*" t)) | |||
| (split-string arg-string "[[:blank:]]*,[[:blank:]]*" t))) | |||
| (defun elpy-snippet-current-method-and-args () | |||
| "Return information on the current definition." | |||
| (let ((current-defun (python-info-current-defun)) | |||
| (current-arglist | |||
| (save-excursion | |||
| (python-nav-beginning-of-defun) | |||
| (when (re-search-forward "(" nil t) | |||
| (let* ((start (point)) | |||
| (end (progn | |||
| (forward-char -1) | |||
| (forward-sexp) | |||
| (- (point) 1)))) | |||
| (elpy-snippet-split-args | |||
| (buffer-substring-no-properties start end)))))) | |||
| class method args) | |||
| (when (not current-arglist) | |||
| (setq current-arglist '(("self")))) | |||
| (if (and current-defun | |||
| (string-match "^\\(.*\\)\\.\\(.*\\)$" current-defun)) | |||
| (setq class (match-string 1 current-defun) | |||
| method (match-string 2 current-defun)) | |||
| (setq class "Class" | |||
| method "method")) | |||
| (setq args (mapcar #'car current-arglist)) | |||
| (list class method args))) | |||
| (defun elpy-snippet-init-assignments (arg-string) | |||
| "Return the typical __init__ assignments for arguments." | |||
| (let ((indentation (make-string (save-excursion | |||
| (goto-char start-point) | |||
| (current-indentation)) | |||
| ?\s))) | |||
| (mapconcat (lambda (arg) | |||
| (if (string-match "^\\*" (car arg)) | |||
| "" | |||
| (format "self.%s = %s\n%s" | |||
| (car arg) | |||
| (car arg) | |||
| indentation))) | |||
| (elpy-snippet-split-args arg-string) | |||
| ""))) | |||
| (defun elpy-snippet-super-form () | |||
| "Return (Class, first-arg).method" | |||
| (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))) | |||
| (defun elpy-snippet-super-arguments () | |||
| "Return the argument list for the current method." | |||
| (mapconcat (lambda (x) x) | |||
| (cdr (nth 2 (elpy-snippet-current-method-and-args))) | |||
| ", ")) | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __abs__ | |||
| # key: __abs__ | |||
| # group: Special methods | |||
| # -- | |||
| def __abs__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __add__ | |||
| # key: __add__ | |||
| # group: Special methods | |||
| # -- | |||
| def __add__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __and__ | |||
| # key: __and__ | |||
| # group: Special methods | |||
| # -- | |||
| def __and__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __bool__ | |||
| # key: __bool__ | |||
| # group: Special methods | |||
| # -- | |||
| def __bool__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __call__ | |||
| # key: __call__ | |||
| # group: Special methods | |||
| # -- | |||
| def __call__(self, ${1:*args}): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __cmp__ | |||
| # key: __cmp__ | |||
| # group: Special methods | |||
| # -- | |||
| def __cmp__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __coerce__ | |||
| # key: __coerce__ | |||
| # group: Special methods | |||
| # -- | |||
| def __coerce__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __complex__ | |||
| # key: __complex__ | |||
| # group: Special methods | |||
| # -- | |||
| def __complex__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __contains__ | |||
| # key: __contains__ | |||
| # group: Special methods | |||
| # -- | |||
| def __contains__(self, item): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __del__ | |||
| # key: __del__ | |||
| # group: Special methods | |||
| # -- | |||
| def __del__(self): | |||
| $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __delattr__ | |||
| # key: __delattr__ | |||
| # group: Special methods | |||
| # -- | |||
| def __delattr__(self, name): | |||
| $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __delete__ | |||
| # key: __delete__ | |||
| # group: Special methods | |||
| # -- | |||
| def __delete__(self, instance): | |||
| $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __delitem__ | |||
| # key: __delitem__ | |||
| # group: Special methods | |||
| # -- | |||
| def __delitem__(self, key): | |||
| $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __div__ | |||
| # key: __div__ | |||
| # group: Special methods | |||
| # -- | |||
| def __div__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __divmod__ | |||
| # key: __divmod__ | |||
| # group: Special methods | |||
| # -- | |||
| def __divmod__(self, other): | |||
| return $0 | |||
| @ -1,9 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __enter__ | |||
| # key: __enter__ | |||
| # group: Special methods | |||
| # -- | |||
| def __enter__(self): | |||
| $0 | |||
| return self | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __eq__ | |||
| # key: __eq__ | |||
| # group: Special methods | |||
| # -- | |||
| def __eq__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __exit__ | |||
| # key: __exit__ | |||
| # group: Special methods | |||
| # -- | |||
| def __exit__(self, exc_type, exc_value, traceback): | |||
| $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __float__ | |||
| # key: __float__ | |||
| # group: Special methods | |||
| # -- | |||
| def __float__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __floordiv__ | |||
| # key: __floordiv__ | |||
| # group: Special methods | |||
| # -- | |||
| def __floordiv__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __ge__ | |||
| # key: __ge__ | |||
| # group: Special methods | |||
| # -- | |||
| def __ge__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __get__ | |||
| # key: __get__ | |||
| # group: Special methods | |||
| # -- | |||
| def __get__(self, instance, owner): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __getattr__ | |||
| # key: __getattr__ | |||
| # group: Special methods | |||
| # -- | |||
| def __getattr__(self, name): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __getattribute__ | |||
| # key: __getattribute__ | |||
| # group: Special methods | |||
| # -- | |||
| def __getattribute__(self, name): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __getitem__ | |||
| # key: __getitem__ | |||
| # group: Special methods | |||
| # -- | |||
| def __getitem__(self, key): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __gt__ | |||
| # key: __gt__ | |||
| # group: Special methods | |||
| # -- | |||
| def __gt__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __hash__ | |||
| # key: __hash__ | |||
| # group: Special methods | |||
| # -- | |||
| def __hash__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __hex__ | |||
| # key: __hex__ | |||
| # group: Special methods | |||
| # -- | |||
| def __hex__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __iadd__ | |||
| # key: __iadd__ | |||
| # group: Special methods | |||
| # -- | |||
| def __iadd__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __iand__ | |||
| # key: __iand__ | |||
| # group: Special methods | |||
| # -- | |||
| def __iand__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __idiv__ | |||
| # key: __idiv__ | |||
| # group: Special methods | |||
| # -- | |||
| def __idiv__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __ifloordiv__ | |||
| # key: __ifloordiv__ | |||
| # group: Special methods | |||
| # -- | |||
| def __ifloordiv__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __ilshift__ | |||
| # key: __ilshift__ | |||
| # group: Special methods | |||
| # -- | |||
| def __ilshift__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __imod__ | |||
| # key: __imod__ | |||
| # group: Special methods | |||
| # -- | |||
| def __imod__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __imul__ | |||
| # key: __imul__ | |||
| # group: Special methods | |||
| # -- | |||
| def __imul__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __index__ | |||
| # key: __index__ | |||
| # group: Special methods | |||
| # -- | |||
| def __index__(self): | |||
| return $0 | |||
| @ -1,10 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __init__ with assignment | |||
| # key: __init__ | |||
| # group: Special methods | |||
| # -- | |||
| def __init__(self${1:, args}): | |||
| """$2 | |||
| """ | |||
| ${1:$(elpy-snippet-init-assignments yas-text)} | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __instancecheck__ | |||
| # key: __instancecheck__ | |||
| # group: Special methods | |||
| # -- | |||
| def __instancecheck__(self, instance): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __int__ | |||
| # key: __int__ | |||
| # group: Special methods | |||
| # -- | |||
| def __int__(self): | |||
| $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __invert__ | |||
| # key: __invert__ | |||
| # group: Special methods | |||
| # -- | |||
| def __invert__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __ior__ | |||
| # key: __ior__ | |||
| # group: Special methods | |||
| # -- | |||
| def __ior__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __ipow__ | |||
| # key: __ipow__ | |||
| # group: Special methods | |||
| # -- | |||
| def __ipow__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __irshift__ | |||
| # key: __irshift__ | |||
| # group: Special methods | |||
| # -- | |||
| def __irshift__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __isub__ | |||
| # key: __isub__ | |||
| # group: Special methods | |||
| # -- | |||
| def __isub__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __iter__ | |||
| # key: __iter__ | |||
| # group: Special methods | |||
| # -- | |||
| def __iter__(self): | |||
| $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __itruediv__ | |||
| # key: __itruediv__ | |||
| # group: Special methods | |||
| # -- | |||
| def __itruediv__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __ixor__ | |||
| # key: __ixor__ | |||
| # group: Special methods | |||
| # -- | |||
| def __ixor__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __le__ | |||
| # key: __le__ | |||
| # group: Special methods | |||
| # -- | |||
| def __le__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __len__ | |||
| # key: __len__ | |||
| # group: Special methods | |||
| # -- | |||
| def __len__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __long__ | |||
| # key: __long__ | |||
| # group: Special methods | |||
| # -- | |||
| def __long__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __lshift__ | |||
| # key: __lshift__ | |||
| # group: Special methods | |||
| # -- | |||
| def __lshift__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __lt__ | |||
| # key: __lt__ | |||
| # group: Special methods | |||
| # -- | |||
| def __lt__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __mod__ | |||
| # key: __mod__ | |||
| # group: Special methods | |||
| # -- | |||
| def __mod__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __mul__ | |||
| # key: __mul__ | |||
| # group: Special methods | |||
| # -- | |||
| def __mul__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __ne__ | |||
| # key: __ne__ | |||
| # group: Special methods | |||
| # -- | |||
| def __ne__(self, other): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __neg__ | |||
| # key: __neg__ | |||
| # group: Special methods | |||
| # -- | |||
| def __neg__(self): | |||
| return $0 | |||
| @ -1,10 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __new__ | |||
| # key: __new__ | |||
| # group: Special methods | |||
| # -- | |||
| def __new__(cls${1:, args}): | |||
| """$2 | |||
| """ | |||
| ${1:$(elpy-snippet-init-assignments yas-text)} | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __nonzero__ | |||
| # key: __nonzero__ | |||
| # group: Special methods | |||
| # -- | |||
| def __nonzero__(self): | |||
| return $0 | |||
| @ -1,7 +0,0 @@ | |||
| # -*- mode: snippet -*- | |||
| # name: __oct__ | |||
| # key: __oct__ | |||
| # group: Special methods | |||
| # -- | |||
| def __oct__(self): | |||
| return $0 | |||