diff --git a/django/tasks/backends/base.py b/django/tasks/backends/base.py index d3bf527bc6..e31c9b954e 100644 --- a/django/tasks/backends/base.py +++ b/django/tasks/backends/base.py @@ -8,7 +8,7 @@ from django.db import connections from django.tasks import DEFAULT_QUEUE_NAME from django.tasks.exceptions import InvalidTaskError from django.tasks.task import MAX_PRIORITY, MIN_PRIORITY, Task -from django.tasks.utils import is_global_function +from django.tasks.utils import is_module_level_function from django.utils import timezone @@ -48,10 +48,8 @@ class BaseTaskBackend(metaclass=ABCMeta): """ Determine whether the provided task is one which can be executed by the backend. """ - if not is_global_function(task.func): - raise InvalidTaskError( - "Task function must be a globally importable function" - ) + if not is_module_level_function(task.func): + raise InvalidTaskError("Task function must be defined at a module level") if not self.supports_async_task and iscoroutinefunction(task.func): raise InvalidTaskError("Backend does not support async tasks") diff --git a/django/tasks/utils.py b/django/tasks/utils.py index 62bf928127..a0cfa9c343 100644 --- a/django/tasks/utils.py +++ b/django/tasks/utils.py @@ -7,7 +7,7 @@ from traceback import format_exception from django.utils.module_loading import import_string -def is_global_function(func): +def is_module_level_function(func): if not inspect.isfunction(func) or inspect.isbuiltin(func): return False diff --git a/tests/tasks/is_global_function_fixture.py b/tests/tasks/is_global_function_fixture.py deleted file mode 100644 index 44bd423bee..0000000000 --- a/tests/tasks/is_global_function_fixture.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -This file is used to test function is considered global even if it's not defined yet -because it's covered by a decorator. -""" - -from django.tasks.utils import is_global_function - - -@is_global_function -def really_global_function() -> None: - pass - - -inner_func_is_global_function = None - - -def main() -> None: - global inner_func_is_global_function - - @is_global_function - def inner_func() -> None: - pass - - inner_func_is_global_function = inner_func - - -main() diff --git a/tests/tasks/is_module_level_function_fixture.py b/tests/tasks/is_module_level_function_fixture.py new file mode 100644 index 0000000000..66d3a63211 --- /dev/null +++ b/tests/tasks/is_module_level_function_fixture.py @@ -0,0 +1,27 @@ +""" +This file is used to test function is considered module level even if it's not defined +yet because it's covered by a decorator. +""" + +from django.tasks.utils import is_module_level_function + + +@is_module_level_function +def really_module_level_function() -> None: + pass + + +inner_func_is_module_level_function = None + + +def main() -> None: + global inner_func_is_module_level_function + + @is_module_level_function + def inner_func() -> None: + pass + + inner_func_is_module_level_function = inner_func + + +main() diff --git a/tests/tasks/test_tasks.py b/tests/tasks/test_tasks.py index 4b9e00ff17..894225c739 100644 --- a/tests/tasks/test_tasks.py +++ b/tests/tasks/test_tasks.py @@ -219,7 +219,7 @@ class TaskTestCase(SimpleTestCase): with self.subTest(invalid_function): with self.assertRaisesMessage( InvalidTaskError, - "Task function must be a globally importable function", + "Task function must be defined at a module level", ): task()(invalid_function) diff --git a/tests/tasks/test_utils.py b/tests/tasks/test_utils.py index 3c66e7d956..9f6fc263ec 100644 --- a/tests/tasks/test_utils.py +++ b/tests/tasks/test_utils.py @@ -13,39 +13,43 @@ from django.test import SimpleTestCase from . import tasks as test_tasks -class IsGlobalFunctionTestCase(SimpleTestCase): +class IsModuleLevelFunctionTestCase(SimpleTestCase): def test_builtin(self): - self.assertFalse(utils.is_global_function(any)) - self.assertFalse(utils.is_global_function(isinstance)) + self.assertFalse(utils.is_module_level_function(any)) + self.assertFalse(utils.is_module_level_function(isinstance)) def test_from_module(self): - self.assertTrue(utils.is_global_function(subprocess.run)) - self.assertTrue(utils.is_global_function(subprocess.check_output)) - self.assertTrue(utils.is_global_function(test_tasks.noop_task.func)) + self.assertTrue(utils.is_module_level_function(subprocess.run)) + self.assertTrue(utils.is_module_level_function(subprocess.check_output)) + self.assertTrue(utils.is_module_level_function(test_tasks.noop_task.func)) def test_private_function(self): def private_function(): pass - self.assertFalse(utils.is_global_function(private_function)) + self.assertFalse(utils.is_module_level_function(private_function)) def test_coroutine(self): - self.assertTrue(utils.is_global_function(test_tasks.noop_task_async.func)) + self.assertTrue(utils.is_module_level_function(test_tasks.noop_task_async.func)) def test_method(self): - self.assertFalse(utils.is_global_function(self.test_method)) - self.assertFalse(utils.is_global_function(self.setUp)) + self.assertFalse(utils.is_module_level_function(self.test_method)) + self.assertFalse(utils.is_module_level_function(self.setUp)) def test_lambda(self): - self.assertFalse(utils.is_global_function(lambda: True)) + self.assertFalse(utils.is_module_level_function(lambda: True)) def test_uninitialised_method(self): # This import has to be here, so the module is loaded during the test - from . import is_global_function_fixture + from . import is_module_level_function_fixture - self.assertTrue(is_global_function_fixture.really_global_function) - self.assertIsNotNone(is_global_function_fixture.inner_func_is_global_function) - self.assertFalse(is_global_function_fixture.inner_func_is_global_function) + self.assertTrue(is_module_level_function_fixture.really_module_level_function) + self.assertIsNotNone( + is_module_level_function_fixture.inner_func_is_module_level_function + ) + self.assertFalse( + is_module_level_function_fixture.inner_func_is_module_level_function + ) class JSONNormalizeTestCase(SimpleTestCase):