PyMySQL Evaluation

This page will capture issues related to Openstack moving to the PyMySQL driver for MySQL/MariaDB dbapi access.

Rationale

While the MySQL-Python driver is a very mature and stable driver, it does not provide compatibility with either Python 3 or with eventlet monkeypatching (MySQL-Python can be monkeypatched with eventlet, but this feature disabled by default and missed in documentation). So OpenStack's usage of MySQL-Python, combined with the fact that concurrency is provided by eventlet, means that we currently have fully serialized database access within a single process, that is, only one database command occurs at a time within an Openstack Python process.

Drivers Under Consideration

The two drivers that are known to provide eventlet-monkeypatch compatibility are MySQL-Connector and PyMySQL, as they are written in pure Python. The two other well-known drivers for MySQL are MySQL-Python and OurSQL, both of which are written in C and offer no explicit support for eventlet or async. They can reportedly be built to support gevent using a system called Greenify, but the maturity and/or stabiltiy of this system is unknown.

A comparison at http://www.diamondtin.com/2014/sqlalchemy-gevent-mysql-python-drivers-comparison/ illustrates performance metrics observed with all four of these drivers, with MySQL-python built both without and with the "greenify" system.

A summary of the status of all four drivers is as follows.

MySQL-Python

MySQL-Python is the most widely used Python driver. However, it does not support Python 3, and it does not support async systems unless used within a thread pool. While a system known as Greenify can potentially resolve the latter situation, the Python 3 limitation is still a deal-breaker - pull request with Python 3 support was proposed to MySQL-python in April 2014 and still not merged.

mysqlclient

mysqlclient is a fork of MySQL-python which works in Python 3.3. It is fully functional and passes all SQLAlchemy unit tests, and is also maintained by the same people that maintain PyMySQL.

OurSQL

OurSQL is an alternative MySQL driver that is also written in C. It features fast performance and uses a different execution model than MySQL-python, using prepared statements. OurSQL does have a Python 3 port hosted on launchpad but is not integrated with OurSQL itself nor is it published on Pypi. Because it is written in C, it does not offer compatibility with async systems including eventlet-style monkeypatching.

Besides the shaky Python 3 support and lack of eventlet support, OurSQL is also not maintained, seeing its last master commit on 2012-06-05.

MySQL-Connector-Python

MySQL-connector-Python is a pure Python MySQL driver, and is now released under the auspices of the MySQL project itself, as owned by Oracle. MySQL-connector-Python supports both Python 3 as well as eventlet monkeypatching, and is well maintained. It is endorsed by Oracle as the official Oracle-supported driver for MySQL, so to that extent, it is in most ways Openstack's first choice in driver. However, Oracle refuses to publish MySQL-connector-Python on Pypi, which is critical to the Openstack infrastructure. Repeated attempts to communicate with Oracle in order to resolve this issue have not made any progress. Therefore, for this one unfortunate reason, MySQL-connector-Python will not have a place in the Openstack ecosystem unless this issue is resolved.

PyMySQL

PyMySQL is a pure Python MySQL driver, first written as a rough port of the MySQL-Python driver. PyMySQL meets all of Openstack's criterion for a driver: it is fully open source, hosted on Github, released on Pypi, is actively maintained, is written in pure Python so is eventlet-monkeypatch compatible, and is fully Python 3 compatible.

As this document is named "PyMySQL Evaluation", it should be apparent that this is the driver Openstack is currently leaning towards; because it is the only one that meets all criteria fully, it is already most likely the "winner". However, it does have some minor code quality issues which hopefully can be addressed in some way; the section below titled "PyMySQL Code Review" will summarize the current state of the code.

MySQL DB Drivers Comparison

Project PyPi hosted Eventlet friendly Python 3 compatibility Maturity and/or stability Comment
MySQL-Python Yes Partial No Yes Can be monkeypatched by eventlet, but only to enable thread pooling
mysqlclient Yes Partial Yes Yes Initial testing shows that this is a promising DBAPI if eventlet requirement can be dropped
OurSQL Yes No Yes, but not Pypi hosted No Development halted fairly early on, and has not seen commits/releases in two years
MySQL-Connector-Python No Yes Yes Yes, though the driver is still fairly new The official Oracle-supported driver for MySQL
PyMySQL Yes Yes Yes Yes, however see notes below. Actively maintained and popular.

PyMySQL Code Review

PyMySQL started roughly as a pure Python port of MySQL-Python. The sections below will detail various aspects of the driver and the status of the code. Overall, the general tone of PyMySQL is one of code that was written in the spirit of pragmatism and immediate need; it is straightforward, free of serious antipatterns and performs its task in a matter-of-fact way. However, it lacks polish and completeness in many areas that would normally be in better condition for a more mature project. These areas are all highly fixable, and it is hoped that these items can be shared with the current PyMySQL developers, through a combination of this document itself as well as new issues and pull requests on the PyMySQL tracker, so that this fairly promising library can be pushed to the next level. This would require that the developers are amenable to these improvements.

