-
Notifications
You must be signed in to change notification settings - Fork 40
Description
I'm working on an application that uses venusian to help with resource registration, and it's excellent for that.
What I'm running into is a problem where ValueError raised by the registration machinery gets silenced by the Scanner during its scan execution. An minimal example that can be run to see the problem:
import sys
import venusian
def register(resource_name):
def decorator(wrapped):
def callback(scanner, name, ob):
scanner.registry.add(resource_name, wrapped)
return
venusian.attach(wrapped, callback)
return wrapped
return decorator
@register(resource_name="unique-thing")
def f(): ...
@register(resource_name="unique-thing")
def g(): ...
class Registry:
def __init__(self):
self.resources = {}
def add(self, name, callable):
if name in self.resources:
print(f"Already have a resource by {name=}")
raise ValueError(f"Already have a resource by {name=}")
self.resources[name] = callable
scanner = venusian.Scanner(registry=Registry())
scanner.scan(sys.modules["__main__"]) # prints, but does not raise a ValueErrorThis all works perfectly fine if I raise something like ConfigurationError rather than ValueError (exactly what Pyramid does), but this is unexpected at least. And I'd like to keep the introduction of new classes to a minimum.
Where the silencing comes from
The indiscriminate catching of ValueError in the scan is documented to be due to metaclass annoyances:
try:
# Metaclasses might trick us by reaching this far and then
# fail with too little values to unpack.
for callback, cb_mod_name, liftid, scope in callbacks:
if cb_mod_name != mod_name:
# avoid processing objects that were imported into
# this module but were not actually defined there
continue
callback(self, name, ob)
except ValueError: # pragma: nocover
continueAs far as I understand, this intends to catch and recover from an error in the destructuring assignment on the for-loop but unfortunately also squashes the same in the callback execution.
I haven't tested this other than on this application, but the following narrows the except block to just the tuple expansion and resolves my immediate problem:
# Metaclasses might trick us by reaching this far and then
# fail with too little values to unpack.
try:
callback, cb_mod_name, liftid, scope = cb_tuple
except ValueError: # pragma: nocover
continue
if cb_mod_name != mod_name:
# avoid processing objects that were imported into
# this module but were not actually defined there
continue
callback(self, name, ob)