mirakuru


Namemirakuru JSON
Version 3.0.0 PyPI version JSON
download
home_pageNone
SummaryProcess executor (not only) for tests.
upload_time2025-10-29 21:39:00
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords process executor tests orchestration
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            .. image:: https://raw.githubusercontent.com/dbfixtures/mirakuru/master/logo.png
    :height: 100px

mirakuru
========

Mirakuru is a process orchestration tool designed for functional and integration tests.

When your application or tests rely on external processes (like databases, APIs, or other services),
ensuring these processes are started and ready *before* your main code executes can be challenging.
**Mirakuru** solves this by orchestrating the startup of these processes and waiting until they
are fully operational (e.g., accepting connections, producing specific output) before allowing
your program or tests to continue.


.. image:: https://img.shields.io/pypi/v/mirakuru.svg
    :target: https://pypi.python.org/pypi/mirakuru/
    :alt: Latest PyPI version

.. image:: https://img.shields.io/pypi/wheel/mirakuru.svg
    :target: https://pypi.python.org/pypi/mirakuru/
    :alt: Wheel Status

.. image:: https://img.shields.io/pypi/pyversions/mirakuru.svg
    :target: https://pypi.python.org/pypi/mirakuru/
    :alt: Supported Python Versions

.. image:: https://img.shields.io/pypi/l/mirakuru.svg
    :target: https://pypi.python.org/pypi/mirakuru/
    :alt: License

Installation
------------

Install mirakuru using pip:

.. code-block:: bash

    pip install mirakuru

Quick Start
-----------

Here's a simple example showing how mirakuru ensures a Redis server is ready before your code runs:

.. code-block:: python

    from mirakuru import TCPExecutor

    # Start Redis server and wait until it accepts connections on port 6379
    redis_executor = TCPExecutor('redis-server', host='localhost', port=6379)
    redis_executor.start()

    # Redis is now running and ready to accept connections
    # ... your code that uses Redis here ...

    # Clean up - stop the Redis server
    redis_executor.stop()

The key benefit: ``start()`` blocks until Redis is actually ready, so you never try to connect too early.



Usage
-----

In projects that rely on multiple processes, there might be a need to guard code
with tests that verify interprocess communication. You need to set up all the
required databases, auxiliary and application services to verify their cooperation.
Synchronizing (or orchestrating) test procedures with tested processes can be challenging.

If so, then **mirakuru** is what you need.

``Mirakuru`` starts your process and waits for a clear indication that it's running.
The library provides seven executors to fit different cases:

.. list-table::
   :header-rows: 1
   :widths: 25 75

   * - Executor
     - Use When
   * - **SimpleExecutor**
     - You just need to start/stop a process without waiting for readiness. Base class for all other executors.
   * - **Executor**
     - Base class for executors that verify process startup.
   * - **OutputExecutor**
     - Your process prints a specific message when ready (e.g., "Server started on port 8080")
   * - **TCPExecutor**
     - Your process opens a TCP port when ready (e.g., Redis, PostgreSQL, Memcached)
   * - **UnixSocketExecutor**
     - Your process opens a Unix socket when ready (e.g., Docker daemon, some databases)
   * - **HTTPExecutor**
     - Your process serves HTTP requests when ready (e.g., web servers, REST APIs)
   * - **PidExecutor**
     - Your process creates a .pid file when ready (e.g., traditional Unix daemons)

SimpleExecutor
++++++++++++++

The simplest executor implementation.
It simply starts the process passed to constructor, and reports it as running.

.. code-block:: python

    from mirakuru import SimpleExecutor

    process = SimpleExecutor('my_special_process')
    process.start()

    # Here you can do your stuff, e.g. communicate with the started process

    process.stop()

OutputExecutor
++++++++++++++

OutputExecutor starts a process and monitors its output for a specific text marker
(banner). The process is not reported as started until this marker appears in the output.

.. code-block:: python

    from mirakuru import OutputExecutor

    process = OutputExecutor('my_special_process', banner='processed!')
    process.start()

    # Here you can do your stuff, e.g. communicate with the started process

    process.stop()

What happens during start here, is that the executor constantly checks output
produced by started process, and looks for the banner part occurring within the
output.
Once the output is identified, as in example `processed!` is found in output.
It is considered as started, and executor releases your script from wait to work.


TCPExecutor
+++++++++++

TCPExecutor should be used to start processes that communicate over TCP connections.
This executor tries to connect to the process on the specified host and port to check
if it started accepting connections. Once it successfully connects, the process is
reported as started and control returns to your code.

