{"id":7237,"library":"flask-executor","title":"Flask-Executor","description":"Flask-Executor is an easy-to-use Flask wrapper for the concurrent.futures module that allows you to initialize and configure executors via common Flask application patterns. It provides a lightweight in-process task queue solution for Flask applications, making it suitable for managing background tasks without the overhead of separate worker processes.","status":"active","version":"1.0.0","language":"en","source_language":"en","source_url":"https://github.com/dchevell/flask-executor","tags":["Flask","concurrency","futures","ThreadPoolExecutor","ProcessPoolExecutor","background tasks","async"],"install":[{"cmd":"pip install flask-executor","lang":"bash","label":"Install with pip"}],"dependencies":[{"reason":"Core web framework integration.","package":"Flask","optional":false}],"imports":[{"symbol":"Executor","correct":"from flask_executor import Executor"}],"quickstart":{"code":"from flask import Flask, jsonify\nfrom flask_executor import Executor\nimport time\nimport os\n\napp = Flask(__name__)\n\n# Configure executor (optional, defaults to ThreadPoolExecutor)\n# app.config['EXECUTOR_TYPE'] = 'thread' # or 'process'\n# app.config['EXECUTOR_MAX_WORKERS'] = 5 # or None\n\nexecutor = Executor(app)\n\ndef long_running_task(duration):\n    time.sleep(duration)\n    return f\"Task finished after {duration} seconds\"\n\n@app.route('/start-task/<int:duration>')\ndef start_task(duration):\n    future = executor.submit(long_running_task, duration)\n    # You can store the future_key if you want to retrieve results later\n    # executor.submit_stored('my_task_key', long_running_task, duration)\n    return jsonify({\"message\": f\"Task submitted, will take {duration} seconds\"}), 202\n\n# Example for retrieving stored future (requires submit_stored instead of submit)\n# @app.route('/get-task-result')\n# def get_task_result():\n#     if not executor.futures.done('my_task_key'):\n#         return jsonify({'status': executor.futures._state('my_task_key')}), 202\n#     future = executor.futures.pop('my_task_key')\n#     return jsonify({'status': 'done', 'result': future.result()})\n\nif __name__ == '__main__':\n    # In a real application, consider using a production-ready WSGI server\n    app.run(debug=True)","lang":"python","description":"This quickstart demonstrates how to initialize `Flask-Executor` with your Flask application and submit a simple background task using `executor.submit()`. Tasks are executed in a thread or process pool, and the route immediately returns a 202 Accepted response."},"warnings":[{"fix":"Pass all necessary data (like configuration values or request parameters) explicitly as arguments to tasks running in a `ProcessPoolExecutor`. Avoid relying on automatic context propagation.","message":"When using `ProcessPoolExecutor`, Flask application and request contexts cannot be automatically propagated to worker processes due to limitations in Python's default object serialization and lack of shared memory. This means tasks in a `ProcessPoolExecutor` cannot directly access `flask.current_app`, `flask.request`, or `flask.g`.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Be aware of context immutability. If bidirectional communication or shared state is needed, implement explicit mechanisms (e.g., database updates, message queues, shared memory with proper locking) instead of relying on context modification.","message":"Callables submitted to a `ThreadPoolExecutor` are wrapped with a *copy* of the current application and request contexts. Changes made to these copies within the task will not be reflected in the original view, and changes in the original contexts after the task is submitted will not be available to the task.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For robust database operations in background tasks, consider either manually pushing a new application/request context for each task (if thread-safe), defining a custom thread-aware `scopefunc` for `Flask-SQLAlchemy`'s session, or re-establishing database sessions within each task. The library owner suggests basic usage works, but complex failure scenarios may require explicit handling.","message":"Integrating `Flask-Executor` with `Flask-SQLAlchemy` (especially in `ThreadPoolExecutor`) can lead to `StatementError: Can't reconnect until invalid transaction is rolled back` if a database transaction fails. This occurs because SQLAlchemy sessions bound to the application context might not be properly cleaned up or recycled after failures.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"For `ProcessPoolExecutor`, pass all necessary data explicitly to the task function as arguments. For `ThreadPoolExecutor`, ensure `Flask-Executor` is initialized correctly with the Flask app, as it is designed to copy the context. However, be mindful of the immutability of copied contexts and their limited lifespan.","cause":"Attempting to access Flask global proxies (e.g., `current_app`, `g`, `request`) within a `ProcessPoolExecutor` task or after the original request context has ended in a `ThreadPoolExecutor` task without proper context handling. `ProcessPoolExecutor` tasks do not receive copied contexts.","error":"RuntimeError: Working outside of application context."},{"fix":"Explicitly pass any required configuration values as arguments to your task functions. Alternatively, if using named executors, configuration can be set via environment variables prefixed with the executor's name (e.g., `CUSTOM_EXECUTOR_TYPE`).","cause":"Tasks run in a `ProcessPoolExecutor` are executed in entirely separate Python processes. The Flask application context, including `app.config`, is not automatically serialized and transferred to these new processes.","error":"Tasks submitted to ProcessPoolExecutor don't seem to access app.config values."},{"fix":"Refactor your task to avoid passing unpickleable objects to a `ProcessPoolExecutor`. Instead, re-create necessary resources (like database sessions) within the task function itself, or switch to a `ThreadPoolExecutor` if the task is I/O-bound and doesn't require separate processes, as threads share memory and don't require pickling for context. Ensure `app.config['EXECUTOR_TYPE'] = 'thread'`.","cause":"You are trying to use a `ProcessPoolExecutor` for a task that involves objects that cannot be serialized (pickled) and passed between processes. This often happens with database connections, Flask application objects, or certain types of locks.","error":"TypeError: cannot pickle '_thread.RLock' object"}]}