Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d249c06

Browse filesBrowse files
committed
Merge pull request #409 from methane/feature/faster-string-escape
Faster bytes escaping on Python 3
2 parents 7dc1817 + 6fa2f1b commit d249c06
Copy full SHA for d249c06

File tree

Expand file treeCollapse file tree

2 files changed

+42
-15
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+42
-15
lines changed

‎pymysql/converters.py

Copy file name to clipboardExpand all lines: pymysql/converters.py
+41-14Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,12 @@
44
import binascii
55
import datetime
66
from decimal import Decimal
7-
import re
87
import time
98

109
from .constants import FIELD_TYPE, FLAG
1110
from .charset import charset_by_id, charset_to_encoding
1211

1312

14-
ESCAPE_REGEX = re.compile(r"[\0\n\r\032\'\"\\]")
15-
ESCAPE_MAP = {'\0': '\\0', '\n': '\\n', '\r': '\\r', '\032': '\\Z',
16-
'\'': '\\\'', '"': '\\"', '\\': '\\\\'}
17-
1813
def escape_item(val, charset, mapping=None):
1914
if mapping is None:
2015
mapping = encoders
@@ -48,8 +43,7 @@ def escape_sequence(val, charset, mapping=None):
4843
return "(" + ",".join(n) + ")"
4944

5045
def escape_set(val, charset, mapping=None):
51-
val = map(lambda x: escape_item(x, charset, mapping), val)
52-
return ','.join(val)
46+
return ','.join([escape_item(x, charset, mapping) for x in val])
5347

5448
def escape_bool(value, mapping=None):
5549
return str(int(value))
@@ -63,20 +57,53 @@ def escape_int(value, mapping=None):
6357
def escape_float(value, mapping=None):
6458
return ('%.15g' % value)
6559

66-
def escape_string(value, mapping=None):
67-
return ("%s" % (ESCAPE_REGEX.sub(
68-
lambda match: ESCAPE_MAP.get(match.group(0)), value),))
60+
if PY2:
61+
def escape_string(value, mapping=None):
62+
"""escape_string escapes *value* but not surround it with quotes.
63+
64+
Value should be bytes or unicode.
65+
"""
66+
value = value.replace('\\', '\\\\')
67+
value = value.replace('\0', '\\0')
68+
value = value.replace('\n', '\\n')
69+
value = value.replace('\r', '\\r')
70+
value = value.replace('\032', '\\Z')
71+
value = value.replace("'", "\\'")
72+
value = value.replace('"', '\\"')
73+
return value
74+
else:
75+
_escape_table = [chr(x) for x in range(128)]
76+
_escape_table[0] = '\\0'
77+
_escape_table[ord('\\')] = '\\\\'
78+
_escape_table[ord('\n')] = '\\n'
79+
_escape_table[ord('\r')] = '\\r'
80+
_escape_table[ord('\032')] = '\\Z'
81+
_escape_table[ord('"')] = '\\"'
82+
_escape_table[ord("'")] = "\\'"
83+
84+
def escape_string(value, mapping=None):
85+
"""escape_string escapes *value* but not surround it with quotes.
86+
87+
Value should be str (unicode).
88+
"""
89+
return value.translate(_escape_table)
90+
91+
# On Python ~3.5, str.decode('ascii', 'surrogateescape') is slow.
92+
# (fixed in Python 3.6, http://bugs.python.org/issue24870)
93+
# Workaround is str.decode('latin1') then translate 0x80-0xff into 0udc80-0udcff.
94+
# We can escape special chars and surrogateescape at once.
95+
_escape_bytes_table = _escape_table + [chr(i) for i in range(0xdc80, 0xdd00)]
96+
97+
def escape_bytes(value, mapping=None):
98+
return "'%s'" % value.decode('latin1').translate(_escape_bytes_table)
99+
69100

70101
def escape_str(value, mapping=None):
71102
return "'%s'" % escape_string(value, mapping)
72103

73104
def escape_unicode(value, mapping=None):
74105
return escape_str(value, mapping)
75106

76-
def escape_bytes(value, mapping=None):
77-
# escape_bytes is calld only on Python 3.
78-
return escape_str(value.decode('ascii', 'surrogateescape'), mapping)
79-
80107
def escape_None(value, mapping=None):
81108
return 'NULL'
82109

‎pymysql/cursors.py

Copy file name to clipboardExpand all lines: pymysql/cursors.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def _escape_args(self, args, conn):
117117
# If it's not a dictionary let's try escaping it anyways.
118118
# Worst case it will throw a Value error
119119
if PY2:
120-
ensure_bytes(args)
120+
args = ensure_bytes(args)
121121
return conn.escape(args)
122122

123123
def mogrify(self, query, args=None):

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.