Coding Style / Pep8

PyMySQL's coding style is pretty good. The majority of it passes all flake8 tests, save for a few dozen whitespace issues and some long lines here and there within the core modules, and moreso within the tests which appear to be a bit more crufty than the core modules. A full run of flake8 produces only 128 errors, and with a default run of autopep8 against E1,E2,E3, we can fix all of them in one pass except for 38 remaining long line warnings. The code layout is mostly idiomatic and reasonable.

The codebase is probably short on docstrings. Those methods that are part of the public DBAPI do have docstrings, but tend to be extremely terse (example, docstring for cursor.execute(): "'Execute a query'"). Other methods that aren't public tend to not have any docstrings, but the codebase is not at all hard to read and the purpose of methods and functions is pretty easy to discern just by their names and implementations, however those who seek inline documentation as a means to understand a codebase will be disappointed. The terseness of public API docstrings is more of an issue in terms of the overall lack of documentation for PyMySQL, see the section "Documentation" below.

Test Coverage

The test coverage for PyMySQL is definitely lacking. From a visual inspection alone, it's apparent that of the dozen or so test modules, most of them have less then ten tests each and the modules are very short. For a full test run, there's only 126 tests; contrast this to MySQL-Connector-Python which has 531 tests.

Additionally, all of the tests are live round-trip tests against a real database. There are no non-live unit tests of any kind, leaving codepaths that are not easily exercised on a generic MySQL database out in the cold. This means that features designed for particular versions of MySQL, particular datatypes, particular error conditions, and particular kinds of message packets that aren't created for real are simply not tested.

The lack of tests is apparent when run with coverage. The test suite itself does not appear to have any signs that it is normally run with coverage turned on, with no directives in tox.ini or similar. With coverage, the overall coverage is 85%, but the modules most lacking in coverage are also the most critical to PyMySQL's core functionality, connections.py, converters.py and cursors.py:

Name                                                              Stmts   Miss  Cover
-------------------------------------------------------------------------------------
pymysql/__init__ 51 3 94%
pymysql/_compat 14 4 71%
pymysql/_socketio 72 30 58%
pymysql/charset 228 3 99%
pymysql/connections 827 188 77%
pymysql/constants/CLIENT 18 0 100%
pymysql/constants/COMMAND 32 0 100%
pymysql/constants/ER 471 0 100%
pymysql/constants/FIELD_TYPE 29 0 100%
pymysql/constants/FLAG 15 0 100%
pymysql/constants/SERVER_STATUS 10 0 100%
pymysql/constants/__init__ 0 0 100%
pymysql/converters 148 51 66%
pymysql/cursors 273 37 86%
pymysql/err 40 1 98%
pymysql/tests/__init__ 14 5 64%
pymysql/tests/base 25 2 92%
pymysql/tests/test_DictCursor 73 2 97%
pymysql/tests/test_SSCursor 56 6 89%
pymysql/tests/test_basic 186 4 98%
pymysql/tests/test_connection 57 8 86%
pymysql/tests/test_example 17 2 88%
pymysql/tests/test_issues 300 48 84%
pymysql/tests/test_load_local 39 2 95%
pymysql/tests/test_nextset 43 4 91%
pymysql/tests/thirdparty/__init__ 7 5 29%
pymysql/tests/thirdparty/test_MySQLdb/__init__ 6 2 67%
pymysql/tests/thirdparty/test_MySQLdb/capabilities 196 23 88%
pymysql/tests/thirdparty/test_MySQLdb/dbapi20 423 121 71%
pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities 73 6 92%
pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20 100 2 98%
pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard 57 3 95%
pymysql/times 12 0 100%
pymysql/util 14 7 50%
-------------------------------------------------------------------------------------
TOTAL 3926 569 86%

The lack of coverage in converters.py refers to functions related to handling incoming bound values as well as outgoing result values. Key data escaping features such as escaping of dictionaries, sets, some unicode objects and byte objects as well as many pathways into date and time-processing functions are not covered.

In connections.py, uncovered features include various SSL and very legacy (e.g. version 3.23) MySQL features, but also some protocol parsing features.

In cursors.py, there is coverage for an elaborate and mostly undocumented performance-related feature that rewrites an INSERT statement to have an extended VALUES clause (_do_execute_many), however it fails to cover the very likely case where the function will need to break the input set into multiple chunks:

>         for arg in args:
> v = values % escape(arg, conn)
> if isinstance(v, text_type):
> v = v.encode(encoding)
> if len(sql) + len(v) + 1 > max_stmt_length:
! rows += self.execute(sql)
! sql = bytearray(prefix)
> else:
> sql += b','
> sql += v
> rows += self.execute(sql)

The most prominent missing coverage in cursors.py seems to be for its "scrollable cursor" support.

