{"id":9966,"library":"mysql-mimic","title":"MySQL Mimic","description":"mysql-mimic is a pure Python implementation of the MySQL server protocol. It enables developers to create fake MySQL servers to intercept connections, log queries, serve custom data, or test database clients without requiring a real MySQL instance. The current version, 3.0.2, primarily features an asynchronous API and aims for robust protocol emulation. Releases generally follow major breaking changes or significant feature additions, with major versions appearing every 2-3 years.","status":"active","version":"3.0.2","language":"en","source_language":"en","source_url":"https://github.com/kelsin/mysql-mimic","tags":["mysql","server","protocol","database","mocking","testing","asyncio"],"install":[{"cmd":"pip install mysql-mimic","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"symbol":"MySQLServer","correct":"from mysql_mimic import MySQLServer"},{"note":"Moved to mysql_mimic.auth in v3.0.0","wrong":"from mysql_mimic import Authenticator","symbol":"Authenticator","correct":"from mysql_mimic.auth import Authenticator"},{"note":"Moved to mysql_mimic.ssl in v3.0.0","wrong":"from mysql_mimic import SSLContextProvider","symbol":"SSLContextProvider","correct":"from mysql_mimic.ssl import SSLContextProvider"}],"quickstart":{"code":"import asyncio\nimport logging\nimport os\nfrom mysql_mimic import MySQLServer\nfrom mysql_mimic.auth import Authenticator\n\nlogging.basicConfig(level=logging.INFO)\n\n# Define a test password via an environment variable for security best practices\n# In a real scenario, this would be a secure, complex password.\nMIMIC_PASSWORD = os.environ.get('MIMIC_TEST_PASSWORD', 'super-secret-password')\nMIMIC_HOST = os.environ.get('MIMIC_HOST', '127.0.0.1')\nMIMIC_PORT = int(os.environ.get('MIMIC_PORT', 3307)) # Use a non-standard port\n\nclass MyAuthenticator(Authenticator):\n    async def authenticate(self, username, password, database):\n        logging.info(f\"Auth attempt: user='{username}', db='{database}'\")\n        if password == MIMIC_PASSWORD:\n            logging.info(f\"Authentication successful for '{username}'\")\n            return True\n        logging.warning(f\"Authentication failed for '{username}' with provided password.\")\n        return False\n\nclass MyServer(MySQLServer):\n    async def on_query(self, query: str, database: str):\n        logging.info(f\"Query received on '{database}': '{query}'\")\n        # Return a list of tuples, mimicking a result set\n        if query.lower().startswith(\"select version()\"):\n            return [(\"3.0.2\",)] # Mimic current version of mysql-mimic\n        elif query.lower().startswith(\"select 'hello world'\"):\n            return [(\"hello world from mysql-mimic!\",)]\n        else:\n            return [(f\"You queried: '{query}'\",)]\n\n# This is the simplest way to run for a quickstart in an async context\n# Make sure to set MIMIC_TEST_PASSWORD in your environment before running, e.g.:\n# export MIMIC_TEST_PASSWORD=\"super-secret-password\"\n# python your_script.py\nserver = MyServer(authenticator=MyAuthenticator(), host=MIMIC_HOST, port=MIMIC_PORT)\nlogging.info(f\"Starting MySQL Mimic server on {MIMIC_HOST}:{MIMIC_PORT}\")\nserver.serve_forever_async()\n\n# To test from your terminal:\n# mysql -h 127.0.0.1 -P 3307 -u anyuser -p'super-secret-password' -e \"SELECT 'Hello World';\"","lang":"python","description":"This quickstart sets up a basic MySQL Mimic server that listens on port 3307 (or `MIMIC_PORT` env var). It uses a custom authenticator to allow any username with a pre-defined password (`MIMIC_TEST_PASSWORD` env var or 'super-secret-password'). The `on_query` method demonstrates how to respond to client queries with a list of tuples, mimicking a database result set. Remember to set the environment variable for the password before running."},"warnings":[{"fix":"Rewrite custom server and authenticator methods using `async def` and ensure the server is run in an `asyncio` event loop (e.g., `server.serve_forever_async()` or `asyncio.run(server.serve())`).","message":"Version 3.0.0 introduced a significant breaking change by moving to an entirely asynchronous API. All custom server methods like `on_query`, `on_connect`, `authenticate` (in `Authenticator`), and `serve()` itself are now `async def` and must be `await`ed or run within an `asyncio` event loop.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Update import statements: `from mysql_mimic.auth import Authenticator` and `from mysql_mimic.ssl import SSLContextProvider`.","message":"In v3.0.0, `Authenticator` and `SSLContextProvider` classes were moved from the top-level `mysql_mimic` module to `mysql_mimic.auth` and `mysql_mimic.ssl` respectively.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Ensure `on_query` always returns a `list` where each element is a `tuple` (e.g., `return [('column1', 'column2'), ('value1', 'value2')]`). For a single string result, use `return [(my_string_result,)]`.","message":"The `on_query` method (since v3.0.0) must return a `list` of `tuple`s, representing rows and columns. Returning a single string, tuple, or non-list will lead to a `TypeError` at runtime.","severity":"gotcha","affected_versions":">=3.0.0"},{"fix":"It is highly recommended to use an alternative port like 3307 for `mysql-mimic` by passing `port=3307` to the `MySQLServer` constructor, or configuring via environment variables.","message":"Running `mysql-mimic` on the default MySQL port (3306) will fail if another MySQL server or process is already using that port. This is a common issue during development.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-17T00:00:00.000Z","next_check":"2026-07-16T00:00:00.000Z","problems":[{"fix":"Ensure all custom server methods are defined using `async def`, e.g., `async def on_query(...)`.","cause":"Attempting to define `on_query` (or other server lifecycle methods like `on_connect`, `authenticate`) as a synchronous function after upgrading to `mysql-mimic` v3.0.0+.","error":"TypeError: object MySQLServer.on_query() is not async"},{"fix":"Update import statements to `from mysql_mimic.auth import Authenticator`.","cause":"Using the old import path for `Authenticator` (or `SSLContextProvider`) after upgrading to `mysql-mimic` v3.0.0+.","error":"AttributeError: module 'mysql_mimic' has no attribute 'Authenticator'"},{"fix":"Ensure `on_query` always returns a `list` where each element is a `tuple` representing a row. E.g., `return [(f\"Result: {query}\",)]`.","cause":"The `on_query` method is returning a single string instead of a list of tuples as expected by `mysql-mimic` v3.0.0+.","error":"TypeError: 'str' object is not iterable"},{"fix":"Change the `port` argument in the `MyServer` constructor to an unused port (e.g., `3307`), or ensure no other applications are using the desired port.","cause":"Another process (e.g., a real MySQL server or a previous run of mysql-mimic) is already listening on the configured port.","error":"OSError: [Errno 98] Address already in use"}]}