Skip to content

Commit

Permalink
codemod to deprecate logging.warn
Browse files Browse the repository at this point in the history
  • Loading branch information
clavedeluna committed Jan 9, 2024
1 parent 269469d commit 4bccd5d
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 0 deletions.
16 changes: 16 additions & 0 deletions integration_tests/test_fix_deprecated_logging_warn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from core_codemods.fix_deprecated_logging_warn import FixDeprecatedLoggingWarn
from integration_tests.base_test import (
BaseIntegrationTest,
original_and_expected_from_code_path,
)


class TestFixDeprecatedLoggingWarn(BaseIntegrationTest):
codemod = FixDeprecatedLoggingWarn
code_path = "tests/samples/fix_deprecated_logging_warn.py"
original_code, expected_new_code = original_and_expected_from_code_path(
code_path, [(3, 'log.warning("hello")\n')]
)
expected_diff = '--- \n+++ \n@@ -1,4 +1,4 @@\n import logging\n \n log = logging.getLogger("my logger")\n-log.warn("hello")\n+log.warning("hello")\n'
expected_line_change = "4"
change_description = FixDeprecatedLoggingWarn.CHANGE_DESCRIPTION
4 changes: 4 additions & 0 deletions src/codemodder/scripts/generate_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ class DocMetadata:
importance="Low",
guidance_explained="Simplifying expressions involving `startswith` or `endswith` calls is safe.",
),
"fix-deprecated-logging-warn": DocMetadata(
importance="Low",
guidance_explained="This change fixes deprecated uses and is safe.",
),
}


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 @@ -43,6 +43,7 @@
from .remove_module_global import RemoveModuleGlobal
from .remove_debug_breakpoint import RemoveDebugBreakpoint
from .combine_startswith_endswith import CombineStartswithEndswith
from .fix_deprecated_logging_warn import FixDeprecatedLoggingWarn

registry = CodemodCollection(
origin="pixee",
Expand Down Expand Up @@ -92,5 +93,6 @@
RemoveModuleGlobal,
RemoveDebugBreakpoint,
CombineStartswithEndswith,
FixDeprecatedLoggingWarn,
],
)
13 changes: 13 additions & 0 deletions src/core_codemods/docs/pixee_python_fix-deprecated-logging-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
The `warn` method from `logging` has been [deprecated](https://docs.python.org/3/library/logging.html#logging.Logger.warning) since Python 3.3.

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

- logging.warn("hello")
+ logging.warning("hello")
...
log = logging.getLogger("my logger")
- log.warn("hello")
+ log.warning("hello")
```
55 changes: 55 additions & 0 deletions src/core_codemods/fix_deprecated_logging_warn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import libcst as cst
from codemodder.codemods.api import SemgrepCodemod, ReviewGuidance
from codemodder.codemods.utils_mixin import NameResolutionMixin


class FixDeprecatedLoggingWarn(SemgrepCodemod, NameResolutionMixin):
NAME = "fix-deprecated-logging-warn"
SUMMARY = "Replace Deprecated `logging.warn`"
REVIEW_GUIDANCE = ReviewGuidance.MERGE_WITHOUT_REVIEW
DESCRIPTION = "Replace deprecated `logging.warn` with `logging.warning`"
REFERENCES = [
{
"url": "https://docs.python.org/3/library/logging.html#logging.Logger.warning",
"description": "",
},
]
_module_name = "logging"

@classmethod
def rule(cls):
return """
rules:
- pattern-either:
- patterns:
- pattern: logging.warn(...)
- pattern-inside: |
import logging
...
- patterns:
- pattern: logging.getLogger(...).warn(...)
- pattern-inside: |
import logging
...
- patterns:
- pattern: $VAR.warn(...)
- pattern-inside: |
import logging
...
$VAR = logging.getLogger(...)
...
"""

def on_result_found(self, original_node, updated_node):
warning = cst.Name(value="warning")
match original_node.func:
case cst.Name():
self.add_needed_import(self._module_name, "warning")
self.remove_unused_import(original_node.func)
return updated_node.with_changes(func=warning)
case cst.Attribute():
return updated_node.with_changes(
func=updated_node.func.with_changes(attr=warning)
)
return original_node
87 changes: 87 additions & 0 deletions tests/codemods/test_fix_deprecated_logging_warn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import pytest
from core_codemods.fix_deprecated_logging_warn import FixDeprecatedLoggingWarn
from tests.codemods.base_codemod_test import BaseSemgrepCodemodTest


class TestFixDeprecatedLoggingWarn(BaseSemgrepCodemodTest):
codemod = FixDeprecatedLoggingWarn

@pytest.mark.parametrize(
"code",
[
"""
import logging
logging.{}('something')
""",
"""
import logging
log = logging.getLogger('anything')
log.{}('something')
""",
],
)
def test_import(self, tmpdir, code):
original_code = code.format("warn")
new_code = code.format("warning")
self.run_and_assert(tmpdir, original_code, new_code)
assert len(self.file_context.codemod_changes) == 1

@pytest.mark.parametrize(
"code",
[
"""
from logging import {0}
{0}('something')
""",
"""
from logging import getLogger
getLogger('anything').{0}('something')
""",
],
)
def test_from_import(self, tmpdir, code):
original_code = code.format("warn")
new_code = code.format("warning")
self.run_and_assert(tmpdir, original_code, new_code)
assert len(self.file_context.codemod_changes) == 1

@pytest.mark.parametrize(
"input_code,expected_output",
[
(
"""from logging import warn as warn_func
warn_func('something')""",
"""from logging import warning
warning('something')""",
),
(
"""from logging import getLogger as make_logger
logger = make_logger('anything')
logger.warn('something')""",
"""from logging import getLogger as make_logger
logger = make_logger('anything')
logger.warning('something')""",
),
],
)
def test_import_alias(self, tmpdir, input_code, expected_output):
self.run_and_assert(tmpdir, input_code, expected_output)
assert len(self.file_context.codemod_changes) == 1

@pytest.mark.parametrize(
"code",
[
"""
import xyz
xyz.warn('something')
""",
"""
import my_logging
log = my_logging.getLogger('anything')
log.warn('something')
""",
],
)
def test_different_warn(self, tmpdir, code):
self.run_and_assert(tmpdir, code, code)
assert len(self.file_context.codemod_changes) == 0
4 changes: 4 additions & 0 deletions tests/samples/fix_deprecated_logging_warn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import logging

log = logging.getLogger("my logger")
log.warn("hello")

0 comments on commit 4bccd5d

Please sign in to comment.