|
| 1 | +# ***************************************************************************** |
| 2 | +# |
| 3 | +# Part of the py5 library |
| 4 | +# Copyright (C) 2020-2024 Jim Schmitz |
| 5 | +# |
| 6 | +# This library is free software: you can redistribute it and/or modify it |
| 7 | +# under the terms of the GNU Lesser General Public License as published by |
| 8 | +# the Free Software Foundation, either version 2.1 of the License, or (at |
| 9 | +# your option) any later version. |
| 10 | +# |
| 11 | +# This library is distributed in the hope that it will be useful, but |
| 12 | +# WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser |
| 14 | +# General Public License for more details. |
| 15 | +# |
| 16 | +# You should have received a copy of the GNU Lesser General Public License |
| 17 | +# along with this library. If not, see <https://www.gnu.org/licenses/>. |
| 18 | +# |
| 19 | +# ***************************************************************************** |
| 20 | +import functools |
| 21 | +import platform |
| 22 | + |
| 23 | +from py5_tools.environ import Environment |
| 24 | + |
| 25 | +_environ = Environment() |
| 26 | + |
| 27 | +_enforce_safety_check = ( |
| 28 | + platform.system() == "Darwin" |
| 29 | + and ( |
| 30 | + platform.processor() == "i386" |
| 31 | + or int(platform.mac_ver()[0].split(".", 1)[0]) < 14 |
| 32 | + ) |
| 33 | + and _environ.in_ipython_session |
| 34 | +) |
| 35 | +_first_renderer_opengl = None |
| 36 | + |
| 37 | + |
| 38 | +def disable_safety_check(): |
| 39 | + global _enforce_safety_check |
| 40 | + _enforce_safety_check = False |
| 41 | + |
| 42 | + |
| 43 | +def enable_safety_check(): |
| 44 | + global _enforce_safety_check |
| 45 | + _enforce_safety_check = True |
| 46 | + |
| 47 | + |
| 48 | +OPENGL_RENDERERS = [ |
| 49 | + "processing.opengl.PGraphics2D", |
| 50 | + "processing.opengl.PGraphics3D", |
| 51 | +] |
| 52 | + |
| 53 | +MESSAGE = """Sorry, but you can't use an OpenGL renderer in your Sketch right now. Doing so might cause Python to crash. |
| 54 | +
|
| 55 | +Here's the problem: On macOS machines with Intel CPUs and/or older macOS versions, this version of py5 seems to crash when you use an OpenGL renderer in an IPython or Jupyter session if the first Sketch run in that Python session used the default (JAVA2D) renderer. Sorry if that sounds crazy. This is an unfortunate side effect of an important code change that significantly improved py5 for all macOS users. |
| 56 | +
|
| 57 | +The root issue is somewhere in native macOS code that Processing and py5 both depend on. Hopefully in the future we will find a real fix or a better workaround. |
| 58 | +
|
| 59 | +You are seeing this message because this version of py5 has a safety feature that detects the sequence of events that might lead to this crash. However, if you'd like to disable this safety feature (and risk Python crashing), use the following code: |
| 60 | +
|
| 61 | + from py5 import macos_problem |
| 62 | +
|
| 63 | + macos_problem.disable_safety_check() |
| 64 | +
|
| 65 | +But before doing that, it would be great if you could do a quick test for us. Please run the following code in a new Jupyter Notebook or IPython REPL, with each line of code executed separately: |
| 66 | +
|
| 67 | + from py5 import macos_problem, test |
| 68 | +
|
| 69 | + macos_problem.disable_safety_check() |
| 70 | +
|
| 71 | + # run a Sketch with the default renderer |
| 72 | + test.test_java2d() |
| 73 | +
|
| 74 | + # run a Sketch with an opengl renderer |
| 75 | + # does this cause a crash??? |
| 76 | + test.test_p2d() |
| 77 | +
|
| 78 | +Then report your findings to the below GitHub issue thread. Include your macOS version and CPU type. (For your convenience, this information will be displayed at the end of this message.) Your feedback will help us understand the problem better and more accurately calibrate this crash protection feature. |
| 79 | +
|
| 80 | +https://github.com/py5coding/py5generator/issues/578 |
| 81 | +
|
| 82 | +If the above test code doesn't cause Python to crash on your machine, great! You can keep using that `disable_safety_check()` function so you never see this warning again. But please take the time report your findings to the GitHub issue thread. The next version of py5 will incorporate your feedback and the safety feature will be adjusted accordingly. |
| 83 | +
|
| 84 | +If the above test code does cause Python to crash on your machine, it's OK. If you really need to mix Java2D and OpenGL renderers together in one Python session, you just need to make sure that the first executed Sketch is always an OpenGL Sketch. For convenience, you can use the following code to open a quick Sketch right after importing py5. This will ensure the first Sketch is always an OpenGL Sketch, eliminating the problem (and this warning) entirely: |
| 85 | +
|
| 86 | + import py5 |
| 87 | + from py5 import test |
| 88 | +
|
| 89 | + test.test_p2d() |
| 90 | +
|
| 91 | +If you'd like to read about our progress understanding this issue, please visit the above GitHub issue thread. |
| 92 | +
|
| 93 | +Sorry again for the weird limitation. We're doing our best to make py5 as stable as possible. This safety feature is here because we don't want users to become upset because Python crashed for confusing reasons. Thank you for your understanding. |
| 94 | +""" |
| 95 | + |
| 96 | + |
| 97 | +def _macos_safety_check(f): |
| 98 | + @functools.wraps(f) |
| 99 | + def decorated(self_, *args): |
| 100 | + global _first_renderer_opengl |
| 101 | + if _enforce_safety_check: |
| 102 | + if _first_renderer_opengl is None: |
| 103 | + # This is the first Sketch. Record if the renderer is OpenGL. |
| 104 | + if len(args) >= 2 and args[2] in OPENGL_RENDERERS: |
| 105 | + _first_renderer_opengl = True |
| 106 | + else: |
| 107 | + _first_renderer_opengl = False |
| 108 | + |
| 109 | + elif _first_renderer_opengl is False: |
| 110 | + # The first Sketch was not OpenGL. OpenGL is not allowed now. |
| 111 | + if len(args) >= 2 and args[2] in OPENGL_RENDERERS: |
| 112 | + self_.println(MESSAGE) |
| 113 | + if platform.system() == "Darwin": # just in case |
| 114 | + self_.println("macOS version:", platform.mac_ver()[0]) |
| 115 | + self_.println("macOS CPU type:", platform.processor()) |
| 116 | + raise RuntimeError( |
| 117 | + "Halting Sketch startup to prevent Python from crashing" |
| 118 | + ) |
| 119 | + |
| 120 | + f(self_, *args) |
| 121 | + |
| 122 | + return decorated |
0 commit comments