Beyond the lack of completeness in testing, many of the tests themselves are written in an expedient "one giant test" style where dozens of assertions and individual behaviors are lumped into one big test case (see test_SSCursor.py, test_basic.py->test_datatypes for examples). Test cases like these are difficult to work with when debugging regressions as well as when tests for new features need to be added, and ideally these should be broken out into clean single-feature tests with consistent fixtures.

There is also a suite called "test_issues", which intends to accumulate tests against specific issues that have been reported. This isn't a bad idea, though it would be nice to see an effort made into categorizing these tests into the actual features they are testing, rather than a meaningless list of issue numbers, which could then form as the basis for new suites centered around those areas of functionality. The issues reported should be used as the inspiration for improvement of the test suite, rather than just another line-item to be filed away.

Library Documentation

As the Python DBAPI is already a well-documented API, and the users of PyMySQL are typically coming from MySQL-Python which is documented to some degree and for which the use contract is widely known, it seems unlikely that users of PyMySQL are eager for comprehensive documentation specific to this library.

That being said, PyMySQL does not appear to have any real library documentation at all. There's a README which essentially refers to pep-249, an example.py in the root which shows a very basic connection / round trip, and that's it. In order to know anything about the API, specific parameters, optimization strategies, behaviors, etc., one either has to go off of the MySQL-Python documentation and assume PyMySQL also features the same parameter, or read the source code.

PyMySQL probably hasn't had much urgent need for real documentation thus far. However, relying upon being a port of MySQL-Python is fast becoming something that can no longer be relied upon, as MySQL-Python's development is quite stalled, and PyMySQL should aspire to move into the future of MySQL and Python with its own featureset and behaviors. To that end, it would be a great idea if it at least made the start of a rudimentary Sphinx build including autodoc for module documentation which could then be published up to RTD. This would be very easy to get started, and once present, new documentation sections can be added iteratively.

Architecture and Performance

As stated before, PyMySQL has a really matter-of-fact and straightforward implementation. The code is very readable and it's easy to discern even unusual features such as the INSERT..VALUES rewriting. As far as the design, there are lots of areas where performance suffers a bit at the Python level; these areas could be greatly improved very easily with a little bit of attention.

As we are dealing with a driver that is communicating with a low level protocol over a socket, it's critical that sending and receiving messages is done as efficiently as possible. The mechanisms used in PyMySQL rely heavily on standard Python objects, without the use of __slots__; this means that for individual messages on a socket, we are creating a heavyweight object, calling its __init__() method and a creating a new __dict__ each time; the most common object, OKPacketWrapper(), already wraps a MysqlPacket() object, so we're doing two __init__()s in this common case. OKPacketWrapper also implements a `__getattr__` scheme to proxy attribute access from itself to the internal MysqlPacket; this is a an inefficient mode of operation in Python as it means a lookup must first fail on the OKPacketWrapper before it invokes the `__getattr__()` method and does another attribute lookup. A reorg of the packet classes to produce far fewer objects for messages as well as to use __slots__ would be recommended.

There are also at least some areas where function call overhead is unnecessarily high due to the organization of calls. For example, when calling upon cursor.execute(), for parameters passed as a dictionary under Python 2K, the isinstance() builtin function will be called on the incoming arguments four times at a minimum, with two of each call against the exact same criteria. Additional logic will then call isinstance() on every key of the dictionary as well as at least twice on every value within the dictionary, in order to determine if the value needs to be converted to bytes as well as for some top-level escaping logic checks; the escaping logic then goes further with more checks for type() to match the object to a final converter function. While the flow of this logic does appear to be reasonable and straightforward, it isn't ideal for a low-level database driver library where speed is a high priority; the various wrappers and processors can easily be reorganized here to reduce the number of isinstance() and type() calls significantly, which would save a lot on function call overhead. It is likely there are many other areas in the codebase where similar reductions in call counts can be made without too much disruption.

In Appendix A, a short performance suite is illustrated. This is run against PyMySQL, Mysql-Connector-Python, and MySQL-Python. PyMySQL does in fact demonstrate the highest call-count behavior, logging 26,851,003 function calls compared to MySQL-connector's 11,674,074 for the same operations. To PyMySQL's credit, it somehow completes the test 25-50% faster than MySQL-connector despite having more than twice as many function calls; this probably indicates the use of something very slow within MySQL-connector, such as catching an exception within a tight loop. However, the real bar we're comparing to is MySQL-Python, which is written in C and does the whole test with only 47,503 Python function calls; while PyMySQL and MySQL-connector-Python compete for time within the 10-15 second range without profiling, MySQL-Python completes the whole test in **1.03 seconds**, a 1000% improvement over PyMySQL. This speed difference is not PyMySQL's fault, as pure Python is known to be extremely slow compared to C code. However, there's a lot of easy wins in PyMySQL's codebase where the function call count could probably be reduced dramatically to be even better than that of MySQL-connector-Python; ideally, the driver would be able to run this test possibly 5-6x slower than MySQL-Python rather than 10x.

