CreativePython 1.1.3__py3-none-any.whl → 1.1.5__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,58 @@ 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
+ # Set to True to redirect stderr to PENCIL_debug.log for diagnostics.
90
+ # When False, stderr is silenced (sent to /dev/null) to suppress Qt/Cocoa noise.
91
+ _DEBUG_LOG = False
92
+
93
+ if _DEBUG_LOG:
94
+ # Redirect stderr (fd 2) to a log file so both Python tracebacks and C-level
95
+ # output from Qt/Cocoa/XPC are captured for diagnostics.
96
+ # The log is removed on clean exit if nothing was written to it.
97
+ _logPath = os.path.join(os.getcwd(), "PENCIL_debug.log")
98
+ _logFile = open(_logPath, "w")
99
+ else:
100
+ _logPath = None
101
+ _logFile = open(os.devnull, "w")
102
+
103
+ os.dup2(_logFile.fileno(), 2)
104
+ sys.stderr = _logFile
105
+
106
+ try:
107
+ from CreativePython.GuiRenderer import GuiRenderer
108
+ renderer = GuiRenderer(childCommandConnection, childPriorityConnection)
109
+ renderer.run()
110
+ except Exception:
111
+ traceback.print_exc()
112
+ raise
113
+ finally:
114
+ _logFile.flush()
115
+ if _DEBUG_LOG and _logPath and os.path.getsize(_logPath) == 0:
116
+ try:
117
+ _logFile.close()
118
+ os.unlink(_logPath)
119
+ except OSError:
120
+ pass
100
121
 
101
122
 
102
123
  #######################################################################################
@@ -119,8 +140,8 @@ class GuiHandler:
119
140
  Registers an atexit handler so the child is always cleaned up when
120
141
  the parent process exits — no cleanup code required in gui.py.
121
142
  """
122
- parentConn, childConn = multiprocessing.Pipe(duplex=True)
123
- self.connection = parentConn
143
+ parentCommandConnection, childCommandConnection = multiprocessing.Pipe(duplex=True)
144
+ self.connection = parentCommandConnection
124
145
 
125
146
  # Choose the spawn context based on the runtime environment:
126
147
  #
@@ -157,13 +178,13 @@ class GuiHandler:
157
178
  else:
158
179
  ctx = multiprocessing.get_context('fork')
159
180
 
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
181
+ # Dedicated duplex admin pipe. The parent holds parentPriorityConnection; the child
182
+ # holds childPriorityConnection. Admin commands (setRate, getRate) travel here,
183
+ # bypassing the command buffer entirely. Closing parentPriorityConnection sends EOF
163
184
  # to the child, which Qt detects via QSocketNotifier and treats as shutdown —
164
185
  # independent of the command queue and independent of whether atexit runs.
165
- adminChildConn, adminParentConn = multiprocessing.Pipe(duplex=True)
166
- self._adminConn = adminParentConn
186
+ childPriorityConnection, parentPriorityConnection = multiprocessing.Pipe(duplex=True)
187
+ self._adminConn = parentPriorityConnection
167
188
 
168
189
  # With 'spawn' on POSIX (fork+exec), the forked helper calls
169
190
  # get_preparation_data() which reads sys.modules['__main__'].__file__.
@@ -184,7 +205,7 @@ class GuiHandler:
184
205
  try:
185
206
  self.childProcess = ctx.Process(
186
207
  target = _launchRenderer,
187
- args = (childConn, adminChildConn, adminParentConn),
208
+ args = (childCommandConnection, childPriorityConnection, parentPriorityConnection),
188
209
  daemon = True # child is killed automatically if parent dies unexpectedly
189
210
  )
190
211
  self.childProcess.start()
@@ -194,8 +215,8 @@ class GuiHandler:
194
215
  _main_mod.__file__ = _saved_file
195
216
  if _saved_spec is not _UNSET:
196
217
  _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
218
+ childCommandConnection.close() # parent no longer needs the child end of the command pipe
219
+ childPriorityConnection.close() # parent no longer needs the child end of the admin pipe
199
220
 
200
221
  # responseId counter — each sendQuery() call gets a unique ID so the
201
222
  # listener thread can route the response back to the correct caller