pyalsoft 0.1.0__py3-none-win_amd64.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.
- pyalsoft/__init__.py +55 -0
- pyalsoft/_api.py +302 -0
- pyalsoft/_extensions.py +130 -0
- pyalsoft/_generated/commands.py +1737 -0
- pyalsoft/_generated/constants.py +1674 -0
- pyalsoft/_generated/enums.py +1397 -0
- pyalsoft/_generated/extensions.py +363 -0
- pyalsoft/_generated/functions.py +1992 -0
- pyalsoft/_generated/objects.py +701 -0
- pyalsoft/_generated/registry.py +1337 -0
- pyalsoft/_generated/semantics.py +660 -0
- pyalsoft/_generated/types.py +104 -0
- pyalsoft/_library.py +431 -0
- pyalsoft/_native/soft_oal.dll +0 -0
- pyalsoft/_objects.py +226 -0
- pyalsoft/_pyinstaller.py +14 -0
- pyalsoft/_pyinstaller_hooks/hook-pyalsoft.py +8 -0
- pyalsoft/_specs.py +186 -0
- pyalsoft/py.typed +1 -0
- pyalsoft/registry.py +71 -0
- pyalsoft-0.1.0.dist-info/METADATA +46 -0
- pyalsoft-0.1.0.dist-info/RECORD +31 -0
- pyalsoft-0.1.0.dist-info/WHEEL +4 -0
- pyalsoft-0.1.0.dist-info/entry_points.txt +2 -0
- pyalsoft-0.1.0.dist-info/licenses/LICENSE +21 -0
- pyalsoft-0.1.0.dist-info/licenses/LICENSES/Microsoft-GSL.txt +21 -0
- pyalsoft-0.1.0.dist-info/licenses/LICENSES/OpenAL-Registry.txt +28 -0
- pyalsoft-0.1.0.dist-info/licenses/LICENSES/THIRD-PARTY.md +42 -0
- pyalsoft-0.1.0.dist-info/licenses/LICENSES/fmt.txt +27 -0
- pyalsoft-0.1.0.dist-info/licenses/vendor/openal-soft/COPYING +437 -0
- pyalsoft-0.1.0.dist-info/licenses/vendor/openal-soft/LICENSE-pffft +38 -0
pyalsoft/__init__.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Generated raw bindings and runtime loading for OpenAL Soft."""
|
|
2
|
+
|
|
3
|
+
from pyalsoft import registry
|
|
4
|
+
from pyalsoft._extensions import Extension
|
|
5
|
+
from pyalsoft._generated import constants, enums, types
|
|
6
|
+
from pyalsoft._generated.constants import * # noqa: F403
|
|
7
|
+
from pyalsoft._generated.enums import * # noqa: F403
|
|
8
|
+
from pyalsoft._generated.extensions import ExtensionCapabilities
|
|
9
|
+
from pyalsoft._generated.objects import (
|
|
10
|
+
AuxiliaryEffectSlot,
|
|
11
|
+
Buffer,
|
|
12
|
+
Effect,
|
|
13
|
+
Filter,
|
|
14
|
+
Listener,
|
|
15
|
+
Source,
|
|
16
|
+
)
|
|
17
|
+
from pyalsoft._generated.types import * # noqa: F403
|
|
18
|
+
from pyalsoft._library import (
|
|
19
|
+
ContextRequiredError,
|
|
20
|
+
ExtensionUnavailableError,
|
|
21
|
+
ForeignFunction,
|
|
22
|
+
FunctionUnavailableError,
|
|
23
|
+
LibraryNotFoundError,
|
|
24
|
+
LibraryPath,
|
|
25
|
+
OpenALError,
|
|
26
|
+
OpenALLibrary,
|
|
27
|
+
load,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
*constants.__all__,
|
|
32
|
+
*enums.__all__,
|
|
33
|
+
*types.__all__,
|
|
34
|
+
"constants",
|
|
35
|
+
"enums",
|
|
36
|
+
"registry",
|
|
37
|
+
"types",
|
|
38
|
+
"AuxiliaryEffectSlot",
|
|
39
|
+
"Buffer",
|
|
40
|
+
"ContextRequiredError",
|
|
41
|
+
"Effect",
|
|
42
|
+
"Extension",
|
|
43
|
+
"ExtensionCapabilities",
|
|
44
|
+
"ExtensionUnavailableError",
|
|
45
|
+
"ForeignFunction",
|
|
46
|
+
"Filter",
|
|
47
|
+
"FunctionUnavailableError",
|
|
48
|
+
"LibraryNotFoundError",
|
|
49
|
+
"LibraryPath",
|
|
50
|
+
"Listener",
|
|
51
|
+
"OpenALError",
|
|
52
|
+
"OpenALLibrary",
|
|
53
|
+
"Source",
|
|
54
|
+
"load",
|
|
55
|
+
]
|
pyalsoft/_api.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""Python-value command marshalling over the generated ctypes layer."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ctypes
|
|
6
|
+
from collections.abc import Mapping, Sequence
|
|
7
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
8
|
+
|
|
9
|
+
from pyalsoft._generated import enums as _enums
|
|
10
|
+
from pyalsoft._generated import types as _types
|
|
11
|
+
from pyalsoft._generated.semantics import (
|
|
12
|
+
COMMAND_WRAPPERS_BY_NAME,
|
|
13
|
+
ENUM_GROUPS_BY_NAME,
|
|
14
|
+
)
|
|
15
|
+
from pyalsoft._specs import CommandWrapperSpec, WrapperParameterSpec
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from pyalsoft._library import OpenALLibrary
|
|
19
|
+
|
|
20
|
+
type ReadableBuffer = bytes | bytearray | memoryview
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _c_type_parts(c_type: str) -> tuple[str, int, bool]:
|
|
24
|
+
tokens = c_type.replace("*", " * ").split()
|
|
25
|
+
is_const = "const" in tokens
|
|
26
|
+
words = [token for token in tokens if token not in {"const", "*"}]
|
|
27
|
+
return " ".join(words), tokens.count("*"), is_const
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _is_character(base: str) -> bool:
|
|
31
|
+
return base in {"char", "ALchar", "ALCchar"}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _is_void(base: str) -> bool:
|
|
35
|
+
return base in {"void", "ALvoid", "ALCvoid"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _ctype_for(base: str) -> Any:
|
|
39
|
+
if _is_character(base):
|
|
40
|
+
return ctypes.c_char
|
|
41
|
+
if _is_void(base):
|
|
42
|
+
return ctypes.c_void_p
|
|
43
|
+
name = base.removeprefix("struct ")
|
|
44
|
+
try:
|
|
45
|
+
return getattr(_types, name)
|
|
46
|
+
except AttributeError as error:
|
|
47
|
+
raise TypeError(f"no generated ctypes type for {base!r}") from error
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _object_identifier(value: object) -> object:
|
|
51
|
+
identifier = getattr(value, "identifier", None)
|
|
52
|
+
return identifier if isinstance(identifier, int) else value
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _encode_string(value: object, name: str) -> bytes | None:
|
|
56
|
+
if value is None:
|
|
57
|
+
return None
|
|
58
|
+
if isinstance(value, bytes):
|
|
59
|
+
return value
|
|
60
|
+
if isinstance(value, str):
|
|
61
|
+
return value.encode("utf-8")
|
|
62
|
+
raise TypeError(f"{name} must be str, bytes, or None")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _buffer_input(value: object, *, writable: bool) -> tuple[object, int | None]:
|
|
66
|
+
if isinstance(value, bytes):
|
|
67
|
+
data = (ctypes.c_ubyte * len(value)).from_buffer_copy(value)
|
|
68
|
+
return data, len(value)
|
|
69
|
+
if isinstance(value, bytearray):
|
|
70
|
+
array_type = ctypes.c_ubyte * len(value)
|
|
71
|
+
data = (
|
|
72
|
+
array_type.from_buffer(value)
|
|
73
|
+
if writable
|
|
74
|
+
else array_type.from_buffer_copy(value)
|
|
75
|
+
)
|
|
76
|
+
return data, len(value)
|
|
77
|
+
if isinstance(value, memoryview):
|
|
78
|
+
contiguous = value.cast("B")
|
|
79
|
+
array_type = ctypes.c_ubyte * contiguous.nbytes
|
|
80
|
+
if writable and not contiguous.readonly:
|
|
81
|
+
data = array_type.from_buffer(contiguous)
|
|
82
|
+
else:
|
|
83
|
+
data = array_type.from_buffer_copy(contiguous)
|
|
84
|
+
return data, contiguous.nbytes
|
|
85
|
+
return value, None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _prepare_input(
|
|
89
|
+
parameter: WrapperParameterSpec, value: object
|
|
90
|
+
) -> tuple[object, int | None]:
|
|
91
|
+
base, pointer_depth, _is_const = _c_type_parts(parameter.c_type)
|
|
92
|
+
value = _object_identifier(value)
|
|
93
|
+
if pointer_depth == 0:
|
|
94
|
+
if base in {"ALboolean", "ALCboolean"}:
|
|
95
|
+
return (b"\x01" if bool(value) else b"\x00"), None
|
|
96
|
+
return value, None
|
|
97
|
+
|
|
98
|
+
if base in {"ALCdevice", "ALCcontext"} and pointer_depth == 1:
|
|
99
|
+
return value, None
|
|
100
|
+
if _is_character(base):
|
|
101
|
+
encoded = _encode_string(value, parameter.python_name)
|
|
102
|
+
return encoded, len(encoded) if encoded is not None else 0
|
|
103
|
+
if _is_void(base) or base.startswith("struct "):
|
|
104
|
+
return _buffer_input(value, writable=parameter.direction == "inout")
|
|
105
|
+
if value is None:
|
|
106
|
+
return None, 0
|
|
107
|
+
if isinstance(value, (ctypes.Array, ctypes._Pointer)):
|
|
108
|
+
return value, None
|
|
109
|
+
if not isinstance(value, Sequence):
|
|
110
|
+
raise TypeError(f"{parameter.python_name} must be a sequence")
|
|
111
|
+
values = list(value)
|
|
112
|
+
if parameter.name in {"attrlist", "attribs"} and (
|
|
113
|
+
not values or int(values[-1]) != 0
|
|
114
|
+
):
|
|
115
|
+
values.append(0)
|
|
116
|
+
ctype = _ctype_for(base)
|
|
117
|
+
array = (ctype * len(values))(*values)
|
|
118
|
+
return array, len(values)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _output_length(
|
|
122
|
+
parameter: WrapperParameterSpec,
|
|
123
|
+
values: Mapping[str, object],
|
|
124
|
+
derived_lengths: Mapping[str, int],
|
|
125
|
+
result_size: int,
|
|
126
|
+
) -> int:
|
|
127
|
+
if parameter.length is None:
|
|
128
|
+
return result_size
|
|
129
|
+
if parameter.length.isdigit():
|
|
130
|
+
return int(parameter.length)
|
|
131
|
+
raw = values.get(parameter.length, derived_lengths.get(parameter.length))
|
|
132
|
+
if raw is None:
|
|
133
|
+
raise TypeError(
|
|
134
|
+
f"cannot determine output length {parameter.length!r} for "
|
|
135
|
+
f"{parameter.name!r}"
|
|
136
|
+
)
|
|
137
|
+
return int(cast(Any, raw))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _allocate_output(
|
|
141
|
+
parameter: WrapperParameterSpec,
|
|
142
|
+
values: Mapping[str, object],
|
|
143
|
+
derived_lengths: Mapping[str, int],
|
|
144
|
+
result_size: int,
|
|
145
|
+
) -> ctypes.Array[Any]:
|
|
146
|
+
base, _pointer_depth, _is_const = _c_type_parts(parameter.c_type)
|
|
147
|
+
length = _output_length(parameter, values, derived_lengths, result_size)
|
|
148
|
+
if length < 0:
|
|
149
|
+
raise ValueError(
|
|
150
|
+
f"output length for {parameter.python_name} cannot be negative"
|
|
151
|
+
)
|
|
152
|
+
return cast(ctypes.Array[Any], (_ctype_for(base) * length)())
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _enum_result(namespace: str, group: str | None, value: int) -> object:
|
|
156
|
+
if group is None:
|
|
157
|
+
return value
|
|
158
|
+
spec = ENUM_GROUPS_BY_NAME.get((namespace, group))
|
|
159
|
+
if spec is None:
|
|
160
|
+
return value
|
|
161
|
+
enum_type = cast(type[Any], getattr(_enums, spec.python_name))
|
|
162
|
+
try:
|
|
163
|
+
return enum_type(value)
|
|
164
|
+
except ValueError:
|
|
165
|
+
return value
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _convert_scalar(base: str, value: object) -> object:
|
|
169
|
+
raw = getattr(value, "value", value)
|
|
170
|
+
if base in {"ALboolean", "ALCboolean"}:
|
|
171
|
+
if isinstance(raw, bytes):
|
|
172
|
+
return raw != b"\x00"
|
|
173
|
+
return bool(raw)
|
|
174
|
+
if _is_character(base):
|
|
175
|
+
if raw is None:
|
|
176
|
+
return None
|
|
177
|
+
if isinstance(raw, bytes):
|
|
178
|
+
return raw.decode("utf-8")
|
|
179
|
+
return raw
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _convert_output(
|
|
183
|
+
parameter: WrapperParameterSpec,
|
|
184
|
+
wrapper: CommandWrapperSpec,
|
|
185
|
+
allocation: ctypes.Array[Any],
|
|
186
|
+
) -> object:
|
|
187
|
+
base, _pointer_depth, _is_const = _c_type_parts(parameter.c_type)
|
|
188
|
+
if _is_character(base):
|
|
189
|
+
return bytes(allocation).split(b"\x00", 1)[0].decode("utf-8")
|
|
190
|
+
converted = [
|
|
191
|
+
_enum_result(
|
|
192
|
+
wrapper.namespace,
|
|
193
|
+
parameter.group,
|
|
194
|
+
cast(int, _convert_scalar(base, item)),
|
|
195
|
+
)
|
|
196
|
+
if parameter.group is not None
|
|
197
|
+
else _convert_scalar(base, item)
|
|
198
|
+
for item in allocation
|
|
199
|
+
]
|
|
200
|
+
is_vector = (parameter.length is not None and parameter.length != "1") or (
|
|
201
|
+
parameter.length is None and wrapper.result_size
|
|
202
|
+
)
|
|
203
|
+
return tuple(converted) if is_vector else converted[0]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _convert_return(wrapper: CommandWrapperSpec, value: object) -> object:
|
|
207
|
+
base, pointer_depth, _is_const = _c_type_parts(wrapper.return_type)
|
|
208
|
+
if base == "void" and pointer_depth == 0:
|
|
209
|
+
return None
|
|
210
|
+
if pointer_depth and _is_character(base):
|
|
211
|
+
if value is None:
|
|
212
|
+
return None
|
|
213
|
+
return cast(bytes, value).decode("utf-8")
|
|
214
|
+
converted = _convert_scalar(base, value)
|
|
215
|
+
if wrapper.return_group is not None:
|
|
216
|
+
return _enum_result(
|
|
217
|
+
wrapper.namespace, wrapper.return_group, cast(int, converted)
|
|
218
|
+
)
|
|
219
|
+
return converted
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class CommandNamespace:
|
|
223
|
+
"""Base class used by generated AL and ALC Python command namespaces."""
|
|
224
|
+
|
|
225
|
+
def __init__(self, library: OpenALLibrary) -> None:
|
|
226
|
+
self._library = library
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def library(self) -> OpenALLibrary:
|
|
230
|
+
"""The raw library underlying this namespace."""
|
|
231
|
+
|
|
232
|
+
return self._library
|
|
233
|
+
|
|
234
|
+
def _invoke(
|
|
235
|
+
self,
|
|
236
|
+
name: str,
|
|
237
|
+
values: Mapping[str, object],
|
|
238
|
+
*,
|
|
239
|
+
result_size: int = 1,
|
|
240
|
+
resolution_device: object | None = None,
|
|
241
|
+
) -> object:
|
|
242
|
+
if result_size < 1:
|
|
243
|
+
raise ValueError("result_size must be at least one")
|
|
244
|
+
wrapper = COMMAND_WRAPPERS_BY_NAME[name]
|
|
245
|
+
prepared: dict[str, object] = {}
|
|
246
|
+
derived_lengths: dict[str, int] = {}
|
|
247
|
+
for parameter in wrapper.parameters:
|
|
248
|
+
if parameter.direction == "out" or not parameter.visible:
|
|
249
|
+
continue
|
|
250
|
+
argument, length = _prepare_input(parameter, values[parameter.name])
|
|
251
|
+
prepared[parameter.name] = argument
|
|
252
|
+
if parameter.length is not None and not parameter.length.isdigit():
|
|
253
|
+
if length is None:
|
|
254
|
+
continue
|
|
255
|
+
previous = derived_lengths.get(parameter.length)
|
|
256
|
+
if previous is not None and previous != length:
|
|
257
|
+
raise ValueError(
|
|
258
|
+
f"inputs controlled by {parameter.length!r} have "
|
|
259
|
+
"different lengths"
|
|
260
|
+
)
|
|
261
|
+
derived_lengths[parameter.length] = length
|
|
262
|
+
|
|
263
|
+
arguments: list[object] = []
|
|
264
|
+
outputs: list[tuple[WrapperParameterSpec, ctypes.Array[Any]]] = []
|
|
265
|
+
for parameter in wrapper.parameters:
|
|
266
|
+
if parameter.direction == "out":
|
|
267
|
+
allocation = _allocate_output(
|
|
268
|
+
parameter, values, derived_lengths, result_size
|
|
269
|
+
)
|
|
270
|
+
outputs.append((parameter, allocation))
|
|
271
|
+
arguments.append(allocation)
|
|
272
|
+
elif parameter.visible:
|
|
273
|
+
arguments.append(prepared[parameter.name])
|
|
274
|
+
else:
|
|
275
|
+
try:
|
|
276
|
+
arguments.append(derived_lengths[parameter.name])
|
|
277
|
+
except KeyError as error:
|
|
278
|
+
raise TypeError(
|
|
279
|
+
f"cannot infer hidden parameter {parameter.name!r}"
|
|
280
|
+
) from error
|
|
281
|
+
|
|
282
|
+
device = (
|
|
283
|
+
resolution_device if resolution_device is not None else values.get("device")
|
|
284
|
+
)
|
|
285
|
+
function = self._library.get_function(name, device=device)
|
|
286
|
+
native_result = function(*arguments)
|
|
287
|
+
results: list[object] = []
|
|
288
|
+
base, pointer_depth, _is_const = _c_type_parts(wrapper.return_type)
|
|
289
|
+
if not (base == "void" and pointer_depth == 0):
|
|
290
|
+
results.append(_convert_return(wrapper, native_result))
|
|
291
|
+
results.extend(
|
|
292
|
+
_convert_output(parameter, wrapper, allocation)
|
|
293
|
+
for parameter, allocation in outputs
|
|
294
|
+
)
|
|
295
|
+
if not results:
|
|
296
|
+
return None
|
|
297
|
+
if len(results) == 1:
|
|
298
|
+
return results[0]
|
|
299
|
+
return tuple(results)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
__all__ = ["CommandNamespace", "ReadableBuffer"]
|
pyalsoft/_extensions.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Runtime support for generated OpenAL extension capability objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterator, Mapping
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from pyalsoft._generated.registry import API_SETS
|
|
9
|
+
from pyalsoft._specs import ApiSetSpec
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from pyalsoft._library import ForeignFunction, OpenALLibrary
|
|
13
|
+
|
|
14
|
+
_EXTENSIONS = {item.name: item for item in API_SETS if item.kind == "extension"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Extension:
|
|
18
|
+
"""Capabilities and runtime access for one registry extension."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, library: OpenALLibrary, spec: ApiSetSpec) -> None:
|
|
21
|
+
self.library = library
|
|
22
|
+
self.spec = spec
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def name(self) -> str:
|
|
26
|
+
return self.spec.name
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def apis(self) -> tuple[str, ...]:
|
|
30
|
+
return self.spec.apis
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def commands(self) -> tuple[str, ...]:
|
|
34
|
+
return tuple(
|
|
35
|
+
dict.fromkeys(
|
|
36
|
+
member.name
|
|
37
|
+
for requirement in self.spec.requirements
|
|
38
|
+
for member in requirement.members
|
|
39
|
+
if member.kind == "command"
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def enums(self) -> tuple[str, ...]:
|
|
45
|
+
return tuple(
|
|
46
|
+
dict.fromkeys(
|
|
47
|
+
member.name
|
|
48
|
+
for requirement in self.spec.requirements
|
|
49
|
+
for member in requirement.members
|
|
50
|
+
if member.kind == "enum"
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def types(self) -> tuple[str, ...]:
|
|
56
|
+
return tuple(
|
|
57
|
+
dict.fromkeys(
|
|
58
|
+
member.name
|
|
59
|
+
for requirement in self.spec.requirements
|
|
60
|
+
for member in requirement.members
|
|
61
|
+
if member.kind == "type"
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def dependencies(self) -> tuple[str, ...]:
|
|
67
|
+
return tuple(
|
|
68
|
+
dict.fromkeys(
|
|
69
|
+
requirement.depends
|
|
70
|
+
for requirement in self.spec.requirements
|
|
71
|
+
if requirement.depends is not None
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def is_present(self, device: object | None = None) -> bool:
|
|
76
|
+
"""Check whether the extension is available for a context or device."""
|
|
77
|
+
|
|
78
|
+
if self.name.startswith("ALC_") or (device is not None and "alc" in self.apis):
|
|
79
|
+
return self.library.is_alc_extension_present(self.name, device)
|
|
80
|
+
return self.library.is_al_extension_present(self.name)
|
|
81
|
+
|
|
82
|
+
def require(self, device: object | None = None) -> None:
|
|
83
|
+
"""Raise :class:`ExtensionUnavailableError` when unavailable."""
|
|
84
|
+
|
|
85
|
+
if self.is_present(device):
|
|
86
|
+
return
|
|
87
|
+
from pyalsoft._library import ExtensionUnavailableError
|
|
88
|
+
|
|
89
|
+
raise ExtensionUnavailableError(
|
|
90
|
+
f"OpenAL extension {self.name!r} is not available"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def get_function(
|
|
94
|
+
self, name: str, *, device: object | None = None
|
|
95
|
+
) -> ForeignFunction:
|
|
96
|
+
"""Resolve a command declared by this extension."""
|
|
97
|
+
|
|
98
|
+
if name not in self.commands:
|
|
99
|
+
raise KeyError(f"{name!r} is not declared by {self.name}")
|
|
100
|
+
return self.library.get_function(name, device=device)
|
|
101
|
+
|
|
102
|
+
def __repr__(self) -> str:
|
|
103
|
+
return f"Extension(name={self.name!r}, apis={self.apis!r})"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class ExtensionNamespace(Mapping[str, Extension]):
|
|
107
|
+
"""Mapping base used by the generated extension capability namespace."""
|
|
108
|
+
|
|
109
|
+
def __init__(self, library: OpenALLibrary) -> None:
|
|
110
|
+
self.library = library
|
|
111
|
+
self._cache: dict[str, Extension] = {}
|
|
112
|
+
|
|
113
|
+
def _get(self, name: str) -> Extension:
|
|
114
|
+
extension = self._cache.get(name)
|
|
115
|
+
if extension is None:
|
|
116
|
+
extension = Extension(self.library, _EXTENSIONS[name])
|
|
117
|
+
self._cache[name] = extension
|
|
118
|
+
return extension
|
|
119
|
+
|
|
120
|
+
def __getitem__(self, name: str) -> Extension:
|
|
121
|
+
return self._get(name)
|
|
122
|
+
|
|
123
|
+
def __iter__(self) -> Iterator[str]:
|
|
124
|
+
return iter(_EXTENSIONS)
|
|
125
|
+
|
|
126
|
+
def __len__(self) -> int:
|
|
127
|
+
return len(_EXTENSIONS)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
__all__ = ["Extension", "ExtensionNamespace"]
|