Skip to content

Commit 6bd1550

Browse files
authored
Update task patterns (#257)
1 parent 11bebcc commit 6bd1550

File tree

5 files changed

+80
-16
lines changed

5 files changed

+80
-16
lines changed

docs/guide/cli.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ That's why taskiq can auto-discover tasks in current directory recursively.
2626
We have two options for this:
2727

2828
- `--tasks-pattern` or `-tp`.
29-
It's a name of files to import. By default is searches for all `tasks.py` files.
29+
It's a glob pattern of files to import. By default it is `**/tasks.py` which searches for all `tasks.py` files. May be specified multiple times.
3030
- `--fs-discover` or `-fsd`. This option enables search of task files in current directory recursively, using the given pattern.
3131

3232
### Acknowledgements
@@ -118,7 +118,7 @@ taskiq scheduler my_project.broker:scheduler my_project.module1 my_project.modul
118118
Path to scheduler is the only required argument.
119119

120120
- `--tasks-pattern` or `-tp`.
121-
It's a name of files to import. By default is searches for all `tasks.py` files.
121+
It's a glob pattern of files to import. By default it is `**/tasks.py` which searches for all `tasks.py` files. May be specified multiple times.
122122
- `--fs-discover` or `-fsd`. This option enables search of task files in current directory recursively, using the given pattern.
123123
- `--no-configure-logging` - use this parameter if your application configures custom logging.
124124
- `--log-level` is used to set a log level (default `INFO`).

taskiq/cli/scheduler/args.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class SchedulerArgs:
1515
log_level: str = LogLevel.INFO.name
1616
configure_logging: bool = True
1717
fs_discover: bool = False
18-
tasks_pattern: str = "tasks.py"
18+
tasks_pattern: Sequence[str] = ("**/tasks.py",)
1919
skip_first_run: bool = False
2020

2121
@classmethod
@@ -52,8 +52,9 @@ def from_cli(cls, args: Optional[Sequence[str]] = None) -> "SchedulerArgs":
5252
parser.add_argument(
5353
"--tasks-pattern",
5454
"-tp",
55-
default="tasks.py",
56-
help="Name of files in which taskiq will try to find modules.",
55+
default=["**/tasks.py"],
56+
action="append",
57+
help="Glob patterns of files in which taskiq will try to find the tasks.",
5758
)
5859
parser.add_argument(
5960
"--log-level",
@@ -76,4 +77,10 @@ def from_cli(cls, args: Optional[Sequence[str]] = None) -> "SchedulerArgs":
7677
"This option skips running tasks immediately after scheduler start."
7778
),
7879
)
79-
return cls(**parser.parse_args(args).__dict__)
80+
81+
namespace = parser.parse_args(args)
82+
# If there are any patterns specified, remove default.
83+
# This is an argparse limitation.
84+
if len(namespace.tasks_pattern) > 1:
85+
namespace.tasks_pattern.pop(0)
86+
return cls(**namespace.__dict__)

taskiq/cli/utils.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from importlib import import_module
55
from logging import getLogger
66
from pathlib import Path
7-
from typing import Any, Generator, List
7+
from typing import Any, Generator, List, Sequence, Union
88

99
from taskiq.utils import remove_suffix
1010

@@ -69,7 +69,11 @@ def import_from_modules(modules: List[str]) -> None:
6969
logger.exception(err)
7070

7171

72-
def import_tasks(modules: List[str], pattern: str, fs_discover: bool) -> None:
72+
def import_tasks(
73+
modules: List[str],
74+
pattern: Union[str, Sequence[str]],
75+
fs_discover: bool,
76+
) -> None:
7377
"""
7478
Import tasks modules.
7579
@@ -82,9 +86,14 @@ def import_tasks(modules: List[str], pattern: str, fs_discover: bool) -> None:
8286
from filesystem.
8387
"""
8488
if fs_discover:
85-
for path in Path().rglob(pattern):
86-
modules.append(
87-
remove_suffix(str(path), ".py").replace(os.path.sep, "."),
88-
)
89-
89+
if isinstance(pattern, str):
90+
pattern = (pattern,)
91+
discovered_modules = set()
92+
for glob_pattern in pattern:
93+
for path in Path().glob(glob_pattern):
94+
discovered_modules.add(
95+
remove_suffix(str(path), ".py").replace(os.path.sep, "."),
96+
)
97+
98+
modules.extend(list(discovered_modules))
9099
import_from_modules(modules)

taskiq/cli/worker/args.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class WorkerArgs:
2626

2727
broker: str
2828
modules: List[str]
29-
tasks_pattern: str = "tasks.py"
29+
tasks_pattern: Sequence[str] = ("**/tasks.py",)
3030
fs_discover: bool = False
3131
configure_logging: bool = True
3232
log_level: LogLevel = LogLevel.INFO
@@ -87,8 +87,9 @@ def from_cli(
8787
parser.add_argument(
8888
"--tasks-pattern",
8989
"-tp",
90-
default="tasks.py",
91-
help="Name of files in which taskiq will try to find modules.",
90+
default=["**/tasks.py"],
91+
action="append",
92+
help="Glob patterns of files in which taskiq will try to find the tasks.",
9293
)
9394
parser.add_argument(
9495
"modules",
@@ -198,4 +199,8 @@ def from_cli(
198199
)
199200

200201
namespace = parser.parse_args(args)
202+
# If there are any patterns specified, remove default.
203+
# This is an argparse limitation.
204+
if len(namespace.tasks_pattern) > 1:
205+
namespace.tasks_pattern.pop(0)
201206
return WorkerArgs(**namespace.__dict__)

tests/cli/test_utils.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from unittest.mock import patch
2+
3+
from taskiq.cli.utils import import_tasks
4+
5+
6+
def test_import_tasks_list_pattern() -> None:
7+
modules = ["taskiq.tasks"]
8+
with patch("taskiq.cli.utils.import_from_modules", autospec=True) as mock:
9+
import_tasks(modules, ["tests/**/test_utils.py"], True)
10+
assert set(modules) == {
11+
"taskiq.tasks",
12+
"tests.test_utils",
13+
"tests.cli.test_utils",
14+
}
15+
mock.assert_called_with(modules)
16+
17+
18+
def test_import_tasks_str_pattern() -> None:
19+
modules = ["taskiq.tasks"]
20+
with patch("taskiq.cli.utils.import_from_modules", autospec=True) as mock:
21+
import_tasks(modules, "tests/**/test_utils.py", True)
22+
assert set(modules) == {
23+
"taskiq.tasks",
24+
"tests.test_utils",
25+
"tests.cli.test_utils",
26+
}
27+
mock.assert_called_with(modules)
28+
29+
30+
def test_import_tasks_empty_pattern() -> None:
31+
modules = ["taskiq.tasks"]
32+
with patch("taskiq.cli.utils.import_from_modules", autospec=True) as mock:
33+
import_tasks(modules, [], True)
34+
assert modules == ["taskiq.tasks"]
35+
mock.assert_called_with(modules)
36+
37+
38+
def test_import_tasks_no_discover() -> None:
39+
modules = ["taskiq.tasks"]
40+
with patch("taskiq.cli.utils.import_from_modules", autospec=True) as mock:
41+
import_tasks(modules, "tests/**/test_utils.py", False)
42+
assert modules == ["taskiq.tasks"]
43+
mock.assert_called_with(modules)

0 commit comments

Comments
 (0)