Skip to content

Commit 903531a

Browse files
authored
Improve errors (#47)
* Work on improving error information * Better runtime error handling + highlighted parameter * Add version number fixup * Update SlangPy->0.19.2, SGL->0.12.3 * Fix incorrect exceptions
1 parent 09ffa7f commit 903531a

13 files changed

+212
-35
lines changed

docs/changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Changelog
22
---------
33

4+
**Version 0.19.2**
5+
- Update SGL -> 0.12.3
6+
- Better error messages during generation
7+
- Fix corrupt error tables
8+
- Restore detailed error information during dispatch
9+
410
**Version 0.19.1**
511
- Update SGL -> 0.12.2
612
- Fix major issue with texture transposes

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "slangpy"
7-
version = "0.19.1"
7+
version = "0.19.2"
88
authors = [
99
{name = "Chris Cummings", email = "[email protected]"},
1010
{name = "Benedikt Bitterli", email = "[email protected]"},
@@ -17,7 +17,7 @@ requires-python = ">=3.9"
1717
dependencies = [
1818
"typing_extensions",
1919
"numpy",
20-
"nv-sgl==0.12.2"
20+
"nv-sgl==0.12.3"
2121
]
2222

2323
[tool.pyright]

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
typing_extensions
22
numpy
3-
nv-sgl == 0.12.2
3+
nv-sgl == 0.12.3
44

55
pre-commit
66
pytest

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
typing_extensions
22
numpy
3-
nv-sgl == 0.12.2
3+
nv-sgl == 0.12.3

slangpy/core/calldata.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ def __init__(
108108
slang_function = specialize(
109109
context, bindings, build_info.reflections, build_info.type_reflection)
110110
if isinstance(slang_function, MismatchReason):
111-
raise KernelGenException(
111+
raise ResolveException(
112112
f"Function signature mismatch: {slang_function.reason}\n\n"
113113
f"{mismatch_info(bindings, build_info.reflections)}\n")
114114

115115
# Check for differentiability error
116116
if not slang_function.differentiable and self.call_mode != CallMode.prim:
117-
raise KernelGenException(
117+
raise ResolveException(
118118
f"Could not call function '{function.name}': Function is not differentiable\n\n"
119119
f"{mismatch_info(bindings, build_info.reflections)}\n")
120120

@@ -220,34 +220,37 @@ def __init__(
220220
slang_function, SlangFunction) else build_info.reflections[0]
221221
raise ValueError(
222222
f"{e.message}\n\n"
223-
f"{bound_exception_info(bindings, ref, e.variable)}\n")
223+
f"{bound_exception_info(bindings, ref, e.variable)}\n") from e
224224
else:
225-
raise e
225+
raise
226226
except SlangCompileError as e:
227227
if bindings is not None:
228228
ref = slang_function.reflection if isinstance(
229229
slang_function, SlangFunction) else build_info.reflections[0]
230230
raise ValueError(
231-
f"Slang compilation error: {e}\n. See .temp directory for generated shader.\n"
232-
f"This most commonly occurs as a result of an invalid explicit type cast, or bug in implicit casting logic.\n"
233-
f"{bound_exception_info(bindings, ref, None)}\n")
231+
f"Slang compilation error: {e}\n. Use set_dump_generated_shaders to enable dump generated shader to .temp.\n"
232+
f"This most commonly occurs as a result of an invalid explicit type cast, or bug in implicit casting logic.\n\n"
233+
f"{bound_exception_info(bindings, ref, None)}\n") from e
234234
else:
235235
raise e
236236
except KernelGenException as e:
237237
if bindings is not None:
238238
ref = slang_function.reflection if isinstance(
239239
slang_function, SlangFunction) else build_info.reflections[0]
240240
raise ValueError(
241-
f"Exception in kernel generation: {e.message}\n."
242-
f"{bound_exception_info(bindings, ref, None)}\n")
241+
f"Exception in kernel generation: {e.message}.\n\n"
242+
f"{bound_exception_info(bindings, ref, None)}\n") from e
243243
else:
244244
raise e
245+
except ResolveException as e:
246+
# Triggered from within calldata, doesn't need augmenting
247+
raise e
245248
except Exception as e:
246249
if bindings is not None:
247250
ref = slang_function.reflection if isinstance(
248251
slang_function, SlangFunction) else build_info.reflections[0]
249252
raise ValueError(
250-
f"Exception in kernel generation: {e}\n."
251-
f"{bound_exception_info(bindings, ref, None)}\n")
253+
f"Exception in kernel generation: {e}.\n"
254+
f"{bound_exception_info(bindings, ref, None)}\n") from e
252255
else:
253256
raise e

slangpy/core/callsignature.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ def __init__(self, reason: str):
2525
self.reason = reason
2626

2727

28+
class ResolveException(Exception):
29+
def __init__(self, message: str):
30+
super().__init__(message)
31+
self.message = message
32+
33+
2834
class KernelGenException(Exception):
2935
def __init__(self, message: str):
3036
super().__init__(message)
@@ -84,7 +90,7 @@ def specialize(
8490

8591
if signature.num_function_kwargs > 0 or signature.has_implicit_args:
8692
if function.is_overloaded:
87-
return MismatchReason("Call an overloaded function with named or implicit arguments is not currently supported.")
93+
return MismatchReason("Calling an overloaded function with named or implicit arguments is not currently supported.")
8894

8995
function_parameters = [x for x in function.parameters]
9096

@@ -195,14 +201,18 @@ def to_type_reflection(input: Any) -> TypeReflection:
195201
f"After implicit casting, cannot convert {input} to TypeReflection.")
196202

197203
types = [to_type_reflection(x.vector_type) for x in root_params]
198-
if any(x is None for x in types):
199-
raise KernelGenException(
200-
"After implicit casting, unable to resolve all Slang types for specialization overload resolution.")
204+
for (type, param) in zip(types, root_params):
205+
if type is None:
206+
raise KernelGenException(
207+
f"After implicit casting, unable to find reflection data for {param.variable_name}"
208+
"This typically suggests the binding system has attempted to generate an invalid Slang type.")
201209

202210
specialized = function.reflection.specialize_with_arg_types(types)
203211
if specialized is None:
204212
raise KernelGenException(
205-
"After implicit casting, no Slang overload found that matches the provided Python argument types.")
213+
"After implicit casting, no Slang overload found that matches the provided Python argument types. "
214+
"This typically suggests SlangPy selected an overload to call, but couldn't find a valid "
215+
"way to pass your Python arguments to it.")
206216

