Skip to content
Open
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
2 changes: 2 additions & 0 deletions sdk/src/dagger/mod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
generate = _default_mod.generate
interface = _default_mod.interface
object_type = _default_mod.object_type
start = _default_mod.start
up = _default_mod.up


Expand All @@ -43,5 +44,6 @@ def default_module() -> Module:
"generate",
"interface",
"object_type",
"start",
"up",
]
43 changes: 40 additions & 3 deletions sdk/src/dagger/mod/_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import cattrs.gen
from cattrs.preconf import is_primitive_enum
from cattrs.preconf.json import JsonConverter
from typing_extensions import dataclass_transform, overload
from typing_extensions import dataclass_transform, deprecated, overload

import dagger
from dagger import dag
Expand Down Expand Up @@ -601,18 +601,55 @@ def wrapper(fn: Func[P, R]) -> Func[P, R]:

return wrapper(func) if func else wrapper

def up(
def start(
self,
func: Func[P, R] | None = None,
) -> Func[P, R] | Callable[[Func[P, R]], Func[P, R]]:
"""Mark a function as a service for ``dagger up``."""
"""Mark a function as a service for ``dagger start``.

Services are functions that return a :py:class:`dagger.Service` and
can be called without arguments. This decorator can be combined with
:py:meth:`function`.

Example usage::

@object_type
class MyModule:
@function
@start
def web(self) -> dagger.Service:
return (
dag.container()
.from_("nginx:alpine")
.with_exposed_port(80)
.as_service()
)

Parameters
----------
func:
The function to mark as a service. Should be an instance method in
a class decorated with :py:meth:`object_type`.
"""

def wrapper(fn: Func[P, R]) -> Func[P, R]:
setattr(fn, UP_DEF_KEY, True)
return fn

return wrapper(func) if func else wrapper

@deprecated("Use 'start' instead.")
def up(
self,
func: Func[P, R] | None = None,
) -> Func[P, R] | Callable[[Func[P, R]], Func[P, R]]:
"""Mark a function as a service for ``dagger start``.

.. deprecated::
Use :py:meth:`start` instead.
"""
return self.start(func)

def agent(
self,
func: Func[P, R] | None = None,
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/dagger/mod/_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def generate(self) -> bool:

@property
def service(self) -> bool:
"""Indicates whether the function is configured as a service for dagger up."""
"""Indicates whether the function is a service for dagger start."""
# Check both the metadata and the attribute to support either decorator order
return self.meta.service or getattr(self.wrapped, UP_DEF_KEY, False)

Expand Down
41 changes: 41 additions & 0 deletions sdk/tests/mod/test_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,47 @@ def function_first(self):
assert function_first_fn.check is True


def test_start_decorator_order():
"""Test that @start works whether applied before or after @function."""
mod = Module()

@mod.object_type
class Foo:
@mod.start
@mod.function
def start_first(self):
"""Start applied before function."""

@mod.function
@mod.start
def function_first(self):
"""Start applied after function."""

@mod.function
def regular(self):
"""Regular function."""

functions = mod.get_object("Foo").functions
assert functions["start_first"].service is True
assert functions["function_first"].service is True
assert functions["regular"].service is False


def test_up_is_deprecated_spelling_of_start():
mod = Module()

with pytest.deprecated_call(match="Use 'start' instead"):

@mod.object_type
class Foo:
@mod.function
@mod.up
def web(self):
"""Service marked with the deprecated spelling."""

assert mod.get_object("Foo").functions["web"].service is True


def test_function_argument_deprecated_metadata():
mod = Module()

Expand Down