History Documentation

PyMySQL includes a CHANGELOG file in its root which describes the bug fixes and features of each release. The construction of this file is definitely within the "expedient" style noted for PyMySQL overall, and for it to be genuinely useful for enterprise-level software, it would need to be much more complete in its detail. Each bug fix mentioned is only referred to tersely without any link to a pull request or bug report. While some can be linked to a specific change through detective work ("Cursor.fetchall() and .fetchmany now return list, not tuple"), it would be very difficult for many ("Fixed BIT type handling", "Fixed GC errors" - what was the issue?) and in many cases impossible ("Improved Py3k support"). The file also includes no dates so one has to look at the git tags to figure this part out (fortunately, releases to seem to be git tagged at least). For a user trying to ascertain if particular issues are fixed or if behaviors have changed, this file is at best highly frustrating.

A listing of release dates illustrates an average of two releases per year, excluding 2012 when PyMySQL was seeking a new maintainer:

2014-12-02 12:05:10 -0200  (tag: pymysql-0.6.3)
2014-04-21 14:28:47 -0300 (tag: pymysql-0.6.2)
2013-10-11 13:36:56 -0300 (tag: pymysql-0.6.1)
2013-10-04 14:25:14 -0300 (tag: pymysql-0.6)
2011-11-08 10:27:41 -0800 (tag: pymysql-0.5)
2010-12-27 17:28:16 +0000 (tag: pymysql-0.4)
2010-09-03 00:55:29 +0000 (tag: pymysql-0.3)

Community Involvement

This is an area where PyMySQL seems to shine, and might even be an advantage as compared to MySQL-Connector-Python, which remains under the cloak of MySQL's vastly overstuffed bugtracker under the ownership of the famously-opaque Oracle. For a small, low key library without any real documentation and only 126 tests, it features 33 contributors on Github and is up to pull request #285, which is quite high. Because PyMySQL is small and straightforward, it does lend itself to contribution and seems to do very well in this area. The developers so far have been responsive to issues and pull requests made by the author.

Based on github contributor graphs it appears that PyMySQL had a lull in 2012 but was picked up again afterwards in 2013. As of 2012, its most prominent developer Pete Hunt stopped development, and on July 25, 2013 added a note to the README that the project was looking for a new maintainer. As of 2013 it was picked up by the current most prominent developers Marcel Rodrigues (github username lecram) and INADA Naoki (github username methane).

Most issues that have been noted thus far within the areas of testing, documentation and architecture are all very fixable; if the Openstack community can provide resources to address some of these areas, and if the PyMySQL developers are reasonably open to accepting new changes and producing new releases in a timely fashion, the PyMySQL driver could rapidly become a fully formidable contender in the MySQL driver field.

Appendix A - Performance Tests

