summaryrefslogtreecommitdiff
path: root/test/lisp/progmodes/python-tests.el
diff options
context:
space:
mode:
Diffstat (limited to 'test/lisp/progmodes/python-tests.el')
-rw-r--r--test/lisp/progmodes/python-tests.el1463
1 files changed, 1359 insertions, 104 deletions
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index a59885637e9..fdaedb5fd7a 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -1,6 +1,6 @@
-;;; python-tests.el --- Test suite for python.el
+;;; python-tests.el --- Test suite for python.el -*- lexical-binding:t -*-
-;; Copyright (C) 2013-2017 Free Software Foundation, Inc.
+;; Copyright (C) 2013-2022 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
@@ -22,6 +22,7 @@
;;; Code:
(require 'ert)
+(require 'ert-x)
(require 'python)
;; Dependencies for testing:
@@ -48,17 +49,17 @@ BODY is code to be executed within the temp buffer. Point is
always located at the beginning of buffer."
(declare (indent 1) (debug t))
;; temp-file never actually used for anything?
- `(let* ((temp-file (make-temp-file "python-tests" nil ".py"))
- (buffer (find-file-noselect temp-file))
- (python-indent-guess-indent-offset nil))
- (unwind-protect
- (with-current-buffer buffer
- (python-mode)
- (insert ,contents)
- (goto-char (point-min))
- ,@body)
- (and buffer (kill-buffer buffer))
- (delete-file temp-file))))
+ `(ert-with-temp-file temp-file
+ :suffix "-python.py"
+ (let ((buffer (find-file-noselect temp-file))
+ (python-indent-guess-indent-offset nil))
+ (unwind-protect
+ (with-current-buffer buffer
+ (python-mode)
+ (insert ,contents)
+ (goto-char (point-min))
+ ,@body)
+ (and buffer (kill-buffer buffer))))))
(defun python-tests-look-at (string &optional num restore-point)
"Move point at beginning of STRING in the current buffer.
@@ -94,6 +95,33 @@ STRING, it is skipped so the next STRING occurrence is selected."
found-point
(and restore-point (goto-char starting-point)))))
+(defun python-tests-assert-faces (content faces)
+ "Assert that font faces for CONTENT are equal to FACES."
+ (python-tests-with-temp-buffer content
+ (font-lock-ensure nil nil)
+ (should (equal faces (python-tests-get-buffer-faces)))))
+
+(defun python-tests-get-buffer-faces ()
+ "Return a list of (position . face) for each face change positions."
+ (cl-loop for pos = (point-min)
+ then (next-single-property-change pos 'face)
+ while pos
+ collect (cons pos (get-text-property pos 'face))))
+
+(defun python-tests-assert-faces-after-change (content faces search replace)
+ "Assert that font faces for CONTENT are equal to FACES after change.
+All occurrences of SEARCH are changed to REPLACE."
+ (python-tests-with-temp-buffer
+ content
+ ;; Force enable font-lock mode without jit-lock.
+ (rename-buffer "*python-font-lock-test*" t)
+ (let (noninteractive font-lock-support-mode)
+ (font-lock-mode))
+ (while
+ (re-search-forward search nil t)
+ (replace-match replace))
+ (should (equal faces (python-tests-get-buffer-faces)))))
+
(defun python-tests-self-insert (char-or-str)
"Call `self-insert-command' for chars in CHAR-OR-STR."
(let ((chars
@@ -118,7 +146,6 @@ Argument MIN and MAX delimit the region to be returned and
default to `point-min' and `point-max' respectively."
(let* ((min (or min (point-min)))
(max (or max (point-max)))
- (buffer (current-buffer))
(buffer-contents (buffer-substring-no-properties min max))
(overlays
(sort (overlays-in min max)
@@ -134,6 +161,16 @@ default to `point-min' and `point-max' respectively."
(overlay-end overlay))))
(buffer-substring-no-properties (point-min) (point-max)))))
+(defun python-virt-bin (&optional virt-root)
+ "Return the virtualenv bin dir, starting from VIRT-ROOT.
+If nil, VIRT-ROOT defaults to `python-shell-virtualenv-root'.
+The name of this directory depends on `system-type'."
+ (expand-file-name
+ (concat
+ (file-name-as-directory (or virt-root
+ python-shell-virtualenv-root))
+ (if (eq system-type 'windows-nt) "Scripts" "bin"))))
+
;;; Tests for your tests, so you can test while you test.
@@ -144,7 +181,7 @@ default to `point-min' and `point-max' respectively."
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua."
(let ((expected (save-excursion
- (dotimes (i 3)
+ (dotimes (_ 3)
(re-search-forward "et" nil t))
(forward-char -2)
(point))))
@@ -153,7 +190,7 @@ aliqua."
;; one should be returned.
(should (= (python-tests-look-at "et" 6 t) expected))
;; If already looking at STRING, it should skip it.
- (dotimes (i 2) (re-search-forward "et"))
+ (dotimes (_ 2) (re-search-forward "et"))
(forward-char -2)
(should (= (python-tests-look-at "et") expected)))))
@@ -168,7 +205,7 @@ aliqua."
(re-search-forward "et" nil t)
(forward-char -2)
(point))))
- (dotimes (i 3)
+ (dotimes (_ 3)
(re-search-forward "et" nil t))
(should (= (python-tests-look-at "et" -3 t) expected))
(should (= (python-tests-look-at "et" -6 t) expected)))))
@@ -184,7 +221,6 @@ aliqua."
(ert-deftest python-syntax-after-python-backspace ()
;; `python-indent-dedent-line-backspace' garbles syntax
- :expected-result :failed
(python-tests-with-temp-buffer
"\"\"\""
(goto-char (point-max))
@@ -192,10 +228,435 @@ aliqua."
(should (string= (buffer-string) "\"\""))
(should (null (nth 3 (syntax-ppss))))))
+(ert-deftest python-font-lock-keywords-level-1-1 ()
+ (python-tests-assert-faces
+ "def func():"
+ '((1 . font-lock-keyword-face) (4)
+ (5 . font-lock-function-name-face) (9))))
+
+(ert-deftest python-font-lock-keywords-level-1-2 ()
+ "Invalid function name should not be font-locked."
+ (python-tests-assert-faces
+ "def 1func():"
+ '((1 . font-lock-keyword-face) (4))))
+
+(ert-deftest python-font-lock-keywords-level-1-3 ()
+ (python-tests-assert-faces
+ "def \\
+ func():"
+ '((1 . font-lock-keyword-face) (4)
+ (15 . font-lock-function-name-face) (19))))
+
+(ert-deftest python-font-lock-assignment-statement-1 ()
+ (python-tests-assert-faces
+ "a, b, c = 1, 2, 3"
+ '((1 . font-lock-variable-name-face) (2)
+ (4 . font-lock-variable-name-face) (5)
+ (7 . font-lock-variable-name-face) (8))))
+
+(ert-deftest python-font-lock-assignment-statement-2 ()
+ (python-tests-assert-faces
+ "a, *b, c = 1, 2, 3, 4, 5"
+ '((1 . font-lock-variable-name-face) (2)
+ (5 . font-lock-variable-name-face) (6)
+ (8 . font-lock-variable-name-face) (9))))
+
+(ert-deftest python-font-lock-assignment-statement-3 ()
+ (python-tests-assert-faces
+ "[a, b] = (1, 2)"
+ '((1)
+ (2 . font-lock-variable-name-face) (3)
+ (5 . font-lock-variable-name-face) (6))))
+
+(ert-deftest python-font-lock-assignment-statement-4 ()
+ (python-tests-assert-faces
+ "(l[1], l[2]) = (10, 11)"
+ '((1)
+ (2 . font-lock-variable-name-face) (3)
+ (8 . font-lock-variable-name-face) (9))))
+
+(ert-deftest python-font-lock-assignment-statement-5 ()
+ (python-tests-assert-faces
+ "(a, b, c, *d) = *x, y = 5, 6, 7, 8, 9"
+ '((1)
+ (2 . font-lock-variable-name-face) (3)
+ (5 . font-lock-variable-name-face) (6)
+ (8 . font-lock-variable-name-face) (9)
+ (12 . font-lock-variable-name-face) (13)
+ (18 . font-lock-variable-name-face) (19)
+ (21 . font-lock-variable-name-face) (22))))
+
+(ert-deftest python-font-lock-assignment-statement-6 ()
+ (python-tests-assert-faces
+ "(a,) = 'foo',"
+ '((1)
+ (2 . font-lock-variable-name-face) (3)
+ (8 . font-lock-string-face) (13))))
+
+(ert-deftest python-font-lock-assignment-statement-7 ()
+ (python-tests-assert-faces
+ "(*a,) = ['foo', 'bar', 'baz']"
+ '((1)
+ (3 . font-lock-variable-name-face) (4)
+ (10 . font-lock-string-face) (15)
+ (17 . font-lock-string-face) (22)
+ (24 . font-lock-string-face) (29))))
+
+(ert-deftest python-font-lock-assignment-statement-8 ()
+ (python-tests-assert-faces
+ "d = D('a', ['b'], 'c')"
+ '((1 . font-lock-variable-name-face) (2)
+ (7 . font-lock-string-face) (10)
+ (13 . font-lock-string-face) (16)
+ (19 . font-lock-string-face) (22))))
+
+(ert-deftest python-font-lock-assignment-statement-9 ()
+ (python-tests-assert-faces
+ "d.x, d.y[0], *d.z = 'a', 'b', 'c', 'd', 'e'"
+ '((1)
+ (3 . font-lock-variable-name-face) (4)
+ (8 . font-lock-variable-name-face) (9)
+ (17 . font-lock-variable-name-face) (18)
+ (21 . font-lock-string-face) (24)
+ (26 . font-lock-string-face) (29)
+ (31 . font-lock-string-face) (34)
+ (36 . font-lock-string-face) (39)
+ (41 . font-lock-string-face))))
+
+(ert-deftest python-font-lock-assignment-statement-10 ()
+ (python-tests-assert-faces
+ "a: int = 5"
+ '((1 . font-lock-variable-name-face) (2)
+ (4 . font-lock-builtin-face) (7))))
+
+(ert-deftest python-font-lock-assignment-statement-11 ()
+ (python-tests-assert-faces
+ "b: Tuple[Optional[int], Union[Sequence[str], str]] = (None, 'foo')"
+ '((1 . font-lock-variable-name-face) (2)
+ (19 . font-lock-builtin-face) (22)
+ (40 . font-lock-builtin-face) (43)
+ (46 . font-lock-builtin-face) (49)
+ (55 . font-lock-constant-face) (59)
+ (61 . font-lock-string-face) (66))))
+
+(ert-deftest python-font-lock-assignment-statement-12 ()
+ (python-tests-assert-faces
+ "c: Collection = {1, 2, 3}"
+ '((1 . font-lock-variable-name-face) (2))))
+
+(ert-deftest python-font-lock-assignment-statement-13 ()
+ (python-tests-assert-faces
+ "d: Mapping[int, str] = {1: 'bar', 2: 'baz'}"
+ '((1 . font-lock-variable-name-face) (2)
+ (12 . font-lock-builtin-face) (15)
+ (17 . font-lock-builtin-face) (20)
+ (28 . font-lock-string-face) (33)
+ (38 . font-lock-string-face) (43))))
+
+(ert-deftest python-font-lock-assignment-statement-14 ()
+ (python-tests-assert-faces
+ "(a) = 5; (b) = 6"
+ '((1)
+ (2 . font-lock-variable-name-face) (3)
+ (11 . font-lock-variable-name-face) (12))))
+
+(ert-deftest python-font-lock-assignment-statement-15 ()
+ (python-tests-assert-faces
+ "[a] = 5,; [b] = 6,"
+ '((1)
+ (2 . font-lock-variable-name-face) (3)
+ (12 . font-lock-variable-name-face) (13))))
+
+(ert-deftest python-font-lock-assignment-statement-16 ()
+ (python-tests-assert-faces
+ "[*a] = 5, 6; [*b] = 7, 8"
+ '((1)
+ (3 . font-lock-variable-name-face) (4)
+ (16 . font-lock-variable-name-face) (17))))
+
+(ert-deftest python-font-lock-assignment-statement-17 ()
+ (python-tests-assert-faces
+ "(a) = (b) = 1"
+ `((1)
+ (2 . font-lock-variable-name-face) (3)
+ (8 . font-lock-variable-name-face) (9))))
+
+(ert-deftest python-font-lock-assignment-statement-18 ()
+ (python-tests-assert-faces
+ "CustomInt = int
+
+def f(x: CustomInt) -> CustomInt:
+ y = x + 1
+ ys: Sequence[CustomInt] = [y, y + 1]
+ res: CustomInt = sum(ys) + 1
+ return res
+"
+ '((1 . font-lock-variable-name-face) (10)
+ (13 . font-lock-builtin-face) (16)
+ (18 . font-lock-keyword-face) (21)
+ (22 . font-lock-function-name-face) (23)
+ (56 . font-lock-variable-name-face) (57)
+ (70 . font-lock-variable-name-face) (72)
+ (111 . font-lock-variable-name-face) (114)
+ (128 . font-lock-builtin-face) (131)
+ (144 . font-lock-keyword-face) (150))))
+
+(ert-deftest python-font-lock-assignment-statement-multiline-1 ()
+ (python-tests-assert-faces-after-change
+ "
+[
+ a,
+ b
+] # (
+ 1,
+ 2
+)
+"
+ '((1)
+ (8 . font-lock-variable-name-face) (9)
+ (15 . font-lock-variable-name-face) (16))
+ "#" "="))
+
+(ert-deftest python-font-lock-assignment-statement-multiline-2 ()
+ (python-tests-assert-faces-after-change
+ "
+[
+ *a
+] # 5, 6
+"
+ '((1)
+ (9 . font-lock-variable-name-face) (10))
+ "#" "="))
+
+(ert-deftest python-font-lock-assignment-statement-multiline-3 ()
+ (python-tests-assert-faces-after-change
+ "a\\
+ ,\\
+ b\\
+ ,\\
+ c\\
+ #\\
+ 1\\
+ ,\\
+ 2\\
+ ,\\
+ 3"
+ '((1 . font-lock-variable-name-face) (2)
+ (15 . font-lock-variable-name-face) (16)
+ (29 . font-lock-variable-name-face) (30))
+ "#" "="))
+
+(ert-deftest python-font-lock-assignment-statement-multiline-4 ()
+ (python-tests-assert-faces-after-change
+ "a\\
+ :\\
+ int\\
+ #\\
+ 5"
+ '((1 . font-lock-variable-name-face) (2)
+ (15 . font-lock-builtin-face) (18))
+ "#" "="))
+
+(ert-deftest python-font-lock-assignment-statement-multiline-5 ()
+ (python-tests-assert-faces-after-change
+ "(\\
+ a\\
+)\\
+ #\\
+ 5\\
+ ;\\
+ (\\
+ b\\
+ )\\
+ #\\
+ 6"
+ '((1)
+ (8 . font-lock-variable-name-face) (9)
+ (46 . font-lock-variable-name-face) (47))
+ "#" "="))
+
+(ert-deftest python-font-lock-assignment-statement-multiline-6 ()
+ (python-tests-assert-faces-after-change
+ "(
+ a
+)\\
+ #\\
+ 5\\
+ ;\\
+ (
+ b
+ )\\
+ #\\
+ 6"
+ '((1)
+ (7 . font-lock-variable-name-face) (8)
+ (43 . font-lock-variable-name-face) (44))
+ "#" "="))
+
+(ert-deftest python-font-lock-escape-sequence-string-newline ()
+ (python-tests-assert-faces
+ "'\\n'
+\"\\n\"
+f'\\n'
+f\"\\n\"
+u'\\n'
+u\"\\n\""
+ '((1 . font-lock-doc-face)
+ (2 . font-lock-constant-face)
+ (4 . font-lock-doc-face) (5)
+ (6 . font-lock-doc-face)
+ (7 . font-lock-constant-face)
+ (9 . font-lock-doc-face) (10)
+ (12 . font-lock-string-face)
+ (13 . font-lock-constant-face)
+ (15 . font-lock-string-face) (16)
+ (18 . font-lock-string-face)
+ (19 . font-lock-constant-face)
+ (21 . font-lock-string-face) (22)
+ (24 . font-lock-string-face)
+ (25 . font-lock-constant-face)
+ (27 . font-lock-string-face) (28)
+ (30 . font-lock-string-face)
+ (31 . font-lock-constant-face)
+ (33 . font-lock-string-face))))
+
+(ert-deftest python-font-lock-escape-sequence-multiline-string ()
+ (python-tests-assert-faces
+ (let ((escape-sequences "\\x12 \123 \\n \\u1234 \\U00010348 \\N{Plus-Minus Sign}"))
+ (cl-loop for string-prefix in '("" "f" "rf" "fr" "r" "rb" "br" "b")
+ concat (cl-loop for quote-string in '("\"\"\"" "'''")
+ concat (concat string-prefix
+ quote-string
+ escape-sequences
+ quote-string
+ "\n"))))
+ '((1 . font-lock-doc-face)
+ (4 . font-lock-constant-face)
+ (8 . font-lock-doc-face)
+ (11 . font-lock-constant-face)
+ (13 . font-lock-doc-face)
+ (14 . font-lock-constant-face)
+ (20 . font-lock-doc-face)
+ (21 . font-lock-constant-face)
+ (31 . font-lock-doc-face)
+ (32 . font-lock-constant-face)
+ (51 . font-lock-doc-face) (54)
+ (55 . font-lock-doc-face)
+ (58 . font-lock-constant-face)
+ (62 . font-lock-doc-face)
+ (65 . font-lock-constant-face)
+ (67 . font-lock-doc-face)
+ (68 . font-lock-constant-face)
+ (74 . font-lock-doc-face)
+ (75 . font-lock-constant-face)
+ (85 . font-lock-doc-face)
+ (86 . font-lock-constant-face)
+ (105 . font-lock-doc-face) (108)
+ (110 . font-lock-string-face)
+ (113 . font-lock-constant-face)
+ (117 . font-lock-string-face)
+ (120 . font-lock-constant-face)
+ (122 . font-lock-string-face)
+ (123 . font-lock-constant-face)
+ (129 . font-lock-string-face)
+ (130 . font-lock-constant-face)
+ (140 . font-lock-string-face)
+ (141 . font-lock-constant-face)
+ (160 . font-lock-string-face) (163)
+ (165 . font-lock-string-face)
+ (168 . font-lock-constant-face)
+ (172 . font-lock-string-face)
+ (175 . font-lock-constant-face)
+ (177 . font-lock-string-face)
+ (178 . font-lock-constant-face)
+ (184 . font-lock-string-face)
+ (185 . font-lock-constant-face)
+ (195 . font-lock-string-face)
+ (196 . font-lock-constant-face)
+ (215 . font-lock-string-face) (218)
+ (221 . font-lock-string-face) (254)
+ (271 . font-lock-string-face) (274)
+ (277 . font-lock-string-face) (310)
+ (327 . font-lock-string-face) (330)
+ (333 . font-lock-string-face) (366)
+ (383 . font-lock-string-face) (386)
+ (389 . font-lock-string-face) (422)
+ (439 . font-lock-string-face) (442)
+ (444 . font-lock-string-face) (497)
+ (499 . font-lock-string-face) (552)
+ (555 . font-lock-string-face) (608)
+ (611 . font-lock-string-face) (664)
+ (667 . font-lock-string-face) (720)
+ (723 . font-lock-string-face) (776)
+ (778 . font-lock-string-face)
+ (781 . font-lock-constant-face)
+ (785 . font-lock-string-face)
+ (788 . font-lock-constant-face)
+ (790 . font-lock-string-face) (831)
+ (833 . font-lock-string-face)
+ (836 . font-lock-constant-face)
+ (840 . font-lock-string-face)
+ (843 . font-lock-constant-face)
+ (845 . font-lock-string-face) (886))))
+
+(ert-deftest python-font-lock-escape-sequence-bytes-newline ()
+ (python-tests-assert-faces
+ "b'\\n'
+b\"\\n\""
+ '((1)
+ (2 . font-lock-doc-face)
+ (3 . font-lock-constant-face)
+ (5 . font-lock-doc-face) (6)
+ (8 . font-lock-doc-face)
+ (9 . font-lock-constant-face)
+ (11 . font-lock-doc-face))))
+
+(ert-deftest python-font-lock-escape-sequence-hex-octal ()
+ (python-tests-assert-faces
+ "b'\\x12 \\777 \\1\\23'
+'\\x12 \\777 \\1\\23'"
+ '((1)
+ (2 . font-lock-doc-face)
+ (3 . font-lock-constant-face)
+ (7 . font-lock-doc-face)
+ (8 . font-lock-constant-face)
+ (12 . font-lock-doc-face)
+ (13 . font-lock-constant-face)
+ (18 . font-lock-doc-face) (19)
+ (20 . font-lock-doc-face)
+ (21 . font-lock-constant-face)
+ (25 . font-lock-doc-face)
+ (26 . font-lock-constant-face)
+ (30 . font-lock-doc-face)
+ (31 . font-lock-constant-face)
+ (36 . font-lock-doc-face))))
+
+(ert-deftest python-font-lock-escape-sequence-unicode ()
+ (python-tests-assert-faces
+ "b'\\u1234 \\U00010348 \\N{Plus-Minus Sign}'
+'\\u1234 \\U00010348 \\N{Plus-Minus Sign}'"
+ '((1)
+ (2 . font-lock-doc-face) (41)
+ (42 . font-lock-doc-face)
+ (43 . font-lock-constant-face)
+ (49 . font-lock-doc-face)
+ (50 . font-lock-constant-face)
+ (60 . font-lock-doc-face)
+ (61 . font-lock-constant-face)
+ (80 . font-lock-doc-face))))
+
+(ert-deftest python-font-lock-raw-escape-sequence ()
+ (python-tests-assert-faces
+ "rb'\\x12 \123 \\n'
+r'\\x12 \123 \\n \\u1234 \\U00010348 \\N{Plus-Minus Sign}'"
+ '((1)
+ (3 . font-lock-doc-face) (14)
+ (16 . font-lock-doc-face))))
+
;;; Indentation
-;; See: http://www.python.org/dev/peps/pep-0008/#indentation
+;; See: https://www.python.org/dev/peps/pep-0008/#indentation
(ert-deftest python-indent-pep8-1 ()
"First pep8 case."
@@ -260,6 +721,19 @@ foo = long_function_name(
(should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 4))))
+(ert-deftest python-indent-hanging-close-paren ()
+ "Like first pep8 case, but with hanging close paren." ;; See Bug#20742.
+ (python-tests-with-temp-buffer
+ "\
+foo = long_function_name(var_one, var_two,
+ var_three, var_four
+ )
+"
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at ")")
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
+ (should (= (python-indent-calculate-indentation) 25))))
+
(ert-deftest python-indent-base-case ()
"Check base case does not trigger errors."
(python-tests-with-temp-buffer
@@ -317,7 +791,7 @@ def func(arg):
# I don't do much
return arg
# This comment is badly indented because the user forced so.
- # At this line python.el wont dedent, user is always right.
+ # At this line python.el won't dedent, user is always right.
comment_wins_over_ender = True
@@ -336,7 +810,7 @@ comment_wins_over_ender = True
;; The return keyword do make indentation lose a level...
(should (= (python-indent-calculate-indentation) 0))
;; ...but the current indentation was forced by the user.
- (python-tests-look-at "# At this line python.el wont dedent")
+ (python-tests-look-at "# At this line python.el won't dedent")
(should (eq (car (python-indent-context)) :after-comment))
(should (= (python-indent-calculate-indentation) 4))
;; Should behave the same for blank lines: potentially a comment.
@@ -780,6 +1254,25 @@ def delete_all_things():
:after-backslash-dotted-continuation))
(should (= (python-indent-calculate-indentation) 16))))
+(ert-deftest python-indent-after-backslash-6 ()
+ "Backslash continuation from for block."
+ (python-tests-with-temp-buffer
+ "
+for long_variable_name \\
+ in (1, 2):
+ print(long_variable_name)
+"
+ (python-tests-look-at "for long_variable_name \\")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "in (1, 2):")
+ (should (eq (car (python-indent-context))
+ :after-backslash-block-continuation))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "print(long_variable_name)")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
(ert-deftest python-indent-block-enders-1 ()
"Test de-indentation for pass keyword."
(python-tests-with-temp-buffer
@@ -921,6 +1414,35 @@ if save:
(python-indent-line t)
(should (= (python-indent-calculate-indentation t) 8))))
+(ert-deftest python-indent-dedenters-comment-else ()
+ "Test de-indentation for the else keyword with comments before it."
+ (python-tests-with-temp-buffer
+ "
+if save:
+ try:
+ write_to_disk(data)
+ except IOError:
+ msg = 'Error saving to disk'
+ message(msg)
+ logger.exception(msg)
+ except Exception:
+ if hide_details:
+ logger.exception('Unhandled exception')
+ # comment
+ else
+ finally:
+ data.free()
+"
+ (python-tests-look-at "else\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 0))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 8))))
+
(ert-deftest python-indent-dedenters-3 ()
"Test de-indentation for the except keyword."
(python-tests-with-temp-buffer
@@ -1109,6 +1631,37 @@ def fn(a, b, c=True):
(should (eq (car (python-indent-context)) :inside-string))
(should (= (python-indent-calculate-indentation) 4))))
+(ert-deftest python-indent-electric-comma-inside-multiline-string ()
+ "Test indentation ...."
+ (python-tests-with-temp-buffer
+ "
+a = (
+ '''\
+- foo,
+- bar
+'''
+"
+ (python-tests-look-at "- bar")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (goto-char (pos-eol))
+ (python-tests-self-insert ",")
+ (should (= (current-indentation) 0))))
+
+(ert-deftest python-indent-electric-comma-after-multiline-string ()
+ "Test indentation ...."
+ (python-tests-with-temp-buffer
+ "
+a = (
+ '''\
+- foo,
+- bar'''
+"
+ (python-tests-look-at "- bar'''")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (goto-char (pos-eol))
+ (python-tests-self-insert ",")
+ (should (= (current-indentation) 0))))
+
(ert-deftest python-indent-electric-colon-1 ()
"Test indentation case from Bug#18228."
(python-tests-with-temp-buffer
@@ -1119,7 +1672,7 @@ def a():
def b()
"
(python-tests-look-at "def b()")
- (goto-char (line-end-position))
+ (goto-char (pos-eol))
(python-tests-self-insert ":")
(should (= (current-indentation) 0))))
@@ -1130,10 +1683,13 @@ def b()
if do:
something()
else
+outside
"
(python-tests-look-at "else")
- (goto-char (line-end-position))
+ (goto-char (pos-eol))
(python-tests-self-insert ":")
+ (should (= (current-indentation) 0))
+ (python-tests-look-at "outside")
(should (= (current-indentation) 0))))
(ert-deftest python-indent-electric-colon-3 ()
@@ -1147,7 +1703,7 @@ if do:
that)
"
(python-tests-look-at "that)")
- (goto-char (line-end-position))
+ (goto-char (pos-eol))
(python-tests-self-insert ":")
(python-tests-look-at "elif" -1)
(should (= (current-indentation) 0))
@@ -1172,7 +1728,7 @@ def f():
else
"
(python-tests-look-at "else")
- (goto-char (line-end-position))
+ (goto-char (pos-eol))
(python-tests-self-insert ":")
(python-tests-look-at "else" -1)
(should (= (current-indentation) 4))))
@@ -1293,6 +1849,60 @@ this is an arbitrarily
(should (string= (buffer-substring-no-properties (point-min) (point-max))
expected)))))
+(ert-deftest python-indent-after-match-block ()
+ "Test PEP634 match."
+ (python-tests-with-temp-buffer
+ "
+match foo:
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-after-case-block ()
+ "Test PEP634 case."
+ (python-tests-with-temp-buffer
+ "
+match foo:
+ case 1:
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 8))))
+
+
+;;; Filling
+
+(ert-deftest python-auto-fill-docstring ()
+ (python-tests-with-temp-buffer
+ "\
+def some_function(arg1,
+ arg2):
+ \"\"\"
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
+ (auto-fill-mode +1)
+ (goto-char (point-max))
+ (newline)
+ (search-backward "Lorem")
+ (let ((docindent (current-indentation)))
+ (forward-line 1)
+ (should (= docindent (current-indentation))))))
+
+(ert-deftest python-fill-docstring ()
+ (python-tests-with-temp-buffer
+ "\
+r'''aaa
+
+this is a test this is a test this is a test this is a test this is a test this is a test.
+'''"
+ (search-forward "test.")
+ (fill-paragraph)
+ (should (= (current-indentation) 0))))
+
;;; Mark
@@ -1378,7 +1988,7 @@ class C:
(expected-mark-beginning-position
(progn
(python-tests-look-at "def __init__(self):")
- (1- (line-beginning-position))))
+ (1- (pos-bol))))
(expected-mark-end-position-1
(save-excursion
(python-tests-look-at "self.b = 'b'")
@@ -1435,7 +2045,7 @@ class C:
(progn
(python-tests-look-at "def fun(self):")
(python-tests-look-at "(self):")
- (1- (line-beginning-position))))
+ (1- (pos-bol))))
(expected-mark-end-position
(save-excursion
(python-tests-look-at "return self.b")
@@ -1447,6 +2057,57 @@ class C:
(should (= (marker-position (mark-marker))
expected-mark-end-position)))))
+(ert-deftest python-mark-defun-4 ()
+ "Test `python-mark-defun' with nested functions."
+ (python-tests-with-temp-buffer
+ "
+def foo(x):
+ def bar():
+ return x
+ if True:
+ return bar
+"
+ (let ((expected-mark-beginning-position
+ (progn
+ (python-tests-look-at "def foo(x):")
+ (1- (pos-bol))))
+ (expected-mark-end-position (point-max)))
+ (python-tests-look-at "return bar")
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position)))))
+
+(ert-deftest python-mark-defun-5 ()
+ "Test `python-mark-defun' with point inside backslash escaped defun."
+ (python-tests-with-temp-buffer
+ "
+def \\
+ foo(x):
+ return x
+"
+ (let ((transient-mark-mode t)
+ (expected-mark-beginning-position
+ (progn
+ (python-tests-look-at "def ")
+ (1- (pos-bol))))
+ (expected-mark-end-position
+ (save-excursion
+ (python-tests-look-at "return x")
+ (forward-line)
+ (point))))
+ (python-tests-look-at "def ")
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position))
+ (deactivate-mark)
+ (python-tests-look-at "foo(x)")
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position)))))
+
;;; Navigation
@@ -1473,12 +2134,20 @@ def decoratorFunctionWithArguments(arg1, arg2, arg3):
return wrapped_f
return wwrap
"
- (python-tests-look-at "return wrap")
+ (python-tests-look-at "return wwrap")
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def decoratorFunctionWithArguments" -1)
+ (beginning-of-line)
+ (point))))
+ (python-tests-look-at "return wrap" -1)
(should (= (save-excursion
(python-nav-beginning-of-defun)
(point))
(save-excursion
- (python-tests-look-at "def wrapped_f(*args):" -1)
+ (python-tests-look-at "def wwrap(f):" -1)
(beginning-of-line)
(point))))
(python-tests-look-at "def wrapped_f(*args):" -1)
@@ -1512,6 +2181,9 @@ class C(object):
def a():
pass
+ if True:
+ return a
+
def c(self):
pass
"
@@ -1524,7 +2196,25 @@ class C(object):
(python-tests-look-at "def m(self):" -1)
(beginning-of-line)
(point))))
+ ;; Nested defuns should be skipped.
+ (forward-line -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def m(self):" -1)
+ (beginning-of-line)
+ (point))))
;; Defuns on same levels should be respected.
+ (python-tests-look-at "if True:" -1)
+ (forward-line -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def a():" -1)
+ (beginning-of-line)
+ (point))))
(python-tests-look-at "def a():" -1)
(should (= (save-excursion
(python-nav-beginning-of-defun)
@@ -1533,7 +2223,7 @@ class C(object):
(python-tests-look-at "def b():" -1)
(beginning-of-line)
(point))))
- ;; Jump to a top level defun.
+ ;; Jump to an upper level defun.
(python-tests-look-at "def b():" -1)
(should (= (save-excursion
(python-nav-beginning-of-defun)
@@ -1542,6 +2232,14 @@ class C(object):
(python-tests-look-at "def m(self):" -1)
(beginning-of-line)
(point))))
+ (forward-line -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def m(self):" -1)
+ (beginning-of-line)
+ (point))))
;; Jump to a top level defun again.
(python-tests-look-at "def m(self):" -1)
(should (= (save-excursion
@@ -1572,6 +2270,93 @@ class C(object):
(beginning-of-line)
(point))))))
+(ert-deftest python-nav-beginning-of-defun-4 ()
+ (python-tests-with-temp-buffer
+ "
+def a():
+ pass
+
+def \\
+ b():
+ return 0
+
+def c():
+ pass
+"
+ (python-tests-look-at "def c():")
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def \\" -1)
+ (beginning-of-line)
+ (point))))
+ (python-tests-look-at "return 0" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def \\" -1)
+ (beginning-of-line)
+ (point))))
+ (python-tests-look-at "b():" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def \\" -1)
+ (beginning-of-line)
+ (point))))
+ (python-tests-look-at "def a():" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun -1)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def \\")
+ (beginning-of-line)
+ (point))))))
+
+(ert-deftest python-nav-beginning-of-defun-5 ()
+ (python-tests-with-temp-buffer
+ "
+class C:
+
+ def \\
+ m(self):
+ pass
+"
+ (python-tests-look-at "m(self):")
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def \\" -1)
+ (beginning-of-line)
+ (point))))
+ (python-tests-look-at "class C:" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun -1)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def \\")
+ (beginning-of-line)
+ (point))))))
+
+(ert-deftest python-nav-beginning-of-defun-6 ()
+ (python-tests-with-temp-buffer
+ "
+class C:
+ def foo(self):
+ pass
+"
+ (python-tests-look-at "self")
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (beginning-of-line)
+ (point))))))
+
(ert-deftest python-nav-end-of-defun-1 ()
(python-tests-with-temp-buffer
"
@@ -1605,12 +2390,25 @@ class C(object):
(point))))
(should (= (save-excursion
(python-tests-look-at "def b():")
+ (forward-line -1)
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def c(self):")
+ (forward-line -1)
+ (point))))
+ (should (= (save-excursion
+ (python-tests-look-at "def b():")
(python-nav-end-of-defun)
(point))
(save-excursion
(python-tests-look-at "def b():")
(forward-line 2)
(point))))
+ (should (not (save-excursion
+ (python-tests-look-at "def a():")
+ (forward-line -1)
+ (python-nav-end-of-defun))))
(should (= (save-excursion
(python-tests-look-at "def c(self):")
(python-nav-end-of-defun)
@@ -1659,21 +2457,55 @@ def decoratorFunctionWithArguments(arg1, arg2, arg3):
(point))
(save-excursion
(python-tests-look-at "return wwrap")
- (line-beginning-position))))
+ (pos-bol))))
(should (= (save-excursion
(python-tests-look-at "def wrapped_f(*args):")
(python-nav-end-of-defun)
(point))
(save-excursion
(python-tests-look-at "return wrapped_f")
- (line-beginning-position))))
+ (pos-bol))))
(should (= (save-excursion
(python-tests-look-at "f(*args)")
(python-nav-end-of-defun)
(point))
(save-excursion
(python-tests-look-at "return wrapped_f")
- (line-beginning-position))))))
+ (pos-bol))))))
+
+(ert-deftest python-nav-end-of-defun-3 ()
+ (python-tests-with-temp-buffer
+ "
+def \\
+ a():
+ return 0
+"
+ (should (= (save-excursion
+ (python-tests-look-at "def \\")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (point-max))))))
+
+(ert-deftest python-end-of-defun-1 ()
+ (python-tests-with-temp-buffer
+ "
+class C:
+ def a(self
+ ):
+ pass
+
+ def b(self):
+ pass
+"
+ (should (= (save-excursion
+ (python-tests-look-at "def a")
+ (end-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def b")
+ (forward-line -1)
+ (point))))))
(ert-deftest python-nav-backward-defun-1 ()
(python-tests-with-temp-buffer
@@ -1773,6 +2605,18 @@ class A(object):
(should (not (python-nav-backward-defun)))
(should (= point (point))))))
+(ert-deftest python-nav-backward-defun-4 ()
+ (python-tests-with-temp-buffer
+ "
+def \\
+ a():
+ return 0
+"
+ (goto-char (point-max))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at "def \\" -1)))
+ (should (not (python-nav-backward-defun)))))
+
(ert-deftest python-nav-forward-defun-1 ()
(python-tests-with-temp-buffer
"
@@ -1871,6 +2715,18 @@ class A(object):
(should (not (python-nav-forward-defun)))
(should (= point (point))))))
+(ert-deftest python-nav-forward-defun-4 ()
+ (python-tests-with-temp-buffer
+ "
+def \\
+ a():
+ return 0
+"
+ (goto-char (point-min))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "():")))
+ (should (not (python-nav-forward-defun)))))
+
(ert-deftest python-nav-beginning-of-statement-1 ()
(python-tests-with-temp-buffer
"
@@ -1948,14 +2804,14 @@ string
(point))
(save-excursion
(python-tests-look-at "789")
- (line-end-position))))
+ (pos-eol))))
(python-tests-look-at "v2 =")
(should (= (save-excursion
(python-nav-end-of-statement)
(point))
(save-excursion
(python-tests-look-at "value4)")
- (line-end-position))))
+ (pos-eol))))
(python-tests-look-at "v3 =")
(should (= (save-excursion
(python-nav-end-of-statement)
@@ -1963,7 +2819,7 @@ string
(save-excursion
(python-tests-look-at
"'continue previous line')")
- (line-end-position))))
+ (pos-eol))))
(python-tests-look-at "v4 =")
(should (= (save-excursion
(python-nav-end-of-statement)
@@ -1973,6 +2829,12 @@ string
(python-util-forward-comment -1)
(point))))))
+(ert-deftest python-nav-end-of-statement-2 ()
+ "Test the string overlap assertion (Bug#30964)."
+ (python-tests-with-temp-buffer
+ "'\n''\n"
+ (python-nav-end-of-statement)))
+
(ert-deftest python-nav-forward-statement-1 ()
(python-tests-with-temp-buffer
"
@@ -2122,6 +2984,28 @@ def decoratorFunctionWithArguments(arg1, arg2, arg3):
(point))
(python-tests-look-at "def wwrap(f):" -1)))))
+(ert-deftest python-nav-beginning-of-block-2 ()
+ (python-tests-with-temp-buffer
+ "
+if True:
+
+ pass
+if False:
+ # comment
+ pass
+"
+ (python-tests-look-at "if True:")
+ (forward-line)
+ (should (= (save-excursion
+ (python-nav-beginning-of-block)
+ (point))
+ (python-tests-look-at "if True:" -1)))
+ (python-tests-look-at "# comment")
+ (should (= (save-excursion
+ (python-nav-beginning-of-block)
+ (point))
+ (python-tests-look-at "if False:" -1)))))
+
(ert-deftest python-nav-end-of-block-1 ()
(python-tests-with-temp-buffer
"
@@ -2159,21 +3043,33 @@ def decoratorFunctionWithArguments(arg1, arg2, arg3):
(point))
(save-excursion
(python-tests-look-at "return wrapped_f")
- (line-end-position))))
+ (pos-eol))))
(end-of-line)
(should (= (save-excursion
(python-nav-end-of-block)
(point))
(save-excursion
(python-tests-look-at "return wrapped_f")
- (line-end-position))))
+ (pos-eol))))
(python-tests-look-at "f(*args)")
(should (= (save-excursion
(python-nav-end-of-block)
(point))
(save-excursion
(python-tests-look-at "print 'After f(*args)'")
- (line-end-position))))))
+ (pos-eol))))))
+
+(ert-deftest python-nav-end-of-block-2 ()
+ "Ensure that `python-nav-end-of-block' does not enter an infinite loop."
+ (python-tests-with-temp-buffer
+ "def
+ =''
+ '
+\"\"\"\"\"\"
+ #
+''
+"
+ (python-nav-end-of-block)))
(ert-deftest python-nav-forward-block-1 ()
"This also accounts as a test for `python-nav-backward-block'."
@@ -2217,6 +3113,22 @@ if request.user.is_authenticated():
(python-tests-look-at
"if request.user.is_authenticated():" -1)))))
+(ert-deftest python-nav-forward-block-2 ()
+ (python-tests-with-temp-buffer
+ "
+if True:
+ pass
+"
+ (python-tests-look-at "if True:")
+ (should (not (save-excursion (python-nav-forward-block))))
+ (should (not (save-excursion (python-nav-forward-block -1))))
+ (forward-char)
+ (should (not (save-excursion (python-nav-forward-block))))
+ (should (= (save-excursion (python-nav-forward-block -1))
+ (progn
+ (end-of-line)
+ (python-tests-look-at "if True:" -1))))))
+
(ert-deftest python-nav-forward-sexp-1 ()
(python-tests-with-temp-buffer
"
@@ -2435,11 +3347,11 @@ if x:
\tabcdefg
"
(python-tests-look-at "abcdefg")
- (goto-char (line-end-position))
+ (goto-char (pos-eol))
(call-interactively #'python-indent-dedent-line-backspace)
(should
(string= (buffer-substring-no-properties
- (line-beginning-position) (line-end-position))
+ (pos-bol) (pos-eol))
"\tabcdef")))))
(ert-deftest python-indent-dedent-line-backspace-3 ()
@@ -2452,27 +3364,27 @@ if x:
\t abcdefg
"
(python-tests-look-at "abcdefg")
- (goto-char (line-end-position))
+ (goto-char (pos-eol))
(call-interactively #'python-indent-dedent-line-backspace)
(should
(string= (buffer-substring-no-properties
- (line-beginning-position) (line-end-position))
+ (pos-bol) (pos-eol))
"\t abcdef"))
(back-to-indentation)
(call-interactively #'python-indent-dedent-line-backspace)
(should
(string= (buffer-substring-no-properties
- (line-beginning-position) (line-end-position))
+ (pos-bol) (pos-eol))
"\tabcdef"))
(call-interactively #'python-indent-dedent-line-backspace)
(should
(string= (buffer-substring-no-properties
- (line-beginning-position) (line-end-position))
+ (pos-bol) (pos-eol))
" abcdef"))
(call-interactively #'python-indent-dedent-line-backspace)
(should
(string= (buffer-substring-no-properties
- (line-beginning-position) (line-end-position))
+ (pos-bol) (pos-eol))
"abcdef")))))
(ert-deftest python-bob-infloop-avoid ()
@@ -2543,58 +3455,59 @@ if x:
"Test `python-shell-process-environment' modification."
(let* ((python-shell-process-environment
'("TESTVAR1=value1" "TESTVAR2=value2"))
- (process-environment (python-shell-calculate-process-environment)))
- (should (equal (getenv "TESTVAR1") "value1"))
- (should (equal (getenv "TESTVAR2") "value2"))))
+ (env (python-shell--calculate-process-environment)))
+ (should (equal (getenv-internal "TESTVAR1" env) "value1"))
+ (should (equal (getenv-internal "TESTVAR2" env) "value2"))))
(ert-deftest python-shell-calculate-process-environment-2 ()
"Test `python-shell-extra-pythonpaths' modification."
(let* ((process-environment process-environment)
- (original-pythonpath (setenv "PYTHONPATH" "/path0"))
+ (_original-pythonpath (setenv "PYTHONPATH" "/path0"))
(python-shell-extra-pythonpaths '("/path1" "/path2"))
- (process-environment (python-shell-calculate-process-environment)))
- (should (equal (getenv "PYTHONPATH")
+ (env (python-shell--calculate-process-environment)))
+ (should (equal (getenv-internal "PYTHONPATH" env)
(concat "/path1" path-separator
"/path2" path-separator "/path0")))))
(ert-deftest python-shell-calculate-process-environment-3 ()
"Test `python-shell-virtualenv-root' modification."
(let* ((python-shell-virtualenv-root "/env")
- (process-environment
+ (env
(let ((process-environment process-environment))
(setenv "PYTHONHOME" "/home")
(setenv "VIRTUAL_ENV")
- (python-shell-calculate-process-environment))))
- (should (not (getenv "PYTHONHOME")))
- (should (string= (getenv "VIRTUAL_ENV") "/env"))))
+ (python-shell--calculate-process-environment))))
+ (should (member "PYTHONHOME" env))
+ (should (string= (getenv-internal "VIRTUAL_ENV" env) "/env"))))
(ert-deftest python-shell-calculate-process-environment-4 ()
"Test PYTHONUNBUFFERED when `python-shell-unbuffered' is non-nil."
(let* ((python-shell-unbuffered t)
- (process-environment
+ (env
(let ((process-environment process-environment))
(setenv "PYTHONUNBUFFERED")
- (python-shell-calculate-process-environment))))
- (should (string= (getenv "PYTHONUNBUFFERED") "1"))))
+ (python-shell--calculate-process-environment))))
+ (should (string= (getenv-internal "PYTHONUNBUFFERED" env) "1"))))
(ert-deftest python-shell-calculate-process-environment-5 ()
"Test PYTHONUNBUFFERED when `python-shell-unbuffered' is nil."
(let* ((python-shell-unbuffered nil)
- (process-environment
+ (env
(let ((process-environment process-environment))
(setenv "PYTHONUNBUFFERED")
- (python-shell-calculate-process-environment))))
- (should (not (getenv "PYTHONUNBUFFERED")))))
+ (python-shell--calculate-process-environment))))
+ (should (not (getenv-internal "PYTHONUNBUFFERED" env)))))
(ert-deftest python-shell-calculate-process-environment-6 ()
"Test PYTHONUNBUFFERED=1 when `python-shell-unbuffered' is nil."
(let* ((python-shell-unbuffered nil)
- (process-environment
+ (env
(let ((process-environment process-environment))
(setenv "PYTHONUNBUFFERED" "1")
- (python-shell-calculate-process-environment))))
+ (append (python-shell--calculate-process-environment)
+ process-environment))))
;; User default settings must remain untouched:
- (should (string= (getenv "PYTHONUNBUFFERED") "1"))))
+ (should (string= (getenv-internal "PYTHONUNBUFFERED" env) "1"))))
(ert-deftest python-shell-calculate-process-environment-7 ()
"Test no side-effects on `process-environment'."
@@ -2604,7 +3517,7 @@ if x:
(python-shell-unbuffered t)
(python-shell-extra-pythonpaths'("/path1" "/path2"))
(original-process-environment (copy-sequence process-environment)))
- (python-shell-calculate-process-environment)
+ (python-shell--calculate-process-environment)
(should (equal process-environment original-process-environment))))
(ert-deftest python-shell-calculate-process-environment-8 ()
@@ -2617,7 +3530,7 @@ if x:
(python-shell-extra-pythonpaths'("/path1" "/path2"))
(original-process-environment
(copy-sequence tramp-remote-process-environment)))
- (python-shell-calculate-process-environment)
+ (python-shell--calculate-process-environment)
(should (equal tramp-remote-process-environment original-process-environment))))
(ert-deftest python-shell-calculate-exec-path-1 ()
@@ -2633,7 +3546,7 @@ if x:
(python-shell-virtualenv-root "/env")
(new-exec-path (python-shell-calculate-exec-path)))
(should (equal new-exec-path
- (list (expand-file-name "/env/bin") "/path0")))))
+ (list (python-virt-bin) "/path0")))))
(ert-deftest python-shell-calculate-exec-path-3 ()
"Test complete `python-shell-virtualenv-root' modification."
@@ -2642,7 +3555,7 @@ if x:
(python-shell-virtualenv-root "/env")
(new-exec-path (python-shell-calculate-exec-path)))
(should (equal new-exec-path
- (list (expand-file-name "/env/bin")
+ (list (python-virt-bin)
"/path1" "/path2" "/path0")))))
(ert-deftest python-shell-calculate-exec-path-4 ()
@@ -2653,7 +3566,7 @@ if x:
(python-shell-virtualenv-root "/env")
(new-exec-path (python-shell-calculate-exec-path)))
(should (equal new-exec-path
- (list (expand-file-name "/env/bin")
+ (list (python-virt-bin)
"/path1" "/path2" "/path0")))))
(ert-deftest python-shell-calculate-exec-path-5 ()
@@ -2683,29 +3596,49 @@ if x:
(python-shell-virtualenv-root "/env"))
(python-shell-with-environment
(should (equal exec-path
- (list (expand-file-name "/env/bin")
+ (list (python-virt-bin)
"/path1" "/path2" "/path0")))
(should (not (getenv "PYTHONHOME")))
(should (string= (getenv "VIRTUAL_ENV") "/env")))
(should (equal exec-path original-exec-path))))
+(defun python--tests-process-env-canonical (pe)
+ ;; `process-environment' can contain various entries for the same
+ ;; var, and the first in the list hides the others.
+ (let ((process-environment '()))
+ (dolist (x (reverse pe))
+ (if (string-match "=" x)
+ (setenv (substring x 0 (match-beginning 0))
+ (substring x (match-end 0)))
+ (setenv x nil)))
+ process-environment))
+
+(defun python--tests-process-env-eql (pe1 pe2)
+ (equal (python--tests-process-env-canonical pe1)
+ (python--tests-process-env-canonical pe2)))
+
(ert-deftest python-shell-with-environment-2 ()
"Test environment with remote `default-directory'."
(let* ((default-directory "/ssh::/example/dir/")
(python-shell-remote-exec-path '("/remote1" "/remote2"))
(python-shell-exec-path '("/path1" "/path2"))
(tramp-remote-process-environment '("EMACS=t"))
- (original-process-environment (copy-sequence tramp-remote-process-environment))
+ (original-process-environment
+ (copy-sequence tramp-remote-process-environment))
(python-shell-virtualenv-root "/env"))
(python-shell-with-environment
(should (equal (python-shell-calculate-exec-path)
- (list (expand-file-name "/env/bin")
+ (list (python-virt-bin)
"/path1" "/path2" "/remote1" "/remote2")))
- (let ((process-environment (python-shell-calculate-process-environment)))
+ (let ((process-environment
+ (append (python-shell--calculate-process-environment)
+ tramp-remote-process-environment)))
(should (not (getenv "PYTHONHOME")))
(should (string= (getenv "VIRTUAL_ENV") "/env"))
- (should (equal tramp-remote-process-environment process-environment))))
- (should (equal tramp-remote-process-environment original-process-environment))))
+ (should (python--tests-process-env-eql
+ tramp-remote-process-environment process-environment))))
+ (should (equal tramp-remote-process-environment
+ original-process-environment))))
(ert-deftest python-shell-with-environment-3 ()
"Test `python-shell-with-environment' is idempotent."
@@ -2714,11 +3647,14 @@ if x:
(python-shell-virtualenv-root "/home/user/env")
(single-call
(python-shell-with-environment
- (list exec-path process-environment)))
+ (list exec-path
+ (python--tests-process-env-canonical process-environment))))
(nested-call
(python-shell-with-environment
(python-shell-with-environment
- (list exec-path process-environment)))))
+ (list exec-path
+ (python--tests-process-env-canonical
+ process-environment))))))
(should (equal single-call nested-call))))
(ert-deftest python-shell-make-comint-1 ()
@@ -3388,10 +4324,7 @@ def foo():
(should (string= (python-shell-buffer-substring
(python-tests-look-at "print ('a')")
(point-max))
- "if True:
-
- print ('a')
-"))))
+ "# -*- coding: utf-8 -*-\nif True:\n print ('a')\n\n"))))
(ert-deftest python-shell-buffer-substring-11 ()
"Check substring from partial block and point within indentation."
@@ -3406,10 +4339,7 @@ def foo():
(backward-char 1)
(point))
(point-max))
- "if True:
-
- print ('a')
-"))))
+ "# -*- coding: utf-8 -*-\nif True:\n print ('a')\n\n"))))
(ert-deftest python-shell-buffer-substring-12 ()
"Check substring from partial block and point in whitespace."
@@ -3424,13 +4354,7 @@ def foo():
(should (string= (python-shell-buffer-substring
(python-tests-look-at "# Whitespace")
(point-max))
- "if True:
-
-
- # Whitespace
-
- print ('a')
-"))))
+ "# -*- coding: utf-8 -*-\n\nif True:\n # Whitespace\n\n print ('a')\n\n"))))
@@ -3462,7 +4386,7 @@ def foo():
;;; Code check
-;;; Eldoc
+;;; ElDoc
(ert-deftest python-eldoc--get-symbol-at-point-1 ()
"Test paren handling."
@@ -3473,11 +4397,11 @@ map(codecs.open('somefile'
"
(python-tests-look-at "ap(xx")
(should (string= (python-eldoc--get-symbol-at-point) "map"))
- (goto-char (line-end-position))
+ (goto-char (pos-eol))
(should (string= (python-eldoc--get-symbol-at-point) "map"))
(python-tests-look-at "('somefile'")
(should (string= (python-eldoc--get-symbol-at-point) "map"))
- (goto-char (line-end-position))
+ (goto-char (pos-eol))
(should (string= (python-eldoc--get-symbol-at-point) "codecs.open"))))
(ert-deftest python-eldoc--get-symbol-at-point-2 ()
@@ -3990,7 +4914,7 @@ def long_function_name(
(should (not (python-info-beginning-of-statement-p)))
(python-tests-look-at "print (var_one)")
(should (python-info-beginning-of-statement-p))
- (goto-char (line-beginning-position))
+ (goto-char (pos-bol))
(should (not (python-info-beginning-of-statement-p)))))
(ert-deftest python-info-beginning-of-statement-p-2 ()
@@ -4010,7 +4934,7 @@ if width == 0 and height == 0 and \\
(should (not (python-info-beginning-of-statement-p)))
(python-tests-look-at "raise ValueError(")
(should (python-info-beginning-of-statement-p))
- (goto-char (line-beginning-position))
+ (goto-char (pos-bol))
(should (not (python-info-beginning-of-statement-p)))))
(ert-deftest python-info-end-of-statement-p-1 ()
@@ -4828,6 +5752,69 @@ def decorat0r(deff):
(python-tests-look-at "deff()")
(should (not (python-info-looking-at-beginning-of-defun)))))
+(ert-deftest python-info-looking-at-beginning-of-defun-2 ()
+ (python-tests-with-temp-buffer
+ "
+def \\
+ foo(arg):
+ pass
+"
+ (python-tests-look-at "def \\")
+ (should (python-info-looking-at-beginning-of-defun))
+ (should (python-info-looking-at-beginning-of-defun nil t))
+ (python-tests-look-at "foo(arg):")
+ (should (not (python-info-looking-at-beginning-of-defun)))
+ (should (python-info-looking-at-beginning-of-defun nil t))
+ (python-tests-look-at "pass")
+ (should (not (python-info-looking-at-beginning-of-defun)))
+ (should (not (python-info-looking-at-beginning-of-defun nil t)))))
+
+(ert-deftest python-info-looking-at-beginning-of-defun-3 ()
+ (python-tests-with-temp-buffer
+ "
+def foo(arg=\"default\"): # Comment
+ pass
+"
+ (python-tests-look-at "arg")
+ (should (python-info-looking-at-beginning-of-defun))
+ (python-tests-look-at "default")
+ (should (python-info-looking-at-beginning-of-defun))
+ (python-tests-look-at "Comment")
+ (should (python-info-looking-at-beginning-of-defun))))
+
+(ert-deftest python-info-looking-at-beginning-of-block-1 ()
+ (python-tests-with-temp-buffer
+ "
+def f():
+ if True:
+ pass
+ l = [x * 2
+ for x in range(5)
+ if x < 3]
+# if False:
+\"\"\"
+if 0:
+\"\"\"
+"
+ (python-tests-look-at "def f():")
+ (should (python-info-looking-at-beginning-of-block))
+ (forward-char)
+ (should (not (python-info-looking-at-beginning-of-block)))
+ (python-tests-look-at "if True:")
+ (should (python-info-looking-at-beginning-of-block))
+ (forward-char)
+ (should (not (python-info-looking-at-beginning-of-block)))
+ (beginning-of-line)
+ (should (python-info-looking-at-beginning-of-block))
+ (python-tests-look-at "for x")
+ (should (not (python-info-looking-at-beginning-of-block)))
+ (python-tests-look-at "if x < 3")
+ (should (not (python-info-looking-at-beginning-of-block)))
+ (python-tests-look-at "if False:")
+ (should (not (python-info-looking-at-beginning-of-block)))
+ (python-tests-look-at "if 0:")
+ (should (not (python-info-looking-at-beginning-of-block)))))
+
(ert-deftest python-info-current-line-comment-p-1 ()
(python-tests-with-temp-buffer
"
@@ -5192,7 +6179,7 @@ urlpatterns = patterns('',
(should (= (current-indentation) 23))))
(or eim (electric-indent-mode -1)))))
-(ert-deftest python-triple-quote-pairing ()
+(ert-deftest python-triple-double-quote-pairing ()
(let ((epm electric-pair-mode))
(unwind-protect
(progn
@@ -5219,6 +6206,33 @@ urlpatterns = patterns('',
"\"\n\"\"\"\n"))))
(or epm (electric-pair-mode -1)))))
+(ert-deftest python-triple-single-quote-pairing ()
+ (let ((epm electric-pair-mode))
+ (unwind-protect
+ (progn
+ (python-tests-with-temp-buffer
+ "''\n"
+ (or epm (electric-pair-mode 1))
+ (goto-char (1- (point-max)))
+ (python-tests-self-insert ?')
+ (should (string= (buffer-string)
+ "''''''\n"))
+ (should (= (point) 4)))
+ (python-tests-with-temp-buffer
+ "\n"
+ (python-tests-self-insert (list ?' ?' ?'))
+ (should (string= (buffer-string)
+ "''''''\n"))
+ (should (= (point) 4)))
+ (python-tests-with-temp-buffer
+ "'\n''\n"
+ (goto-char (1- (point-max)))
+ (python-tests-self-insert ?')
+ (should (= (point) (1- (point-max))))
+ (should (string= (buffer-string)
+ "'\n'''\n"))))
+ (or epm (electric-pair-mode -1)))))
+
;;; Hideshow support
@@ -5254,8 +6268,11 @@ class SomeClass:
class SomeClass:
def __init__(self, arg, kwarg=1):
+
def filter(self, nums):
- def __str__(self):"))))
+
+ def __str__(self):
+"))))
(or enabled (hs-minor-mode -1)))))
(ert-deftest python-hideshow-hide-levels-2 ()
@@ -5301,17 +6318,186 @@ class SomeClass:
"))))
(or enabled (hs-minor-mode -1)))))
+(ert-deftest python-hideshow-hide-levels-3 ()
+ "Should hide all blocks."
+ (python-tests-with-temp-buffer
+ "
+def f():
+ if 0:
+ l = [i for i in range(5)
+ if i < 3]
+ abc = o.match(1, 2, 3)
+
+def g():
+ pass
+"
+ (hs-minor-mode 1)
+ (hs-hide-level 1)
+ (should
+ (string=
+ (python-tests-visible-string)
+ "
+def f():
+
+def g():
+"))))
+
+(ert-deftest python-hideshow-hide-levels-4 ()
+ "Should hide 2nd level block."
+ (python-tests-with-temp-buffer
+ "
+def f():
+ if 0:
+ l = [i for i in range(5)
+ if i < 3]
+ abc = o.match(1, 2, 3)
+
+def g():
+ pass
+"
+ (hs-minor-mode 1)
+ (hs-hide-level 2)
+ (should
+ (string=
+ (python-tests-visible-string)
+ "
+def f():
+ if 0:
+
+def g():
+ pass
+"))))
+
+(ert-deftest python-hideshow-hide-all-1 ()
+ "Should hide all blocks."
+ (python-tests-with-temp-buffer
+ "if 0:
+
+ aaa
+ l = [i for i in range(5)
+ if i < 3]
+ ccc
+ abc = o.match(1, 2, 3)
+ ddd
+
+def f():
+ pass
+"
+ (hs-minor-mode 1)
+ (hs-hide-all)
+ (should
+ (string=
+ (python-tests-visible-string)
+ "if 0:
+
+def f():
+"))))
+
+(ert-deftest python-hideshow-hide-all-2 ()
+ "Should hide comments."
+ (python-tests-with-temp-buffer
+ "
+# Multi line
+# comment
+
+\"\"\"
+# Multi line
+# string
+\"\"\"
+"
+ (hs-minor-mode 1)
+ (hs-hide-all)
+ (should
+ (string=
+ (python-tests-visible-string)
+ "
+# Multi line
+
+\"\"\"
+# Multi line
+# string
+\"\"\"
+"))))
+
+(ert-deftest python-hideshow-hide-all-3 ()
+ "Should not hide comments when `hs-hide-comments-when-hiding-all' is nil."
+ (python-tests-with-temp-buffer
+ "
+# Multi line
+# comment
+
+\"\"\"
+# Multi line
+# string
+\"\"\"
+"
+ (hs-minor-mode 1)
+ (let ((hs-hide-comments-when-hiding-all nil))
+ (hs-hide-all))
+ (should
+ (string=
+ (python-tests-visible-string)
+ "
+# Multi line
+# comment
+
+\"\"\"
+# Multi line
+# string
+\"\"\"
+"))))
+
+(ert-deftest python-hideshow-hide-block-1 ()
+ "Should hide current block."
+ (python-tests-with-temp-buffer
+ "
+if 0:
+
+ aaa
+ l = [i for i in range(5)
+ if i < 3]
+ ccc
+ abc = o.match(1, 2, 3)
+ ddd
+
+def f():
+ pass
+"
+ (hs-minor-mode 1)
+ (python-tests-look-at "ddd")
+ (forward-line)
+ (hs-hide-block)
+ (should
+ (string=
+ (python-tests-visible-string)
+ "
+if 0:
+
+def f():
+ pass
+"))))
+
(ert-deftest python-tests--python-nav-end-of-statement--infloop ()
"Checks that `python-nav-end-of-statement' doesn't infloop in a
buffer with overlapping strings."
+ ;; FIXME: The treatment of strings has changed in the mean time, and the
+ ;; test below now neither signals an error nor inf-loops.
+ ;; The description of the problem it's trying to catch is not clear enough
+ ;; to be able to see if the underlying problem is really fixed, sadly.
+ ;; E.g. I don't know what is meant by "overlap", really.
+ :tags '(:unstable)
(python-tests-with-temp-buffer "''' '\n''' ' '\n"
(syntax-propertize (point-max))
;; Create a situation where strings nominally overlap. This
;; shouldn't happen in practice, but apparently it can happen when
;; a package calls `syntax-ppss' in a narrowed buffer during JIT
;; lock.
+ ;; FIXME: 4-5 is the SPC right after the opening triple quotes: why
+ ;; put a string-fence syntax on it?
(put-text-property 4 5 'syntax-table (string-to-syntax "|"))
+ ;; FIXME: 8-9 is the middle quote in the closing triple quotes:
+ ;; it shouldn't have any syntax-table property to remove anyway!
(remove-text-properties 8 9 '(syntax-table nil))
(goto-char 4)
(setq-local syntax-propertize-function nil)
@@ -5321,11 +6507,80 @@ buffer with overlapping strings."
(python-nav-end-of-statement)))
(should (eolp))))
+;; Interactively, `run-python' focuses the buffer running the
+;; interpreter.
+(ert-deftest python-tests--run-python-selects-window ()
+ "Test for bug#31398. See also bug#44421 and bug#52380."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((buffer (process-buffer (run-python nil nil 'show)))
+ (window (get-buffer-window buffer)))
+ ;; We look at `selected-window' rather than `current-buffer'
+ ;; because as `(elisp)Current buffer' says, the latter will only
+ ;; be synchronized with the former when returning to the "command
+ ;; loop"; until then, `current-buffer' can change arbitrarily.
+ (should (eq window (selected-window)))
+ (pop-to-buffer (other-buffer))
+ (run-python nil nil 'show)
+ (should (eq window (selected-window)))))
+
+(ert-deftest python-tests--fill-long-first-line ()
+ (should
+ (equal
+ (with-temp-buffer
+ (insert "def asdf():
+ \"\"\"123 123 123 123 123 123 123 123 123 123 123 123 123 SHOULDBEWRAPPED 123 123 123 123
-(provide 'python-tests)
+ \"\"\"
+ a = 1
+")
+ (python-mode)
+ (goto-char (point-min))
+ (forward-line 1)
+ (end-of-line)
+ (fill-paragraph)
+ (buffer-substring-no-properties (point-min) (point-max)))
+ "def asdf():
+ \"\"\"123 123 123 123 123 123 123 123 123 123 123 123 123
+ SHOULDBEWRAPPED 123 123 123 123
+
+ \"\"\"
+ a = 1
+")))
-;; Local Variables:
-;; indent-tabs-mode: nil
-;; End:
+
+;;; Flymake
+
+(ert-deftest python-tests--flymake-command-output-pattern ()
+ (pcase-let ((`(,patt ,line ,col ,type ,msg)
+ python-flymake-command-output-pattern))
+ ;; Pyflakes output as of version 2.4.0
+ (let ((output "<stdin>:12:34 'a.b.c as d' imported but unused"))
+ (string-match patt output)
+ (should (equal (match-string line output) "12"))
+ (when col (should (equal (match-string col output) "34")))
+ (should (equal (match-string msg output)
+ "'a.b.c as d' imported but unused")))
+ ;; Flake8 output as of version 4.0.1
+ (let ((output "stdin:12:34: F401 'a.b.c as d' imported but unused"))
+ (string-match patt output)
+ (should (equal (match-string line output) "12"))
+ (when col (should (equal (match-string col output) "34")))
+ (when type (should (equal (match-string type output) "F401")))
+ (should (equal (match-string msg output)
+ (if type
+ "'a.b.c as d' imported but unused"
+ "F401 'a.b.c as d' imported but unused"))))
+ ;; Pylint output as of version 2.14.5
+ (let ((output "stdin:12:34: W0611: Unused import a.b.c (unused-import)"))
+ (string-match patt output)
+ (should (equal (match-string line output) "12"))
+ (when col (should (equal (match-string col output) "34")))
+ (when type (should (equal (match-string type output) "W0611")))
+ (should (equal (match-string msg output)
+ (if type
+ "Unused import a.b.c (unused-import)"
+ "W0611: Unused import a.b.c (unused-import)"))))))
+
+(provide 'python-tests)
;;; python-tests.el ends here