bpo-13153: Use OS native encoding for converting between Python and Tcl. (GH-16545)

On Windows use UTF-16 (or UTF-32 for 32-bit Tcl_UniChar) with the
"surrogatepass" error handler for converting to/from Tcl Unicode objects.

On Linux use UTF-8 with the "surrogateescape" error handler for converting
to/from Tcl String objects.

Converting strings from Tcl to Python and back now never fails
(except MemoryError).
This commit is contained in:
Serhiy Storchaka 2019-10-04 13:09:52 +03:00 committed by GitHub
parent 2290b23dfc
commit 06cb94bc84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 241 additions and 242 deletions

View file

@ -358,21 +358,6 @@ def set_width(self):
Font(text, font=text.cget('font')).measure('0')
self.width = pixel_width // zero_char_width
def _filename_to_unicode(self, filename):
"""Return filename as BMP unicode so displayable in Tk."""
# Decode bytes to unicode.
if isinstance(filename, bytes):
try:
filename = filename.decode(self.filesystemencoding)
except UnicodeDecodeError:
try:
filename = filename.decode(self.encoding)
except UnicodeDecodeError:
# byte-to-byte conversion
filename = filename.decode('iso8859-1')
# Replace non-BMP char with diamond questionmark.
return re.sub('[\U00010000-\U0010FFFF]', '\ufffd', filename)
def new_callback(self, event):
dirname, basename = self.io.defaultfilename()
self.flist.new(dirname)
@ -963,10 +948,8 @@ def update_recent_files_list(self, new_file=None):
menu.delete(0, END) # clear, and rebuild:
for i, file_name in enumerate(rf_list):
file_name = file_name.rstrip() # zap \n
# make unicode string to display non-ASCII chars correctly
ufile_name = self._filename_to_unicode(file_name)
callback = instance.__recent_file_callback(file_name)
menu.add_command(label=ulchars[i] + " " + ufile_name,
menu.add_command(label=ulchars[i] + " " + file_name,
command=callback,
underline=0)
@ -1004,16 +987,10 @@ def reset_undo(self):
def short_title(self):
filename = self.io.filename
if filename:
filename = os.path.basename(filename)
else:
filename = "untitled"
# return unicode string to display non-ASCII chars correctly
return self._filename_to_unicode(filename)
return os.path.basename(filename) if filename else "untitled"
def long_title(self):
# return unicode string to display non-ASCII chars correctly
return self._filename_to_unicode(self.io.filename or "")
return self.io.filename or ""
def center_insert_event(self, event):
self.center()

View file

@ -30,18 +30,6 @@ def test_init(self):
e._close()
class EditorFunctionTest(unittest.TestCase):
def test_filename_to_unicode(self):
func = Editor._filename_to_unicode
class dummy():
filesystemencoding = 'utf-8'
pairs = (('abc', 'abc'), ('a\U00011111c', 'a\ufffdc'),
(b'abc', 'abc'), (b'a\xf0\x91\x84\x91c', 'a\ufffdc'))
for inp, out in pairs:
self.assertEqual(func(dummy, inp), out)
class TestGetLineIndent(unittest.TestCase):
def test_empty_lines(self):
for tabwidth in [1, 2, 4, 6, 8]:

View file

@ -679,14 +679,6 @@ def runsource(self, source):
self.more = 0
# at the moment, InteractiveInterpreter expects str
assert isinstance(source, str)
#if isinstance(source, str):
# from idlelib import iomenu
# try:
# source = source.encode(iomenu.encoding)
# except UnicodeError:
# self.tkconsole.resetoutput()
# self.write("Unsupported characters in input\n")
# return
# InteractiveInterpreter.runsource() calls its runcode() method,
# which is overridden (see below)
return InteractiveInterpreter.runsource(self, source, filename)
@ -1298,16 +1290,6 @@ def resetoutput(self):
self.set_line_and_column()
def write(self, s, tags=()):
if isinstance(s, str) and len(s) and max(s) > '\uffff':
# Tk doesn't support outputting non-BMP characters
# Let's assume what printed string is not very long,
# find first non-BMP character and construct informative
# UnicodeEncodeError exception.
for start, char in enumerate(s):
if char > '\uffff':
break
raise UnicodeEncodeError("UCS-2", char, start, start+1,
'Non-BMP character not supported in Tk')
try:
self.text.mark_gravity("iomark", "right")
count = OutputWindow.write(self, s, tags, "iomark")

View file

@ -147,8 +147,7 @@ def _run_module_event(self, event, *, customize=False):
interp = self.shell.interp
if pyshell.use_subprocess and restart:
interp.restart_subprocess(
with_cwd=False, filename=
self.editwin._filename_to_unicode(filename))
with_cwd=False, filename=filename)
dirname = os.path.dirname(filename)
argv = [filename]
if self.cli_args:

