CreativePython 1.1.3__py3-none-any.whl → 1.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,37 +66,49 @@ def _createEvent(eventType, target, args=None):
66
66
  # Child process entry point
67
67
  #######################################################################################
68
68
 
69
- def _launchRenderer(childConn, adminChildConn, adminParentConn):
69
+ def _launchRenderer(childCommandConnection, childPriorityConnection, parentPriorityConnection):
70
70
  """
71
71
  Entry point for the GuiRenderer child process.
72
72
  Defined here so GuiHandler can reference it without importing from GuiRenderer
73
73
  globally (which would also import Qt/PySide6 in the parent process).
74
74
 
75
- adminChildConn is the child end of the duplex admin pipe; the child watches it
75
+ childPriorityConnection is the child end of the duplex admin pipe; the child watches it
76
76
  with QSocketNotifier. Admin commands (setRate, getRate) arrive here, bypassing
77
- the command buffer. When the parent closes its end (adminParentConn), the child
77
+ the command buffer. When the parent closes its end (parentPriorityConnection), the child
78
78
  receives EOF and initiates shutdown.
79
79
 
80
- adminParentConn is the parent's end; the child closes it immediately so that
80
+ parentPriorityConnection is the parent's end; the child closes it immediately so that
81
81
  only the parent holds it — ensuring the child sees EOF when the parent closes.
82
82
 
83
83
  At runtime this function only executes in the child process.
84
84
  """
85
- import sys, os
86
-
87
- adminParentConn.close() # child must not hold the parent end open
88
-
89
- # Suppress macOS system noise that would confuse students.
90
- # Redirect at the OS level (fd 2) so that
91
- # C-level libraries (Cocoa, XPC) are also silenced.
92
- _devnull = os.open(os.devnull, os.O_WRONLY)
93
- os.dup2(_devnull, 2)
94
- os.close(_devnull)
95
- sys.stderr = open(os.devnull, 'w')
96
-
97
- from CreativePython.GuiRenderer import GuiRenderer
98
- renderer = GuiRenderer(childConn, adminChildConn)
99
- renderer.run()
85
+ import sys, os, traceback
86
+
87
+ parentPriorityConnection.close() # child must not hold the parent end open
88
+
89
+ # Redirect stderr (fd 2) to a log file so both Python tracebacks and C-level
90
+ # output from Qt/Cocoa/XPC are captured for diagnostics.
91
+ # The log is removed on clean exit if nothing was written to it.
92
+ _logPath = os.path.join(os.getcwd(), "PENCIL_debug.log")
93
+ _logFile = open(_logPath, "w")
94
+ os.dup2(_logFile.fileno(), 2)
95
+ sys.stderr = _logFile
96
+
97
+ try:
98
+ from CreativePython.GuiRenderer import GuiRenderer
99
+ renderer = GuiRenderer(childCommandConnection, childPriorityConnection)
100
+ renderer.run()
101
+ except Exception:
102
+ traceback.print_exc()
103
+ raise
104
+ finally:
105
+ _logFile.flush()
106
+ if os.path.getsize(_logPath) == 0:
107
+ try:
108
+ _logFile.close()
109
+ os.unlink(_logPath)
110
+ except OSError:
111
+ pass
100
112
 
101
113
 
102
114
  #######################################################################################
@@ -119,8 +131,8 @@ class GuiHandler:
119
131
  Registers an atexit handler so the child is always cleaned up when
120
132
  the parent process exits — no cleanup code required in gui.py.
121
133
  """
122
- parentConn, childConn = multiprocessing.Pipe(duplex=True)
123
- self.connection = parentConn
134
+ parentCommandConnection, childCommandConnection = multiprocessing.Pipe(duplex=True)
135
+ self.connection = parentCommandConnection
124
136
 
125
137
  # Choose the spawn context based on the runtime environment:
126
138
  #
@@ -157,13 +169,13 @@ class GuiHandler:
157
169
  else:
158
170
  ctx = multiprocessing.get_context('fork')
159
171
 
160
- # Dedicated duplex admin pipe. The parent holds adminParentConn; the child
161
- # holds adminChildConn. Admin commands (setRate, getRate) travel here,
162
- # bypassing the command buffer entirely. Closing adminParentConn sends EOF
172
+ # Dedicated duplex admin pipe. The parent holds parentPriorityConnection; the child
173
+ # holds childPriorityConnection. Admin commands (setRate, getRate) travel here,
174
+ # bypassing the command buffer entirely. Closing parentPriorityConnection sends EOF
163
175
  # to the child, which Qt detects via QSocketNotifier and treats as shutdown —
164
176
  # independent of the command queue and independent of whether atexit runs.
165
- adminChildConn, adminParentConn = multiprocessing.Pipe(duplex=True)
166
- self._adminConn = adminParentConn
177
+ childPriorityConnection, parentPriorityConnection = multiprocessing.Pipe(duplex=True)
178
+ self._adminConn = parentPriorityConnection
167
179
 
168
180
  # With 'spawn' on POSIX (fork+exec), the forked helper calls
169
181
  # get_preparation_data() which reads sys.modules['__main__'].__file__.
@@ -184,7 +196,7 @@ class GuiHandler:
184
196
  try:
185
197
  self.childProcess = ctx.Process(
186
198
  target = _launchRenderer,
187
- args = (childConn, adminChildConn, adminParentConn),
199
+ args = (childCommandConnection, childPriorityConnection, parentPriorityConnection),
188
200
  daemon = True # child is killed automatically if parent dies unexpectedly
189
201
  )
190
202
  self.childProcess.start()
@@ -194,8 +206,8 @@ class GuiHandler:
194
206
  _main_mod.__file__ = _saved_file
195
207
  if _saved_spec is not _UNSET:
196
208
  _main_mod.__spec__ = _saved_spec
197
- childConn.close() # parent no longer needs the child end of the command pipe
198
- adminChildConn.close() # parent no longer needs the child end of the admin pipe
209
+ childCommandConnection.close() # parent no longer needs the child end of the command pipe
210
+ childPriorityConnection.close() # parent no longer needs the child end of the admin pipe
199
211
 
200
212
  # responseId counter — each sendQuery() call gets a unique ID so the
201
213
  # listener thread can route the response back to the correct caller