;;; elpy.el --- Emacs Python Development Environment -*- lexical-binding: t -*- ;; Copyright (C) 2012-2019 Jorgen Schaefer ;; Author: Jorgen Schaefer , Gaby Launay ;; URL: https://github.com/jorgenschaefer/elpy ;; Version: 1.35.0 ;; Keywords: Python, IDE, Languages, Tools ;; Package-Requires: ((company "0.9.10") (emacs "24.4") (highlight-indentation "0.7.0") (pyvenv "1.20") (yasnippet "0.13.0") (s "1.12.0")) ;; 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 . ;;; Commentary: ;; Copy of the content necessary to run elpy-test--current-test-name function from the Elpy project (defun elpy-library-root () "Return the root of the Python package chain of the current buffer. That is, if you have /foo/package/module.py, it will return /foo, so that import package.module will pick up module.py." (locate-dominating-file default-directory (lambda (dir) (not (file-exists-p (format "%s/__init__.py" dir)))))) (defcustom elpy-test-runner 'elpy-test-discover-runner "The test runner to use to run tests." :type '(choice (const :tag "Unittest Discover" elpy-test-discover-runner) (const :tag "Green" elpy-test-green-runner) (const :tag "Django Discover" elpy-test-django-runner) (const :tag "Nose" elpy-test-nose-runner) (const :tag "py.test" elpy-test-pytest-runner) (const :tag "Twisted Trial" elpy-test-trial-runner)) :safe 'elpy-test-runner-p :group 'elpy) (defcustom elpy-test-discover-runner-command '("python-shell-interpreter" "-m" "unittest") "The command to use for `elpy-test-discover-runner'. If the string \"python-shell-interpreter\" is present, it will be replaced with the value of `python-shell-interpreter'." :type '(repeat string) :group 'elpy) (defcustom elpy-test-green-runner-command '("green") "The command to use for `elpy-test-green-runner'." :type '(repeat string) :group 'elpy) (defcustom elpy-test-nose-runner-command '("nosetests") "The command to use for `elpy-test-nose-runner'." :type '(repeat string) :group 'elpy) (defcustom elpy-test-trial-runner-command '("trial") "The command to use for `elpy-test-trial-runner'." :type '(repeat string) :group 'elpy) (defcustom elpy-test-pytest-runner-command '("py.test") "The command to use for `elpy-test-pytest-runner'." :type '(repeat string) :group 'elpy) (defcustom elpy-test-compilation-function 'compile "Function used by `elpy-test-run' to run a test command. The function should behave similarly to `compile'. Another good option is `pdb'." :type 'string :group 'elpy) (defvar elpy-set-test-runner-history nil "History variable for `elpy-set-test-runner'.") (defun elpy-test (&optional test-whole-project) "Run tests on the current test, or the whole project. If there is a test at point, run that test. If not, or if a prefix is given, run all tests in the current project." (interactive "P") (let ((current-test (elpy-test-at-point))) (if test-whole-project ;; With prefix arg, test the whole project. (funcall elpy-test-runner (car current-test) nil nil nil) ;; Else, run only this test (apply elpy-test-runner current-test)))) (defun elpy-set-test-runner (test-runner) "Tell Elpy to use TEST-RUNNER to run tests. See `elpy-test' for how to invoke it." (interactive (list (let* ((runners (mapcar (lambda (value) (cons (nth 2 value) (nth 3 value))) (cdr (get 'elpy-test-runner 'custom-type)))) (current (cdr (assq elpy-test-runner (mapcar (lambda (cell) (cons (cdr cell) (car cell))) runners)))) (choice (completing-read (if current (format "Test runner (currently %s): " current) "Test runner: ") runners nil t nil 'elpy-set-test-runner-history))) (if (equal choice "") elpy-test-runner (cdr (assoc choice runners)))))) (setq elpy-test-runner test-runner)) (defun elpy-test-at-point () "Return a list specifying the test at point, if any. This is used as the interactive This list has four elements. - Top level directory: All test files should be importable from here. - Test file: The current file name. - Test module: The module name, relative to the top level directory. - Test name: The full name of the current test within the module, for example TestClass.test_method If there is no test at point, test name is nil. If the current buffer is not visiting a file, only the top level directory is not nil." (if (not buffer-file-name) (progn (save-some-buffers) (list (elpy-library-root) nil nil nil)) (let* ((top (elpy-library-root)) (file buffer-file-name) (module (elpy-test--module-name-for-file top file)) (test (elpy-test--current-test-name))) (if (and file (string-match "test" (or module test ""))) (progn (save-buffer) (list top file module test)) (save-some-buffers) (list top nil nil nil))))) (defun elpy-test--current-test-name () "Return the name of the test at point." (let ((name (python-info-current-defun))) (if (and name (string-match "\\`\\([^.]+\\.[^.]+\\)\\." name)) (match-string 1 name) name))) (defun elpy-test--module-name-for-file (top-level module-file) "Return the module name relative to TOP-LEVEL for MODULE-FILE. For example, for a top level of /project/root/ and a module file of /project/root/package/module.py, this would return \"package.module\"." (let* ((relative-name (file-relative-name module-file top-level)) (no-extension (replace-regexp-in-string "\\.py\\'" "" relative-name)) (no-init (replace-regexp-in-string "/__init__\\'" "" no-extension)) (dotted (replace-regexp-in-string "/" "." no-init))) (if (string-match "^\\." dotted) (concat "." (replace-regexp-in-string (regexp-quote "...") "." dotted)) dotted))) (defun elpy-test-runner-p (obj) "Return t iff OBJ is a test runner. This uses the `elpy-test-runner-p' symbol property." (get obj 'elpy-test-runner-p)) (defun elpy-test-run (working-directory command &rest args) "Run COMMAND with ARGS in WORKING-DIRECTORY as a test command." (let ((default-directory working-directory)) (funcall elpy-test-compilation-function (mapconcat #'shell-quote-argument (cons command args) " ")))) (defun elpy-test-get-discover-runner () "Return the test discover runner from `elpy-test-discover-runner-command'." (cl-loop for string in elpy-test-discover-runner-command if (string= string "python-shell-interpreter") collect python-shell-interpreter else collect string)) (defun elpy-test-discover-runner (top _file module test) "Test the project using the python unittest discover runner. This requires Python 2.7 or later." (interactive (elpy-test-at-point)) (let ((test (cond (test (format "%s.%s" module test)) (module module) (t "discover")))) (apply #'elpy-test-run top (append (elpy-test-get-discover-runner) (list test))))) (put 'elpy-test-discover-runner 'elpy-test-runner-p t) (defun elpy-test-green-runner (top _file module test) "Test the project using the green runner." (interactive (elpy-test-at-point)) (let* ((test (cond (test (format "%s.%s" module test)) (module module))) (command (if test (append elpy-test-green-runner-command (list test)) elpy-test-green-runner-command))) (apply #'elpy-test-run top command))) (put 'elpy-test-green-runner 'elpy-test-runner-p t) (defun elpy-test-nose-runner (top _file module test) "Test the project using the nose test runner. This requires the nose package to be installed." (interactive (elpy-test-at-point)) (if module (apply #'elpy-test-run top (append elpy-test-nose-runner-command (list (if test (format "%s:%s" module test) module)))) (apply #'elpy-test-run top elpy-test-nose-runner-command))) (put 'elpy-test-nose-runner 'elpy-test-runner-p t) (defun elpy-test-trial-runner (top _file module test) "Test the project using Twisted's Trial test runner. This requires the twisted-core package to be installed." (interactive (elpy-test-at-point)) (if module (apply #'elpy-test-run top (append elpy-test-trial-runner-command (list (if test (format "%s.%s" module test) module)))) (apply #'elpy-test-run top elpy-test-trial-runner-command))) (put 'elpy-test-trial-runner 'elpy-test-runner-p t) (defun elpy-test-pytest-runner (top file module test) "Test the project using the py.test test runner. This requires the pytest package to be installed." (interactive (elpy-test-at-point)) (cond (test (let ((test-list (split-string test "\\."))) (apply #'elpy-test-run top (append elpy-test-pytest-runner-command (list (mapconcat #'identity (cons file test-list) "::")))))) (module (apply #'elpy-test-run top (append elpy-test-pytest-runner-command (list file)))) (t (apply #'elpy-test-run top elpy-test-pytest-runner-command)))) (put 'elpy-test-pytest-runner 'elpy-test-runner-p t)