pyglet 2.1.dev2__py3-none-any.whl → 2.1.dev3__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.
Files changed (87) hide show
  1. pyglet/__init__.py +317 -219
  2. pyglet/__init__.pyi +22 -6
  3. pyglet/app/__init__.py +6 -8
  4. pyglet/app/base.py +86 -88
  5. pyglet/clock.py +93 -117
  6. pyglet/display/__init__.py +24 -29
  7. pyglet/display/base.py +44 -100
  8. pyglet/event.py +105 -98
  9. pyglet/experimental/hidraw.py +28 -87
  10. pyglet/experimental/particles.py +389 -0
  11. pyglet/extlibs/earcut.py +714 -0
  12. pyglet/font/__init__.py +54 -41
  13. pyglet/font/base.py +147 -133
  14. pyglet/font/directwrite.py +654 -597
  15. pyglet/font/fontconfig.py +109 -91
  16. pyglet/font/freetype.py +102 -71
  17. pyglet/font/freetype_lib.py +346 -306
  18. pyglet/font/quartz.py +42 -36
  19. pyglet/font/ttf.py +238 -239
  20. pyglet/font/user.py +166 -57
  21. pyglet/font/win32.py +32 -36
  22. pyglet/gl/base.py +131 -192
  23. pyglet/gl/wgl.py +175 -199
  24. pyglet/graphics/__init__.py +9 -2
  25. pyglet/graphics/shader.py +4 -1
  26. pyglet/graphics/vertexdomain.py +2 -4
  27. pyglet/gui/__init__.py +1 -0
  28. pyglet/gui/frame.py +68 -38
  29. pyglet/gui/ninepatch.py +269 -0
  30. pyglet/gui/widgets.py +137 -129
  31. pyglet/image/__init__.py +48 -10
  32. pyglet/image/animation.py +38 -77
  33. pyglet/image/atlas.py +74 -110
  34. pyglet/image/buffer.py +91 -50
  35. pyglet/input/__init__.py +27 -38
  36. pyglet/input/base.py +379 -427
  37. pyglet/input/controller.py +33 -37
  38. pyglet/input/linux/evdev.py +12 -64
  39. pyglet/input/win32/__init__.py +4 -4
  40. pyglet/input/win32/wintab.py +8 -10
  41. pyglet/libs/__init__.py +18 -0
  42. pyglet/libs/darwin/cocoapy/runtime.py +262 -31
  43. pyglet/libs/darwin/coreaudio.py +86 -19
  44. pyglet/libs/darwin/quartzkey.py +32 -13
  45. pyglet/libs/ioctl.py +129 -0
  46. pyglet/libs/x11/xlib.py +1480 -947
  47. pyglet/math.py +64 -65
  48. pyglet/media/__init__.py +22 -17
  49. pyglet/media/codecs/__init__.py +4 -1
  50. pyglet/media/player.py +34 -67
  51. pyglet/media/player_worker_thread.py +31 -21
  52. pyglet/media/synthesis.py +105 -81
  53. pyglet/model/__init__.py +106 -58
  54. pyglet/model/codecs/__init__.py +12 -3
  55. pyglet/resource.py +295 -363
  56. pyglet/shapes.py +892 -498
  57. pyglet/sprite.py +255 -260
  58. pyglet/text/__init__.py +218 -244
  59. pyglet/text/caret.py +45 -92
  60. pyglet/text/document.py +216 -210
  61. pyglet/text/formats/__init__.py +1 -4
  62. pyglet/text/formats/attributed.py +32 -29
  63. pyglet/text/formats/html.py +165 -165
  64. pyglet/text/formats/plaintext.py +10 -4
  65. pyglet/text/formats/structured.py +97 -85
  66. pyglet/text/layout/__init__.py +28 -16
  67. pyglet/text/layout/base.py +372 -383
  68. pyglet/text/layout/incremental.py +189 -258
  69. pyglet/text/layout/scrolling.py +77 -60
  70. pyglet/text/runlist.py +91 -84
  71. pyglet/window/__init__.py +440 -580
  72. pyglet/window/cocoa/__init__.py +66 -68
  73. pyglet/window/cocoa/pyglet_delegate.py +49 -36
  74. pyglet/window/cocoa/pyglet_textview.py +81 -69
  75. pyglet/window/cocoa/pyglet_view.py +75 -55
  76. pyglet/window/cocoa/pyglet_window.py +31 -18
  77. pyglet/window/cocoa/systemcursor.py +4 -2
  78. pyglet/window/event.py +57 -52
  79. pyglet/window/headless/__init__.py +39 -31
  80. pyglet/window/key.py +20 -33
  81. pyglet/window/mouse.py +48 -34
  82. pyglet/window/win32/__init__.py +373 -340
  83. pyglet/window/xlib/__init__.py +256 -221
  84. {pyglet-2.1.dev2.dist-info → pyglet-2.1.dev3.dist-info}/METADATA +1 -1
  85. {pyglet-2.1.dev2.dist-info → pyglet-2.1.dev3.dist-info}/RECORD +87 -83
  86. {pyglet-2.1.dev2.dist-info → pyglet-2.1.dev3.dist-info}/LICENSE +0 -0
  87. {pyglet-2.1.dev2.dist-info → pyglet-2.1.dev3.dist-info}/WHEEL +0 -0
