Skip to content

Commit

Permalink
document fix task init codemod
Browse files Browse the repository at this point in the history
  • Loading branch information
clavedeluna committed Feb 9, 2024
1 parent 97c5e00 commit 5bdc3e8
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 10 deletions.
39 changes: 39 additions & 0 deletions integration_tests/test_fix_task_instantiation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from core_codemods.fix_task_instantiation import FixTaskInstantiation
from integration_tests.base_test import (
BaseIntegrationTest,
original_and_expected_from_code_path,
)


class TestFixTaskInstantiation(BaseIntegrationTest):
codemod = FixTaskInstantiation
code_path = "tests/samples/fix_task_instantiation.py"
original_code, expected_new_code = original_and_expected_from_code_path(
code_path,
[
(
7,
""" task = asyncio.create_task(my_coroutine(), name="my task")\n""",
),
],
)

# fmt: off
expected_diff =(
"""--- \n"""
"""+++ \n"""
"""@@ -5,7 +5,7 @@\n"""
""" print("Task completed")\n"""
""" \n"""
""" async def main():\n"""
"""- task = asyncio.Task(my_coroutine(), name="my task")\n"""
"""+ task = asyncio.create_task(my_coroutine(), name="my task")\n"""
""" await task\n"""
""" \n"""
""" asyncio.run(main())\n"""
)
# fmt: on

expected_line_change = "8"
change_description = FixTaskInstantiation.change_description
num_changed_files = 1
4 changes: 4 additions & 0 deletions src/codemodder/scripts/generate_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ class DocMetadata:
importance="Medium",
guidance_explained="We believe this change is safe and will not cause any issues.",
),
"fix-task-instantiation": DocMetadata(
importance="Low",
guidance_explained="Manual instantiation of `asyncio.Task` is discouraged. We believe this change is safe and will not cause any issues.",
),
}

METADATA = CORE_METADATA | {
Expand Down
2 changes: 2 additions & 0 deletions src/core_codemods/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
from .sonar.sonar_flask_json_response_type import SonarFlaskJsonResponseType
from .sonar.sonar_django_json_response_type import SonarDjangoJsonResponseType
from .lazy_logging import LazyLogging
from .fix_task_instantiation import FixTaskInstantiation

registry = CodemodCollection(
origin="pixee",
Expand Down Expand Up @@ -116,6 +117,7 @@
RemoveAssertionInPytestRaises,
FixAssertTuple,
LazyLogging,
FixTaskInstantiation,
],
)

Expand Down
9 changes: 9 additions & 0 deletions src/core_codemods/docs/pixee_python_fix-task-instantiation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The `asyncio` [documentation](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task) explicitly discourages manual instantiation of a `Task` instance and instead recommends calling `create_task`.

Our changes look like the following:
```diff
import asyncio

- task = asyncio.Task(my_coroutine(), name="my task")
+ task = asyncio.create_task(my_coroutine(), name="my task")
```
21 changes: 11 additions & 10 deletions src/core_codemods/fix_task_instantiation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@
class FixTaskInstantiation(SimpleCodemod, NameAndAncestorResolutionMixin):
metadata = Metadata(
name="fix-task-instantiation",
summary="TODOReplace Comparisons to Empty Sequence with Implicit Boolean Comparison",
review_guidance=ReviewGuidance.MERGE_AFTER_REVIEW,
summary="Use high-level `asyncio.create_task` API",
review_guidance=ReviewGuidance.MERGE_WITHOUT_REVIEW,
references=[
Reference(
url="todo: https://docs.python.org/3/library/stdtypes.html#truth-value-testing"
url="https://docs.python.org/3/library/asyncio-task.html#asyncio.Task"
),
],
)
change_description = (
"TODO: Replace comparisons to empty sequence with implicit boolean comparison."
)
change_description = "Replace instantiation of `asyncio.Task` with `create_task`"
_module_name = "asyncio"

def leave_Call(self, original_node: cst.Call, updated_node: cst.Call) -> cst.Call:
Expand All @@ -35,14 +33,14 @@ def leave_Call(self, original_node: cst.Call, updated_node: cst.Call) -> cst.Cal
)
if loop_type == BaseType.NONE:
return self.node_create_task(original_node, updated_node)
elif loop_type in (
if loop_type in (
BaseType.NUMBER,
BaseType.LIST,
BaseType.STRING,
BaseType.BYTES,
BaseType.BOOL,
):
# User incorrectly assigned loop to something that is not a loop.
# incorrectly assigned loop kwarg to something that is not a loop.
# We won't do anything.
return updated_node

Expand All @@ -56,6 +54,7 @@ def leave_Call(self, original_node: cst.Call, updated_node: cst.Call) -> cst.Cal
def node_create_task(
self, original_node: cst.Call, updated_node: cst.Call
) -> cst.Call:
"""Convert `asyncio.Task(...)` to `asyncio.create_task(...)`"""
self.report_change(original_node)
maybe_name = self.get_aliased_prefix_name(original_node, self._module_name)
if (maybe_name := maybe_name or self._module_name) == self._module_name:
Expand All @@ -70,7 +69,7 @@ def node_loop_create_task(
loop_arg: cst.Arg,
other_args: list[cst.Arg],
) -> cst.Call:
"""todo: document"""
"""Convert `asyncio.Task(..., loop=loop,...)` to `loop.create_task(...)`"""
self.report_change(original_node)
coroutine_arg = coroutine_arg.with_changes(comma=cst.MaybeSentinel.DEFAULT)
loop_attr = loop_arg.value
Expand All @@ -82,7 +81,9 @@ def node_loop_create_task(
return new_call

def _find_loop_arg(self, node: cst.Call) -> tuple[Optional[cst.Arg], list[cst.Arg]]:
"""dcoment args[:1: bc first arg is coroutine"""
"""Find the loop kwarg from a call to `asyncio.Task(...)`
First arg is always the coroutine so we ignore it.
"""
loop_arg = None
other_args = []
for arg in node.args[1:]:
Expand Down
11 changes: 11 additions & 0 deletions tests/samples/fix_task_instantiation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import asyncio

async def my_coroutine():
await asyncio.sleep(1)
print("Task completed")

async def main():
task = asyncio.Task(my_coroutine(), name="my task")
await task

asyncio.run(main())

0 comments on commit 5bdc3e8

Please sign in to comment.