Skip to content

Commit

Permalink
New Sonar codemod for django model without dunder str (#498)
Browse files Browse the repository at this point in the history
new sonar django dunder codemod
  • Loading branch information
clavedeluna authored Apr 25, 2024
1 parent fcc4270 commit 6dc5009
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from codemodder.codemods.test import SonarIntegrationTest
from core_codemods.django_model_without_dunder_str import (
DjangoModelWithoutDunderStrTransformer,
)
from core_codemods.sonar.sonar_django_model_without_dunder_str import (
SonarDjangoModelWithoutDunderStr,
)


class TestSonarDjangoModelWithoutDunderStr(SonarIntegrationTest):
codemod = SonarDjangoModelWithoutDunderStr
code_path = "tests/samples/django_model.py"
replacement_lines = [
(15, """\n"""),
(16, """ def __str__(self):\n"""),
(17, """ model_name = self.__class__.__name__\n"""),
(
18,
""" fields_str = ", ".join((f"{field.name}={getattr(self, field.name)}" for field in self._meta.fields))\n""",
),
(19, """ return f"{model_name}({fields_str})"\n"""),
]

# fmt: off
expected_diff = (
"""--- \n"""
"""+++ \n"""
"""@@ -12,3 +12,8 @@\n"""
""" phone = models.IntegerField(blank=True)\n"""
""" class Meta:\n"""
""" app_label = 'myapp'\n"""
"""+\n"""
"""+ def __str__(self):\n"""
"""+ model_name = self.__class__.__name__\n"""
"""+ fields_str = ", ".join((f"{field.name}={getattr(self, field.name)}" for field in self._meta.fields))\n"""
"""+ return f"{model_name}({fields_str})"\n"""
)
# fmt: on

expected_line_change = "10"
change_description = DjangoModelWithoutDunderStrTransformer.change_description
num_changed_files = 1
1 change: 1 addition & 0 deletions src/codemodder/scripts/generate_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ class DocMetadata:
"fix-float-equality-S1244",
"fix-math-isclose-S6727",
"sql-parameterization-S3649",
"django-model-without-dunder-str-S6554",
]
SONAR_CODEMODS = {
name: DocMetadata(
Expand Down
4 changes: 4 additions & 0 deletions src/core_codemods/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
from .secure_flask_session_config import SecureFlaskSessionConfig
from .secure_random import SecureRandom
from .sonar.sonar_django_json_response_type import SonarDjangoJsonResponseType
from .sonar.sonar_django_model_without_dunder_str import (
SonarDjangoModelWithoutDunderStr,
)
from .sonar.sonar_django_receiver_on_top import SonarDjangoReceiverOnTop
from .sonar.sonar_enable_jinja2_autoescape import SonarEnableJinja2Autoescape
from .sonar.sonar_exception_without_raise import SonarExceptionWithoutRaise
Expand Down Expand Up @@ -168,6 +171,7 @@
SonarFixFloatEquality,
SonarFixMathIsClose,
SonarSQLParameterization,
SonarDjangoModelWithoutDunderStr,
],
)

Expand Down
10 changes: 10 additions & 0 deletions src/core_codemods/sonar/sonar_django_model_without_dunder_str.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from core_codemods.django_model_without_dunder_str import DjangoModelWithoutDunderStr
from core_codemods.sonar.api import SonarCodemod

SonarDjangoModelWithoutDunderStr = SonarCodemod.from_core_codemod(
name="django-model-without-dunder-str-S6554",
other=DjangoModelWithoutDunderStr,
rule_id="python:S6554",
rule_name='Django models should define a "__str__" method',
rule_url="https://rules.sonarsource.com/python/RSPEC-6554/",
)
52 changes: 52 additions & 0 deletions tests/codemods/sonar/test_sonar_django_model_without_dunder_str.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import json

from codemodder.codemods.test import BaseSASTCodemodTest
from core_codemods.sonar.sonar_django_model_without_dunder_str import (
SonarDjangoModelWithoutDunderStr,
)


class TestSonarDjangoModelWithoutDunderStr(BaseSASTCodemodTest):
codemod = SonarDjangoModelWithoutDunderStr
tool = "sonar"

def test_name(self):
assert self.codemod.name == "django-model-without-dunder-str-S6554"
assert self.codemod.id == "sonar:python/django-model-without-dunder-str-S6554"

def test_simple(self, tmpdir):
input_code = """
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
phone = models.IntegerField(blank=True)
"""
expected = """
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
phone = models.IntegerField(blank=True)
def __str__(self):
model_name = self.__class__.__name__
fields_str = ", ".join((f"{field.name}={getattr(self, field.name)}" for field in self._meta.fields))
return f"{model_name}({fields_str})"
"""
issues = {
"issues": [
{
"rule": "python:S6554",
"status": "OPEN",
"component": "code.py",
"textRange": {
"startLine": 3,
"endLine": 3,
"startOffset": 6,
"endOffset": 10,
},
}
]
}
self.run_and_assert(tmpdir, input_code, expected, results=json.dumps(issues))
14 changes: 14 additions & 0 deletions tests/samples/django_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import django
from django.conf import settings
from django.db import models

# required to run this module standalone for testing
settings.configure()
django.setup()


class User(models.Model):
name = models.CharField(max_length=100)
phone = models.IntegerField(blank=True)
class Meta:
app_label = 'myapp'
37 changes: 36 additions & 1 deletion tests/samples/sonar_issues.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"total": 41,
"total": 42,
"p": 1,
"ps": 500,
"paging": {
Expand Down Expand Up @@ -1862,6 +1862,41 @@
"severity": "HIGH"
}
]
},
{
"key": "AY8LdruyywzNF5GhDiHJ",
"rule": "python:S6554",
"severity": "MAJOR",
"component": "pixee_codemodder-python:django_model.py",
"project": "pixee_codemodder-python",
"line": 4,
"hash": "8eca309fd79127651d55034d0f9981ea",
"textRange": {
"startLine": 10,
"endLine": 10,
"startOffset": 6,
"endOffset": 10
},
"flows": [],
"status": "OPEN",
"message": "Define a \"__str__\" method for this Django model.",
"effort": "10min",
"debt": "10min",
"assignee": "clavedeluna@github",
"author": "[email protected]",
"tags": [],
"creationDate": "2024-04-23T16:56:52+0200",
"updateDate": "2024-04-23T16:57:31+0200",
"type": "CODE_SMELL",
"organization": "pixee",
"cleanCodeAttribute": "COMPLETE",
"cleanCodeAttributeCategory": "INTENTIONAL",
"impacts": [
{
"softwareQuality": "MAINTAINABILITY",
"severity": "MEDIUM"
}
]
}
],
"components": [
Expand Down

0 comments on commit 6dc5009

Please sign in to comment.