pyglet/__init__.py CHANGED
@@ -2,25 +2,27 @@
2
2
 
3
3
  More information is available at http://www.pyglet.org
4
4
  """
5
+ from __future__ import annotations
5
6
 
6
7
  import os
7
8
  import sys
8
9
 
9
- from typing import TYPE_CHECKING, Dict
10
+ from dataclasses import dataclass
11
+ from typing import TYPE_CHECKING, Any, ItemsView
10
12
 
11
13
  #: The release version
12
- version = '2.1.dev2'
14
+ version = '2.1.dev3'
13
15
  __version__ = version
14
16
 
15
17
  MIN_PYTHON_VERSION = 3, 8
16
- MIN_PYTHON_VERSION_STR = '.'.join([str(v) for v in MIN_PYTHON_VERSION])
18
+ MIN_PYTHON_VERSION_STR = ".".join([str(v) for v in MIN_PYTHON_VERSION])
17
19
 
18
20
  if sys.version_info < MIN_PYTHON_VERSION:
19
- raise Exception(f"pyglet {version} requires Python {MIN_PYTHON_VERSION_STR} or newer.")
21
+ msg = f"pyglet {version} requires Python {MIN_PYTHON_VERSION_STR} or newer."
22
+ raise Exception(msg)
20
23
 
21
- if 'sphinx' in sys.modules:
22
- setattr(sys, 'is_pyglet_doc_run', True)
23
- _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
24
+ if "sphinx" in sys.modules:
25
+ sys.is_pyglet_doc_run = True
24
26
 
25
27
  # pyglet platform treats *BSD systems as Linux
26
28
  compat_platform = sys.platform
@@ -28,158 +30,253 @@ if "bsd" in compat_platform:
28
30
  compat_platform = "linux-compat"
29
31
 
30
32
  _enable_optimisations = not __debug__
31
- if getattr(sys, 'frozen', None):
33
+ if getattr(sys, "frozen", None):
32
34
  _enable_optimisations = True
33
35
 
34
- #: Global dict of pyglet options.
35
- #:
36
- #: To change an option from its default, you must import
37
- #: ``pyglet`` before any sub-packages. For example::
38
- #:
39
- #: import pyglet
40
- #: pyglet.options['debug_gl'] = False
41
- #:
42
- #: The default options can be overridden from the OS environment. The
43
- #: corresponding environment variable for each option key is prefaced by
44
- #: ``PYGLET_``. For example, in Bash you can set the ``debug_gl`` option with::
45
- #:
46
- #: PYGLET_DEBUG_GL=True; export PYGLET_DEBUG_GL
47
- #:
48
- #: For options requiring a tuple of values, separate each value with a comma.
49
- #:
50
- #: The non-development options are:
51
- #:
52
- #: audio
53
- #: A :py:class:`~typing.Sequence` of valid audio modules names. They will
54
- #: be tried from first to last until either a driver loads or no entries
55
- #: remain. See :ref:`guide-audio-driver-order` for more information.
56
- #:
57
- #: Valid driver names are:
58
- #:
59
- #: * ``'xaudio2'``, the Windows Xaudio2 audio module (Windows only)
60
- #: * ``'directsound'``, the Windows DirectSound audio module (Windows only)
61
- #: * ``'pulse'``, the :ref:`guide-audio-driver-pulseaudio` module
62
- #: (Linux only, otherwise nearly ubiquitous. Limited features; use
63
- #: ``'openal'`` for more.)
64
- #: * ``'openal'``, the :ref:`guide-audio-driver-openal` audio module
65
- #: (A library may need to be installed on Windows and Linux)
66
- #: * ``'silent'``, no audio
67
- #:
68
- #: debug_lib
69
- #: If True, prints the path of each dynamic library loaded.
70
- #: debug_gl
71
- #: If True, all calls to OpenGL functions are checked afterwards for
72
- #: errors using ``glGetError``. This will severely impact performance,
73
- #: but provides useful exceptions at the point of failure. By default,
74
- #: this option is enabled if ``__debug__`` is (i.e., if Python was not run
75
- #: with the -O option). It is disabled by default when pyglet is "frozen"
76
- #: within a py2exe or py2app library archive.
77
- #: shadow_window
78
- #: By default, pyglet creates a hidden window with a GL context when
79
- #: pyglet.gl is imported. This allows resources to be loaded before
80
- #: the application window is created, and permits GL objects to be
81
- #: shared between windows even after they've been closed. You can
82
- #: disable the creation of the shadow window by setting this option to
83
- #: False.
84
- #:
85
- #: Some OpenGL driver implementations may not support shared OpenGL
86
- #: contexts and may require disabling the shadow window (and all resources
87
- #: must be loaded after the window using them was created). Recommended
88
- #: for advanced developers only.
89
- #:
90
- #: .. versionadded:: 1.1
91
- #: vsync
92
- #: If set, the `pyglet.window.Window.vsync` property is ignored, and
93
- #: this option overrides it (to either force vsync on or off). If unset,
94
- #: or set to None, the `pyglet.window.Window.vsync` property behaves
95
- #: as documented.
96
- #: search_local_libs
97
- #: If False, pyglet won't try to search for libraries in the script
98
- #: directory and its `lib` subdirectory. This is useful to load a local
99
- #: library instead of the system installed version. This option is set
100
- #: to True by default.
101
- #:
102
- #: .. versionadded:: 1.2
103
- #:
104
- options = {
105
- 'audio': ('xaudio2', 'directsound', 'openal', 'pulse', 'silent'),
106
- 'debug_font': False,
107
- 'debug_gl': not _enable_optimisations,
108
- 'debug_gl_trace': False,
109
- 'debug_gl_trace_args': False,
110
- 'debug_gl_shaders': False,
111
- 'debug_graphics_batch': False,
112
- 'debug_lib': False,
113
- 'debug_media': False,
114
- 'debug_texture': False,
115
- 'debug_trace': False,
116
- 'debug_trace_args': False,
117
- 'debug_trace_depth': 1,
118
- 'debug_trace_flush': True,
119
- 'debug_win32': False,
120
- 'debug_input': False,
121
- 'debug_x11': False,
122
- 'shadow_window': True,
123
- 'vsync': None,
124
- 'search_local_libs': True,
125
- 'win32_gdi_font': False,
126
- 'scale_with_dpi': False,
127
- 'headless': False,
128
- 'headless_device': 0,
129
- 'win32_disable_shaping': False,
130
- 'dw_legacy_naming': False,
131
- 'win32_disable_xinput': False,
132
- 'com_mta': False,
133
- 'osx_alt_loop': False
134
- }
135
-
136
- _option_types = {
137
- 'audio': tuple,
138
- 'debug_font': bool,
139
- 'debug_gl': bool,
140
- 'debug_gl_trace': bool,
141
- 'debug_gl_trace_args': bool,
142
- 'debug_gl_shaders': bool,
143
- 'debug_graphics_batch': bool,
144
- 'debug_lib': bool,
145
- 'debug_media': bool,
146
- 'debug_texture': bool,
147
- 'debug_trace': bool,
148
- 'debug_trace_args': bool,
149
- 'debug_trace_depth': int,
150
- 'debug_trace_flush': bool,
151
- 'debug_win32': bool,
152
- 'debug_input': bool,
153
- 'debug_x11': bool,
154
- 'shadow_window': bool,
155
- 'vsync': bool,
156
- 'search_local_libs': bool,
157
- 'win32_gdi_font': bool,
158
- 'scale_with_dpi': bool,
159
- 'headless': bool,
160
- 'headless_device': int,
161
- 'win32_disable_shaping': bool,
162
- 'dw_legacy_naming': bool,
163
- 'win32_disable_xinput': bool,
164
- 'com_mta': bool,
165
- 'osx_alt_loop': bool,
166
- }
167
-
168
-
169
- for _key in options:
36
+
37
+ @dataclass
38
+ class Options:
39
+ """Dataclass for global pyglet options."""
40
+
41
+ audio: tuple[str] = ("xaudio2", "directsound", "openal", "pulse", "silent")
42
+ """A :py:class:`~typing.Sequence` of valid audio modules names. They will
43
+ be tried from first to last until either a driver loads or no entries
44
+ remain. See :ref:`guide-audio-driver-order` for more information.
45
+
46
+ Valid driver names are:
47
+
48
+ * ``'xaudio2'``, the Windows Xaudio2 audio module (Windows only)
49
+ * ``'directsound'``, the Windows DirectSound audio module (Windows only)
50
+ * ``'pulse'``, the :ref:`guide-audio-driver-pulseaudio` module
51
+ (Linux only, otherwise nearly ubiquitous. Limited features; use
52
+ ``'openal'`` for more.)
53
+ * ``'openal'``, the :ref:`guide-audio-driver-openal` audio module
54
+ (A library may need to be installed on Windows and Linux)
55
+ * ``'silent'``, no audio"""
56
+
57
+ debug_font: bool = False
58
+ """If ``True``, will print more verbose information when :py:class:`~pyglet.font.base.Font`'s are loaded."""
59
+
60
+ debug_gl: bool = True
61
+ """If ``True``, all calls to OpenGL functions are checked afterwards for
62
+ errors using ``glGetError``. This will severely impact performance,
63
+ but provides useful exceptions at the point of failure. By default,
64
+ this option is enabled if ``__debug__`` is enabled (i.e., if Python was not run
65
+ with the -O option). It is disabled by default when pyglet is "frozen", such as
66
+ within pyinstaller or nuitka."""
67
+
68
+ debug_gl_trace: bool = False
69
+ """If ``True``, will print the names of OpenGL calls being executed. For example, ``glBlendFunc``"""
70
+
71
+ debug_gl_trace_args: bool = False
72
+ """If ``True``, in addition to printing the names of OpenGL calls, it will also print the arguments passed
73
+ into those calls. For example, ``glBlendFunc(770, 771)``
74
+
75
+ .. note:: Requires ``debug_gl_trace`` to be enabled."""
76
+
77
+ debug_gl_shaders: bool = False
78
+ """If ``True``, prints shader compilation information such as creation and deletion of shader's. Also includes
79
+ information on shader ID's, attributes, and uniforms."""
80
+
81
+ debug_graphics_batch: bool = False
82
+ """If ``True``, prints batch information being drawn, including :py:class:`~pyglet.graphics.Group`'s, VertexDomains,
83
+ and :py:class:`~pyglet.image.Texture` information. This can be useful to see how many Group's are being
84
+ consolidated."""
85
+
86
+ debug_lib: bool = False
87
+ """If ``True``, prints the path of each dynamic library loaded."""
88
+
89
+ debug_media: bool = False
90
+ """If ``True``, prints more detailed media information for audio codecs and drivers. Will be very verbose."""
91
+
92
+ debug_texture: bool = False
93
+ """If ``True``, prints information on :py:class:`~pyglet.image.Texture` size (in bytes) when they are allocated and
94
+ deleted."""
95
+
96
+ debug_trace: bool = False
97
+ debug_trace_args: bool = False
98
+ debug_trace_depth: int = 1
99
+ debug_trace_flush: bool = True
100
+
101
+ debug_win32: bool = False
102
+ """If ``True``, prints error messages related to Windows library calls. Usually get's information from
103
+ ``Kernel32.GetLastError``. This information is output to a file called ``debug_win32.log``."""
104
+
105
+ debug_input: bool = False
106
+ """If ``True``, prints information on input devices such as controllers, tablets, and more."""
107
+
108
+ debug_x11: bool = False
109
+ """If ``True``, prints information related to Linux X11 calls. This can potentially help narrow down driver or
110
+ operating system issues."""
111
+
112
+ shadow_window: bool = True
113
+ """By default, pyglet creates a hidden window with a GL context when
114
+ pyglet.gl is imported. This allows resources to be loaded before
115
+ the application window is created, and permits GL objects to be
116
+ shared between windows even after they've been closed. You can
117
+ disable the creation of the shadow window by setting this option to
118
+ False.
119
+
120
+ Some OpenGL driver implementations may not support shared OpenGL
121
+ contexts and may require disabling the shadow window (and all resources
122
+ must be loaded after the window using them was created). Recommended
123
+ for advanced developers only.
124
+
125
+ .. versionadded:: 1.1"""
126
+
127
+ vsync: bool | None = None
128
+ """If set to ``True`` or ``False``, this option takes overrides the
129
+ ``vsync`` argument passed to :py:class:`~pyglet.window.Window`. This
130
+ allows forcing vsync on or off. If set to None (the default), the
131
+ as documented.
132
+ """
133
+
134
+ xsync: bool = True
135
+ """If ``True`` (the default), pyglet will attempt to synchronise the
136
+ drawing of double-buffered windows to the border updates of the X11
137
+ window manager. This improves the appearance of the window during
138
+ resize operations. This option only affects double-buffered windows on
139
+ X11 servers supporting the Xsync extension with a window manager that
140
+ implements the _NET_WM_SYNC_REQUEST protocol.
141
+
142
+ .. versionadded:: 1.1
143
+ """
144
+
145
+ xlib_fullscreen_override_redirect: bool = False
146
+ """If ``True``, pass the xlib.CWOverrideRedirect flag when creating a fullscreen window.
147
+ This option is generally not necessary anymore and is considered deprecated.
148
+ """
149
+
150
+ search_local_libs: bool = True
151
+ """If ``False``, pyglet won't try to search for libraries in the script
152
+ directory and its ``lib`` subdirectory. This is useful to load a local
153
+ library instead of the system installed version."""
154
+
155
+ win32_gdi_font: bool = False
156
+ """If ``True``, pyglet will fallback to the legacy ``GDIPlus`` font renderer for Windows. This may provide
157
+ better font compatibility for older fonts. The legacy renderer does not support shaping, colored fonts,
158
+ substitutions, or other OpenType features. It may also have issues with certain languages.
159
+
160
+ Due to those lack of features, it can potentially be more performant.
161
+
162
+ .. versionadded:: 2.0
163
+ """
164
+
165
+ headless: bool = False
166
+ """If ``True``, visible Windows are not created and a running desktop environment is not required. This option
167
+ is useful when running pyglet on a headless server, or compute cluster. OpenGL drivers with ``EGL`` support are
168
+ required for this mode.
169
+ """
170
+
171
+ headless_device: int = 0
172
+ """If using ``headless`` mode (``pyglet.options['headless'] = True``), this option allows you to set which
173
+ GPU to use. This is only useful on multi-GPU systems.
174
+ """
175
+
176
+ win32_disable_shaping: bool = False
177
+ """If ``True``, will disable the shaping process for the default Windows font renderer to offer a performance
178
+ speed up. If your font is simple, monospaced, or you require no advanced OpenType features, this option may be
179
+ useful. You can try enabling this to see if there is any impact on clarity for your font. The advance will be
180
+ determined by the glyph width.
181
+
182
+ .. note:: Shaping is the process of determining which character glyphs to use and specific placements of those
183
+ glyphs when given a full string of characters.
184
+
185
+ .. versionadded:: 2.0
186
+ """
187
+
188
+ dw_legacy_naming: bool = False
189
+ """If ``True``, will enable legacy naming support for the default Windows font renderer (``DirectWrite``).
190
+ Attempt to parse fonts by the passed name, to best match legacy RBIZ naming.
191
+
192
+ :see: https://learn.microsoft.com/en-us/windows/win32/directwrite/font-selection#rbiz-font-family-model
193
+
194
+ For example, this allows specifying ``"Arial Narrow"`` rather than ``"Arial"`` with a ``"condensed"`` stretch or
195
+ ``"Arial Black"`` instead of ``"Arial"`` with a weight of ``black``. This may enhance naming compatibility
196
+ cross-platform for select fonts as older font renderers went by this naming scheme.
197
+
198
+ Starts by parsing the string for any known style names, and searches all font collections for a matching RBIZ name.
199
+ If a perfect match is not found, it will choose a second best match.
200
+
201
+ .. note:: Due to the high variation of styles and limited capability of some fonts, there is no guarantee the
202
+ second closest match will be exactly what the user wants.
203
+
204
+ .. note:: The ``debug_font`` option can provide information on what settings are being selected.
205
+
206
+ .. versionadded:: 2.0.3
207
+ """
208
+
209
+ win32_disable_xinput: bool = False
210
+ """If ``True``, this will disable the ``XInput`` controller usage in Windows and fallback to ``DirectInput``. Can
211
+ be useful for debugging or special uses cases. A controller can only be controlled by either ``XInput`` or
212
+ ``DirectInput``, not both.
213
+
214
+ .. versionadded:: 2.0
215
+ """
216
+
217
+ com_mta: bool = False
218
+ """If ``True``, this will enforce COM Multithreaded Apartment Mode for Windows applications. By default, pyglet
219
+ has opted to go for Single-Threaded Apartment (STA) for compatibility reasons. Many other third party libraries
220
+ used with Python explicitly set STA. However, Windows recommends MTA with a lot of their API's such as Windows
221
+ Media Foundation (WMF).
222
+
223
+ :see: https://learn.microsoft.com/en-us/windows/win32/cossdk/com--threading-models
224
+
225
+ .. versionadded:: 2.0.5
226
+ """
227
+
228
+ osx_alt_loop: bool = False
229
+ """If ``True``, this will enable an alternative loop for Mac OSX. This is enforced for all ARM64 architecture Mac's.
230
+
231
+ Due to various issues with the ctypes interface with Objective C, Python, and Mac ARM64 processors, the standard
232
+ event loop eventually starts breaking down to where inputs are either missed or delayed. Even on Intel based Mac's
233
+ other odd behavior can be seen with the standard event loop such as randomly crashing from events.
234
+
235
+ .. versionadded:: 2.0.5"""
236
+
237
+ scale_with_dpi: bool = False
238
+ """For 'HiDPI' ('Retina') displays, scale Window creation size with desktop scaling. Defaults to ``False``.
239
+
240
+ For high pixel density displays, it is common for the desktop to have some form of application window scaling.
241
+ Setting this option to ``True`` will make pyglet aware of these settings when Windows are created. For instance,
242
+ if the desktop scaling factor is set to 150%, a Window created with a resolution of 1000x1000 will have an actual
243
+ framebuffer size of 1500x1500. Keep in mind that pyglet objects may not be scaled proportionately, so this is left
244
+ up to the developer. The :py:attr:`~pyglet.window.Window.scale` & :py:attr:`~pyglet.window.Window.dpi` attributes
245
+ can be queried as a reference when determining object creation. By default, windows are created with the actual
246
+ number of pixels requested.
247
+ """
248
+
249
+ def get(self, item: str, default: Any = None) -> Any:
250
+ return self.__dict__.get(item, default)
251
+
252
+ def items(self) -> ItemsView[str, Any]:
253
+ return self.__dict__.items()
254
+
255
+ def __getitem__(self, item: str) -> Any:
256
+ return self.__dict__[item]
257
+
258
+ def __setitem__(self, key: str, value: Any) -> None:
259
+ assert key in self.__annotations__, f"Invalid option name: '{key}'"
260
+ assert type(value).__name__ == self.__annotations__[key], f"Invalid type: '{type(value)}' for '{key}'"
261
+ self.__dict__[key] = value
262
+
263
+
264
+ #: Instance of :py:class:`~pyglet.Options` used to set runtime options.
265
+ options = Options()
266
+
267
+
268
+ for _key, _type in options.__annotations__.items():
170
269
  """Check Environment Variables for pyglet options"""