.. code-block:: python

    from mirakuru import TCPExecutor

    process = TCPExecutor('my_special_process', host='localhost', port=1234)
    process.start()

    # Here you can do your stuff, e.g. communicate with the started process

    process.stop()

HTTPExecutor
++++++++++++

HTTPExecutor is designed for starting web applications and HTTP services.
In addition to the command, you need to pass a URL that will be used to check
if the service is ready. By default, it makes a HEAD request to this URL.
Once the request succeeds, the executor reports the process as started and
control returns to your code.

.. code-block:: python

    from mirakuru import HTTPExecutor

    process = HTTPExecutor('my_special_process', url='http://localhost:6543/status')
    process.start()

    # Here you can do your stuff, e.g. communicate with the started process

    process.stop()

This executor, however, apart from HEAD request, also inherits TCPExecutor,
so it'll try to connect to process over TCP first, to determine,
if it can try to make a HEAD request already.

By default HTTPExecutor waits until its subprocess responds with 2XX HTTP status code.
If you consider other codes as valid you need to specify them in 'status' argument.

.. code-block:: python

    from mirakuru import HTTPExecutor

    process = HTTPExecutor('my_special_process', url='http://localhost:6543/status', status='(200|404)')
    process.start()

The "status" argument can be a single code integer like 200, 404, 500 or a regular expression string -
'^(2|4)00$', '2\d\d', '\d{3}', etc.

There's also a possibility to change the request method used to perform request to the server.
By default it's HEAD, but GET, POST or other are also possible.

.. code-block:: python

    from mirakuru import HTTPExecutor

    process = HTTPExecutor('my_special_process', url='http://localhost:6543/status', status='(200|404)', method='GET')
    process.start()


PidExecutor
+++++++++++

Is an executor that starts the given
process, and then waits for a given file to be found before it gives back control.
An example use for this class is writing integration tests for processes that
notify their running by creating a .pid file.

.. code-block:: python

    from mirakuru import PidExecutor

    process = PidExecutor('my_special_process', filename='/var/msp/my_special_process.pid')
    process.start()

    # Here you can do your stuff, e.g. communicate with the started process

    process.stop()


.. code-block:: python

    from mirakuru import HTTPExecutor
    from http.client import HTTPConnection, OK


    def test_it_works():
        # The ``./http_server`` here launches some HTTP server on the 6543 port,
        # but naturally it is not immediate and takes a non-deterministic time:
        executor = HTTPExecutor("./http_server", url="http://127.0.0.1:6543/")

        # Start the server and wait for it to run (blocking):
        executor.start()
        # Here the server should be running!
        conn = HTTPConnection("127.0.0.1", 6543)
        conn.request("GET", "/")
        assert conn.getresponse().status is OK
        executor.stop()


A command by which executor spawns a process can be defined by either string or list.

.. code-block:: python

    # command as string
    TCPExecutor('python -m smtpd -n -c DebuggingServer localhost:1025', host='localhost', port=1025)
    # command as list
    TCPExecutor(
        ['python', '-m', 'smtpd', '-n', '-c', 'DebuggingServer', 'localhost:1025'],
        host='localhost', port=1025
    )

Use as a Context manager
------------------------

Starting
++++++++

Mirakuru executors can also work as a context managers.

.. code-block:: python

    from mirakuru import HTTPExecutor

    with HTTPExecutor('my_special_process', url='http://localhost:6543/status') as process:

        # Here you can do your stuff, e.g. communicate with the started process
        assert process.running() is True

    assert process.running() is False

Defined process starts upon entering context, and exit upon exiting it.

Stopping
++++++++

Mirakuru also allows to stop process for given context.
To do this, simply use built-in stopped context manager.

.. code-block:: python

    from mirakuru import HTTPExecutor

    process = HTTPExecutor('my_special_process', url='http://localhost:6543/status').start()

    # Here you can do your stuff, e.g. communicate with the started process

    with process.stopped():

        # Here you will not be able to communicate with the process as it is killed here
        assert process.running() is False

    assert process.running() is True

Defined process stops upon entering context, and starts upon exiting it.


Methods chaining
++++++++++++++++

Mirakuru encourages methods chaining so you can inline some operations, e.g.:

.. code-block:: python

    from mirakuru import SimpleExecutor

    command_stdout = SimpleExecutor('my_special_process').start().stop().output

Contributing and reporting bugs
-------------------------------

Source code is available at: `dbfixtures/mirakuru <https://github.com/dbfixtures/mirakuru>`_.
Issue tracker is located at `GitHub Issues <https://github.com/dbfixtures/mirakuru/issues>`_.
Projects `PyPI page <https://pypi.python.org/pypi/mirakuru>`_.

Windows support
---------------

