Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transformer for request.xreadlines() #199

Merged
merged 1 commit into from
Sep 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions django_codemod/visitors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)
from .html import UnescapeEntitiesTransformer
from .http import (
HttpRequestXReadLinesTransformer,
HttpUrlQuotePlusTransformer,
HttpUrlQuoteTransformer,
HttpUrlUnQuotePlusTransformer,
Expand Down Expand Up @@ -41,6 +42,7 @@
"FloatRangeFormFieldTransformer",
"FloatRangeModelFieldTransformer",
"ForceTextTransformer",
"HttpRequestXReadLinesTransformer",
"HttpUrlQuotePlusTransformer",
"HttpUrlQuoteTransformer",
"HttpUrlUnQuotePlusTransformer",
Expand Down
37 changes: 35 additions & 2 deletions django_codemod/visitors/http.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from django_codemod.constants import DJANGO_3_0, DJANGO_4_0
from django_codemod.visitors.base import BaseFuncRenameTransformer
from libcst import BaseExpression, Call
from libcst import matchers as m

from django_codemod.constants import DJANGO_2_0, DJANGO_3_0, DJANGO_4_0
from django_codemod.visitors.base import (
BaseDjCodemodTransformer,
BaseFuncRenameTransformer,
)


class HttpUrlQuoteTransformer(BaseFuncRenameTransformer):
Expand Down Expand Up @@ -45,3 +51,30 @@ class IsSafeUrlTransformer(BaseFuncRenameTransformer):
removed_in = DJANGO_4_0
rename_from = "django.utils.http.is_safe_url"
rename_to = "django.utils.http.url_has_allowed_host_and_scheme"


class HttpRequestXReadLinesTransformer(BaseDjCodemodTransformer):
"""Replace `HttpRequest.xreadlines()` by iterating over the request."""

deprecated_in = DJANGO_2_0
removed_in = DJANGO_3_0

# This should be conservative and only apply changes to:
# - variables called `request`/`req`
# - `request`/`req` attributes (e.g `self.request`/`view.req`...)
matcher = m.Call(
func=m.Attribute(
value=m.OneOf(
m.Name(value="request"),
m.Name(value="req"),
m.Attribute(attr=m.Name(value="request")),
m.Attribute(attr=m.Name(value="req")),
),
attr=m.Name(value="xreadlines"),
)
)

def leave_Call(self, original_node: Call, updated_node: Call) -> BaseExpression:
if m.matches(updated_node, self.matcher):
return updated_node.func.value
return super().leave_Call(original_node, updated_node)
15 changes: 11 additions & 4 deletions docs/codemods.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,24 @@ Applied by passing the `--removed-in 3.0` or `--deprecated-in 2.0` option:

- Replaces `render_to_response()` by `render()` and add `request=None`
as the first argument of `render()`.
- Replaces `django.utils.lru_cache.lru_cache()` by the function it\'s
- Replaces `django.utils.lru_cache.lru_cache()` by the function it's
an alias of: `functools.lru_cache()`.
- Replaces `django.utils._os.abspathu()` by the function it\'s an
- Replaces `django.utils._os.abspathu()` by the function it's an
alias of: `os.path.abspath()`.
- Replaces `django.utils.encoding.python_2_unicode_compatible()` by
the function it\'s an alias of: `six.python_2_unicode_compatible()`.
the function it's an alias of: `six.python_2_unicode_compatible()`.
- Replaces `django.utils.decorators.ContextDecorator` by the class
from the standard library it\'s an alias to
from the standard library it's an alias to
`contextlib.ContextDecorator`.
- Replace `django.utils.decorators.available_attrs()` by its return
value `functools.WRAPPER_ASSIGNMENTS`.
- Replace `HttpRequest.xreadlines()` by iterating over the request.

This codemodder is quite conservative and will only replace function
calls on variables or attributes called `request` or `req`.

For example, it will fix `request.xreadlines()` or `self.request.xreadlines()`
but not `r.xreadlines()`.

Applied by passing the `--removed-in 3.0` or `--deprecated-in 2.1` option:

Expand Down
2 changes: 2 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ def test_deprecated_in_mapping():
"AbsPathTransformer",
"AvailableAttrsTransformer",
"ContextDecoratorTransformer",
"HttpRequestXReadLinesTransformer",
"LRUCacheTransformer",
"RenderToResponseTransformer",
"UnicodeCompatibleTransformer",
Expand Down Expand Up @@ -254,6 +255,7 @@ def test_removed_in_mapping():
"AbsPathTransformer",
"AvailableAttrsTransformer",
"ContextDecoratorTransformer",
"HttpRequestXReadLinesTransformer",
"InlineHasAddPermissionsTransformer",
"LRUCacheTransformer",
"RenderToResponseTransformer",
Expand Down
64 changes: 64 additions & 0 deletions tests/visitors/test_http.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from parameterized import parameterized

from django_codemod.visitors import (
HttpRequestXReadLinesTransformer,
HttpUrlQuotePlusTransformer,
HttpUrlQuoteTransformer,
HttpUrlUnQuotePlusTransformer,
Expand Down Expand Up @@ -96,3 +99,64 @@ def test_simple_substitution(self) -> None:
result = url_has_allowed_host_and_scheme('http://test.com/some-path', None)
"""
self.assertCodemod(before, after)


class TestHttpRequestXReadLinesTransformer(BaseVisitorTest):

transformer = HttpRequestXReadLinesTransformer

def test_noop_wrong_name(self) -> None:
"""Don't replace calls for name other than `request` or `req`."""
before = after = """
for line in r.xreadlines():
print(line)
"""
self.assertCodemod(before, after)

@parameterized.expand(["req", "request"])
def test_simple_substitution(self, variable_name) -> None:
before = f"""
for line in {variable_name}.xreadlines():
print(line)
"""
after = f"""
for line in {variable_name}:
print(line)
"""
self.assertCodemod(before, after)

@parameterized.expand(["req", "request"])
def test_substitution_class_attribute(self, attribute_name) -> None:
before = f"""
for line in self.{attribute_name}.xreadlines():
print(line)
"""
after = f"""
for line in self.{attribute_name}:
print(line)
"""
self.assertCodemod(before, after)

@parameterized.expand(["req", "request"])
def test_substitution_view_attribute(self, attribute_name) -> None:
before = f"""
for line in view.{attribute_name}.xreadlines():
print(line)
"""
after = f"""
for line in view.{attribute_name}:
print(line)
"""
self.assertCodemod(before, after)

@parameterized.expand(["req", "request"])
def test_substitution_nested_attribute(self, attribute_name) -> None:
before = f"""
for line in self.view.{attribute_name}.xreadlines():
print(line)
"""
after = f"""
for line in self.view.{attribute_name}:
print(line)
"""
self.assertCodemod(before, after)