@@ -309,6 +309,7 @@ typedef struct {
309309 int threaded ; /* True if tcl_platform[threaded] */
310310 Tcl_ThreadId thread_id ;
311311 int dispatching ;
312+ PyObject * trace ;
312313 /* We cannot include tclInt.h, as this is internal.
313314 So we cache interesting types here. */
314315 const Tcl_ObjType * OldBooleanType ;
@@ -574,6 +575,7 @@ Tkapp_New(const char *screenName, const char *className,
574575 TCL_GLOBAL_ONLY ) != NULL ;
575576 v -> thread_id = Tcl_GetCurrentThread ();
576577 v -> dispatching = 0 ;
578+ v -> trace = NULL ;
577579
578580#ifndef TCL_THREADS
579581 if (v -> threaded ) {
@@ -1316,6 +1318,29 @@ Tkapp_ObjectResult(TkappObject *self)
13161318 return res ;
13171319}
13181320
1321+ static int
1322+ Tkapp_Trace (TkappObject * self , PyObject * args )
1323+ {
1324+ if (args == NULL ) {
1325+ return 0 ;
1326+ }
1327+ if (self -> trace ) {
1328+ PyObject * res = PyObject_CallObject (self -> trace , args );
1329+ if (res == NULL ) {
1330+ Py_DECREF (args );
1331+ return 0 ;
1332+ }
1333+ Py_DECREF (res );
1334+ }
1335+ Py_DECREF (args );
1336+ return 1 ;
1337+ }
1338+
1339+ #define TRACE (_self , ARGS ) do { \
1340+ if ((_self)->trace && !Tkapp_Trace((_self), Py_BuildValue ARGS)) { \
1341+ return NULL; \
1342+ } \
1343+ } while (0)
13191344
13201345/* Tkapp_CallProc is the event procedure that is executed in the context of
13211346 the Tcl interpreter thread. Initially, it holds the Tcl lock, and doesn't
@@ -1329,7 +1354,12 @@ Tkapp_CallProc(Tkapp_CallEvent *e, int flags)
13291354 int objc ;
13301355 int i ;
13311356 ENTER_PYTHON
1332- objv = Tkapp_CallArgs (e -> args , objStore , & objc );
1357+ if (e -> self -> trace && !Tkapp_Trace (e -> self , PyTuple_Pack (1 , e -> args ))) {
1358+ objv = NULL ;
1359+ }
1360+ else {
1361+ objv = Tkapp_CallArgs (e -> args , objStore , & objc );
1362+ }
13331363 if (!objv ) {
13341364 * (e -> exc ) = PyErr_GetRaisedException ();
13351365 * (e -> res ) = NULL ;
@@ -1422,6 +1452,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args)
14221452 }
14231453 else
14241454 {
1455+ TRACE (self , ("(O)" , args ));
14251456
14261457 objv = Tkapp_CallArgs (args , objStore , & objc );
14271458 if (!objv )
@@ -1464,6 +1495,8 @@ _tkinter_tkapp_eval_impl(TkappObject *self, const char *script)
14641495 CHECK_STRING_LENGTH (script );
14651496 CHECK_TCL_APPARTMENT ;
14661497
1498+ TRACE (self , ("((ss))" , "eval" , script ));
1499+
14671500 ENTER_TCL
14681501 err = Tcl_Eval (Tkapp_Interp (self ), script );
14691502 ENTER_OVERLAP
@@ -1493,6 +1526,8 @@ _tkinter_tkapp_evalfile_impl(TkappObject *self, const char *fileName)
14931526 CHECK_STRING_LENGTH (fileName );
14941527 CHECK_TCL_APPARTMENT ;
14951528
1529+ TRACE (self , ("((ss))" , "source" , fileName ));
1530+
14961531 ENTER_TCL
14971532 err = Tcl_EvalFile (Tkapp_Interp (self ), fileName );
14981533 ENTER_OVERLAP
@@ -1522,6 +1557,8 @@ _tkinter_tkapp_record_impl(TkappObject *self, const char *script)
15221557 CHECK_STRING_LENGTH (script );
15231558 CHECK_TCL_APPARTMENT ;
15241559
1560+ TRACE (self , ("((ssss))" , "history" , "add" , script , "exec" ));
1561+
15251562 ENTER_TCL
15261563 err = Tcl_RecordAndEval (Tkapp_Interp (self ), script , TCL_NO_EVAL );
15271564 ENTER_OVERLAP
@@ -1710,6 +1747,15 @@ SetVar(TkappObject *self, PyObject *args, int flags)
17101747 newval = AsObj (newValue );
17111748 if (newval == NULL )
17121749 return NULL ;
1750+
1751+ if (flags & TCL_GLOBAL_ONLY ) {
1752+ TRACE ((TkappObject * )self , ("((ssssO))" , "uplevel" , "#0" , "set" ,
1753+ name1 , newValue ));
1754+ }
1755+ else {
1756+ TRACE ((TkappObject * )self , ("((ssO))" , "set" , name1 , newValue ));
1757+ }
1758+
17131759 ENTER_TCL
17141760 ok = Tcl_SetVar2Ex (Tkapp_Interp (self ), name1 , NULL ,
17151761 newval , flags );
@@ -1727,8 +1773,22 @@ SetVar(TkappObject *self, PyObject *args, int flags)
17271773 return NULL ;
17281774 CHECK_STRING_LENGTH (name1 );
17291775 CHECK_STRING_LENGTH (name2 );
1776+
17301777 /* XXX must hold tcl lock already??? */
17311778 newval = AsObj (newValue );
1779+ if (((TkappObject * )self )-> trace ) {
1780+ if (flags & TCL_GLOBAL_ONLY ) {
1781+ TRACE ((TkappObject * )self , ("((sssNO))" , "uplevel" , "#0" , "set" ,
1782+ PyUnicode_FromFormat ("%s(%s)" , name1 , name2 ),
1783+ newValue ));
1784+ }
1785+ else {
1786+ TRACE ((TkappObject * )self , ("((sNO))" , "set" ,
1787+ PyUnicode_FromFormat ("%s(%s)" , name1 , name2 ),
1788+ newValue ));
1789+ }
1790+ }
1791+
17321792 ENTER_TCL
17331793 ok = Tcl_SetVar2Ex (Tkapp_Interp (self ), name1 , name2 , newval , flags );
17341794 ENTER_OVERLAP
@@ -1815,6 +1875,28 @@ UnsetVar(TkappObject *self, PyObject *args, int flags)
18151875
18161876 CHECK_STRING_LENGTH (name1 );
18171877 CHECK_STRING_LENGTH (name2 );
1878+
1879+ if (((TkappObject * )self )-> trace ) {
1880+ if (flags & TCL_GLOBAL_ONLY ) {
1881+ if (name2 ) {
1882+ TRACE ((TkappObject * )self , ("((sssN))" , "uplevel" , "#0" , "unset" ,
1883+ PyUnicode_FromFormat ("%s(%s)" , name1 , name2 )));
1884+ }
1885+ else {
1886+ TRACE ((TkappObject * )self , ("((ssss))" , "uplevel" , "#0" , "unset" , name1 ));
1887+ }
1888+ }
1889+ else {
1890+ if (name2 ) {
1891+ TRACE ((TkappObject * )self , ("((sN))" , "unset" ,
1892+ PyUnicode_FromFormat ("%s(%s)" , name1 , name2 )));
1893+ }
1894+ else {
1895+ TRACE ((TkappObject * )self , ("((ss))" , "unset" , name1 ));
1896+ }
1897+ }
1898+ }
1899+
18181900 ENTER_TCL
18191901 code = Tcl_UnsetVar2 (Tkapp_Interp (self ), name1 , name2 , flags );
18201902 ENTER_OVERLAP
@@ -1981,6 +2063,8 @@ _tkinter_tkapp_exprstring_impl(TkappObject *self, const char *s)
19812063 CHECK_STRING_LENGTH (s );
19822064 CHECK_TCL_APPARTMENT ;
19832065
2066+ TRACE (self , ("((ss))" , "expr" , s ));
2067+
19842068 ENTER_TCL
19852069 retval = Tcl_ExprString (Tkapp_Interp (self ), s );
19862070 ENTER_OVERLAP
@@ -2011,6 +2095,8 @@ _tkinter_tkapp_exprlong_impl(TkappObject *self, const char *s)
20112095 CHECK_STRING_LENGTH (s );
20122096 CHECK_TCL_APPARTMENT ;
20132097
2098+ TRACE (self , ("((ss))" , "expr" , s ));
2099+
20142100 ENTER_TCL
20152101 retval = Tcl_ExprLong (Tkapp_Interp (self ), s , & v );
20162102 ENTER_OVERLAP
@@ -2040,6 +2126,9 @@ _tkinter_tkapp_exprdouble_impl(TkappObject *self, const char *s)
20402126
20412127 CHECK_STRING_LENGTH (s );
20422128 CHECK_TCL_APPARTMENT ;
2129+
2130+ TRACE (self , ("((ss))" , "expr" , s ));
2131+
20432132 ENTER_TCL
20442133 retval = Tcl_ExprDouble (Tkapp_Interp (self ), s , & v );
20452134 ENTER_OVERLAP
@@ -2069,6 +2158,9 @@ _tkinter_tkapp_exprboolean_impl(TkappObject *self, const char *s)
20692158
20702159 CHECK_STRING_LENGTH (s );
20712160 CHECK_TCL_APPARTMENT ;
2161+
2162+ TRACE (self , ("((ss))" , "expr" , s ));
2163+
20722164 ENTER_TCL
20732165 retval = Tcl_ExprBoolean (Tkapp_Interp (self ), s , & v );
20742166 ENTER_OVERLAP
@@ -2293,6 +2385,8 @@ _tkinter_tkapp_createcommand_impl(TkappObject *self, const char *name,
22932385 !WaitForMainloop (self ))
22942386 return NULL ;
22952387
2388+ TRACE (self , ("((ss()O))" , "proc" , name , func ));
2389+
22962390 data = PyMem_NEW (PythonCmd_ClientData , 1 );
22972391 if (!data )
22982392 return PyErr_NoMemory ();
@@ -2351,6 +2445,8 @@ _tkinter_tkapp_deletecommand_impl(TkappObject *self, const char *name)
23512445
23522446 CHECK_STRING_LENGTH (name );
23532447
2448+ TRACE (self , ("((sss))" , "rename" , name , "" ));
2449+
23542450 if (self -> threaded && self -> thread_id != Tcl_GetCurrentThread ()) {
23552451 Tcl_Condition cond = NULL ;
23562452 CommandEvent * ev ;
@@ -2476,6 +2572,8 @@ _tkinter_tkapp_createfilehandler_impl(TkappObject *self, PyObject *file,
24762572 return NULL ;
24772573 }
24782574
2575+ TRACE (self , ("((ssiiO))" , "#" , "createfilehandler" , tfile , mask , func ));
2576+
24792577 data = NewFHCD (func , file , tfile );
24802578 if (data == NULL )
24812579 return NULL ;
@@ -2507,6 +2605,8 @@ _tkinter_tkapp_deletefilehandler(TkappObject *self, PyObject *file)
25072605 if (tfile < 0 )
25082606 return NULL ;
25092607
2608+ TRACE (self , ("((ssi))" , "#" , "deletefilehandler" , tfile ));
2609+
25102610 DeleteFHCD (tfile );
25112611
25122612 /* Ought to check for null Tcl_File object... */
@@ -2541,6 +2641,7 @@ _tkinter_tktimertoken_deletetimerhandler_impl(TkttObject *self)
25412641 PyObject * func = v -> func ;
25422642
25432643 if (v -> token != NULL ) {
2644+ /* TRACE(...) */
25442645 Tcl_DeleteTimerHandler (v -> token );
25452646 v -> token = NULL ;
25462647 }
@@ -2643,6 +2744,8 @@ _tkinter_tkapp_createtimerhandler_impl(TkappObject *self, int milliseconds,
26432744
26442745 CHECK_TCL_APPARTMENT ;
26452746
2747+ TRACE (self , ("((siO))" , "after" , milliseconds , func ));
2748+
26462749 v = Tktt_New (func );
26472750 if (v ) {
26482751 v -> token = Tcl_CreateTimerHandler (milliseconds , TimerHandler ,
@@ -2810,6 +2913,47 @@ Tkapp_WantObjects(PyObject *self, PyObject *args)
28102913 Py_RETURN_NONE ;
28112914}
28122915
2916+ /*[clinic input]
2917+ _tkinter.tkapp.settrace
2918+
2919+ func: object
2920+ /
2921+
2922+ Set the tracing function.
2923+ [clinic start generated code]*/
2924+
2925+ static PyObject *
2926+ _tkinter_tkapp_settrace (TkappObject * self , PyObject * func )
2927+ /*[clinic end generated code: output=847f6ebdf46e84fa input=31b260d46d3d018a]*/
2928+ {
2929+ if (func == Py_None ) {
2930+ func = NULL ;
2931+ }
2932+ else {
2933+ Py_INCREF (func );
2934+ }
2935+ Py_XSETREF (self -> trace , func );
2936+ Py_RETURN_NONE ;
2937+ }
2938+
2939+ /*[clinic input]
2940+ _tkinter.tkapp.gettrace
2941+
2942+ Get the tracing function.
2943+ [clinic start generated code]*/
2944+
2945+ static PyObject *
2946+ _tkinter_tkapp_gettrace_impl (TkappObject * self )
2947+ /*[clinic end generated code: output=d4e2ba7d63e77bb5 input=ac2aea5be74e8c4c]*/
2948+ {
2949+ PyObject * func = self -> trace ;
2950+ if (!func ) {
2951+ func = Py_None ;
2952+ }
2953+ Py_INCREF (func );
2954+ return func ;
2955+ }
2956+
28132957/*[clinic input]
28142958_tkinter.tkapp.willdispatch
28152959
@@ -2835,6 +2979,7 @@ Tkapp_Dealloc(PyObject *self)
28352979 ENTER_TCL
28362980 Tcl_DeleteInterp (Tkapp_Interp (self ));
28372981 LEAVE_TCL
2982+ Py_XDECREF ( ((TkappObject * )self )-> trace );
28382983 PyObject_Free (self );
28392984 Py_DECREF (tp );
28402985 DisableEventHook ();
@@ -3045,6 +3190,8 @@ static PyMethodDef Tkapp_methods[] =
30453190{
30463191 _TKINTER_TKAPP_WILLDISPATCH_METHODDEF
30473192 {"wantobjects" , Tkapp_WantObjects , METH_VARARGS },
3193+ _TKINTER_TKAPP_SETTRACE_METHODDEF
3194+ _TKINTER_TKAPP_GETTRACE_METHODDEF
30483195 {"call ", Tkapp_Call , METH_VARARGS },
30493196 _TKINTER_TKAPP_EVAL_METHODDEF
30503197 _TKINTER_TKAPP_EVALFILE_METHODDEF
0 commit comments