207217

208218
def bind(

slangpy/core/function.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,24 @@ def call(self, *args: Any, **kwargs: Any) -> Any:
205205
del kwargs['_result']
206206
return self.return_type(resval).call(*args, **kwargs)
207207
else:
208-
return self._native_call(self.module.call_data_cache, *args, **kwargs)
208+
try:
209+
return self._native_call(self.module.call_data_cache, *args, **kwargs)
210+
except ValueError as e:
211+
# If runtime returned useful information, reformat it and raise a new exception
212+
# Otherwise just throw the original.
213+
if len(e.args) != 1 or not isinstance(e.args[0], dict) or not 'message' in e.args[0] or not 'source' in e.args[0] or not 'context' in e.args[0]:
214+
raise
215+
from slangpy.bindings.boundvariableruntime import BoundVariableRuntime
216+
from slangpy.core.native import NativeCallData
217+
from slangpy.core.logging import bound_runtime_call_table
218+
msg: str = e.args[0]['message']
219+
source: BoundVariableRuntime = e.args[0]['source']
220+
context: NativeCallData = e.args[0]['context']
221+
runtime = context.runtime
222+
msg += "\n\n" + \
223+
bound_runtime_call_table(runtime, source) + \
224+
"\n\nFor help and support: https://khr.io/slangdiscord"
225+
raise ValueError(msg) from e
209226

210227
def append_to(self, command_buffer: CommandBuffer, *args: Any, **kwargs: Any):
211228
"""

slangpy/core/logging.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def _generate_table_recurse(data: list[Any], columns: list[TableColumn], depth:
6868
row_str = " | ".join(cols)
6969

7070
if row == highlight:
71-
row_str = f"\033[1;32;40m{row_str}\033[0m"
71+
row_str += "<-------"
7272

7373
rows.append(row_str)
7474
children = children_id(row)
@@ -139,7 +139,7 @@ def bound_call_table(data: 'BoundCall', highlight: Optional['BoundVariable'] = N
139139
def bound_runtime_variables_table(data: list['BoundVariableRuntime'], highlight: Optional['BoundVariableRuntime'] = None, filter: Optional[dict[str, bool]] = None):
140140
columns = [
141141
TableColumn("Name", 20, lambda x: _pyarg_name(x._source_for_exceptions.name)),
142-
TableColumn("Index", 10, "param_index"),
142+
TableColumn("Index", 10, lambda x: x._source_for_exceptions.param_index),
143143
TableColumn("PyType", 30, lambda x: _type_name(x._source_for_exceptions.python)),
144144
TableColumn("SlType", 30, lambda x: _type_name(
145145
x._source_for_exceptions.slang_type)),
@@ -206,6 +206,7 @@ def mismatch_info(call: 'BoundCall', reflections: list[FunctionReflection]):
206206
text.append("")
207207
text.append(f"Python arguments:")
208208
text.append(f"{bound_call_table(call)}")
209+
text.append(f"For help and support: https://khr.io/slangdiscord")
209210

210211
return "\n".join(text)
211212

@@ -214,9 +215,14 @@ def bound_exception_info(call: 'BoundCall', concrete_reflection: FunctionReflect
214215
text: list[str] = []
215216

216217
text.append(f"Selected overload:")
217-
text.append(f"{function_reflection(concrete_reflection)}")
218+
text.append(f" {function_reflection(concrete_reflection)}")
218219
text.append("")
220+
if variable is not None and variable.name != "":
221+
text.append(f"Error caused by argument: {variable.name}")
222+
text.append("")
219223
text.append(f"Python arguments:")
220224
text.append(f"{bound_call_table(call, highlight=variable)}")
225+
text.append("")
226+
text.append(f"For help and support: https://khr.io/slangdiscord")
221227

222228
return "\n".join(text)

slangpy/tests/test_differential_function_call.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def test_call_none_differentiable(device_type: DeviceType):
6262
res = function(a, b)
6363
assert res == python_eval_polynomial(a, b)
6464

65-
with pytest.raises(ValueError, match="Could not call function 'polynomial': Function is not differentiable"):
65+
with pytest.raises(Exception, match="Could not call function 'polynomial': Function is not differentiable"):
6666
function.bwds(a, b, res)
6767

6868

slangpy/tests/test_errors.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def test_no_matching_arg_count(device_type: DeviceType):
2424
device = helpers.get_device(device_type)
2525
function = helpers.create_function_from_module(device, "foo", MODULE)
2626

27-
with pytest.raises(ValueError, match=r'Too many positional arguments'):
27+
with pytest.raises(Exception, match=r'Too many positional arguments'):
2828
function.call(1.0, 2.0)
2929

3030

@@ -34,7 +34,7 @@ def test_no_matching_arg_name(device_type: DeviceType):
3434
device = helpers.get_device(device_type)
3535
function = helpers.create_function_from_module(device, "foo", MODULE)
3636

37-
with pytest.raises(ValueError, match=r'No parameter named'):
37+
with pytest.raises(Exception, match=r'No parameter named'):
3838
function.call(b=10.0)
3939

4040

@@ -46,7 +46,7 @@ def test_not_enough_args(device_type: DeviceType):
4646

4747
# note: due to no implicit args, falls straight through to slang resolution which provides
4848
# no special error info yet
49-
with pytest.raises(ValueError, match=r'No Slang overload found'):
49+
with pytest.raises(Exception, match=r'No Slang overload found'):
5050
function.call()
5151

5252

@@ -56,7 +56,7 @@ def test_not_enough_args_2(device_type: DeviceType):
5656
device = helpers.get_device(device_type)
5757
function = helpers.create_function_from_module(device, "foo2", MODULE)
5858

59-
with pytest.raises(ValueError, match=r'all parameters must be specified'):
59+
with pytest.raises(Exception, match=r'all parameters must be specified'):
6060
function.call(10.0)
6161

6262

@@ -66,7 +66,7 @@ def test_specify_twice(device_type: DeviceType):
6666
device = helpers.get_device(device_type)
6767
function = helpers.create_function_from_module(device, "foo2", MODULE)
6868

69-
with pytest.raises(ValueError, match=r'already specified'):
69+
with pytest.raises(Exception, match=r'already specified'):
7070
function.call(10.0, a=20.0)
7171

7272

@@ -76,7 +76,7 @@ def test_implicit_overload(device_type: DeviceType):
7676
device = helpers.get_device(device_type)
7777
function = helpers.create_function_from_module(device, "foo_ol", MODULE)
7878

79-
with pytest.raises(ValueError, match=r'overloaded function with named or implicit arguments'):
79+
with pytest.raises(Exception, match=r'overloaded function with named or implicit arguments'):
8080
function.call(10.0)
8181

8282

@@ -129,5 +129,19 @@ def test_invalid_broadcast(device_type: DeviceType):
129129
function(buffer, buffer2)
130130

131131

132+
@pytest.mark.parametrize("device_type", helpers.DEFAULT_DEVICE_TYPES)
133+
def test_invalid_broadcast_during_dispatch(device_type: DeviceType):
134+
135+
device = helpers.get_device(device_type)
136+
function = helpers.create_function_from_module(device, "foo2", MODULE)
137+
138+
buffer = NDBuffer(device, dtype=float, shape=(10, 5))
139+
buffer2 = NDBuffer(device, dtype=float, shape=(10, 10))
140+
141+
# fail to specialize a float3 against a float
142+
with pytest.raises(ValueError, match=r'Shape mismatch'):
143+
function(buffer, buffer2)
144+
145+
132146
if __name__ == "__main__":
133147
pytest.main([__file__, "-v"])

0 commit comments

Comments
 (0)