Skip to content

Commit

Permalink
document dataclass codemod
Browse files Browse the repository at this point in the history
  • Loading branch information
clavedeluna committed Mar 10, 2024
1 parent 32f0a32 commit d61ca4b
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 2 deletions.
43 changes: 43 additions & 0 deletions integration_tests/test_fix_dataclass_defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from codemodder.codemods.test import (
BaseIntegrationTest,
original_and_expected_from_code_path,
)
from core_codemods.fix_dataclass_defaults import FixDataclassDefaults


class TestFixDataclassDefaults(BaseIntegrationTest):
codemod = FixDataclassDefaults
code_path = "tests/samples/fix_dataclass_defaults.py"
original_code, expected_new_code = original_and_expected_from_code_path(
code_path,
[
(0, """from dataclasses import field, dataclass\n"""),
(5, """ phones: list = field(default_factory=list)\n"""),
(6, """ friends: dict = field(default_factory=dict)\n"""),
(7, """ family: set = field(default_factory=set)\n"""),
],
)

# fmt: off
expected_diff =(
"""--- \n"""
"""+++ \n"""
"""@@ -1,8 +1,8 @@\n"""
"""-from dataclasses import dataclass\n"""
"""+from dataclasses import field, dataclass\n"""
""" \n"""
""" @dataclass\n"""
""" class Test:\n"""
""" name: str = ""\n"""
"""- phones: list = []\n"""
"""- friends: dict = {}\n"""
"""- family: set = set()\n"""
"""+ phones: list = field(default_factory=list)\n"""
"""+ friends: dict = field(default_factory=dict)\n"""
"""+ family: set = field(default_factory=set)\n"""
)
# fmt: on

expected_line_change = "6"
change_description = FixDataclassDefaults.change_description
num_changes = 3
4 changes: 4 additions & 0 deletions src/codemodder/scripts/generate_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ class DocMetadata:
importance="High",
guidance_explained="This change may impact performance in some cases, but it is recommended when handling untrusted data.",
),
"fix-dataclass-defaults": DocMetadata(
importance="Medium",
guidance_explained="This change is safe and will prevent runtime `ValueError`.",
),
}

METADATA = CORE_METADATA | {
Expand Down
18 changes: 18 additions & 0 deletions src/core_codemods/docs/pixee_python_fix-dataclass-defaults.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
This codemod will fix instances of `dataclasses.dataclass` that define default lists, sets, or dicts which raise a runtime `ValueError`. The [dataclass documentation](https://docs.python.org/3/library/dataclasses.html#mutable-default-values) provides a clear explanation of why this code is disallowed and explains how to use `field(default_factory=...)` instead.

Our changes look something like this:

```diff
-from dataclasses import dataclass
+from dataclasses import field, dataclass

@dataclass
class Person:
name: str = ""
- phones: list = []
- friends: dict = {}
- family: set = set()
+ phones: list = field(default_factory=list)
+ friends: dict = field(default_factory=dict)
+ family: set = field(default_factory=set)
```
6 changes: 4 additions & 2 deletions src/core_codemods/fix_dataclass_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@
class FixDataclassDefaults(SimpleCodemod, NameAndAncestorResolutionMixin, UtilsMixin):
metadata = Metadata(
name="fix-dataclass-defaults",
summary="todo",
summary="Replace `dataclass` Mutable Default Values with Call to `field`",
review_guidance=ReviewGuidance.MERGE_WITHOUT_REVIEW,
references=[
Reference(
url="https://docs.python.org/3/library/dataclasses.html#mutable-default-values"
)
],
)
change_description = "todo"
change_description = (
"Replace `dataclass` mutable default values with call to `field`"
)

def leave_AnnAssign(
self, original_node: cst.Assign, updated_node: cst.Assign
Expand Down
8 changes: 8 additions & 0 deletions tests/samples/fix_dataclass_defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from dataclasses import dataclass

@dataclass
class Test:
name: str = ""
phones: list = []
friends: dict = {}
family: set = set()

0 comments on commit d61ca4b

Please sign in to comment.