Frankly, there's none, Python's support differs a bit in required places
and the team has no experience in developing for Windows.
However we'd welcome contributions that will allow the windows support.

See:

* `#392 <https://github.com/dbfixtures/mirakuru/issues/392>`_
* `#336 <https://github.com/dbfixtures/mirakuru/issues/336>`_

Also, with the introduction of `WSL <https://docs.microsoft.com/en-us/windows/wsl/install-win10>`_
the need for raw Windows support might not be that urgent... If you've got any thoughts or are willing to contribute,
please start with the issues listed above.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "mirakuru",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "process, executor, tests, orchestration",
    "author": null,
    "author_email": "Grzegorz \u015aliwi\u0144ski <fizyk+pypi@fizyk.dev>",
    "download_url": "https://files.pythonhosted.org/packages/6a/90/fa19eb0678c11e778e464659bc56b3c208b94503a8e8ce6c1646655b18b8/mirakuru-3.0.0.tar.gz",
    "platform": null,
    "description": ".. image:: https://raw.githubusercontent.com/dbfixtures/mirakuru/master/logo.png\n    :height: 100px\n\nmirakuru\n========\n\nMirakuru is a process orchestration tool designed for functional and integration tests.\n\nWhen your application or tests rely on external processes (like databases, APIs, or other services),\nensuring these processes are started and ready *before* your main code executes can be challenging.\n**Mirakuru** solves this by orchestrating the startup of these processes and waiting until they\nare fully operational (e.g., accepting connections, producing specific output) before allowing\nyour program or tests to continue.\n\n\n.. image:: https://img.shields.io/pypi/v/mirakuru.svg\n    :target: https://pypi.python.org/pypi/mirakuru/\n    :alt: Latest PyPI version\n\n.. image:: https://img.shields.io/pypi/wheel/mirakuru.svg\n    :target: https://pypi.python.org/pypi/mirakuru/\n    :alt: Wheel Status\n\n.. image:: https://img.shields.io/pypi/pyversions/mirakuru.svg\n    :target: https://pypi.python.org/pypi/mirakuru/\n    :alt: Supported Python Versions\n\n.. image:: https://img.shields.io/pypi/l/mirakuru.svg\n    :target: https://pypi.python.org/pypi/mirakuru/\n    :alt: License\n\nInstallation\n------------\n\nInstall mirakuru using pip:\n\n.. code-block:: bash\n\n    pip install mirakuru\n\nQuick Start\n-----------\n\nHere's a simple example showing how mirakuru ensures a Redis server is ready before your code runs:\n\n.. code-block:: python\n\n    from mirakuru import TCPExecutor\n\n    # Start Redis server and wait until it accepts connections on port 6379\n    redis_executor = TCPExecutor('redis-server', host='localhost', port=6379)\n    redis_executor.start()\n\n    # Redis is now running and ready to accept connections\n    # ... your code that uses Redis here ...\n\n    # Clean up - stop the Redis server\n    redis_executor.stop()\n\nThe key benefit: ``start()`` blocks until Redis is actually ready, so you never try to connect too early.\n\n\n\nUsage\n-----\n\nIn projects that rely on multiple processes, there might be a need to guard code\nwith tests that verify interprocess communication. You need to set up all the\nrequired databases, auxiliary and application services to verify their cooperation.\nSynchronizing (or orchestrating) test procedures with tested processes can be challenging.\n\nIf so, then **mirakuru** is what you need.\n\n``Mirakuru`` starts your process and waits for a clear indication that it's running.\nThe library provides seven executors to fit different cases:\n\n.. list-table::\n   :header-rows: 1\n   :widths: 25 75\n\n   * - Executor\n     - Use When\n   * - **SimpleExecutor**\n     - You just need to start/stop a process without waiting for readiness. Base class for all other executors.\n   * - **Executor**\n     - Base class for executors that verify process startup.\n   * - **OutputExecutor**\n     - Your process prints a specific message when ready (e.g., \"Server started on port 8080\")\n   * - **TCPExecutor**\n     - Your process opens a TCP port when ready (e.g., Redis, PostgreSQL, Memcached)\n   * - **UnixSocketExecutor**\n     - Your process opens a Unix socket when ready (e.g., Docker daemon, some databases)\n   * - **HTTPExecutor**\n     - Your process serves HTTP requests when ready (e.g., web servers, REST APIs)\n   * - **PidExecutor**\n     - Your process creates a .pid file when ready (e.g., traditional Unix daemons)\n\nSimpleExecutor\n++++++++++++++\n\nThe simplest executor implementation.\nIt simply starts the process passed to constructor, and reports it as running.\n\n.. code-block:: python\n\n    from mirakuru import SimpleExecutor\n\n    process = SimpleExecutor('my_special_process')\n    process.start()\n\n    # Here you can do your stuff, e.g. communicate with the started process\n\n    process.stop()\n\nOutputExecutor\n++++++++++++++\n\nOutputExecutor starts a process and monitors its output for a specific text marker\n(banner). The process is not reported as started until this marker appears in the output.\n\n.. code-block:: python\n\n    from mirakuru import OutputExecutor\n\n    process = OutputExecutor('my_special_process', banner='processed!')\n    process.start()\n\n    # Here you can do your stuff, e.g. communicate with the started process\n\n    process.stop()\n\nWhat happens during start here, is that the executor constantly checks output\nproduced by started process, and looks for the banner part occurring within the\noutput.\nOnce the output is identified, as in example `processed!` is found in output.\nIt is considered as started, and executor releases your script from wait to work.\n\n\nTCPExecutor\n+++++++++++\n\nTCPExecutor should be used to start processes that communicate over TCP connections.\nThis executor tries to connect to the process on the specified host and port to check\nif it started accepting connections. Once it successfully connects, the process is\nreported as started and control returns to your code.\n\n.. code-block:: python\n\n    from mirakuru import TCPExecutor\n\n    process = TCPExecutor('my_special_process', host='localhost', port=1234)\n    process.start()\n\n    # Here you can do your stuff, e.g. communicate with the started process\n\n    process.stop()\n\nHTTPExecutor\n++++++++++++\n\nHTTPExecutor is designed for starting web applications and HTTP services.\nIn addition to the command, you need to pass a URL that will be used to check\nif the service is ready. By default, it makes a HEAD request to this URL.\nOnce the request succeeds, the executor reports the process as started and\ncontrol returns to your code.\n\n.. code-block:: python\n\n    from mirakuru import HTTPExecutor\n\n    process = HTTPExecutor('my_special_process', url='http://localhost:6543/status')\n    process.start()\n\n    # Here you can do your stuff, e.g. communicate with the started process\n\n    process.stop()\n\nThis executor, however, apart from HEAD request, also inherits TCPExecutor,\nso it'll try to connect to process over TCP first, to determine,\nif it can try to make a HEAD request already.\n\nBy default HTTPExecutor waits until its subprocess responds with 2XX HTTP status code.\nIf you consider other codes as valid you need to specify them in 'status' argument.\n\n.. code-block:: python\n\n    from mirakuru import HTTPExecutor\n\n    process = HTTPExecutor('my_special_process', url='http://localhost:6543/status', status='(200|404)')\n    process.start()\n\nThe \"status\" argument can be a single code integer like 200, 404, 500 or a regular expression string -\n'^(2|4)00$', '2\\d\\d', '\\d{3}', etc.\n\nThere's also a possibility to change the request method used to perform request to the server.\nBy default it's HEAD, but GET, POST or other are also possible.\n\n.. code-block:: python\n\n    from mirakuru import HTTPExecutor\n\n    process = HTTPExecutor('my_special_process', url='http://localhost:6543/status', status='(200|404)', method='GET')\n    process.start()\n\n\nPidExecutor\n+++++++++++\n\nIs an executor that starts the given\nprocess, and then waits for a given file to be found before it gives back control.\nAn example use for this class is writing integration tests for processes that\nnotify their running by creating a .pid file.\n\n.. code-block:: python\n\n    from mirakuru import PidExecutor\n\n    process = PidExecutor('my_special_process', filename='/var/msp/my_special_process.pid')\n    process.start()\n\n    # Here you can do your stuff, e.g. communicate with the started process\n\n    process.stop()\n\n\n.. code-block:: python\n\n    from mirakuru import HTTPExecutor\n    from http.client import HTTPConnection, OK\n\n\n    def test_it_works():\n        # The ``./http_server`` here launches some HTTP server on the 6543 port,\n        # but naturally it is not immediate and takes a non-deterministic time:\n        executor = HTTPExecutor(\"./http_server\", url=\"http://127.0.0.1:6543/\")\n\n        # Start the server and wait for it to run (blocking):\n        executor.start()\n        # Here the server should be running!\n        conn = HTTPConnection(\"127.0.0.1\", 6543)\n        conn.request(\"GET\", \"/\")\n        assert conn.getresponse().status is OK\n        executor.stop()\n\n\nA command by which executor spawns a process can be defined by either string or list.\n\n.. code-block:: python\n\n    # command as string\n    TCPExecutor('python -m smtpd -n -c DebuggingServer localhost:1025', host='localhost', port=1025)\n    # command as list\n    TCPExecutor(\n        ['python', '-m', 'smtpd', '-n', '-c', 'DebuggingServer', 'localhost:1025'],\n        host='localhost', port=1025\n    )\n\nUse as a Context manager\n------------------------\n\nStarting\n++++++++\n\nMirakuru executors can also work as a context managers.\n\n.. code-block:: python\n\n    from mirakuru import HTTPExecutor\n\n    with HTTPExecutor('my_special_process', url='http://localhost:6543/status') as process:\n\n        # Here you can do your stuff, e.g. communicate with the started process\n        assert process.running() is True\n\n    assert process.running() is False\n\nDefined process starts upon entering context, and exit upon exiting it.\n\nStopping\n++++++++\n\nMirakuru also allows to stop process for given context.\nTo do this, simply use built-in stopped context manager.\n\n.. code-block:: python\n\n    from mirakuru import HTTPExecutor\n\n    process = HTTPExecutor('my_special_process', url='http://localhost:6543/status').start()\n\n    # Here you can do your stuff, e.g. communicate with the started process\n\n    with process.stopped():\n\n        # Here you will not be able to communicate with the process as it is killed here\n        assert process.running() is False\n\n    assert process.running() is True\n\nDefined process stops upon entering context, and starts upon exiting it.\n\n\nMethods chaining\n++++++++++++++++\n\nMirakuru encourages methods chaining so you can inline some operations, e.g.:\n\n.. code-block:: python\n\n    from mirakuru import SimpleExecutor\n\n    command_stdout = SimpleExecutor('my_special_process').start().stop().output\n\nContributing and reporting bugs\n-------------------------------\n\nSource code is available at: `dbfixtures/mirakuru <https://github.com/dbfixtures/mirakuru>`_.\nIssue tracker is located at `GitHub Issues <https://github.com/dbfixtures/mirakuru/issues>`_.\nProjects `PyPI page <https://pypi.python.org/pypi/mirakuru>`_.\n\nWindows support\n---------------\n\nFrankly, there's none, Python's support differs a bit in required places\nand the team has no experience in developing for Windows.\nHowever we'd welcome contributions that will allow the windows support.\n\nSee:\n\n* `#392 <https://github.com/dbfixtures/mirakuru/issues/392>`_\n* `#336 <https://github.com/dbfixtures/mirakuru/issues/336>`_\n\nAlso, with the introduction of `WSL <https://docs.microsoft.com/en-us/windows/wsl/install-win10>`_\nthe need for raw Windows support might not be that urgent... If you've got any thoughts or are willing to contribute,\nplease start with the issues listed above.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Process executor (not only) for tests.",
    "version": "3.0.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/dbfixtures/mirakuru/issues",
        "Changelog": "https://github.com/dbfixtures/mirakuru/blob/v3.0.0/CHANGES.rst",
        "Source": "https://github.com/dbfixtures/mirakuru"
    },
    "split_keywords": [
        "process",
        " executor",
        " tests",
        " orchestration"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "2cbcfdcd9ba2703e031b1613a541fce9760a422a252042f2ba3d20b265bd5b47",
                "md5": "311f41119ded73a8239132cc8f80af07",
                "sha256": "99cd12532908e7e5e581034317843113884676a295b47747e685e9fa35235228"
            },
            "downloads": -1,
            "filename": "mirakuru-3.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "311f41119ded73a8239132cc8f80af07",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 27414,
            "upload_time": "2025-10-29T21:38:59",
            "upload_time_iso_8601": "2025-10-29T21:38:59.952913Z",
            "url": "https://files.pythonhosted.org/packages/2c/bc/fdcd9ba2703e031b1613a541fce9760a422a252042f2ba3d20b265bd5b47/mirakuru-3.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "6a90fa19eb0678c11e778e464659bc56b3c208b94503a8e8ce6c1646655b18b8",
                "md5": "b973961fa27d841924e03205f863e4af",
                "sha256": "8621706f40bd12182a6d785005be64d72ed355390f1d0701ce8f745ee47a10a5"
            },
            "downloads": -1,
            "filename": "mirakuru-3.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "b973961fa27d841924e03205f863e4af",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 28919,
            "upload_time": "2025-10-29T21:39:00",
            "upload_time_iso_8601": "2025-10-29T21:39:00.891900Z",
            "url": "https://files.pythonhosted.org/packages/6a/90/fa19eb0678c11e778e464659bc56b3c208b94503a8e8ce6c1646655b18b8/mirakuru-3.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-29 21:39:00",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dbfixtures",
    "github_project": "mirakuru",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "mirakuru"
}
        
Elapsed time: 1.91946s