import cProfile
import StringIO
import pstats
import contextlib
import time @contextlib.contextmanager
def profiled(dbapi):
pr = cProfile.Profile()
pr.enable()
yield
pr.disable()
s = StringIO.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats('cumulative')
ps.print_stats()
print "DBAPI:  %s" % dbapi
print s.getvalue() @contextlib.contextmanager
def timeonly(dbapi):
now = time.time()
try:
yield
finally:
total = time.time() - now
print "DBAPI:  %s, total seconds %f" % (dbapi, total) import MySQLdb
import pymysql
from mysql import connector as mysqlconnector def go(dbapi, ctx):
conn = dbapi.connect(
user='scott', passwd='tiger', host='localhost', db='test')
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS test_things (
x INTEGER,
y VARCHAR(255),
z FLOAT
) engine=InnoDB
""")
cursor.execute("DELETE from test_things") with ctx(dbapi):
for row in xrange(1000):
cursor.execute(
"INSERT INTO test_things (x, y, z) "
"VALUES (%(x)s, %(y)s, %(z)s)",
{"x": row, "y": "row number %d" % row,
"z": row * 4.57292, }
) for x in xrange(500):
cursor.execute(
"select * from test_things")
for row in cursor.fetchall():
row[0], row[1], row[2] cursor.close()
conn.close() go(pymysql, profiled)
go(mysqlconnector, profiled)
go(MySQLdb, profiled) go(pymysql, timeonly)
go(mysqlconnector, timeonly)
go(MySQLdb, timeonly)
DBAPI:  <module 'pymysql' from '/Users/classic/dev/PyMySQL/pymysql/__init__.pyc'>
26851003 function calls in 15.924 seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function)
1500 0.010 0.000 15.923 0.011 /Users/classic/dev/PyMySQL/pymysql/cursors.py:105(execute)
1500 0.003 0.000 15.883 0.011 /Users/classic/dev/PyMySQL/pymysql/cursors.py:269(_query)
1500 0.004 0.000 15.859 0.011 /Users/classic/dev/PyMySQL/pymysql/connections.py:746(query)
1500 0.003 0.000 15.819 0.011 /Users/classic/dev/PyMySQL/pymysql/connections.py:892(_read_query_result)
1500 0.004 0.000 15.812 0.011 /Users/classic/dev/PyMySQL/pymysql/connections.py:1097(read)
500 0.003 0.000 15.559 0.031 /Users/classic/dev/PyMySQL/pymysql/connections.py:1154(_read_result_packet)
500 0.900 0.002 15.429 0.031 /Users/classic/dev/PyMySQL/pymysql/connections.py:1187(_read_rowdata_packet)
500000 3.417 0.000 9.173 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:1200(_read_row_from_packet)
2012000 1.157 0.000 5.307 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:311(read_length_coded_string)
504500 1.495 0.000 5.209 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:845(_read_packet)
2014500 1.248 0.000 3.035 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:293(read_length_encoded_integer)
3539500 2.439 0.000 2.750 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:245(read)
1009000 0.779 0.000 2.307 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:870(_read_bytes)
1009000 1.197 0.000 1.423 0.000 {method 'read' of '_io.BufferedReader' objects}
508500 0.379 0.000 0.695 0.000 /Users/classic/dev/PyMySQL/pymysql/util.py:3(byte2int)
5058000 0.465 0.000 0.465 0.000 {len}
500500 0.222 0.000 0.455 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:1146(_check_packet_is_eof)
504500 0.205 0.000 0.375 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:342(check_error)
2504000 0.275 0.000 0.275 0.000 {method 'append' of 'list' objects}
501500 0.182 0.000 0.230 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:326(is_eof_packet)
3500 0.009 0.000 0.226 0.000 /Users/classic/dev/PyMySQL/pymysql/_socketio.py:45(readinto)
3500 0.207 0.000 0.207 0.000 {method 'recv_into' of '_socket.socket' objects}
1022000 0.203 0.000 0.203 0.000 {_struct.unpack}
526000 0.200 0.000 0.200 0.000 {isinstance}
1501000 0.195 0.000 0.195 0.000 {method 'get' of 'dict' objects}
504500 0.176 0.000 0.176 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:238(__init__)
504500 0.170 0.000 0.170 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:339(is_error_packet)
2014500 0.167 0.000 0.167 0.000 {ord}
500 0.006 0.000 0.125 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:1224(_get_descriptions)
2000 0.003 0.000 0.087 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:361(__init__)
2000 0.019 0.000 0.084 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:365(__parse_field_descriptor)
500000 0.056 0.000 0.056 0.000 /Users/classic/dev/PyMySQL/pymysql/converters.py:301(through)
1500 0.005 0.000 0.037 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:915(_execute_command)
1500 0.003 0.000 0.027 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:886(_write_bytes)
1500 0.002 0.000 0.024 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py:223(meth)
1500 0.021 0.000 0.021 0.000 {method 'sendall' of '_socket.socket' objects}
1500 0.020 0.000 0.020 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:276(_do_get_result)
1000 0.003 0.000 0.019 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:95(_escape_args)
4000 0.002 0.000 0.015 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:99(<genexpr>)
1000 0.002 0.000 0.014 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:1127(_read_ok_packet)
3000 0.003 0.000 0.013 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:712(escape)
1000 0.005 0.000 0.012 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:416(__init__)
8000 0.011 0.000 0.011 0.000 {method 'decode' of 'str' objects}
3500 0.003 0.000 0.007 0.000 {method '_checkReadable' of '_io._IOBase' objects}
2000 0.003 0.000 0.006 0.000 /Users/classic/dev/PyMySQL/pymysql/converters.py:19(escape_item)
4000 0.002 0.000 0.006 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:126(<genexpr>)
7500 0.003 0.000 0.005 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:115(ensure_bytes)
2000 0.003 0.000 0.005 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:386(description)
5500 0.004 0.000 0.004 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:268(advance)
1000 0.001 0.000 0.004 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:722(escape_string)
3500 0.004 0.000 0.004 0.000 /Users/classic/dev/PyMySQL/pymysql/_socketio.py:87(readable)
500 0.002 0.000 0.004 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:442(__init__)
3500 0.003 0.000 0.003 0.000 {method '_checkClosed' of '_io._IOBase' objects}
1000 0.001 0.000 0.003 0.000 /Users/classic/dev/PyMySQL/pymysql/converters.py:59(escape_string)
1500 0.003 0.000 0.003 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:1080(__init__)
1500 0.001 0.000 0.003 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:92(nextset)
4000 0.002 0.000 0.002 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:397(get_column_length)
1000 0.002 0.000 0.002 0.000 /Users/classic/dev/PyMySQL/pymysql/converters.py:56(escape_float)
1000 0.002 0.000 0.002 0.000 {method 'sub' of '_sre.SRE_Pattern' objects}
3000 0.002 0.000 0.002 0.000 {_struct.pack}
6000 0.002 0.000 0.002 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:62(_get_db)
1500 0.001 0.000 0.002 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:80(_nextset)
1500 0.001 0.000 0.001 0.000 /Users/classic/dev/PyMySQL/pymysql/util.py:9(int2byte)
500 0.001 0.000 0.001 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:244(fetchall)
1500 0.001 0.000 0.001 0.000 {min}
2500 0.001 0.000 0.001 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:323(is_ok_packet)
1000 0.001 0.000 0.001 0.000 /Users/classic/dev/PyMySQL/pymysql/converters.py:52(escape_int)
2000 0.001 0.000 0.001 0.000 {method 'items' of 'dict' objects}
1500 0.001 0.000 0.001 0.000 {getattr}
1500 0.000 0.000 0.000 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:1093(__del__)
1000 0.000 0.000 0.000 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:259(read_all)
500 0.000 0.000 0.000 0.000 /Users/classic/dev/PyMySQL/pymysql/cursors.py:67(_check_executed)
500 0.000 0.000 0.000 0.000 /Users/classic/dev/PyMySQL/pymysql/connections.py:336(is_load_local_packet)
1 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py:21(__exit__)
1 0.000 0.000 0.000 0.000 test.py:8(profiled)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
DBAPI:  <module 'mysql.connector' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/__init__.pyc'>
11674074 function calls in 19.049 seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function)
500 0.640 0.001 18.556 0.037 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:820(fetchall)
500 0.003 0.000 11.196 0.022 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:655(get_rows)
500 1.561 0.003 11.191 0.022 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/protocol.py:292(read_text_result)
504500 3.303 0.000 7.306 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/network.py:219(recv_plain)
500000 3.572 0.000 6.625 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:359(row_to_python)
1009000 3.155 0.000 3.155 0.000 {method 'recv_into' of '_socket.socket' objects}
500000 2.049 0.000 2.329 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/utils.py:220(read_lc_string_list)
500000 0.653 0.000 2.116 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:531(_STRING_to_python)
502000 0.378 0.000 1.329 0.000 {method 'decode' of 'bytearray' objects}
502000 0.221 0.000 0.951 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.py:15(decode)
502000 0.730 0.000 0.730 0.000 {_codecs.utf_8_decode}
500000 0.514 0.000 0.514 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:413(_INT_to_python)
509500 0.510 0.000 0.510 0.000 {_struct.unpack_from}
1500 0.009 0.000 0.492 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:452(execute)
1500 0.004 0.000 0.437 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:705(cmd_query)
500000 0.367 0.000 0.367 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:405(_FLOAT_to_python)
504500 0.343 0.000 0.343 0.000 {method 'extend' of 'bytearray' objects}
1500 0.007 0.000 0.315 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:481(_send_cmd)
2501500 0.275 0.000 0.275 0.000 {method 'append' of 'list' objects}
500500 0.246 0.000 0.246 0.000 {method 'startswith' of 'bytearray' objects}
524002 0.150 0.000 0.150 0.000 {isinstance}
1500 0.011 0.000 0.118 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:613(_handle_result)
1004500 0.112 0.000 0.112 0.000 {len}
500000 0.092 0.000 0.092 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:204(description)
1500 0.007 0.000 0.042 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/network.py:118(send_plain)
2000 0.013 0.000 0.041 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/protocol.py:226(parse_column)
1000 0.008 0.000 0.034 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:346(_process_params_dict)
1500 0.002 0.000 0.029 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py:223(meth)
1500 0.026 0.000 0.026 0.000 {method 'sendall' of '_socket.socket' objects}
5000 0.007 0.000 0.014 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/catch23.py:79(struct_unpack)
1000 0.001 0.000 0.013 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:562(_handle_ok)
12000 0.011 0.000 0.011 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/utils.py:167(read_lc_string)
1000 0.004 0.000 0.010 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/protocol.py:199(parse_ok)
3000 0.005 0.000 0.009 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:152(to_mysql)
1500 0.003 0.000 0.007 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/protocol.py:118(make_command)
3000 0.003 0.000 0.007 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:130(quote)
1500 0.005 0.000 0.006 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/network.py:53(_prepare_packets)
1500 0.003 0.000 0.006 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:406(_handle_result)
3000 0.003 0.000 0.006 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:102(escape)
1000 0.002 0.000 0.006 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/protocol.py:252(parse_eof)
500 0.001 0.000 0.005 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:579(_handle_eof)
2000 0.003 0.000 0.005 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:545(_handle_server_status)
1500 0.003 0.000 0.004 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/utils.py:53(int1store)
3000 0.003 0.000 0.003 0.000 {method 'encode' of 'str' objects}
9001 0.003 0.000 0.003 0.000 {method 'replace' of 'str' objects}
1500 0.003 0.000 0.003 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:295(_reset_result)
4500 0.003 0.000 0.003 0.000 {_struct.pack}
2500 0.003 0.000 0.003 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/utils.py:296(read_lc_int)
500 0.002 0.000 0.003 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:757(_handle_eof)
6027 0.003 0.000 0.003 0.000 {method 'format' of 'str' objects}
3000 0.002 0.000 0.003 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:1221(_set_unread_result)
500 0.001 0.000 0.002 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/protocol.py:219(parse_column_count)
1000 0.002 0.000 0.002 0.000 {repr}
4527 0.002 0.000 0.002 0.000 {getattr}
4000 0.002 0.000 0.002 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/constants.py:34(flag_is_set)
1000 0.001 0.000 0.001 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:383(_handle_noresultset)
4000 0.001 0.000 0.001 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:1233(_get_unread_result)
2001 0.001 0.000 0.001 0.000 {method 'items' of 'dict' objects}
500 0.001 0.000 0.001 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:307(_have_unread_result)
3000 0.001 0.000 0.001 0.000 {method 'lower' of 'str' objects}
500 0.001 0.000 0.001 0.000 {range}
1000 0.001 0.000 0.001 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:173(_str_to_mysql)
1000 0.001 0.000 0.001 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:169(_float_to_mysql)
1000 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/conversion.py:161(_int_to_mysql)
1500 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/connection.py:1313(_get_getwarnings)
1500 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:200(reset)
500 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/cursor.py:397(_handle_resultset)
1 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/__init__.py:71(search_function)
1 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/ascii.py:41(getregentry)
1 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py:21(__exit__)
1 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/__init__.py:49(normalize_encoding)
1 0.000 0.000 0.000 0.000 test.py:8(profiled)
1 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/codecs.py:77(__new__)
1 0.000 0.000 0.000 0.000 {__import__}
3 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'split' of 'str' objects}
1 0.000 0.000 0.000 0.000 {built-in method __new__ of type object at 0x1001534e8}
1 0.000 0.000 0.000 0.000 {method 'translate' of 'str' objects}
1 0.000 0.000 0.000 0.000 {method 'join' of 'str' objects}
1 0.000 0.000 0.000 0.000 {hasattr}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
DBAPI:  <module 'MySQLdb' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/__init__.pyc'>
47503 function calls in 0.918 seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function)
1500 0.009 0.000 0.917 0.001 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:164(execute)
1500 0.002 0.000 0.890 0.001 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:353(_query)
1500 0.004 0.000 0.498 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:315(_do_query)
1500 0.016 0.000 0.391 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:358(_post_get_result)
1500 0.001 0.000 0.375 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:324(_fetch_row)
500 0.374 0.001 0.374 0.001 {built-in method fetch_row}
1500 0.007 0.000 0.362 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:142(_do_get_result)
1500 0.002 0.000 0.351 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:351(_get_result)
1500 0.349 0.000 0.349 0.000 {method 'store_result' of '_mysql.connection' objects}
1500 0.131 0.000 0.131 0.000 {method 'query' of '_mysql.connection' objects}
4000 0.002 0.000 0.010 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:184(<genexpr>)
3000 0.002 0.000 0.008 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/connections.py:267(literal)
1500 0.007 0.000 0.007 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:107(_warning_check)
3000 0.002 0.000 0.006 0.000 {method 'escape' of '_mysql.connection' objects}
6000 0.002 0.000 0.002 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:159(_get_db)
1000 0.002 0.000 0.002 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/converters.py:81(Float2Str)
2500 0.001 0.000 0.001 0.000 {isinstance}
1000 0.001 0.000 0.001 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/connections.py:202(string_literal)
500 0.001 0.000 0.001 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:380(fetchall)
500 0.001 0.000 0.001 0.000 {built-in method describe}
1000 0.001 0.000 0.001 0.000 {method 'string_literal' of '_mysql.connection' objects}
1500 0.001 0.000 0.001 0.000 {method 'insert_id' of '_mysql.connection' objects}
1500 0.001 0.000 0.001 0.000 {method 'affected_rows' of '_mysql.connection' objects}
1000 0.001 0.000 0.001 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/converters.py:69(Thing2Str)
1500 0.000 0.000 0.000 0.000 {method 'warning_count' of '_mysql.connection' objects}
1500 0.000 0.000 0.000 0.000 {method 'info' of '_mysql.connection' objects}
1000 0.000 0.000 0.000 0.000 {method 'iteritems' of 'dict' objects}
500 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/cursors.py:103(_check_executed)
500 0.000 0.000 0.000 0.000 {len}
500 0.000 0.000 0.000 0.000 {built-in method field_flags}
1 0.000 0.000 0.000 0.000 /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py:21(__exit__)
1 0.000 0.000 0.000 0.000 test.py:8(profiled)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
DBAPI:  <module 'pymysql' from '/Users/classic/dev/PyMySQL/pymysql/__init__.pyc'>, total seconds 10.046012
DBAPI: <module 'mysql.connector' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mysql/connector/__init__.pyc'>, total seconds 15.403260
DBAPI: <module 'MySQLdb' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/MySQLdb/__init__.pyc'>, total seconds 1.028737
 

PyMySQL Evaluation的更多相关文章

  1. pyMysql

    本篇对于Python操作MySQL主要使用两种方式: 原生模块 pymsql ORM框架 SQLAchemy pymsql pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb ...

  2. Python 3.x 连接数据库(pymysql 方式)

    ==================pymysql=================== 由于 MySQLdb 模块还不支持 Python3.x,所以 Python3.x 如果想连接MySQL需要安装 ...

  3. Python中操作mysql的pymysql模块详解

    Python中操作mysql的pymysql模块详解 前言 pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同.但目前pymysql支持python3.x而后者不支持 ...

  4. Utility2:Appropriate Evaluation Policy

    UCP收集所有Managed Instance的数据的机制,是通过启用各个Managed Instances上的Collection Set:Utility information(位于Managem ...

  5. SQL SERVER 2012 从Enterprise Evaluation Edtion 升级到 Standard Edtion SP1

    案例背景:公司从意大利购买了一套中控系统,前期我也没有参与其中(包括安装.实施都是第三方),直到最近项目负责人告诉我:前期谈判以为是数据库的License费用包含在合同中,现在经过确认SQL Serv ...

  6. 杂项之pymysql连接池

    杂项之pymysql连接池 本节内容 本文的诞生 连接池及单例模式 多线程提升 协程提升 后记 1.本文的诞生 由于前几天接触了pymysql,在测试数据过程中,使用普通的pymysql插入100W条 ...

  7. Python3中使用PyMySQL连接Mysql

    Python3中使用PyMySQL连接Mysql 在Python2中连接Mysql数据库用的是MySQLdb,在Python3中连接Mysql数据库用的是PyMySQL,因为MySQLdb不支持Pyt ...

  8. Python之路-python(mysql介绍和安装、pymysql、ORM sqlachemy)

    本节内容 1.数据库介绍 2.mysql管理 3.mysql数据类型 4.常用mysql命令 创建数据库 外键 增删改查表 5.事务 6.索引 7.python 操作mysql 8.ORM sqlac ...

  9. python pymysql和orm

    pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同. 1. 安装 管理员打开cmd,切换到python的安装路径,进入到Scripts目录下(如:C:\Users\A ...

随机推荐

  1. iOS Crash常规跟踪方法及Bugly集成运用

    当app出现崩溃, 研发阶段一般可以通过以下方式来跟踪crash信息 #1.模拟器运行, 查看xcode错误日志 #2.真机调试, 查看xcode错误日志 #3.真机运行, 查看device系统日志 ...

  2. Redis的使用模式之计数器模式实例

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/123.html?1455853785 Redis 是目前 NoSQL 领域 ...

  3. 1编写一个Java程序,计算半径为3.0的圆周长和面积并输出结果。2编写一个Java项目,定义包,在包下定义包含main方法的类。

  4. Atitit.加密算法ati Aes的框架设计v2.2

    Atitit.加密算法ati Aes的框架设计v2.2 版本进化1 V2.2   add def decode key api1 v1版本1 Aes的历史2 Atitit.加密算法 des  aes  ...

  5. Atitit 项目培训与学校的一些思路总结

    Atitit 项目培训与学校的一些思路总结 1.1. Overview implet review  OIR学习大法1 1.2. "录取流程,对报名者唯一的要求是学习该项目所必须的先修知识和 ...

  6. DataGridView的Cell事件的先后触发顺序

    最近正在使用“DataGridView”对一个旧的Vs 2003开发的WINDOWS应用程序进行改造. 发现Vs 2003中的"DataGrid"中的一些事件已经在新的控件Data ...

  7. 实现步骤: 推送&传感器&UIDynamic

    一.本地通知基本使用: #01.请求授权(8.0以前默人授权) #02.创建本地通知 #03.设置通知内容 #04.设置通知时间(多久后发通知) #05.发送通知 二.本地通知而外设置: #01.设置 ...

  8. linux安装locust

    linux安装locust 1. 安装epel扩展源(目的是为了在安装Pip时不出现一堆乱七八糟的错误信息) EPEL(http://fedoraproject.org/wiki/EPEL) 是由 F ...

  9. CentOS 下运维自动化 Shell 脚本之 expect

    CentOS 下运维自动化 Shell脚本之expect 一.预备知识: 1.在 Terminal 中反斜杠,即 "" 代表转义符,或称逃脱符.("echo -e与pri ...

  10. C#委托(delegate)

    C#中委托(delegate)是一种安全地封装方法的类型,委托是面向对象的.类型安全的. 使用委托的步骤: 1.声明委托 public delegate void DelegateHandler(st ...