Skip to content
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: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
matrix:
python-version: ['pypy2.7', 3.8, 3.9, '3.10', '3.11', '3.12', '3.13']
python-version: ['3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v4
- name: Set up MySQL
Expand Down
9 changes: 3 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ This package contains a python MySQL client library.

It is a fork project from PyMySQL https://pymysql.readthedocs.io/en/latest/.

CyMySQL is accerarated by Cython and supports Python versions 2 and 3.
CyMySQL is accerarated by Cython.

Documentation on the MySQL client/server protocol can be found here:
http://dev.mysql.com/doc/internals/en/client-server-protocol.html

Requirements
-------------

- Python 2.7, 3.5+
- Python 3.10+
- MySQL 5.7 or higher, MariaDB

Installation
Expand Down Expand Up @@ -109,10 +109,7 @@ https://peps.python.org/pep-0249/
asyncio
++++++++++++++++++++++++++++++++++++++

In Python3, you can use asyncio to write the following.

This API is experimental.
If there are any mistakes, please correct them in the pull request and send.
You can use asyncio to write the following.

Use connect
::
Expand Down
4 changes: 1 addition & 3 deletions cymysql/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
THE SOFTWARE.

'''
import sys
from cymysql import converters
from cymysql.converters import escape_dict, escape_sequence, escape_string
from cymysql.err import (
Expand All @@ -35,8 +34,7 @@
)
from cymysql.connections import Connection
from cymysql.constants import FIELD_TYPE
if sys.version_info[0] > 2:
from cymysql import aio
from cymysql import aio

from .__version__ import VERSION, __version__

Expand Down
2 changes: 1 addition & 1 deletion cymysql/aio/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ async def set_charset(self, charset):
async def read_packet(self):
"""Read an entire "mysql packet" in its entirety from the network
and return a MysqlPacket type that represents the results."""
return MysqlPacket(await self.socket.recv_packet(self.loop), self.charset, self.encoding, self.use_unicode)
return MysqlPacket(await self.socket.recv_packet(self.loop), self.charset, self.encoding)

async def _request_authentication(self):
if self.user is None:
Expand Down
5 changes: 0 additions & 5 deletions cymysql/aio/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ async def read_result(self):
await self.connection.socket.recv_packet(self.connection.loop),
self.connection.charset,
self.connection.encoding,
self.connection.use_unicode,
)

if self.first_packet.is_ok_packet():
Expand All @@ -41,7 +40,6 @@ async def read_rest_rowdata_packet(self):
await self.connection.socket.recv_packet(self.connection.loop),
self.connection.charset,
self.connection.encoding,
self.connection.use_unicode,
)
is_eof, warning_count, server_status = packet.is_eof_and_status()
if is_eof:
Expand All @@ -62,7 +60,6 @@ async def _get_descriptions(self):
await self.connection.socket.recv_packet(self.connection.loop),
self.connection.charset,
self.connection.encoding,
self.connection.use_unicode,
)
self.fields.append(field)
description.append(field.description())
Expand All @@ -71,7 +68,6 @@ async def _get_descriptions(self):
await self.connection.socket.recv_packet(self.connection.loop),
self.connection.charset,
self.connection.encoding,
self.connection.use_unicode,
)
assert eof_packet.is_eof_packet(), 'Protocol error, expecting EOF'
self.description = tuple(description)
Expand All @@ -84,7 +80,6 @@ async def fetchone(self):
await self.connection.socket.recv_packet(self.connection.loop),
self.connection.charset,
self.connection.encoding,
self.connection.use_unicode,
)
is_eof, warning_count, server_status = packet.is_eof_and_status()
if is_eof:
Expand Down
39 changes: 7 additions & 32 deletions cymysql/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
from cymysql.result import MySQLResult
from cymysql.socketwrapper import SocketWrapper

PYTHON3 = sys.version_info[0] > 2

DEFAULT_USER = getpass.getuser()
DEFAULT_CHARSET = 'utf8mb4'

Expand All @@ -47,17 +45,11 @@ def byte2int(b):


def int2byte(i):
if PYTHON3:
return bytes([i])
else:
return chr(i)
return bytes([i])


def pack_int24(n):
if PYTHON3:
return bytes([n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF])
else:
return chr(n & 0xFF) + chr((n >> 8) & 0xFF) + chr((n >> 16) & 0xFF)
return bytes([n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF])


SCRAMBLE_LENGTH = 20
Expand Down Expand Up @@ -122,7 +114,7 @@ def errorhandler(connection, cursor, errorclass, errorvalue):
def __init__(self, host="localhost", user=None, passwd="",
db=None, port=3306, unix_socket=None,
charset='', sql_mode=None,
read_default_file=None, use_unicode=None,
read_default_file=None,
client_flag=0, cursorclass=None, init_command=None,
connect_timeout=None, ssl=None, read_default_group=None,
compress="", zstd_compression_level=3, named_pipe=None,
Expand All @@ -141,7 +133,6 @@ def __init__(self, host="localhost", user=None, passwd="",
sql_mode: Default SQL_MODE to use.
read_default_file: Specifies my.cnf file to read these parameters from under the [client] section.
conv: Decoders dictionary to use instead of the default one. This is used to provide custom marshalling of types. See converters.
use_unicode: Whether or not to default to unicode strings. This option defaults to true for Py3k.
client_flag: Custom flags to send to MySQL. Find potential values in constants.CLIENT.
cursorclass: Custom cursor class to use.
init_command: Initial SQL statement to run when connection is established.
Expand All @@ -152,10 +143,6 @@ def __init__(self, host="localhost", user=None, passwd="",
zstd_compression_level: zstd compression leve (1-22), default is 3.
named_pipe: Not supported
"""

if use_unicode is None and sys.version_info[0] > 2:
use_unicode = True

if named_pipe:
raise NotImplementedError("named_pipe argument are not supported")

Expand Down Expand Up @@ -230,16 +217,7 @@ def _config(key, default):
self.unix_socket = unix_socket
self.conv = conv
self.encoders = encoders
if charset:
self.charset = charset
self.use_unicode = True
else:
self.charset = DEFAULT_CHARSET
self.use_unicode = False

if use_unicode is not None:
self.use_unicode = use_unicode

self.charset = charset if charset else DEFAULT_CHARSET
self.encoding = encoding_by_charset(self.charset)

client_flag |= CLIENT.CAPABILITIES
Expand Down Expand Up @@ -439,7 +417,7 @@ def _connect(self):
def read_packet(self):
"""Read an entire "mysql packet" in its entirety from the network
and return a MysqlPacket type that represents the results."""
return MysqlPacket(self.socket.recv_packet(), self.charset, self.encoding, self.use_unicode)
return MysqlPacket(self.socket.recv_packet(), self.charset, self.encoding)

def insert_id(self):
if self._result:
Expand All @@ -451,10 +429,7 @@ def _execute_command(self, command, sql):
if not self.socket:
self.errorhandler(None, InterfaceError, (-1, 'socket not found'))

if (
(PYTHON3 and isinstance(sql, str)) or
(not PYTHON3 and isinstance(sql, unicode))
):
if isinstance(sql, str):
sql = sql.encode(self.encoding)

if len(sql) + 1 > 0xffffff:
Expand Down Expand Up @@ -525,7 +500,7 @@ def _request_authentication(self):
self.socket.send_uncompress_packet(data)
auth_packet = self.socket.recv_uncompress_packet()

if auth_packet[0] == (0xfe if PYTHON3 else b'\xfe'): # EOF packet
if auth_packet[0] == 0xfe: # EOF packet
# AuthSwitchRequest
# https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
i = auth_packet.find(b'\0', 1)
Expand Down
37 changes: 12 additions & 25 deletions cymysql/converters.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import re
import datetime
import time
import sys
import decimal

from cymysql.constants import FIELD_TYPE

PYTHON3 = sys.version_info[0] > 2

ESCAPE_REGEX = re.compile(r"[\0\n\r\032\'\"\\]")
ESCAPE_MAP = {'\0': '\\0', '\n': '\\n', '\r': '\\r', '\032': '\\Z',
'\'': '\\\'', '"': '\\"', '\\': '\\\\'}
Expand Down Expand Up @@ -113,7 +110,7 @@ def convert_datetime(obj):
True

"""
if PYTHON3 and not isinstance(obj, str):
if not isinstance(obj, str):
obj = obj.decode('ascii')
if ' ' in obj:
sep = ' '
Expand Down Expand Up @@ -150,7 +147,7 @@ def convert_timedelta(obj):
can accept values as (+|-)DD HH:MM:SS. The latter format will not
be parsed correctly by this function.
"""
if PYTHON3 and not isinstance(obj, str):
if not isinstance(obj, str):
obj = obj.decode('ascii')
try:
microseconds = 0
Expand Down Expand Up @@ -216,7 +213,7 @@ def convert_date(obj):
True

"""
if PYTHON3 and not isinstance(obj, str):
if not isinstance(obj, str):
obj = obj.decode('ascii')
try:
return datetime.date(*[int(x) for x in obj.split('-', 2)])
Expand Down Expand Up @@ -245,7 +242,7 @@ def convert_mysql_timestamp(obj):
True

"""
if PYTHON3 and not isinstance(obj, str):
if not isinstance(obj, str):
obj = obj.decode('ascii')
if obj[4] == '-':
return convert_datetime(obj)
Expand Down Expand Up @@ -273,35 +270,29 @@ def convert_bit(b):
return b


def convert_characters(data, encoding=None, field=None, use_unicode=None):
def convert_characters(data, encoding=None, field=None):
if field.is_set:
return convert_set(data.decode(field.encoding))
if field.is_binary:
if PYTHON3 and field.charset != 'binary':
if field.charset != 'binary':
return data.decode(field.encoding)
else:
return data

if use_unicode or PYTHON3:
return data.decode(field.encoding)
elif encoding != field.encoding:
return data.decode(field.encoding).encode(encoding)
return data
return data.decode(field.encoding)


def convert_vector(data, encoding=None, field=None, use_unicode=None):
def convert_vector(data, encoding=None, field=None):
import numpy as np
return np.frombuffer(data, dtype=np.float32)


def convert_json(data, encoding=None, field=None, use_unicode=None):
if use_unicode or PYTHON3:
return data.decode(encoding)
return data
def convert_json(data, encoding=None, field=None):
return data.decode(encoding)


def convert_decimal(obj):
if PYTHON3 and not isinstance(obj, str):
if not isinstance(obj, str):
obj = obj.decode('ascii')
return decimal.Decimal(obj)

Expand Down Expand Up @@ -337,6 +328,7 @@ def convert_decimal(obj):
}

encoders = {
bytes:escape_bytes,
bool: escape_bool,
int: escape_int,
float: escape_float,
Expand All @@ -354,11 +346,6 @@ def convert_decimal(obj):
time.struct_time: escape_struct_time,
}

if PYTHON3:
encoders[bytes] = escape_bytes
else:
encoders[unicode] = escape_string

try:
import numpy as np
encoders[np.ndarray] = escape_vector
Expand Down
14 changes: 3 additions & 11 deletions cymysql/cursors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
NotSupportedError, ProgrammingError
)

PYTHON3 = sys.version_info[0] > 2


class Cursor(object):
'''
Expand Down Expand Up @@ -115,10 +113,8 @@ def execute(self, query, args=None):
encoding = conn.encoding
del self.messages[:]

if PYTHON3 and (not isinstance(query, str)):
if not isinstance(query, str):
query = query.decode(encoding)
if (not PYTHON3) and isinstance(query, unicode):
query = query.encode(encoding)

if args is not None:
if isinstance(args, (tuple, list)):
Expand Down Expand Up @@ -187,20 +183,16 @@ def callproc(self, procname, args=()):
conn = self._get_db()
for index, arg in enumerate(args):
q = "SET @_%s_%d=%s" % (procname, index, conn.escape(arg))
if PYTHON3 and (not isinstance(q, str)):
if not isinstance(q, str):
q = q.decode(conn.encoding)
if (not PYTHON3) and isinstance(q, unicode):
q = q.encode(conn.encoding)
self._query(q)
self.nextset()

q = "CALL %s(%s)" % (procname,
','.join(['@_%s_%d' % (procname, i)
for i in range(len(args))]))
if PYTHON3 and (not isinstance(q, str)):
if not isinstance(q, str):
q = q.decode(conn.encoding)
if (not PYTHON3) and isinstance(q, unicode):
q = q.encode(conn.encoding)
self._query(q)
self._executed = q

Expand Down
Loading