Skip to content

Commit 13d2bd2

Browse files
committed
Add W605 warning for invalid escape sequences in string literals
Starting with Python 3.6, invalid escape sequences in string literals are now deprecated. In a future version of Python, invalid escape sequences will be a syntax error. While this deprecation produces a runtime warning, it only appears if warnings are enabled and the first time the Python source is compiled to byte code. By adding a check to pycodestyle, projects can take advantage of static analysis to catch and fix these future syntax errors. For more information on the deprecation, see the Python release notes, https://docs.python.org/3/whatsnew/3.6.html#deprecated-python-behavior > A backslash-character pair that is not a valid escape sequence now > generates a DeprecationWarning. Although this will eventually become a > SyntaxError, that will not be for several Python releases. Fixes #633
1 parent 769ea41 commit 13d2bd2

File tree

5 files changed

+80
-4
lines changed

5 files changed

+80
-4
lines changed

CHANGES.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Changelog
22
=========
33

4+
UNRELEASED
5+
----------
6+
7+
New checks:
8+
9+
* Add W605 warning for invalid escape sequences in string literals
10+
411
2.3.1 (2017-01-31)
512
------------------
613

docs/intro.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ This is the current list of error and warning codes:
413413
+------------+----------------------------------------------------------------------+
414414
| W604 | backticks are deprecated, use 'repr()' |
415415
+------------+----------------------------------------------------------------------+
416+
| W605 | invalid escape sequence '\x' |
417+
+------------+----------------------------------------------------------------------+
416418

417419

418420
**(*)** In the default configuration, the checks **E121**, **E123**, **E126**,

pycodestyle.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,57 @@ def python_3000_backticks(logical_line):
13881388
yield pos, "W604 backticks are deprecated, use 'repr()'"
13891389

13901390

1391+
@register_check
1392+
def python_3000_invalid_escape_sequence(logical_line, tokens):
1393+
r"""Invalid escape sequences are deprecated in Python 3.6.
1394+
1395+
Okay: regex = r'\.png$'
1396+
W605: regex = '\.png$'
1397+
"""
1398+
# https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
1399+
valid = [
1400+
'\n',
1401+
'\\',
1402+
'\'',
1403+
'"',
1404+
'a',
1405+
'b',
1406+
'f',
1407+
'n',
1408+
'r',
1409+
't',
1410+
'v',
1411+
'0', '1', '2', '3', '4', '5', '6', '7',
1412+
'x',
1413+
1414+
# Escape sequences only recognized in string literals
1415+
'N',
1416+
'u',
1417+
'U',
1418+
]
1419+
1420+
for token_type, text, start, end, line in tokens:
1421+
if token_type == tokenize.STRING:
1422+
quote = text[-3:] if text[-3:] in ('"""', "'''") else text[-1]
1423+
# Extract string modifiers (e.g. u or r)
1424+
quote_pos = text.index(quote)
1425+
prefix = text[:quote_pos].lower()
1426+
start = quote_pos + len(quote)
1427+
string = text[start:-len(quote)]
1428+
1429+
if 'r' not in prefix:
1430+
pos = string.find('\\')
1431+
while pos >= 0:
1432+
pos += 1
1433+
if string[pos] not in valid:
1434+
yield (
1435+
pos,
1436+
"W605 invalid escape sequence '\\%s'" %
1437+
string[pos],
1438+
)
1439+
pos = string.find('\\', pos + 1)
1440+
1441+
13911442
##############################################################################
13921443
# Helper functions
13931444
##############################################################################

testsuite/E12not.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,10 @@ def qualify_by_address(self, cr, uid, ids, context=None,
358358
""" This gets called by the web server """
359359

360360

361-
_ipv4_re = re.compile('^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
362-
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
363-
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
364-
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$')
361+
_ipv4_re = re.compile(r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
362+
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
363+
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
364+
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$')
365365

366366

367367
fct("""

testsuite/W60.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,19 @@
1313
x = 0
1414
#: W604
1515
val = `1 + 2`
16+
#: W605
17+
regex = '\.png$'
18+
#: W605
19+
regex = '''
20+
\.png$
21+
'''
22+
#: Okay
23+
regex = r'\.png$'
24+
regex = '\\.png$'
25+
regex = r'''
26+
\.png$
27+
'''
28+
regex = r'''
29+
\\.png$
30+
'''
31+
s = '\\'

0 commit comments

Comments
 (0)