171
- assert _key in _option_types, f"Option '{_key}' must have a type set in _option_types."
172
-
173
- if _value := os.environ.get(f'PYGLET_{_key.upper()}'):
174
- if _option_types[_key] is tuple:
175
- options[_key] = _value.split(',')
176
- elif _option_types[_key] is bool:
177
- options[_key] = _value in ('true', 'TRUE', 'True', '1')
178
- elif _option_types[_key] is int:
270
+ if _value := os.environ.get(f"PYGLET_{_key.upper()}"):
271
+ if _type == 'tuple':
272
+ options[_key] = _value.split(",")
273
+ elif _type == 'bool':
274
+ options[_key] = _value in ("true", "TRUE", "True", "1")
275
+ elif _type == 'int':
179
276
  options[_key] = int(_value)
180
277
 
181
278
 
182
- if compat_platform == 'cygwin':
279
+ if compat_platform == "cygwin":
183
280
  # This hack pretends that the posix-like ctypes provides windows
184
281
  # functionality. COM does not work with this hack, so there is no
185
282
  # DirectSound support.
@@ -193,27 +290,26 @@ if compat_platform == 'cygwin':
193
290
  # Call tracing
194
291
  # ------------
195
292
 
196
- _trace_filename_abbreviations: Dict[str, str] = {}
293
+ _trace_filename_abbreviations: dict[str, str] = {}
197
294
  _trace_thread_count = 0