View file

@ -429,9 +429,12 @@ def passValue(value):
self.assertEqual(passValue(False), False if self.wantobjects else '0')
self.assertEqual(passValue('string'), 'string')
self.assertEqual(passValue('string\u20ac'), 'string\u20ac')
self.assertEqual(passValue('string\U0001f4bb'), 'string\U0001f4bb')
self.assertEqual(passValue('str\x00ing'), 'str\x00ing')
self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd')
self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac')
self.assertEqual(passValue('str\x00ing\U0001f4bb'),
'str\x00ing\U0001f4bb')
self.assertEqual(passValue(b'str\x00ing'),
b'str\x00ing' if self.wantobjects else 'str\x00ing')
self.assertEqual(passValue(b'str\xc0\x80ing'),
@ -490,6 +493,7 @@ def float_eq(actual, expected):
check('string')
check('string\xbd')
check('string\u20ac')
check('string\U0001f4bb')
check('')
check(b'string', 'string')
check(b'string\xe2\x82\xac', 'string\xe2\x82\xac')
@ -531,6 +535,7 @@ def test_splitlist(self):
('a\n b\t\r c\n ', ('a', 'b', 'c')),
(b'a\n b\t\r c\n ', ('a', 'b', 'c')),
('a \u20ac', ('a', '\u20ac')),
('a \U0001f4bb', ('a', '\U0001f4bb')),
(b'a \xe2\x82\xac', ('a', '\u20ac')),
(b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
('a {b c}', ('a', 'b c')),

View file

@ -170,6 +170,28 @@ def callback():
with self.assertRaises(tkinter.TclError):
root.tk.call('after', 'info', idle1)
def test_clipboard(self):
root = self.root
root.clipboard_clear()
root.clipboard_append('Ùñî')
self.assertEqual(root.clipboard_get(), 'Ùñî')
root.clipboard_append('çōđě')
self.assertEqual(root.clipboard_get(), 'Ùñîçōđě')
root.clipboard_clear()
with self.assertRaises(tkinter.TclError):
root.clipboard_get()
def test_clipboard_astral(self):
root = self.root
root.clipboard_clear()
root.clipboard_append('𝔘𝔫𝔦')
self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦')
root.clipboard_append('𝔠𝔬𝔡𝔢')
self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦𝔠𝔬𝔡𝔢')
root.clipboard_clear()
with self.assertRaises(tkinter.TclError):
root.clipboard_get()
tests_gui = (MiscTest, )

View file

@ -489,8 +489,7 @@ def check_get_current(getval, currval):
expected=('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string'))
self.checkParam(self.combo, 'values', '',
expected='' if get_tk_patchlevel() < (8, 5, 10) else ())
self.checkParam(self.combo, 'values', '')
self.combo['values'] = ['a', 1, 'c']
@ -1245,12 +1244,7 @@ def test_values(self):
expected=('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.spin, 'values', (42, 3.14, '', 'any string'))
self.checkParam(
self.spin,
'values',
'',
expected='' if get_tk_patchlevel() < (8, 5, 10) else ()
)
self.checkParam(self.spin, 'values', '')
self.spin['values'] = ['a', 1, 'c']
@ -1308,8 +1302,7 @@ def test_columns(self):
self.checkParam(widget, 'columns', 'a b c',
expected=('a', 'b', 'c'))
self.checkParam(widget, 'columns', ('a', 'b', 'c'))
self.checkParam(widget, 'columns', (),
expected='' if get_tk_patchlevel() < (8, 5, 10) else ())
self.checkParam(widget, 'columns', '')
def test_displaycolumns(self):
widget = self.create()

View file

@ -0,0 +1,4 @@
OS native encoding is now used for converting between Python strings and
Tcl objects. This allows to display, copy and paste to clipboard emoji and
other non-BMP characters. Converting strings from Tcl to Python and back
now never fails (except MemoryError).

View file

@ -96,6 +96,24 @@ Copyright (C) 1994 Steen Lumholt.
#endif /* HAVE_CREATEFILEHANDLER */
/* Use OS native encoding for converting between Python strings and
Tcl objects.
On Windows use UTF-16 (or UTF-32 for 32-bit Tcl_UniChar) with the
"surrogatepass" error handler for converting to/from Tcl Unicode objects.
On Linux use UTF-8 with the "surrogateescape" error handler for converting
to/from Tcl String objects. */
#ifdef MS_WINDOWS
#define USE_TCL_UNICODE 1
#else
#define USE_TCL_UNICODE 0
#endif
#if PY_LITTLE_ENDIAN
#define NATIVE_BYTEORDER -1
#else
#define NATIVE_BYTEORDER 1
#endif
#ifdef MS_WINDOWS
#include <conio.h>
#define WAIT_FOR_STDIN
@ -290,7 +308,6 @@ typedef struct {
} TkappObject;
#define Tkapp_Interp(v) (((TkappObject *) (v))->interp)
#define Tkapp_Result(v) Tcl_GetStringResult(Tkapp_Interp(v))
#define DEBUG_REFCNT(v) (printf("DEBUG: id=%p, refcnt=%i\n", \
(void *) v, Py_REFCNT(v)))
@ -311,10 +328,16 @@ static int tk_load_failed = 0;
#endif
static PyObject *Tkapp_UnicodeResult(TkappObject *);
static PyObject *
Tkinter_Error(PyObject *v)
Tkinter_Error(TkappObject *self)
{
PyErr_SetString(Tkinter_TclError, Tkapp_Result(v));
PyObject *res = Tkapp_UnicodeResult(self);
if (res != NULL) {
PyErr_SetObject(Tkinter_TclError, res);
Py_DECREF(res);
}
return NULL;
}
@ -368,30 +391,35 @@ static PyObject *
unicodeFromTclStringAndSize(const char *s, Py_ssize_t size)
{
PyObject *r = PyUnicode_DecodeUTF8(s, size, NULL);
if (!r && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
/* Tcl encodes null character as \xc0\x80 */
if (memchr(s, '\xc0', size)) {
char *buf, *q;
const char *e = s + size;
PyErr_Clear();
q = buf = (char *)PyMem_Malloc(size);
if (buf == NULL) {
PyErr_NoMemory();
return NULL;
}
while (s != e) {
if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') {
*q++ = '\0';
s += 2;
}
else
*q++ = *s++;
}
s = buf;
size = q - s;
r = PyUnicode_DecodeUTF8(s, size, NULL);
PyMem_Free(buf);
if (r != NULL || !PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
return r;
}
char *buf = NULL;
PyErr_Clear();
/* Tcl encodes null character as \xc0\x80 */
if (memchr(s, '\xc0', size)) {
char *q;
const char *e = s + size;
q = buf = (char *)PyMem_Malloc(size);
if (buf == NULL) {
PyErr_NoMemory();
return NULL;
}
while (s != e) {
if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') {
*q++ = '\0';
s += 2;
}
else
*q++ = *s++;
}
s = buf;
size = q - s;
}
r = PyUnicode_DecodeUTF8(s, size, "surrogateescape");
if (buf != NULL) {
PyMem_Free(buf);
}
return r;
}
@ -406,8 +434,21 @@ static PyObject *
unicodeFromTclObj(Tcl_Obj *value)
{
int len;
char *s = Tcl_GetStringFromObj(value, &len);
#if USE_TCL_UNICODE
int byteorder = NATIVE_BYTEORDER;
const Tcl_UniChar *u = Tcl_GetUnicodeFromObj(value, &len);
if (sizeof(Tcl_UniChar) == 2)
return PyUnicode_DecodeUTF16((const char *)u, len * 2,
"surrogatepass", &byteorder);
else if (sizeof(Tcl_UniChar) == 4)
return PyUnicode_DecodeUTF32((const char *)u, len * 4,
"surrogatepass", &byteorder);
else
Py_UNREACHABLE();
#else
const char *s = Tcl_GetStringFromObj(value, &len);
return unicodeFromTclStringAndSize(s, len);
#endif
}
@ -746,7 +787,7 @@ Tkapp_New(const char *screenName, const char *className,
#endif
if (Tcl_AppInit(v->interp) != TCL_OK) {
PyObject *result = Tkinter_Error((PyObject *)v);
PyObject *result = Tkinter_Error(v);
#ifdef TKINTER_PROTECT_LOADTK
if (wantTk) {
const char *_tkinter_tk_failed;
@ -817,12 +858,6 @@ PyTclObject_dealloc(PyTclObject *self)
Py_DECREF(tp);
}
static const char *
PyTclObject_TclString(PyObject *self)
{
return Tcl_GetString(((PyTclObject*)self)->value);
}
/* Like _str, but create Unicode if necessary. */
PyDoc_STRVAR(PyTclObject_string__doc__,
"the string representation of this object, either as str or bytes");
@ -1048,53 +1083,51 @@ AsObj(PyObject *value)
}
if (PyUnicode_Check(value)) {
void *inbuf;
Py_ssize_t size;
int kind;
Tcl_UniChar *outbuf = NULL;
Py_ssize_t i;
size_t allocsize;
if (PyUnicode_READY(value) == -1)
return NULL;
inbuf = PyUnicode_DATA(value);
size = PyUnicode_GET_LENGTH(value);
if (size == 0)
return Tcl_NewUnicodeObj((const void *)"", 0);
Py_ssize_t size = PyUnicode_GET_LENGTH(value);
if (size == 0) {
return Tcl_NewStringObj("", 0);
}
if (!CHECK_SIZE(size, sizeof(Tcl_UniChar))) {
PyErr_SetString(PyExc_OverflowError, "string is too long");
return NULL;
}
kind = PyUnicode_KIND(value);
if (kind == sizeof(Tcl_UniChar))
return Tcl_NewUnicodeObj(inbuf, (int)size);
allocsize = ((size_t)size) * sizeof(Tcl_UniChar);
outbuf = (Tcl_UniChar*)PyMem_Malloc(allocsize);
/* Else overflow occurred, and we take the next exit */
if (!outbuf) {
PyErr_NoMemory();
if (PyUnicode_IS_ASCII(value)) {
return Tcl_NewStringObj((const char *)PyUnicode_DATA(value),
(int)size);
}
PyObject *encoded;
#if USE_TCL_UNICODE
if (sizeof(Tcl_UniChar) == 2)
encoded = _PyUnicode_EncodeUTF16(value,
"surrogatepass", NATIVE_BYTEORDER);
else if (sizeof(Tcl_UniChar) == 4)
encoded = _PyUnicode_EncodeUTF32(value,
"surrogatepass", NATIVE_BYTEORDER);
else
Py_UNREACHABLE();
#else
encoded = _PyUnicode_AsUTF8String(value, "surrogateescape");
#endif
if (!encoded) {
return NULL;
}
for (i = 0; i < size; i++) {
Py_UCS4 ch = PyUnicode_READ(kind, inbuf, i);
/* We cannot test for sizeof(Tcl_UniChar) directly,
so we test for UTF-8 size instead. */
#if TCL_UTF_MAX == 3
if (ch >= 0x10000) {
/* Tcl doesn't do UTF-16, yet. */
PyErr_Format(Tkinter_TclError,
"character U+%x is above the range "
"(U+0000-U+FFFF) allowed by Tcl",
ch);
PyMem_Free(outbuf);
return NULL;
}
#endif
outbuf[i] = ch;
size = PyBytes_GET_SIZE(encoded);
if (size > INT_MAX) {
Py_DECREF(encoded);
PyErr_SetString(PyExc_OverflowError, "string is too long");
return NULL;
}
result = Tcl_NewUnicodeObj(outbuf, (int)size);
PyMem_Free(outbuf);
#if USE_TCL_UNICODE
result = Tcl_NewUnicodeObj((const Tcl_UniChar *)PyBytes_AS_STRING(encoded),
(int)(size / sizeof(Tcl_UniChar)));
#else
result = Tcl_NewStringObj(PyBytes_AS_STRING(encoded), (int)size);
#endif
Py_DECREF(encoded);
return result;
}
@ -1113,7 +1146,7 @@ AsObj(PyObject *value)
}
static PyObject *
fromBoolean(PyObject* tkapp, Tcl_Obj *value)
fromBoolean(TkappObject *tkapp, Tcl_Obj *value)
{
int boolValue;
if (Tcl_GetBooleanFromObj(Tkapp_Interp(tkapp), value, &boolValue) == TCL_ERROR)
@ -1122,7 +1155,7 @@ fromBoolean(PyObject* tkapp, Tcl_Obj *value)
}
static PyObject*
fromWideIntObj(PyObject* tkapp, Tcl_Obj *value)
fromWideIntObj(TkappObject *tkapp, Tcl_Obj *value)
{
Tcl_WideInt wideValue;
if (Tcl_GetWideIntFromObj(Tkapp_Interp(tkapp), value, &wideValue) == TCL_OK) {
@ -1138,7 +1171,7 @@ fromWideIntObj(PyObject* tkapp, Tcl_Obj *value)
#ifdef HAVE_LIBTOMMAMTH
static PyObject*
fromBignumObj(PyObject* tkapp, Tcl_Obj *value)
fromBignumObj(TkappObject *tkapp, Tcl_Obj *value)
{
mp_int bigValue;
unsigned long numBytes;
@ -1174,32 +1207,31 @@ fromBignumObj(PyObject* tkapp, Tcl_Obj *value)
#endif
static PyObject*
FromObj(PyObject* tkapp, Tcl_Obj *value)
FromObj(TkappObject *tkapp, Tcl_Obj *value)
{
PyObject *result = NULL;
TkappObject *app = (TkappObject*)tkapp;
Tcl_Interp *interp = Tkapp_Interp(tkapp);
if (value->typePtr == NULL) {
return unicodeFromTclStringAndSize(value->bytes, value->length);
return unicodeFromTclObj(value);
}
if (value->typePtr == app->BooleanType ||
value->typePtr == app->OldBooleanType) {
if (value->typePtr == tkapp->BooleanType ||
value->typePtr == tkapp->OldBooleanType) {
return fromBoolean(tkapp, value);
}
if (value->typePtr == app->ByteArrayType) {
if (value->typePtr == tkapp->ByteArrayType) {
int size;
char *data = (char*)Tcl_GetByteArrayFromObj(value, &size);
return PyBytes_FromStringAndSize(data, size);
}
if (value->typePtr == app->DoubleType) {
if (value->typePtr == tkapp->DoubleType) {
return PyFloat_FromDouble(value->internalRep.doubleValue);
}
if (value->typePtr == app->IntType) {
if (value->typePtr == tkapp->IntType) {
long longValue;
if (Tcl_GetLongFromObj(interp, value, &longValue) == TCL_OK)
return PyLong_FromLong(longValue);
@ -1207,8 +1239,8 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
fall through to wideInt handling. */
}
if (value->typePtr == app->IntType ||
value->typePtr == app->WideIntType) {
if (value->typePtr == tkapp->IntType ||
value->typePtr == tkapp->WideIntType) {
result = fromWideIntObj(tkapp, value);
if (result != NULL || PyErr_Occurred())
return result;
@ -1218,14 +1250,14 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
}
#ifdef HAVE_LIBTOMMAMTH
if (value->typePtr == app->IntType ||
value->typePtr == app->WideIntType ||
value->typePtr == app->BignumType) {
if (value->typePtr == tkapp->IntType ||
value->typePtr == tkapp->WideIntType ||
value->typePtr == tkapp->BignumType) {
return fromBignumObj(tkapp, value);
}
#endif
if (value->typePtr == app->ListType) {
if (value->typePtr == tkapp->ListType) {
int size;
int i, status;
PyObject *elem;
@ -1253,30 +1285,28 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
return result;
}
if (value->typePtr == app->ProcBodyType) {
if (value->typePtr == tkapp->ProcBodyType) {
/* fall through: return tcl object. */
}
if (value->typePtr == app->StringType) {
return PyUnicode_FromKindAndData(
sizeof(Tcl_UniChar), Tcl_GetUnicode(value),
Tcl_GetCharLength(value));
if (value->typePtr == tkapp->StringType) {
return unicodeFromTclObj(value);
}
#if TK_HEX_VERSION >= 0x08050000
if (app->BooleanType == NULL &&
if (tkapp->BooleanType == NULL &&
strcmp(value->typePtr->name, "booleanString") == 0) {
/* booleanString type is not registered in Tcl */
app->BooleanType = value->typePtr;
tkapp->BooleanType = value->typePtr;
return fromBoolean(tkapp, value);
}
#endif
#ifdef HAVE_LIBTOMMAMTH
if (app->BignumType == NULL &&
if (tkapp->BignumType == NULL &&
strcmp(value->typePtr->name, "bignum") == 0) {
/* bignum type is not registered in Tcl */
app->BignumType = value->typePtr;
tkapp->BignumType = value->typePtr;
return fromBignumObj(tkapp, value);
}
#endif
@ -1366,19 +1396,28 @@ Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc)
return NULL;
}
/* Convert the results of a command call into a Python string. */
static PyObject *
Tkapp_UnicodeResult(TkappObject *self)
{
return unicodeFromTclObj(Tcl_GetObjResult(self->interp));
}
/* Convert the results of a command call into a Python objects. */
static PyObject*
Tkapp_CallResult(TkappObject *self)
static PyObject *
Tkapp_ObjectResult(TkappObject *self)
{
PyObject *res = NULL;
Tcl_Obj *value = Tcl_GetObjResult(self->interp);
if(self->wantobjects) {
if (self->wantobjects) {
/* Not sure whether the IncrRef is necessary, but something
may overwrite the interpreter result while we are
converting it. */
Tcl_IncrRefCount(value);
res = FromObj((PyObject*)self, value);
res = FromObj(self, value);
Tcl_DecrRefCount(value);
} else {
res = unicodeFromTclObj(value);
@ -1410,15 +1449,13 @@ Tkapp_CallProc(Tkapp_CallEvent *e, int flags)
i = Tcl_EvalObjv(e->self->interp, objc, objv, e->flags);
ENTER_PYTHON
if (i == TCL_ERROR) {
*(e->res) = NULL;
*(e->exc_type) = NULL;
*(e->exc_tb) = NULL;
*(e->exc_value) = PyObject_CallFunction(
Tkinter_TclError, "s",
Tcl_GetStringResult(e->self->interp));
*(e->res) = Tkinter_Error(e->self);
}
else {
*(e->res) = Tkapp_CallResult(e->self);
*(e->res) = Tkapp_ObjectResult(e->self);
}
if (*(e->res) == NULL) {
PyErr_Fetch(e->exc_type, e->exc_value, e->exc_tb);
}
LEAVE_PYTHON
@ -1506,9 +1543,9 @@ Tkapp_Call(PyObject *selfptr, PyObject *args)
ENTER_OVERLAP
if (i == TCL_ERROR)
Tkinter_Error(selfptr);
Tkinter_Error(self);
else
res = Tkapp_CallResult(self);
res = Tkapp_ObjectResult(self);
LEAVE_OVERLAP_TCL
@ -1540,9 +1577,9 @@ _tkinter_tkapp_eval_impl(TkappObject *self, const char *script)
err = Tcl_Eval(Tkapp_Interp(self), script);
ENTER_OVERLAP
if (err == TCL_ERROR)
res = Tkinter_Error((PyObject *)self);
res = Tkinter_Error(self);
else
res = unicodeFromTclString(Tkapp_Result(self));
res = Tkapp_UnicodeResult(self);
LEAVE_OVERLAP_TCL
return res;
}
@ -1569,9 +1606,9 @@ _tkinter_tkapp_evalfile_impl(TkappObject *self, const char *fileName)
err = Tcl_EvalFile(Tkapp_Interp(self), fileName);
ENTER_OVERLAP
if (err == TCL_ERROR)
res = Tkinter_Error((PyObject *)self);
res = Tkinter_Error(self);
else
res = unicodeFromTclString(Tkapp_Result(self));
res = Tkapp_UnicodeResult(self);
LEAVE_OVERLAP_TCL
return res;
}
@ -1598,9 +1635,9 @@ _tkinter_tkapp_record_impl(TkappObject *self, const char *script)
err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL);
ENTER_OVERLAP
if (err == TCL_ERROR)
res = Tkinter_Error((PyObject *)self);
res = Tkinter_Error(self);
else
res = unicodeFromTclString(Tkapp_Result(self));
res = Tkapp_UnicodeResult(self);
LEAVE_OVERLAP_TCL
return res;
}
@ -1631,13 +1668,13 @@ _tkinter_tkapp_adderrorinfo_impl(TkappObject *self, const char *msg)
/** Tcl Variable **/
typedef PyObject* (*EventFunc)(PyObject*, PyObject *args, int flags);
typedef PyObject* (*EventFunc)(TkappObject *, PyObject *, int);
TCL_DECLARE_MUTEX(var_mutex)
typedef struct VarEvent {
Tcl_Event ev; /* must be first */
PyObject *self;
TkappObject *self;
PyObject *args;
int flags;
EventFunc func;
@ -1692,7 +1729,7 @@ varname_converter(PyObject *in, void *_out)
return 1;
}
if (PyTclObject_Check(in)) {
*out = PyTclObject_TclString(in);
*out = Tcl_GetString(((PyTclObject *)in)->value);
return 1;
}
PyErr_Format(PyExc_TypeError,
@ -1750,7 +1787,7 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags)
PyErr_NoMemory();
return NULL;
}
ev->self = selfptr;
ev->self = self;
ev->args = args;
ev->flags = flags;
ev->func = func;
@ -1770,11 +1807,11 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags)
return res;
}
/* Tcl is not threaded, or this is the interpreter thread. */
return func(selfptr, args, flags);
return func(self, args, flags);
}
static PyObject *
SetVar(PyObject *self, PyObject *args, int flags)
SetVar(TkappObject *self, PyObject *args, int flags)
{
const char *name1, *name2;
PyObject *newValue;
@ -1843,7 +1880,7 @@ Tkapp_GlobalSetVar(PyObject *self, PyObject *args)
static PyObject *
GetVar(PyObject *self, PyObject *args, int flags)
GetVar(TkappObject *self, PyObject *args, int flags)
{
const char *name1, *name2=NULL;
PyObject *res = NULL;
@ -1858,10 +1895,9 @@ GetVar(PyObject *self, PyObject *args, int flags)
tres = Tcl_GetVar2Ex(Tkapp_Interp(self), name1, name2, flags);
ENTER_OVERLAP
if (tres == NULL) {
PyErr_SetString(Tkinter_TclError,
Tcl_GetStringResult(Tkapp_Interp(self)));
Tkinter_Error(self);
} else {
if (((TkappObject*)self)->wantobjects) {
if (self->wantobjects) {
res = FromObj(self, tres);
}
else {
@ -1887,7 +1923,7 @@ Tkapp_GlobalGetVar(PyObject *self, PyObject *args)
static PyObject *
UnsetVar(PyObject *self, PyObject *args, int flags)
UnsetVar(TkappObject *self, PyObject *args, int flags)
{
char *name1, *name2=NULL;
int code;
@ -1959,7 +1995,7 @@ _tkinter_tkapp_getint(TkappObject *self, PyObject *arg)
CHECK_STRING_LENGTH(s);
value = Tcl_NewStringObj(s, -1);
if (value == NULL)
return Tkinter_Error((PyObject *)self);
return Tkinter_Error(self);
}
/* Don't use Tcl_GetInt() because it returns ambiguous result for value
in ranges -2**32..-2**31-1 and 2**31..2**32-1 (on 32-bit platform).
@ -1968,14 +2004,14 @@ _tkinter_tkapp_getint(TkappObject *self, PyObject *arg)
value in ranges -2**64..-2**63-1 and 2**63..2**64-1 (on 32-bit platform).
*/
#ifdef HAVE_LIBTOMMAMTH
result = fromBignumObj((PyObject *)self, value);
result = fromBignumObj(self, value);
#else
result = fromWideIntObj((PyObject *)self, value);
result = fromWideIntObj(self, value);
#endif
Tcl_DecrRefCount(value);
if (result != NULL || PyErr_Occurred())
return result;
return Tkinter_Error((PyObject *)self);
return Tkinter_Error(self);
}
/*[clinic input]
@ -2006,7 +2042,7 @@ _tkinter_tkapp_getdouble(TkappObject *self, PyObject *arg)
if (Tcl_GetDoubleFromObj(Tkapp_Interp(self),
((PyTclObject*)arg)->value,
&v) == TCL_ERROR)
return Tkinter_Error((PyObject *)self);
return Tkinter_Error(self);
return PyFloat_FromDouble(v);
}
@ -2014,7 +2050,7 @@ _tkinter_tkapp_getdouble(TkappObject *self, PyObject *arg)
return NULL;
CHECK_STRING_LENGTH(s);
if (Tcl_GetDouble(Tkapp_Interp(self), s, &v) == TCL_ERROR)
return Tkinter_Error((PyObject *)self);
return Tkinter_Error(self);
return PyFloat_FromDouble(v);
}
@ -2041,7 +2077,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
if (Tcl_GetBooleanFromObj(Tkapp_Interp(self),
((PyTclObject*)arg)->value,
&v) == TCL_ERROR)
return Tkinter_Error((PyObject *)self);
return Tkinter_Error(self);
return PyBool_FromLong(v);
}
@ -2049,7 +2085,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
return NULL;
CHECK_STRING_LENGTH(s);
if (Tcl_GetBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR)
return Tkinter_Error((PyObject *)self);
return Tkinter_Error(self);
return PyBool_FromLong(v);
}
@ -2075,9 +2111,9 @@ _tkinter_tkapp_exprstring_impl(TkappObject *self, const char *s)
retval = Tcl_ExprString(Tkapp_Interp(self), s);
ENTER_OVERLAP
if (retval == TCL_ERROR)
res = Tkinter_Error((PyObject *)self);
res = Tkinter_Error(self);
else
res = unicodeFromTclString(Tkapp_Result(self));
res = Tkapp_UnicodeResult(self);
LEAVE_OVERLAP_TCL
return res;
}
@ -2105,7 +2141,7 @@ _tkinter_tkapp_exprlong_impl(TkappObject *self, const char *s)
retval = Tcl_ExprLong(Tkapp_Interp(self), s, &v);
ENTER_OVERLAP
if (retval == TCL_ERROR)
res = Tkinter_Error((PyObject *)self);
res = Tkinter_Error(self);
else
res = PyLong_FromLong(v);
LEAVE_OVERLAP_TCL
@ -2136,7 +2172,7 @@ _tkinter_tkapp_exprdouble_impl(TkappObject *self, const char *s)
ENTER_OVERLAP
PyFPE_END_PROTECT(retval)
if (retval == TCL_ERROR)
res = Tkinter_Error((PyObject *)self);
res = Tkinter_Error(self);
else
res = PyFloat_FromDouble(v);
LEAVE_OVERLAP_TCL
@ -2165,7 +2201,7 @@ _tkinter_tkapp_exprboolean_impl(TkappObject *self, const char *s)
retval = Tcl_ExprBoolean(Tkapp_Interp(self), s, &v);
ENTER_OVERLAP
if (retval == TCL_ERROR)
res = Tkinter_Error((PyObject *)self);
res = Tkinter_Error(self);
else
res = PyLong_FromLong(v);
LEAVE_OVERLAP_TCL
@ -2198,12 +2234,12 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg)
if (Tcl_ListObjGetElements(Tkapp_Interp(self),
((PyTclObject*)arg)->value,
&objc, &objv) == TCL_ERROR) {
return Tkinter_Error((PyObject *)self);
return Tkinter_Error(self);
}
if (!(v = PyTuple_New(objc)))
return NULL;
for (i = 0; i < objc; i++) {
PyObject *s = FromObj((PyObject*)self, objv[i]);
PyObject *s = FromObj(self, objv[i]);
if (!s) {
Py_DECREF(v);
return NULL;
@ -2231,7 +2267,7 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg)
if (Tcl_SplitList(Tkapp_Interp(self), list,
&argc, &argv) == TCL_ERROR) {
PyMem_Free(list);
return Tkinter_Error((PyObject *)self);
return Tkinter_Error(self);
}
if (!(v = PyTuple_New(argc)))
@ -2275,16 +2311,16 @@ _tkinter_tkapp_split(TkappObject *self, PyObject *arg)
int i;
if (Tcl_ListObjGetElements(Tkapp_Interp(self), value,
&objc, &objv) == TCL_ERROR) {
return FromObj((PyObject*)self, value);
return FromObj(self, value);
}
if (objc == 0)
return PyUnicode_FromString("");
if (objc == 1)
return FromObj((PyObject*)self, objv[0]);
return FromObj(self, objv[0]);
if (!(v = PyTuple_New(objc)))
return NULL;
for (i = 0; i < objc; i++) {
PyObject *s = FromObj((PyObject*)self, objv[i]);
PyObject *s = FromObj(self, objv[i]);
if (!s) {
Py_DECREF(v);
return NULL;
@ -2331,34 +2367,31 @@ PythonCmd_Error(Tcl_Interp *interp)
* function or method.
*/
static int
PythonCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
PythonCmd(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[])
{
PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData;
PyObject *func, *arg, *res;
int i, rv;
PyObject *args, *res;
int i;
Tcl_Obj *obj_res;
ENTER_PYTHON
/* TBD: no error checking here since we know, via the
* Tkapp_CreateCommand() that the client data is a two-tuple
*/
func = data->func;
/* Create argument list (argv1, ..., argvN) */
if (!(arg = PyTuple_New(argc - 1)))
/* Create argument tuple (objv1, ..., objvN) */
if (!(args = PyTuple_New(objc - 1)))
return PythonCmd_Error(interp);
for (i = 0; i < (argc - 1); i++) {
PyObject *s = unicodeFromTclString(argv[i + 1]);
for (i = 0; i < (objc - 1); i++) {
PyObject *s = unicodeFromTclObj(objv[i + 1]);
if (!s) {
Py_DECREF(arg);
Py_DECREF(args);
return PythonCmd_Error(interp);
}
PyTuple_SET_ITEM(arg, i, s);
PyTuple_SET_ITEM(args, i, s);
}
res = PyObject_Call(func, arg, NULL);
Py_DECREF(arg);
res = PyObject_Call(data->func, args, NULL);
Py_DECREF(args);
if (res == NULL)
return PythonCmd_Error(interp);
@ -2368,18 +2401,15 @@ PythonCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[
Py_DECREF(res);
return PythonCmd_Error(interp);
}
else {
Tcl_SetObjResult(interp, obj_res);
rv = TCL_OK;
}
Tcl_SetObjResult(interp, obj_res);
Py_DECREF(res);
LEAVE_PYTHON
return rv;
return TCL_OK;
}
static void
PythonCmdDelete(ClientData clientData)
{
@ -2411,7 +2441,7 @@ static int
Tkapp_CommandProc(CommandEvent *ev, int flags)
{
if (ev->create)
*ev->status = Tcl_CreateCommand(
*ev->status = Tcl_CreateObjCommand(
ev->interp, ev->name, PythonCmd,
ev->data, PythonCmdDelete) == NULL;
else
@ -2477,7 +2507,7 @@ _tkinter_tkapp_createcommand_impl(TkappObject *self, const char *name,
else
{
ENTER_TCL
err = Tcl_CreateCommand(
err = Tcl_CreateObjCommand(
Tkapp_Interp(self), name, PythonCmd,
(ClientData)data, PythonCmdDelete) == NULL;
LEAVE_TCL
@ -2953,9 +2983,9 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self)
if (err == TCL_ERROR) {
/* This sets an exception, but we cannot return right
away because we need to exit the overlap first. */
Tkinter_Error((PyObject *)self);
Tkinter_Error(self);
} else {
_tk_exists = Tkapp_Result(self);
_tk_exists = Tcl_GetStringResult(Tkapp_Interp(self));
}
LEAVE_OVERLAP_TCL
if (err == TCL_ERROR) {
@ -2963,8 +2993,7 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self)
}
if (_tk_exists == NULL || strcmp(_tk_exists, "1") != 0) {
if (Tk_Init(interp) == TCL_ERROR) {
PyErr_SetString(Tkinter_TclError,
Tcl_GetStringResult(Tkapp_Interp(self)));
Tkinter_Error(self);
#ifdef TKINTER_PROTECT_LOADTK
tk_load_failed = 1;
#endif