Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions runpod/apps/handles.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ def __init__(
fn: Callable,
spec: ResourceSpec,
):
if inspect.isclass(fn):
raise InvalidResourceError(
f"@app.{spec.kind.value} does not support class target '{fn.__name__}'. "
"Decorate a function and use @function.init for worker startup "
"initialization."
)
self._app = app
self._fn = fn
self.spec = spec
Expand Down
54 changes: 54 additions & 0 deletions tests/test_apps/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,60 @@ def t():
assert t.spec.cpu == ["cpu5c-2-4"]


@pytest.mark.parametrize("decorator", ["queue", "task"])
@pytest.mark.parametrize("name", [None, "inference"])
def test_function_decorators_reject_classes_before_registration(decorator, name):
app = App("a")

@app.queue()
def existing():
return "ok"

class Inference:
def __init__(self):
raise AssertionError("class targets must not be instantiated")

def __call__(self, value):
return value

with patch.object(app, "_register") as register:
with pytest.raises(InvalidResourceError) as exc:
getattr(app, decorator)(name=name)(Inference)

register.assert_not_called()
assert app.resources == {"existing": existing}
assert f"@app.{decorator}" in str(exc.value)
assert "Inference" in str(exc.value)
assert "function" in str(exc.value)
assert "@function.init" in str(exc.value)


@pytest.mark.parametrize("decorator", ["queue", "task"])
@pytest.mark.parametrize("is_async", [False, True])
async def test_function_decorators_support_functions_with_startup_hooks(
decorator, is_async
):
app = App("a")

def sync_handler(value):
return value * 2

async def async_handler(value):
return value * 2

handler = async_handler if is_async else sync_handler
function = getattr(app, decorator)()(handler)

@function.init
def startup():
raise AssertionError("startup hooks must not run during decoration or locally")

assert app.resources == {handler.__name__: function}
assert function._init_fn is startup
result = function.local(21)
assert (await result if is_async else result) == 42


@pytest.mark.parametrize("cpu", [[], "", ["cpu3c-2-4", ""], [None]])
def test_cpu_selection_requires_nonempty_instance_ids(cpu):
app = App("a")
Expand Down
Loading