198
- _trace_args = options['debug_trace_args']
199
- _trace_depth = options['debug_trace_depth']
200
- _trace_flush = options['debug_trace_flush']
295
+ _trace_args = options["debug_trace_args"]
296
+ _trace_depth = options["debug_trace_depth"]
297
+ _trace_flush = options["debug_trace_flush"]
201
298
 
202
299
 
203
300
  def _trace_repr(value, size=40):
204
301
  value = repr(value)
205
302
  if len(value) > size:
206
- value = value[:size // 2 - 2] + '...' + value[-size // 2 - 1:]
303
+ value = value[:size // 2 - 2] + "..." + value[-size // 2 - 1:]
207
304
  return value
208
305
 
209
306
 
210
307
  def _trace_frame(thread, frame, indent):
211
-
212
308
  if frame.f_code is lib._TraceFunction.__call__.__code__:
213
309
  is_ctypes = True
214
- func = frame.f_locals['self']._func
310
+ func = frame.f_locals["self"]._func
215
311
  name = func.__name__
216
- location = '[ctypes]'
312
+ location = "[ctypes]"
217
313
  else:
218
314
  is_ctypes = False
219
315
  code = frame.f_code
@@ -225,7 +321,7 @@ def _trace_frame(thread, frame, indent):
225
321
  filename = _trace_filename_abbreviations[path]
226
322
  except KeyError:
227
323
  # Trim path down
228
- directory = ''
324
+ directory = ""
229
325
  path, filename = os.path.split(path)
230
326
 
231
327
  while len(directory + filename) < 30:
@@ -234,24 +330,24 @@ def _trace_frame(thread, frame, indent):
234
330
  if not directory:
235
331
  break
236
332
  else:
237
- filename = os.path.join('...', filename)
333
+ filename = os.path.join("...", filename)
238
334
  _trace_filename_abbreviations[path] = filename
239
335
 
240
- location = f'({filename}:{line})'
336
+ location = f"({filename}:{line})"
241
337
 
242
338
  if indent:
243
- name = f'Called from {name}'
244
- print(f'[{thread}] {indent}{name} {location}')
339
+ name = f"Called from {name}"
340
+ print(f"[{thread}] {indent}{name} {location}")
245
341
 
246
342
  if _trace_args:
247
343
  if is_ctypes:
248
- args = [_trace_repr(arg) for arg in frame.f_locals['args']]
344
+ args = [_trace_repr(arg) for arg in frame.f_locals["args"]]
249
345
  print(f' {indent}args=({", ".join(args)})')
250
346
  else:
251
347
  for argname in code.co_varnames[:code.co_argcount]:
252
348
  try:
253
349
  argvalue = _trace_repr(frame.f_locals[argname])
254
- print(f' {indent}{argname}={argvalue}')
350
+ print(f" {indent}{argname}={argvalue}")
255
351
  except:
256
352
  pass
257
353
 
@@ -261,18 +357,18 @@ def _trace_frame(thread, frame, indent):
261
357
 
262
358
  def _thread_trace_func(thread):
263
359
  def _trace_func(frame, event, arg):
264
- if event == 'call':
265
- indent = ''
360
+ if event == "call":
361
+ indent = ""
266
362
  for i in range(_trace_depth):
267
363
  _trace_frame(thread, frame, indent)
268
- indent += ' '
364
+ indent += " "
269
365
  frame = frame.f_back
270
366
  if not frame:
271
367
  break
272
368
 
273
- elif event == 'exception':
369
+ elif event == "exception":
274
370
  (exception, value, traceback) = arg
275
- print('First chance exception raised:', repr(exception))
371
+ print("First chance exception raised:", repr(exception))
276
372
 
277
373
  return _trace_func
278
374
 
@@ -290,7 +386,7 @@ class _ModuleProxy:
290
386
  _module = None
291
387
 
292
388
  def __init__(self, name: str):
293
- self.__dict__['_module_name'] = name
389
+ self.__dict__["_module_name"] = name
294
390
 
295
391
  def __getattr__(self, name):
296
392
  try:
@@ -299,10 +395,10 @@ class _ModuleProxy:
299
395
  if self._module is not None:
300
396
  raise
301
397
 
302
- import_name = f'pyglet.{self._module_name}'
398
+ import_name = f"pyglet.{self._module_name}"
303
399
  __import__(import_name)
304
400
  module = sys.modules[import_name]
305
- object.__setattr__(self, '_module', module)
401
+ object.__setattr__(self, "_module", module)
306
402
  globals()[self._module_name] = module
307
403
  return getattr(module, name)
308
404
 
@@ -313,58 +409,60 @@ class _ModuleProxy:
313
409
  if self._module is not None:
314
410
  raise
315
411
 
316
- import_name = f'pyglet.{self._module_name}'
412
+ import_name = f"pyglet.{self._module_name}"
317
413
  __import__(import_name)
318
414
  module = sys.modules[import_name]
319
- object.__setattr__(self, '_module', module)
415
+ object.__setattr__(self, "_module", module)
320
416
  globals()[self._module_name] = module
321
417
  setattr(module, name, value)
322
418
 
323
419
 
324
420
  # Lazily load all modules, except if performing type checking or code inspection.
325
421
  if TYPE_CHECKING:
326
- from . import app
327
- from . import clock
328
- from . import customtypes
329
- from . import display
330
- from . import event
331
- from . import font
332
- from . import gl
333
- from . import graphics
334
- from . import gui
335
- from . import image
336
- from . import input
337
- from . import lib
338
- from . import math
339
- from . import media
340
- from . import model
341
- from . import resource
342
- from . import sprite
343
- from . import shapes
344
- from . import text
345
- from . import window
422
+ from . import (
423
+ app,
424
+ clock,
425
+ customtypes,
426
+ display,
427
+ event,
428
+ font,
429
+ gl,
430
+ graphics,
431
+ gui,
432
+ image,
433
+ input,
434
+ lib,
435
+ math,
436
+ media,
437
+ model,
438
+ resource,
439
+ shapes,
440
+ sprite,
441
+ text,
442
+ window,
443
+ )
346
444
  else:
347
- app = _ModuleProxy('app') # type: ignore
348
- clock = _ModuleProxy('clock') # type: ignore
349
- customtypes = _ModuleProxy('customtypes') # type: ignore
350
- display = _ModuleProxy('display')
351
- event = _ModuleProxy('event') # type: ignore
352
- font = _ModuleProxy('font') # type: ignore
353
- gl = _ModuleProxy('gl') # type: ignore
354
- graphics = _ModuleProxy('graphics') # type: ignore
355
- gui = _ModuleProxy('gui') # type: ignore
356
- image = _ModuleProxy('image') # type: ignore
357
- input = _ModuleProxy('input') # type: ignore
358
- lib = _ModuleProxy('lib') # type: ignore
359
- math = _ModuleProxy('math') # type: ignore
360
- media = _ModuleProxy('media') # type: ignore
361
- model = _ModuleProxy('model') # type: ignore
362
- resource = _ModuleProxy('resource') # type: ignore
363
- sprite = _ModuleProxy('sprite') # type: ignore
364
- shapes = _ModuleProxy('shapes') # type: ignore
365
- text = _ModuleProxy('text') # type: ignore
366
- window = _ModuleProxy('window') # type: ignore
445
+ app = _ModuleProxy("app") # type: ignore
446
+ clock = _ModuleProxy("clock") # type: ignore
447
+ customtypes = _ModuleProxy("customtypes") # type: ignore
448
+ display = _ModuleProxy("display") # type: ignore
449
+ event = _ModuleProxy("event") # type: ignore
450
+ font = _ModuleProxy("font") # type: ignore
451
+ gl = _ModuleProxy("gl") # type: ignore
452
+ graphics = _ModuleProxy("graphics") # type: ignore
453
+ gui = _ModuleProxy("gui") # type: ignore
454
+ image = _ModuleProxy("image") # type: ignore
455
+ input = _ModuleProxy("input") # type: ignore
456
+ lib = _ModuleProxy("lib") # type: ignore
457
+ math = _ModuleProxy("math") # type: ignore
458
+ media = _ModuleProxy("media") # type: ignore
459
+ model = _ModuleProxy("model") # type: ignore
460
+ resource = _ModuleProxy("resource") # type: ignore
461
+ sprite = _ModuleProxy("sprite") # type: ignore
462
+ shapes = _ModuleProxy("shapes") # type: ignore
463
+ text = _ModuleProxy("text") # type: ignore
464
+ window = _ModuleProxy("window") # type: ignore
367
465
 
368
466
  # Call after creating proxies:
369
- if options['debug_trace']:
467
+ if options.debug_trace is True:
370
468
  _install_trace()