Skip to content

Commit 10d9c48

Browse files
authored
Merge pull request #579 from hx2A/intel-silicon-problem
Intel silicon problem
2 parents 6c1d316 + e745c3d commit 10d9c48

File tree

7 files changed

+239
-17
lines changed

7 files changed

+239
-17
lines changed

environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ dependencies:
3333
- pip:
3434
- bump2version==1.0.*
3535
- stackprinter==0.2.11
36-
- opencv-contrib-python>=4.10.*
36+
- opencv-contrib-python>=4.10

py5_jar/src/main/java/py5/core/SketchBase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public void py5Println(String text, boolean stderr) {
123123
@Override
124124
public void exit() {
125125
if (!interceptEscape) {
126-
if (platform == MACOS && g.isGL() && !isLooping()) {
126+
if (platform == MACOS && g != null && g.isGL() && surface != null && !isLooping()) {
127127
loop();
128128
}
129129
super.exit();

py5_resources/data/sketch.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ concat,concat,,method,data,array_functions,SKIP,user should use python instead
254254
split,split,,method,data,string_functions,SKIP,user should use python instead
255255
join,join,,method,data,string_functions,SKIP,user should use python instead
256256
trim,trim,,method,data,string_functions,SKIP,user should use python instead
257-
size,size,@_settings_only('size'),method,environment,,JAVA,
257+
size,size,@_settings_only('size');@_macos_safety_check,method,environment,,JAVA,
258258
fill,fill,@_convert_hex_color(),method,color,setting,JAVA,
259259
apply_filter,filter,,method,image,pixels,JAVA,filter method has name conflict with python builtin function; replaced with apply_filter method
260260
second,second,,static method,input,time_date,JAVA,

py5_resources/py5_module/py5/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,13 @@
3737
import numpy as np # noqa
3838
import numpy.typing as npt # noqa
3939
import py5_tools
40+
import py5_tools.environ # noqa
4041
from jpype import JClass # noqa
4142
from jpype.types import JArray, JChar, JFloat, JInt, JString # noqa
4243
from PIL import Image # noqa
4344

45+
_environ = py5_tools.environ.Environment()
46+
4447
if not py5_tools.is_jvm_running():
4548
base_path = (
4649
Path(getattr(sys, "_MEIPASS")) / "py5"
@@ -318,3 +321,11 @@ def _prepare_dynamic_variables(caller_locals, caller_globals):
318321

319322

320323
_prepare_dynamic_variables(locals(), globals())
324+
325+
326+
if platform.system() == "Darwin" and _environ.in_ipython_session:
327+
if _environ.ipython_shell.active_eventloop != "osx":
328+
print(
329+
"Importing py5 on macOS but the necessary Jupyter macOS event loop has not been activated. I'll activate it for you, but next time, execute `%gui osx` before importing this library."
330+
)
331+
_environ.ipython_shell.run_line_magic("gui", "osx")
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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

py5_resources/py5_module/py5/sketch.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
from .graphics import Py5Graphics, _return_py5graphics # noqa
5656
from .image import Py5Image, _return_py5image # noqa
5757
from .keyevent import Py5KeyEvent, _convert_jchar_to_chr, _convert_jint_to_int # noqa
58+
from .macos_problem import _macos_safety_check
5859
from .mixins import DataMixin, MathMixin, PixelMixin, PrintlnStream, ThreadsMixin
5960
from .mixins.threads import Py5Promise # noqa
6061
from .mouseevent import Py5MouseEvent # noqa
@@ -78,18 +79,6 @@
7879
_Sketch = jpype.JClass("py5.core.Sketch")
7980
_SketchBase = jpype.JClass("py5.core.SketchBase")
8081

81-
_environ = py5_tools.environ.Environment()
82-
if (
83-
sys.platform == "darwin"
84-
and _environ.in_ipython_session
85-
and _environ.ipython_shell.active_eventloop != "osx"
86-
):
87-
print(
88-
"Importing py5 on macOS but the necessary Jupyter macOS event loop has not been activated. I'll activate it for you, but next time, execute `%gui osx` before importing this library."
89-
)
90-
_environ.ipython_shell.run_line_magic("gui", "osx")
91-
92-
9382
_PY5_LAST_WINDOW_X = None
9483
_PY5_LAST_WINDOW_Y = None
9584

@@ -222,7 +211,6 @@ def __init__(self, *args, **kwargs):
222211
# must always keep the _py5_bridge reference count from hitting zero.
223212
# otherwise, it will be garbage collected and lead to segmentation faults!
224213
self._py5_bridge = None
225-
self._environ = None
226214
# TODO: shouldn't this be like the base_path variable in __init__.py?
227215
iconPath = Path(__file__).parent.parent / "py5_tools/resources/logo-64x64.png"
228216
if iconPath.exists() and hasattr(self._instance, "setPy5IconPath"):
@@ -312,7 +300,7 @@ def _run_sketch(
312300
_caller_globals: dict[str, Any] = None,
313301
_osx_alt_run_method: bool = True,
314302
) -> None:
315-
self._environ = _environ
303+
self._environ = py5_tools.environ.Environment()
316304
self.set_println_stream(
317305
_DisplayPubPrintlnStream()
318306
if self._environ.in_jupyter_zmq_shell

py5_resources/py5_module/py5/test.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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+
from pathlib import Path
21+
22+
from .sketch import Sketch
23+
24+
25+
class RendererTest(Sketch):
26+
27+
def __init__(self, renderer, renderer_name):
28+
super().__init__()
29+
self.renderer = renderer
30+
self.renderer_name = renderer_name
31+
32+
def settings(self):
33+
self.size(150, 150, self.renderer)
34+
35+
def setup(self):
36+
self.text_align(self.CENTER, self.CENTER)
37+
self.text_size(20)
38+
self.image_mode(self.CENTER)
39+
self.fill(24)
40+
41+
self.logo = self.load_image(
42+
Path(__file__).parent.parent / "py5_tools/resources/logo-64x64.png"
43+
)
44+
45+
def draw(self):
46+
self.background(240)
47+
self.image(self.logo, self.width / 2, 50)
48+
self.text("testing " + self.renderer_name, self.width / 2, 125)
49+
50+
if self.frame_count == 60:
51+
self.exit_sketch()
52+
53+
54+
def test_java2d():
55+
test = RendererTest(Sketch.JAVA2D, "JAVA2D")
56+
test.run_sketch()
57+
58+
59+
def test_p2d():
60+
test = RendererTest(Sketch.P2D, "P2D")
61+
test.run_sketch()
62+
63+
64+
def test_p3d():
65+
test = RendererTest(Sketch.P3D, "P3D")
66+
test.run_sketch()
67+
68+
69+
class TestInteractivity(Sketch):
70+
71+
def __init__(self, renderer, renderer_name):
72+
super().__init__()
73+
self.renderer = renderer
74+
self.renderer_name = renderer_name
75+
76+
def settings(self):
77+
self.size(250, 250, self.renderer)
78+
79+
def setup(self):
80+
self.rect_mode(self.CENTER)
81+
82+
def draw(self):
83+
self.rect(self.mouse_x, self.mouse_y, 10, 10)
84+
85+
def key_pressed(self):
86+
self.println("key =", self.key)
87+
88+
89+
def test_interactivity_java2d():
90+
test = TestInteractivity(Sketch.JAVA2D, "JAVA2D")
91+
test.run_sketch()
92+
93+
94+
def test_interactivity_p2d():
95+
test = TestInteractivity(Sketch.P2D, "P2D")
96+
test.run_sketch()
97+
98+
99+
def test_interactivity_p3d():
100+
test = TestInteractivity(Sketch.P3D, "P3D")
101+
test.run_sketch()

0 commit comments

Comments
 (0)