Skip to content

Commit 276fb6a

Browse files
committed
fixed some unicode hiccups
- compile() is a little finnicky. everything’s great if you pass it a unicode source string *unless* the string contains a line like `# encoding: foo` (which raises a syntax error) - to get around this, we encode the script back to its preferred encoding then pass the bytestring to compile() - as a side-effect, the document class is now careful to honor the encoding string while reading in the text of a .pv file (but seriously, just use utf-8. it’s on by default…)
1 parent 855bb3d commit 276fb6a

3 files changed

Lines changed: 32 additions & 12 deletions

File tree

plotdevice/gui/document.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,19 @@
1010
from Foundation import *
1111
from AppKit import *
1212

13-
# from plotdevice.gui.preferences import editor_info
14-
from plotdevice.gui.editor import OutputTextView, EditorView
15-
from plotdevice.gui.widgets import DashboardController, ExportSheet
16-
from plotdevice.gui.views import FullscreenWindow, FullscreenView
17-
from plotdevice.gui import set_timeout
18-
from plotdevice.run import Sandbox
19-
from plotdevice import util
13+
from .editor import OutputTextView, EditorView
14+
from .widgets import DashboardController, ExportSheet
15+
from .views import FullscreenWindow, FullscreenView
16+
from ..run import Sandbox, encoding
17+
from . import set_timeout
2018

2119
NSEventGestureAxisVertical = 2
2220

2321
class PlotDeviceDocument(NSDocument):
2422
def init(self):
2523
self.stationery = "TMPL:sketch" # untitled/example docs flag
2624
self.source = None # string read in from file
25+
self.source_enc = 'utf-8' # or the contents of a coding: comment
2726
self.script = None # window controller
2827
return super(PlotDeviceDocument,self).init()
2928

@@ -54,14 +53,18 @@ def setFileURL_(self, url):
5453

5554
def writeToURL_ofType_error_(self, url, tp, err):
5655
path = url.fileSystemRepresentation()
57-
text = self.script._get_source().encode("utf8")
56+
src = self.script._get_source()
57+
# always use the same encoding we were read in with
58+
text = src.encode(self.source_enc)
5859
with file(path, 'w', 0) as f:
5960
f.write(text)
6061
return True, err
6162

6263
def readFromURL_ofType_error_(self, url, tp, err):
6364
path = url.fileSystemRepresentation()
64-
self.source = file(path).read().decode("utf-8")
65+
src = file(path).read()
66+
self.source_enc = encoding(src) or 'utf-8'
67+
self.source = src.decode(self.source_enc)
6568
if self.script:
6669
self.script.setPath_source_(self.path, self.source)
6770
return True, err

plotdevice/run/__init__.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
1-
import linecache
1+
import linecache, re
22
from sys import exc_info
33
from os.path import abspath, dirname, relpath
44
from traceback import format_list, format_exception_only
55

6+
def encoding(src):
7+
"""Searches the first two lines of a string looking for an `# encoding: ???` comment."""
8+
re_enc = re.compile(r'coding[=:]\s*([-\w.]+)')
9+
for line in src.split('\n')[:2]:
10+
if not line.strip().startswith('#'):
11+
continue
12+
m = re_enc.search(line)
13+
if not m:
14+
continue
15+
return m.group(1)
16+
return None
17+
618
def stacktrace(script=None, src=None):
719
"""print a clean traceback and optionally rewrite the paths relative to a script path"""
820

plotdevice/run/sandbox.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from PyObjCTools import AppHelper
77
from Foundation import *
88
from AppKit import *
9-
from ..run import stacktrace, coredump
9+
from ..run import stacktrace, coredump, encoding
1010
from ..lib.io import MovieExportSession, ImageExportSession
1111
from plotdevice import util, context, gfx, DeviceError
1212

@@ -138,12 +138,17 @@ def compile(self, src=None):
138138
# self.__doc__ = {}
139139
# self.namespace.update(dict( __doc__=self.__doc__, ))
140140

141+
141142
result = Outcome(True, [])
142143
if not self._code:
143144
# Compile the script
144145
def compileScript():
145146
scriptname = self._path or "<Untitled>"
146-
self._code = compile("%s\n\n"%self._source, scriptname.encode('ascii', 'ignore'), "exec")
147+
# src needs to be a bytestring if the script defines its encoding in an
148+
# `encoding: ...` comment. otherwise just pass the unicode to compile()
149+
enc = encoding(self._source)
150+
src = self._source.encode(enc) if enc else self._source
151+
self._code = compile(src, scriptname.encode('ascii', 'ignore'), "exec")
147152
result = self.call(compileScript)
148153
if not result.ok:
149154
return result

0 commit comments

Comments
 (0)