pinard/Pymacs

Fix for python3.2

Closed this issue · 5 comments

Hi,

I think you should add an if statement for the config scripts when python3 is specified:

  1. in pymac.el remove the lines (str objects are UTF-8 by default):
    (when multibyte
    (princ ".decode('UTF-8')"))
  2. in Pymacs.py modify to (sys.exc_type and value are removed):
    value = traceback.format_exception_only(sys.exc_info()[0],
    sys.exc_info()[1])

I can confirm this issue. I have used the following patch as a workaround.


diff --git a/Pymacs.py.in b/Pymacs.py.in
index 808cfb2..787d0e7 100755
--- a/Pymacs.py.in
+++ b/Pymacs.py.in
@@ -232,8 +232,8 @@ class Protocol:
                     import traceback
                     action = 'raise'
                     if lisp.debug_on_error.value() is None:
-                        value = traceback.format_exception_only(sys.exc_type,
-                            sys.exc_value)
+                        value = traceback.format_exception_only(sys.exc_info()[0],
+                            sys.exc_info()[1])
                         value = ''.join(value).rstrip()
                     else:
                         value = traceback.format_exc()
diff --git a/pymacs.el.in b/pymacs.el.in
index 3deed86..1d9db35 100644
--- a/pymacs.el.in
+++ b/pymacs.el.in
@@ -488,6 +488,14 @@ The timer is used only if `post-gc-hook' is not available.")
       (pymacs-print-for-eval argument))
     (princ ")")))

+(defun pymacs-python3-p ()
+  "Return true if python command ends with '3'."
+  (let ((proc (get-buffer-process "*Pymacs*")))
+    (when (processp proc)
+      (let* ((cmd (process-command proc))
+             (exe (car cmd)))
+        (and (> (length exe) 0) (string= "3" (substring exe -1)))))))
+
 (defun pymacs-print-for-eval (expression)
   ;; This function prints a Python expression out of a Lisp EXPRESSION.
   (let (done)
@@ -511,7 +519,7 @@ The timer is used only if `post-gc-hook' is not available.")
                (princ (mapconcat 'identity
                                  (split-string (prin1-to-string text) "\n")
                                  "\\n"))
-               (when multibyte
+               (when (and multibyte (not (pymacs-python3-p)))
                  (princ ".decode('UTF-8')")))
              (setq done t)))
           ((symbolp expression)

The function "pymacs-python3-p" is just a quick hack to find out if Python 3 is running. Is there a function providing the Python version for Pymacs?

By the way: I could not "setup.py install" the current Git head, so I patched the 0.25 release. Is there an issue with the setup? If have a standard Ubuntu 12.04 Python installation and built the package using "make" (after modifying "PYTHON = python3" in the Makefile), followed by "python3 ./setup.py install --user".

Hi, Patrik and (?) ufleish.

Thanks for the note about sys.exc_info() in Python3, happily applied here.

About "an issue with the setup", Will Roberts submitted a correction in that area, I do not know if it addresses the issue you are reporting.

For the multibyte issue, I am still reluctant as I do not understand your comment. In Python3, str objects are not UTF-8, they are Unicode, which is not the same thing. But if Emacs transmits UTF-8 bytes, and are received on the Python side as bytes, it seems to me that decoding is still required to turn those bytes from UTF-8 into Unicode.

I'm not that assertive, however, as I do not swim often enough in these waters to feel solid in whatever I say.

François

Hi François,

Here is a simple test to reproduce the bug by passing a multibyte string to a Python function:

pymacstest.py:

def greet(name):
    return 'Hello ' + name
(pymacs-load "pymacstest")
(pymacstest-greet "François")
Debugger entered--Lisp error: (error "Python: Traceback (most recent call last):
  File \"/home/urs/python/packages/Pymacs-0.25-py3.2/Pymacs.py\", line 191, in loop
    value = eval(text)
  File \"<string>\", line 1, in <module>
AttributeError: 'str' object has no attribute 'decode'
")

*Pymacs*
>44 eval pymacs_load_helper("pymacstest", None)
<78 (return '(progn (pymacs-defuns '(8 pymacstest-greet nil)) (pymacs-python 9)))
>50 eval python[8]("Fran\303\247ois".decode('UTF-8'))
<22 (eval debug-on-error)
>15 return Lisp(0)
<263    (free (0) raise "Traceback (most recent call last):\n  File \"/home/urs/python/packages/Pymacs-0.25-py3.2/Pymacs.py\", line 191, in loop\n    value = eval(text)\n  File \"<string>\", line 1, in <module>\nAttributeError: 'str' object has no attribute 'decode'\n")

The problem is that str in Python 3 does not have a decode() method. Instead of a string, a byte array should be passed, so that the UTF-8 bytes are decoded to a correct unicode string.

So b"Fran\303\247ois".decode('UTF-8') should be passed instead of "Fran\303\247ois".decode('UTF-8'). Just omitting ".decode('UTF-8')" as suggested in the patch above is not correct since the UTF-8 bytes are not decoded. Byte array b"..." literals works since Python 2.6 (it is a no-op for Python 2), so the Elisp part of Pymacs does not have to know the Python version.

A patch is below.

Regards,
Urs

--- pymacs.el.orig  2012-06-26 17:58:18.785507900 +0200
+++ pymacs.el   2012-09-01 14:15:12.191417377 +0200
@@ -502,6 +502,8 @@
                               (encode-coding-string expression 'utf-8)
                             (copy-sequence expression))))
                (set-text-properties 0 (length text) nil text)
+               (when multibyte
+                 (princ "b"))
                (princ (mapconcat 'identity
                                  (split-string (prin1-to-string text) "\n")
                                  "\\n"))

Sorry for the delay but your patch with using a byte array works fine!

This issue was closed but the patch provided by Urs ended up not being applied and so the problem remains in master. Shouldn't the patch be applied to master?