Skip to content

Commit 2ed75d4

Browse files
PCManticorebrycepg
authored andcommitted
Separate string and bytes classes patching (pylint-dev#807)
Fixes pylint-dev/pylint#3599
1 parent 184b591 commit 2ed75d4

File tree

4 files changed

+108
-55
lines changed

4 files changed

+108
-55
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Release Date: TBA
99

1010
* Added a brain for ``sqlalchemy.orm.session``
1111

12+
* Separate string and bytes classes patching
13+
14+
Fixes PyCQA/pylint#3599
15+
1216
* Added missing methods to the brain for ``mechanize``, to fix pylint false positives
1317

1418
Close #793

astroid/brain/brain_builtin_inference.py

Lines changed: 85 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -40,52 +40,90 @@
4040

4141
OBJECT_DUNDER_NEW = "object.__new__"
4242

43-
44-
def _extend_str(class_node, rvalue):
43+
STR_CLASS = """
44+
class whatever(object):
45+
def join(self, iterable):
46+
return {rvalue}
47+
def replace(self, old, new, count=None):
48+
return {rvalue}
49+
def format(self, *args, **kwargs):
50+
return {rvalue}
51+
def encode(self, encoding='ascii', errors=None):
52+
return b''
53+
def decode(self, encoding='ascii', errors=None):
54+
return u''
55+
def capitalize(self):
56+
return {rvalue}
57+
def title(self):
58+
return {rvalue}
59+
def lower(self):
60+
return {rvalue}
61+
def upper(self):
62+
return {rvalue}
63+
def swapcase(self):
64+
return {rvalue}
65+
def index(self, sub, start=None, end=None):
66+
return 0
67+
def find(self, sub, start=None, end=None):
68+
return 0
69+
def count(self, sub, start=None, end=None):
70+
return 0
71+
def strip(self, chars=None):
72+
return {rvalue}
73+
def lstrip(self, chars=None):
74+
return {rvalue}
75+
def rstrip(self, chars=None):
76+
return {rvalue}
77+
def rjust(self, width, fillchar=None):
78+
return {rvalue}
79+
def center(self, width, fillchar=None):
80+
return {rvalue}
81+
def ljust(self, width, fillchar=None):
82+
return {rvalue}
83+
"""
84+
85+
86+
BYTES_CLASS = """
87+
class whatever(object):
88+
def join(self, iterable):
89+
return {rvalue}
90+
def replace(self, old, new, count=None):
91+
return {rvalue}
92+
def decode(self, encoding='ascii', errors=None):
93+
return u''
94+
def capitalize(self):
95+
return {rvalue}
96+
def title(self):
97+
return {rvalue}
98+
def lower(self):
99+
return {rvalue}
100+
def upper(self):
101+
return {rvalue}
102+
def swapcase(self):
103+
return {rvalue}
104+
def index(self, sub, start=None, end=None):
105+
return 0
106+
def find(self, sub, start=None, end=None):
107+
return 0
108+
def count(self, sub, start=None, end=None):
109+
return 0
110+
def strip(self, chars=None):
111+
return {rvalue}
112+
def lstrip(self, chars=None):
113+
return {rvalue}
114+
def rstrip(self, chars=None):
115+
return {rvalue}
116+
def rjust(self, width, fillchar=None):
117+
return {rvalue}
118+
def center(self, width, fillchar=None):
119+
return {rvalue}
120+
def ljust(self, width, fillchar=None):
121+
return {rvalue}
122+
"""
123+
124+
125+
def _extend_string_class(class_node, code, rvalue):
45126
"""function to extend builtin str/unicode class"""
46-
code = dedent(
47-
"""
48-
class whatever(object):
49-
def join(self, iterable):
50-
return {rvalue}
51-
def replace(self, old, new, count=None):
52-
return {rvalue}
53-
def format(self, *args, **kwargs):
54-
return {rvalue}
55-
def encode(self, encoding='ascii', errors=None):
56-
return ''
57-
def decode(self, encoding='ascii', errors=None):
58-
return u''
59-
def capitalize(self):
60-
return {rvalue}
61-
def title(self):
62-
return {rvalue}
63-
def lower(self):
64-
return {rvalue}
65-
def upper(self):
66-
return {rvalue}
67-
def swapcase(self):
68-
return {rvalue}
69-
def index(self, sub, start=None, end=None):
70-
return 0
71-
def find(self, sub, start=None, end=None):
72-
return 0
73-
def count(self, sub, start=None, end=None):
74-
return 0
75-
def strip(self, chars=None):
76-
return {rvalue}
77-
def lstrip(self, chars=None):
78-
return {rvalue}
79-
def rstrip(self, chars=None):
80-
return {rvalue}
81-
def rjust(self, width, fillchar=None):
82-
return {rvalue}
83-
def center(self, width, fillchar=None):
84-
return {rvalue}
85-
def ljust(self, width, fillchar=None):
86-
return {rvalue}
87-
"""
88-
)
89127
code = code.format(rvalue=rvalue)
90128
fake = AstroidBuilder(MANAGER).string_build(code)["whatever"]
91129
for method in fake.mymethods():
@@ -106,8 +144,8 @@ def _extend_builtins(class_transforms):
106144

107145
_extend_builtins(
108146
{
109-
"bytes": partial(_extend_str, rvalue="b''"),
110-
"str": partial(_extend_str, rvalue="''"),
147+
"bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),
148+
"str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"),
111149
}
112150
)
113151

