# Defining a Function
source: https://docs.chalk.ai/docs/functions/defining-a-function

## Define a Chalk Function with the @chalkcompute.function decorator, call it, and ship the helpers it imports.

To define a Chalk Function, decorate a Python callable with @chalkcompute.function:

```
import chalkcompute

@chalkcompute.function
def add(x: int, y: int) -> int:
    return x + y

if __name__ == "__main__":
    add.wait_ready()
    result = add.remote(3, 5)
    print(result)  # 8
```

Deploy by running the script: the decorator registers the function with
Chalk as soon as it runs. wait_ready() blocks until the deployed function
is available, and .remote(...) invokes it. add(3, 5) runs the Python body
locally instead; use .remote(...) to call the deployed version.

To call an already-deployed function from a different script — without
redeploying — attach to it by name with RemoteFunction.from_name:

```
import chalkcompute

fn = chalkcompute.RemoteFunction.from_name("add")
result = fn.remote(3, 5)
print(result)  # 8
```

### Function dependencies in external files

A deployed function often calls helpers defined in other files. Whether a
helper resolves remotely depends on where it lives relative to the decorated
function (the function under @chalkcompute.function):

- Helper in the decorated function's own package: the function's entire
top-level package is shipped automatically, so imports of sibling modules
resolve remotely with no extra configuration.
- Helper anywhere else (a standalone module, or a different package):
must be declared on the image with
Image.add_local_python_source.
The same applies to all file-external helpers when the decorated function is
defined in a standalone script rather than a package.

### Same package: automatic

If the decorated function's module runs as part of a package (imported from
an entrypoint, or executed directly with python -m), Chalk ships the whole
top-level package in the deploy image:

```
# mypkg/helpers.py
def normalize(text: str) -> str:
    return text.strip().lower()
```

```
# mypkg/pipeline.py
import chalkcompute

from mypkg.helpers import normalize

@chalkcompute.function
def clean(text: str) -> str:
    return normalize(text)
```

```
# main.py  (outside mypkg/)
from mypkg.pipeline import clean

print(clean.remote("  Hello "))
```

```
uv run python main.py
```

Running the entrypoint (or the function's own module with
uv run python -m mypkg.pipeline) deploys clean with all of mypkg, so
normalize is importable remotely exactly as it is locally.

Running the file by path (uv run python mypkg/pipeline.py) ships only that one file
because Python doesn't know that it belongs to a package. In this scenario, Chalk will
raise an error at deploy time.

Every run of an entrypoint that imports the decorated function deploys a new
function version. To call an existing deployment without redeploying, attach
to it by name with RemoteFunction.from_name instead.

```
# call_clean.py: call the deployed function without triggering a redeploy
import chalkcompute

fn = chalkcompute.RemoteFunction.from_name("clean")
print(fn.remote("  Hello "))
```

```
uv run python call_clean.py
```

### Everything else: add_local_python_source

When the decorated function is defined in a standalone script, or imports helpers
from outside its own top-level package, list those local modules or packages on the
image:

```
# helpers.py  (next to deploy.py)
def normalize(text: str) -> str:
    return text.strip().lower()
```

```
# deploy.py
import chalkcompute

from helpers import normalize

@chalkcompute.function(
    image=chalkcompute.Image.debian_slim().add_local_python_source("helpers"),
)
def clean(text: str) -> str:
    return normalize(text)
```

```
uv run python deploy.py
```

add_local_python_source accepts any number of top-level module or package
names. Each must be importable on your local Python path; packages are copied
whole (excluding __pycache__). Dotted names, namespace packages spanning
multiple directories, and compiled extensions are rejected with a
ValueError.

This ships your own source code only. Third-party dependencies still need to
be installed in the image with pip_install or run_commands.





