aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabián Ezequiel Gallina2014-12-27 01:30:21 -0300
committerFabián Ezequiel Gallina2014-12-27 01:30:21 -0300
commit2dd5163d764f395eb31a2306dba385d123af4aba (patch)
tree27b1ade1b6af5e984040a8acc840f81ee4f5adaa
parent7aa506eed8881788485a9774165454404bac2623 (diff)
downloademacs-2dd5163d764f395eb31a2306dba385d123af4aba.tar.gz
emacs-2dd5163d764f395eb31a2306dba385d123af4aba.zip
python.el: Handle file encoding for shell.
* lisp/progmodes/python.el (python-rx-constituents): Add coding-cookie. (python-shell--save-temp-file): Write file with proper encoding. (python-shell-buffer-substring): Add coding cookie for detected encoding to generated content. Fix blank lines when removing if-name-main block. (python-shell-send-file): Handle file encoding. (python-info-encoding-from-cookie) (python-info-encoding): New functions. * test/automated/python-tests.el (python-shell-buffer-substring-1) (python-shell-buffer-substring-2, python-shell-buffer-substring-3) (python-shell-buffer-substring-4, python-shell-buffer-substring-5) (python-shell-buffer-substring-6, python-shell-buffer-substring-7) (python-shell-buffer-substring-8) (python-info-encoding-from-cookie-1) (python-info-encoding-from-cookie-2) (python-info-encoding-from-cookie-3) (python-info-encoding-from-cookie-4) (python-info-encoding-from-cookie-5) (python-info-encoding-from-cookie-6) (python-info-encoding-from-cookie-7, python-info-encoding-1) (python-info-encoding-2): New tests.
-rw-r--r--lisp/ChangeLog13
-rw-r--r--lisp/progmodes/python.el110
-rw-r--r--test/ChangeLog16
-rw-r--r--test/automated/python-tests.el271
4 files changed, 385 insertions, 25 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 22728121a15..b73732a1a7e 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,16 @@
12014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org>
2
3 python.el: Handle file encoding for shell.
4
5 * progmodes/python.el (python-rx-constituents): Add coding-cookie.
6 (python-shell--save-temp-file): Write file with proper encoding.
7 (python-shell-buffer-substring): Add coding cookie for detected
8 encoding to generated content. Fix blank lines when removing
9 if-name-main block.
10 (python-shell-send-file): Handle file encoding.
11 (python-info-encoding-from-cookie)
12 (python-info-encoding): New functions.
13
12014-12-24 Michael Albinus <michael.albinus@gmx.de> 142014-12-24 Michael Albinus <michael.albinus@gmx.de>
2 15
3 * net/tramp-sh.el (tramp-do-copy-or-rename-file-out-of-band): 16 * net/tramp-sh.el (tramp-do-copy-or-rename-file-out-of-band):
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 632659c28bb..02d0cbef262 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -386,7 +386,18 @@
386 (* ?\\ ?\\) (any ?\' ?\"))) 386 (* ?\\ ?\\) (any ?\' ?\")))
387 (* ?\\ ?\\) 387 (* ?\\ ?\\)
388 ;; Match single or triple quotes of any kind. 388 ;; Match single or triple quotes of any kind.
389 (group (or "\"" "\"\"\"" "'" "'''")))))) 389 (group (or "\"" "\"\"\"" "'" "'''")))))
390 (coding-cookie . ,(rx line-start ?# (* space)
391 (or
392 ;; # coding=<encoding name>
393 (: "coding" (or ?: ?=) (* space) (group-n 1 (+ (or word ?-))))
394 ;; # -*- coding: <encoding name> -*-
395 (: "-*-" (* space) "coding:" (* space)
396 (group-n 1 (+ (or word ?-))) (* space) "-*-")
397 ;; # vim: set fileencoding=<encoding name> :
398 (: "vim:" (* space) "set" (+ space)
399 "fileencoding" (* space) ?= (* space)
400 (group-n 1 (+ (or word ?-))) (* space) ":")))))
390 "Additional Python specific sexps for `python-rx'") 401 "Additional Python specific sexps for `python-rx'")
391 402
392 (defmacro python-rx (&rest regexps) 403 (defmacro python-rx (&rest regexps)
@@ -2400,11 +2411,7 @@ there for compatibility with CEDET.")
2400 (concat (file-remote-p default-directory) "/tmp") 2411 (concat (file-remote-p default-directory) "/tmp")
2401 temporary-file-directory)) 2412 temporary-file-directory))
2402 (temp-file-name (make-temp-file "py")) 2413 (temp-file-name (make-temp-file "py"))
2403 ;; XXX: Python's built-in compile function accepts utf-8 as 2414 (coding-system-for-write (python-info-encoding)))
2404 ;; input so there's no need to enforce a coding cookie. In
2405 ;; the future making `coding-system-for-write' match the
2406 ;; current buffer's coding may be a good idea.
2407 (coding-system-for-write 'utf-8))
2408 (with-temp-file temp-file-name 2415 (with-temp-file temp-file-name
2409 (insert string) 2416 (insert string)
2410 (delete-trailing-whitespace)) 2417 (delete-trailing-whitespace))
@@ -2511,16 +2518,28 @@ the python shell:
2511 \"if __name__ == '__main__'\" block will be removed. 2518 \"if __name__ == '__main__'\" block will be removed.
2512 2. When a subregion of the buffer is sent, it takes care of 2519 2. When a subregion of the buffer is sent, it takes care of
2513 appending extra empty lines so tracebacks are correct. 2520 appending extra empty lines so tracebacks are correct.
2514 3. Wraps indented regions under an \"if True:\" block so the 2521 3. When the region sent is a substring of the current buffer, a
2522 coding cookie is added.
2523 4. Wraps indented regions under an \"if True:\" block so the
2515 interpreter evaluates them correctly." 2524 interpreter evaluates them correctly."
2516 (let ((substring (buffer-substring-no-properties start end)) 2525 (let* ((substring (buffer-substring-no-properties start end))
2517 (fillstr (make-string (1- (line-number-at-pos start)) ?\n)) 2526 (buffer-substring-p (save-restriction
2518 (toplevel-block-p (save-excursion 2527 (widen)
2519 (goto-char start) 2528 (not (equal (list (point-min) (point-max))
2520 (or (zerop (line-number-at-pos start)) 2529 (list start end)))))
2521 (progn 2530 (encoding (python-info-encoding))
2522 (python-util-forward-comment 1) 2531 (fillstr (concat
2523 (zerop (current-indentation))))))) 2532 (when buffer-substring-p
2533 (format "# -*- coding: %s -*-\n" encoding))
2534 (make-string
2535 (- (line-number-at-pos start)
2536 (if buffer-substring-p 2 1)) ?\n)))
2537 (toplevel-block-p (save-excursion
2538 (goto-char start)
2539 (or (zerop (line-number-at-pos start))
2540 (progn
2541 (python-util-forward-comment 1)
2542 (zerop (current-indentation)))))))
2524 (with-temp-buffer 2543 (with-temp-buffer
2525 (python-mode) 2544 (python-mode)
2526 (if fillstr (insert fillstr)) 2545 (if fillstr (insert fillstr))
@@ -2536,17 +2555,26 @@ the python shell:
2536 (when (python-nav-if-name-main) 2555 (when (python-nav-if-name-main)
2537 (cons (point) 2556 (cons (point)
2538 (progn (python-nav-forward-sexp-safe) 2557 (progn (python-nav-forward-sexp-safe)
2558 ;; Include ending newline
2559 (forward-line 1)
2539 (point))))))) 2560 (point)))))))
2540 ;; Oh destructuring bind, how I miss you. 2561 ;; Oh destructuring bind, how I miss you.
2541 (if-name-main-start (car if-name-main-start-end)) 2562 (if-name-main-start (car if-name-main-start-end))
2542 (if-name-main-end (cdr if-name-main-start-end))) 2563 (if-name-main-end (cdr if-name-main-start-end))
2564 (fillstr (make-string
2565 (- (line-number-at-pos if-name-main-end)
2566 (line-number-at-pos if-name-main-start)) ?\n)))
2543 (when if-name-main-start-end 2567 (when if-name-main-start-end
2544 (goto-char if-name-main-start) 2568 (goto-char if-name-main-start)
2545 (delete-region if-name-main-start if-name-main-end) 2569 (delete-region if-name-main-start if-name-main-end)
2546 (insert 2570 (insert fillstr))))
2547 (make-string 2571 ;; Ensure there's only one coding cookie in the generated string.
2548 (- (line-number-at-pos if-name-main-end) 2572 (goto-char (point-min))
2549 (line-number-at-pos if-name-main-start)) ?\n))))) 2573 (when (looking-at-p (python-rx coding-cookie))
2574 (forward-line 1)
2575 (when (looking-at-p (python-rx coding-cookie))
2576 (delete-region
2577 (line-beginning-position) (line-end-position))))
2550 (buffer-substring-no-properties (point-min) (point-max))))) 2578 (buffer-substring-no-properties (point-min) (point-max)))))
2551 2579
2552(defun python-shell-send-region (start end &optional nomain) 2580(defun python-shell-send-region (start end &optional nomain)
@@ -2604,15 +2632,21 @@ If DELETE is non-nil, delete the file afterwards."
2604 (expand-file-name 2632 (expand-file-name
2605 (or (file-remote-p file-name 'localname) 2633 (or (file-remote-p file-name 'localname)
2606 file-name))) 2634 file-name)))
2607 temp-file-name))) 2635 temp-file-name))
2636 (encoding
2637 (with-temp-buffer
2638 (insert-file-contents
2639 (or temp-file-name file-name))
2640 (python-info-encoding))))
2608 (when (not file-name) 2641 (when (not file-name)
2609 (error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil")) 2642 (error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil"))
2610 (python-shell-send-string 2643 (python-shell-send-string
2611 (format 2644 (format
2612 (concat "__pyfile = open('''%s''');" 2645 (concat
2613 "exec(compile(__pyfile.read(), '''%s''', 'exec'));" 2646 "import codecs; __pyfile = codecs.open('''%s''', encoding='''%s''');"
2614 "__pyfile.close()%s") 2647 "exec(compile(__pyfile.read().encode('''%s'''), '''%s''', 'exec'));"
2615 (or temp-file-name file-name) file-name 2648 "__pyfile.close()%s")
2649 (or temp-file-name file-name) encoding encoding file-name
2616 (if delete (format "; import os; os.remove('''%s''')" 2650 (if delete (format "; import os; os.remove('''%s''')"
2617 (or temp-file-name file-name)) 2651 (or temp-file-name file-name))
2618 "")) 2652 ""))
@@ -3912,6 +3946,32 @@ operator."
3912 (* whitespace) line-end)) 3946 (* whitespace) line-end))
3913 (string-equal "" (match-string-no-properties 1)))) 3947 (string-equal "" (match-string-no-properties 1))))
3914 3948
3949(defun python-info-encoding-from-cookie ()
3950 "Detect current buffer's encoding from its coding cookie.
3951Returns the enconding as a symbol."
3952 (let ((first-two-lines
3953 (save-excursion
3954 (save-restriction
3955 (widen)
3956 (goto-char (point-min))
3957 (forward-line 2)
3958 (buffer-substring-no-properties
3959 (point)
3960 (point-min))))))
3961 (when (string-match (python-rx coding-cookie) first-two-lines)
3962 (intern (match-string-no-properties 1 first-two-lines)))))
3963
3964(defun python-info-encoding ()
3965 "Return encoding for file.
3966Try `python-info-encoding-from-cookie', if none is found then
3967default to utf-8."
3968 ;; If no enconding is defined, then it's safe to use UTF-8: Python 2
3969 ;; uses ASCII as default while Python 3 uses UTF-8. This means that
3970 ;; in the worst case escenario python.el will make things work for
3971 ;; Python 2 files with unicode data and no encoding defined.
3972 (or (python-info-encoding-from-cookie)
3973 'utf-8))
3974
3915 3975
3916;;; Utility functions 3976;;; Utility functions
3917 3977
diff --git a/test/ChangeLog b/test/ChangeLog
index 14780c09e6f..101e9d9caa7 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,19 @@
12014-12-27 Fabián Ezequiel Gallina <fgallina@gnu.org>
2
3 * automated/python-tests.el (python-shell-buffer-substring-1)
4 (python-shell-buffer-substring-2, python-shell-buffer-substring-3)
5 (python-shell-buffer-substring-4, python-shell-buffer-substring-5)
6 (python-shell-buffer-substring-6, python-shell-buffer-substring-7)
7 (python-shell-buffer-substring-8)
8 (python-info-encoding-from-cookie-1)
9 (python-info-encoding-from-cookie-2)
10 (python-info-encoding-from-cookie-3)
11 (python-info-encoding-from-cookie-4)
12 (python-info-encoding-from-cookie-5)
13 (python-info-encoding-from-cookie-6)
14 (python-info-encoding-from-cookie-7, python-info-encoding-1)
15 (python-info-encoding-2): New tests.
16
12014-12-25 Michael Albinus <michael.albinus@gmx.de> 172014-12-25 Michael Albinus <michael.albinus@gmx.de>
2 18
3 * automated/tramp-tests.el (tramp-test17-insert-directory): Do not 19 * automated/tramp-tests.el (tramp-test17-insert-directory): Do not
diff --git a/test/automated/python-tests.el b/test/automated/python-tests.el
index d1713ac1851..8fcda58e1e0 100644
--- a/test/automated/python-tests.el
+++ b/test/automated/python-tests.el
@@ -2459,6 +2459,198 @@ and `python-shell-interpreter-args' in the new shell buffer."
2459 "^\\(o\\.t \\|\\)"))) 2459 "^\\(o\\.t \\|\\)")))
2460 (ignore-errors (delete-file startup-file))))) 2460 (ignore-errors (delete-file startup-file)))))
2461 2461
2462(ert-deftest python-shell-buffer-substring-1 ()
2463 "Selecting a substring of the whole buffer must match its contents."
2464 (python-tests-with-temp-buffer
2465 "
2466class Foo(models.Model):
2467 pass
2468
2469
2470class Bar(models.Model):
2471 pass
2472"
2473 (should (string= (buffer-string)
2474 (python-shell-buffer-substring (point-min) (point-max))))))
2475
2476(ert-deftest python-shell-buffer-substring-2 ()
2477 "Main block should be removed if NOMAIN is non-nil."
2478 (python-tests-with-temp-buffer
2479 "
2480class Foo(models.Model):
2481 pass
2482
2483class Bar(models.Model):
2484 pass
2485
2486if __name__ == \"__main__\":
2487 foo = Foo()
2488 print (foo)
2489"
2490 (should (string= (python-shell-buffer-substring (point-min) (point-max) t)
2491 "
2492class Foo(models.Model):
2493 pass
2494
2495class Bar(models.Model):
2496 pass
2497
2498
2499
2500
2501"))))
2502
2503(ert-deftest python-shell-buffer-substring-3 ()
2504 "Main block should be removed if NOMAIN is non-nil."
2505 (python-tests-with-temp-buffer
2506 "
2507class Foo(models.Model):
2508 pass
2509
2510if __name__ == \"__main__\":
2511 foo = Foo()
2512 print (foo)
2513
2514class Bar(models.Model):
2515 pass
2516"
2517 (should (string= (python-shell-buffer-substring (point-min) (point-max) t)
2518 "
2519class Foo(models.Model):
2520 pass
2521
2522
2523
2524
2525
2526class Bar(models.Model):
2527 pass
2528"))))
2529
2530(ert-deftest python-shell-buffer-substring-4 ()
2531 "Coding cookie should be added for substrings."
2532 (python-tests-with-temp-buffer
2533 "# coding: latin-1
2534
2535class Foo(models.Model):
2536 pass
2537
2538if __name__ == \"__main__\":
2539 foo = Foo()
2540 print (foo)
2541
2542class Bar(models.Model):
2543 pass
2544"
2545 (should (string= (python-shell-buffer-substring
2546 (python-tests-look-at "class Foo(models.Model):")
2547 (progn (python-nav-forward-sexp) (point)))
2548 "# -*- coding: latin-1 -*-
2549
2550class Foo(models.Model):
2551 pass"))))
2552
2553(ert-deftest python-shell-buffer-substring-5 ()
2554 "The proper amount of blank lines is added for a substring."
2555 (python-tests-with-temp-buffer
2556 "# coding: latin-1
2557
2558class Foo(models.Model):
2559 pass
2560
2561if __name__ == \"__main__\":
2562 foo = Foo()
2563 print (foo)
2564
2565class Bar(models.Model):
2566 pass
2567"
2568 (should (string= (python-shell-buffer-substring
2569 (python-tests-look-at "class Bar(models.Model):")
2570 (progn (python-nav-forward-sexp) (point)))
2571 "# -*- coding: latin-1 -*-
2572
2573
2574
2575
2576
2577
2578
2579
2580class Bar(models.Model):
2581 pass"))))
2582
2583(ert-deftest python-shell-buffer-substring-6 ()
2584 "Handle substring with coding cookie in the second line."
2585 (python-tests-with-temp-buffer
2586 "
2587# coding: latin-1
2588
2589class Foo(models.Model):
2590 pass
2591
2592if __name__ == \"__main__\":
2593 foo = Foo()
2594 print (foo)
2595
2596class Bar(models.Model):
2597 pass
2598"
2599 (should (string= (python-shell-buffer-substring
2600 (python-tests-look-at "# coding: latin-1")
2601 (python-tests-look-at "if __name__ == \"__main__\":"))
2602 "# -*- coding: latin-1 -*-
2603
2604
2605class Foo(models.Model):
2606 pass
2607
2608"))))
2609
2610(ert-deftest python-shell-buffer-substring-7 ()
2611 "Ensure first coding cookie gets precedence."
2612 (python-tests-with-temp-buffer
2613 "# coding: utf-8
2614# coding: latin-1
2615
2616class Foo(models.Model):
2617 pass
2618
2619if __name__ == \"__main__\":
2620 foo = Foo()
2621 print (foo)
2622
2623class Bar(models.Model):
2624 pass
2625"
2626 (should (string= (python-shell-buffer-substring
2627 (python-tests-look-at "# coding: latin-1")
2628 (python-tests-look-at "if __name__ == \"__main__\":"))
2629 "# -*- coding: utf-8 -*-
2630
2631
2632class Foo(models.Model):
2633 pass
2634
2635"))))
2636
2637(ert-deftest python-shell-buffer-substring-8 ()
2638 "Ensure first coding cookie gets precedence when sending whole buffer."
2639 (python-tests-with-temp-buffer
2640 "# coding: utf-8
2641# coding: latin-1
2642
2643class Foo(models.Model):
2644 pass
2645"
2646 (should (string= (python-shell-buffer-substring (point-min) (point-max))
2647 "# coding: utf-8
2648
2649
2650class Foo(models.Model):
2651 pass
2652"))))
2653
2462 2654
2463;;; Shell completion 2655;;; Shell completion
2464 2656
@@ -3773,6 +3965,85 @@ foo = True # another comment
3773 (forward-line 1) 3965 (forward-line 1)
3774 (should (python-info-current-line-empty-p)))) 3966 (should (python-info-current-line-empty-p))))
3775 3967
3968(ert-deftest python-info-encoding-from-cookie-1 ()
3969 "Should detect it on first line."
3970 (python-tests-with-temp-buffer
3971 "# coding=latin-1
3972
3973foo = True # another comment
3974"
3975 (should (eq (python-info-encoding-from-cookie) 'latin-1))))
3976
3977(ert-deftest python-info-encoding-from-cookie-2 ()
3978 "Should detect it on second line."
3979 (python-tests-with-temp-buffer
3980 "
3981# coding=latin-1
3982
3983foo = True # another comment
3984"
3985 (should (eq (python-info-encoding-from-cookie) 'latin-1))))
3986
3987(ert-deftest python-info-encoding-from-cookie-3 ()
3988 "Should not be detected on third line (and following ones)."
3989 (python-tests-with-temp-buffer
3990 "
3991
3992# coding=latin-1
3993foo = True # another comment
3994"
3995 (should (not (python-info-encoding-from-cookie)))))
3996
3997(ert-deftest python-info-encoding-from-cookie-4 ()
3998 "Should detect Emacs style."
3999 (python-tests-with-temp-buffer
4000 "# -*- coding: latin-1 -*-
4001
4002foo = True # another comment"
4003 (should (eq (python-info-encoding-from-cookie) 'latin-1))))
4004
4005(ert-deftest python-info-encoding-from-cookie-5 ()
4006 "Should detect Vim style."
4007 (python-tests-with-temp-buffer
4008 "# vim: set fileencoding=latin-1 :
4009
4010foo = True # another comment"
4011 (should (eq (python-info-encoding-from-cookie) 'latin-1))))
4012
4013(ert-deftest python-info-encoding-from-cookie-6 ()
4014 "First cookie wins."
4015 (python-tests-with-temp-buffer
4016 "# -*- coding: iso-8859-1 -*-
4017# vim: set fileencoding=latin-1 :
4018
4019foo = True # another comment"
4020 (should (eq (python-info-encoding-from-cookie) 'iso-8859-1))))
4021
4022(ert-deftest python-info-encoding-from-cookie-7 ()
4023 "First cookie wins."
4024 (python-tests-with-temp-buffer
4025 "# vim: set fileencoding=latin-1 :
4026# -*- coding: iso-8859-1 -*-
4027
4028foo = True # another comment"
4029 (should (eq (python-info-encoding-from-cookie) 'latin-1))))
4030
4031(ert-deftest python-info-encoding-1 ()
4032 "Should return the detected encoding from cookie."
4033 (python-tests-with-temp-buffer
4034 "# vim: set fileencoding=latin-1 :
4035
4036foo = True # another comment"
4037 (should (eq (python-info-encoding) 'latin-1))))
4038
4039(ert-deftest python-info-encoding-2 ()
4040 "Should default to utf-8."
4041 (python-tests-with-temp-buffer
4042 "# No encoding for you
4043
4044foo = True # another comment"
4045 (should (eq (python-info-encoding) 'utf-8))))
4046
3776 4047
3777;;; Utility functions 4048;;; Utility functions
3778 4049