tests/unittest_brain.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,5 +2020,20 @@ class Other:
20202020
assert isinstance(name[0], astroid.Unknown)
20212021

20222022

2023+
@pytest.mark.parametrize(
2024+
"code,expected_class,expected_value",
2025+
[
2026+
("'hey'.encode()", astroid.Const, b""),
2027+
("b'hey'.decode()", astroid.Const, ""),
2028+
("'hey'.encode().decode()", astroid.Const, ""),
2029+
],
2030+
)
2031+
def test_str_and_bytes(code, expected_class, expected_value):
2032+
node = astroid.extract_node(code)
2033+
inferred = next(node.infer())
2034+
assert isinstance(inferred, expected_class)
2035+
assert inferred.value == expected_value
2036+
2037+
20232038
if __name__ == "__main__":
20242039
unittest.main()

tests/unittest_inference.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,8 +2083,6 @@ def test_dict_invalid_args(self):
20832083
def test_str_methods(self):
20842084
code = """
20852085
' '.decode() #@
2086-
2087-
' '.encode() #@
20882086
' '.join('abcd') #@
20892087
' '.replace('a', 'b') #@
20902088
' '.format('a') #@
@@ -2106,15 +2104,13 @@ def test_str_methods(self):
21062104
"""
21072105
ast = extract_node(code, __name__)
21082106
self.assertInferConst(ast[0], "")
2109-
for i in range(1, 16):
2107+
for i in range(1, 15):
21102108
self.assertInferConst(ast[i], "")
2111-
for i in range(16, 19):
2109+
for i in range(15, 18):
21122110
self.assertInferConst(ast[i], 0)
21132111

21142112
def test_unicode_methods(self):
21152113
code = """
2116-
u' '.encode() #@
2117-
21182114
u' '.decode() #@
21192115
u' '.join('abcd') #@
21202116
u' '.replace('a', 'b') #@
@@ -2137,9 +2133,9 @@ def test_unicode_methods(self):
21372133
"""
21382134
ast = extract_node(code, __name__)
21392135
self.assertInferConst(ast[0], "")
2140-
for i in range(1, 16):
2136+
for i in range(1, 15):
21412137
self.assertInferConst(ast[i], "")
2142-
for i in range(16, 19):
2138+
for i in range(15, 18):
21432139
self.assertInferConst(ast[i], 0)
21442140

21452141
def test_scope_lookup_same_attributes(self):

0 commit comments

Comments
 (0)