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
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@ of those changes to CLEARTYPE SRL.
| [@5tefan](https://github.com/5tefan/) | Stefan Codrescu |
| [@kuba-lilz](https://github.com/kuba-lilz/) | Jakub Kolodziejczyk |
| [@dbowring](https://github.com/dbowring/) | Daniel Bowring |
| [@bagowix](https://github.com/bagowix/) | Bogdan Galushko |
160 changes: 160 additions & 0 deletions examples/use_actors_from_different_files/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Using Actors from Different Files

## Overview

If you want to use actors from different files in your project, you must ensure that all actors are properly initialized and visible to Dramatiq. This example demonstrates how to set up a project with multiple actor files and run them through the Dramatiq CLI.

## Project Structure

```
project/
├── core/
│ ├── dramatiq_service.py
├── tasks/
│ ├── foo/
│ │ ├── __init__.py
│ │ ├── tasks.py
│ ├── bar/
│ │ ├── __init__.py
│ │ ├── tasks.py
│ ├── __init__.py
├── main.py
```

- `tasks/`: Contains actor definitions (`foo/tasks.py`, `bar/tasks.py`).
- `dramatiq_service.py`: Initializes the broker and imports all actors.
- `main.py`: FastAPI application.

## Setup

1. **Broker Initialization (`dramatiq_service.py`)**

```python
import dramatiq
from dramatiq.brokers.rabbitmq import RabbitmqBroker
from dramatiq.middleware import AsyncIO

# Initialize RabbitMQ broker
rabbitmq_broker = RabbitmqBroker(url="amqp://guest:guest@localhost:5672")
rabbitmq_broker.add_middleware(AsyncIO())
dramatiq.set_broker(rabbitmq_broker)

# Import all actors
from tasks.bar import * # Ensure all actors are imported
from tasks.foo import *
```

2. **Actor Definitions**

`tasks/bar/tasks.py`:

```python
import dramatiq

@dramatiq.actor
def bar_task():
print("Bar task done.")
```

`tasks/foo/tasks.py`:

```python
import dramatiq

@dramatiq.actor
def foo_task():
print("Foo task done.")
```

3. **FastAPI Endpoint (`main.py`)**

```python
from fastapi import FastAPI
from tasks.bar.tasks import bar_task
from tasks.foo.tasks import foo_task

app = FastAPI()

@app.get("/test-task")
def test_task():
bar_task.send()
foo_task.send()
return {"message": "Tasks sent"}
```

## Steps to Run

1. **Start the FastAPI application**:

```
uvicorn main:app --host 0.0.0.0 --port 8001 --reload
```

2. **Run the Dramatiq worker**:

```
dramatiq core.dramatiq_service
```

3. **Trigger tasks by making a GET request**:

```
curl http://localhost:8001/test-task
```

4. **Check the logs in the Dramatiq terminal**:

```
Bar task done.
Foo task done.
```

## Optional Enhancements

### Use Automatic Task Registration

Instead of manually importing actors in `dramatiq_service.py`:

```python
from tasks.bar import * # Manual import
from tasks.foo import * # Manual import
```

You can replace it with an **automatic task registration** method, which dynamically imports all modules with tasks under the `tasks` package.

**How to Implement**:

Add the following function to `dramatiq_service.py`:

```python
import pkgutil
import importlib

def auto_register_tasks(base_package: str):
"""
Automatically imports all modules within the specified base package.

Args:
base_package (str): The root package containing task modules.
"""
for module_info in pkgutil.iter_modules([base_package.replace(".", "/")]):
importlib.import_module(f"{base_package}.{module_info.name}")
```

Replace the manual imports with a call to `auto_register_tasks`:

```python
# Manual imports
# from tasks.bar import *
# from tasks.foo import *

# Use automatic task registration
auto_register_tasks("tasks")
```

### Important Note
It is crucial to call `auto_register_tasks` in the exact same location where the manual imports were previously defined. This ensures that all tasks are registered before the Dramatiq worker starts. Failing to do this will result in tasks not being visible to the Dramatiq CLI.

## Conclusion

This setup ensures that actors from `multiple files` are visible to `Dramatiq` while maintaining scalability and organization.
Empty file.
12 changes: 12 additions & 0 deletions examples/use_actors_from_different_files/core/dramatiq_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import dramatiq
from dramatiq.brokers.rabbitmq import RabbitmqBroker
from dramatiq.middleware import AsyncIO

rabbitmq_broker = RabbitmqBroker(url="amqp://guest:guest@localhost:5672")

rabbitmq_broker.add_middleware(AsyncIO())

dramatiq.set_broker(rabbitmq_broker)

from tasks.bar import * # !!! IT'S IMPORTANT !!!
from tasks.foo import * # !!! IT'S IMPORTANT !!!
14 changes: 14 additions & 0 deletions examples/use_actors_from_different_files/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from fastapi import FastAPI

from tasks.bar.tasks import bar_task
from tasks.foo.tasks import foo_task


app = FastAPI()


@app.get("/test-task")
async def test_task():
foo_task.send()
bar_task.send()
return {"message": "Tasks sent."}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from tasks.bar.tasks import *
6 changes: 6 additions & 0 deletions examples/use_actors_from_different_files/tasks/bar/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import dramatiq


@dramatiq.actor
async def bar_task():
print("Bar task done.")
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from tasks.foo.tasks import *
6 changes: 6 additions & 0 deletions examples/use_actors_from_different_files/tasks/foo/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import dramatiq


@dramatiq.actor
async def foo_task():
print("Foo task done.")