Hubbry Logo
Flask (web framework)Flask (web framework)Main
Open search
Flask (web framework)
Community hub
Flask (web framework)
logo
8 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Flask (web framework)
Flask (web framework)
from Wikipedia
Flask
DeveloperArmin Ronacher
Initial release1 April 2010; 15 years ago (2010-04-01)
Stable release
3.1.2[1] Edit this on Wikidata / 19 August 2025; 5 months ago (19 August 2025)
Written inPython
TypeWeb framework
LicenseBSD 3-clause license
Websitepalletsprojects.com/p/flask/
Repositorygithub.com/pallets/flask

Flask is a micro web framework written in Python. It is classified as a microframework because it does not require particular tools or libraries.[2] It has no database abstraction layer, form validation, or any other components where pre-existing third-party libraries provide common functions. However, Flask supports extensions that can add application features as if they were implemented in Flask itself. Extensions exist for object-relational mappers, form validation, upload handling, various open authentication technologies and several common framework related tools.[3]

Applications that use the Flask framework include Pinterest and LinkedIn.[4][5]

History

[edit]

Flask was created by Armin Ronacher of Pocoo, an international group of Python enthusiasts formed in 2004.[6] According to Ronacher, the idea was originally an April Fool's joke that was popular enough to make into a serious application.[7][8][9] The name is a play on the earlier Bottle framework.[7]

When Ronacher and Georg Brandl created a bulletin board system written in Python in 2004, the Pocoo projects Werkzeug and Jinja were developed.[10]

In April 2016, the Pocoo team was disbanded and development of Flask and related libraries passed to the newly formed Pallets project.[11][12]

Flask has become popular among Python enthusiasts. As of October 2020, it has the second-most number of stars on GitHub among Python web-development frameworks, only slightly behind Django,[13] and was voted the most popular web framework in the Python Developers Survey for years between and including 2018 and 2022.[14][15][16][17][18]

Components

[edit]

The microframework Flask is part of the Pallets Projects (formerly Pocoo), and based on several others of them, all under a BSD license.

Werkzeug

[edit]

Werkzeug (German for "tool") is a utility library for the Python programming language for Web Server Gateway Interface (WSGI) applications. Werkzeug can instantiate objects for request, response, and utility functions. It can be used as the basis for a custom software framework and supports Python 2.7 and 3.5 and later.[19][20]

Jinja

[edit]

Jinja, also by Ronacher, is a template engine for the Python programming language. Similar to the Django web framework, it handles templates in a sandbox.

MarkupSafe

[edit]

MarkupSafe is a string handling library for the Python programming language. The eponymous MarkupSafe type extends the Python string type and marks its contents as "safe"; combining MarkupSafe with regular strings automatically escapes the unmarked strings, while avoiding double escaping of already marked strings.

ItsDangerous

[edit]

ItsDangerous is a safe data serialization library for the Python programming language. It is used to store the session of a Flask application in a cookie without allowing users to tamper with the session contents.

Click

[edit]

Click is a Python package used by Flask to create command-line interfaces (CLI) by providing a simple and composable way to define commands, arguments, and options.

Features

[edit]
  • Development server and debugger
  • Integrated support for unit testing
  • RESTful request dispatching
  • Uses Jinja templating
  • Support for secure cookies (client side sessions)
  • 100% WSGI 1.0 compliant
  • Unicode-based
  • Complete documentation
  • Google App Engine compatibility
  • Extensions available to extend functionality

Example

[edit]

The following code shows a simple web application that displays "Hello World!" when visited:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello() -> str:
    return "Hello World"

if __name__ == "__main__":
    app.run()

Render Template with Flask

[edit]
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def message():
  message = "Hello World!"

  return render_template("index.html",message=message)

Jinja in HTML for the Render Template

[edit]
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ message }}</title>
</head>
<body>
    <p>{{ message }}</p>
</body>
</html>

See also

[edit]

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
Flask is a lightweight, open-source for Python designed to facilitate the development of web applications by providing essential tools for routing, request handling, and templating while remaining unopinionated and extensible. It operates on the (WSGI) standard and is built upon the Werkzeug toolkit for handling HTTP requests and the Jinja templating engine, allowing developers to create simple applications quickly or scale to more complex systems with minimal boilerplate. As a microframework, Flask intentionally avoids including built-in features like form validation, database abstraction, or administrative interfaces, instead encouraging the use of extensions to add functionality as needed. Flask was created by Austrian software developer Armin Ronacher in 2010 as part of the Pocoo projects, an international group focused on Python web technologies, initially emerging from an internal tool at his workplace before being released publicly on April 1. The framework's philosophy emphasizes simplicity, flexibility, and developer freedom, enabling and deployment of web services, RESTful APIs, and full-stack applications. By November 2025, Flask had reached version 3.1.2, supporting Python 3.9 and later, with a vast ecosystem of over 100 official and community extensions for tasks such as , ORM integration, and testing. Flask's popularity stems from its ease of use and performance, making it a preferred choice for startups and large enterprises alike; notable users include for backend services, for parts of its web infrastructure, for API development, and for . Its minimalistic design has influenced modern Python web development, promoting patterns like modularization for large applications and integration with tools such as SQLAlchemy for databases or Flask-RESTful for s. Overall, Flask balances brevity with power, serving as a foundational tool in Python's web ecosystem.

Introduction

Overview

Flask is an open-source, lightweight Python designed for developing web applications and APIs. It serves as a minimalistic tool that provides the essential functionality needed to build web servers, while allowing developers to choose and integrate additional components as required. Classified as a microframework, Flask stands out due to its minimal core dependencies, which include only Werkzeug for request handling and Jinja2 for templating, emphasizing extensibility through a rich ecosystem of extensions rather than comprehensive built-in tools. This approach enables developers to customize and scale applications without unnecessary overhead. Flask's primary use cases include , development of small to medium-sized web applications, creation of RESTful APIs, and building , making it suitable for projects that benefit from simplicity and flexibility. As of August 2025, the latest version is Flask 3.1.2, which supports Python 3.9 and newer, including asynchronous view functions for improved performance in concurrent scenarios. Released under the BSD-3-Clause license, Flask is actively maintained by the Pallets Projects community, ensuring ongoing updates and compatibility with modern Python features.

Design Philosophy

Flask's design philosophy centers on a "micro" framework approach, emphasizing a minimal core that provides essential without imposing unnecessary dependencies or features. This means Flask excludes built-in components such as a database ORM, form validation, or admin interface by default, allowing developers to integrate these only as needed through optional extensions. The term "micro" reflects a of and extensibility, where the framework remains lightweight—depending solely on Werkzeug for WSGI handling and Jinja2 for templating—enabling quick starts for small applications while scaling to larger ones without bloat. This unopinionated stance prioritizes developer choice over a prescribed structure, fostering flexibility in tool selection, such as using SQLAlchemy for database interactions or WTForms for form handling. At the heart of Flask's architecture is the explicit application object, a central, configurable instance around which applications are constructed. This object encapsulates the application's configuration, , and behavior, promoting immutability in core elements while allowing pluggable modifications through blueprints and extensions. Unlike full-stack frameworks like Django, which adopt a "batteries-included" model with integrated ORM, authentication, and admin tools to enforce a specific development , Flask's lightweight avoids such opinions, enabling and customization for diverse use cases. This contrast highlights Flask's focus on freedom and minimalism, making it ideal for developers seeking control over their stack without overhead. Following the release of Flask 2.0 in 2021, the philosophy evolved to incorporate asynchronous support while preserving the framework's core minimalism. Async and await capabilities were added for view functions, executed via separate threads rather than a full overhaul, ensuring compatibility with WSGI while enabling efficient handling of I/O-bound tasks. This update maintained the emphasis on extensibility, allowing seamless integration with ASGI servers if desired, without compromising the simple, unintrusive that defines Flask.

History

Origins and Early Development

Flask was developed by Armin Ronacher, an Austrian software developer, as part of the Pocoo projects ecosystem. In 2010, Ronacher initially conceived the framework as an April Fool's joke named "Denied," which bundled existing tools into a minimal setup to satirize the rising popularity of microframeworks in Python. Despite its humorous origins, the concept proved compelling, leading Ronacher to transform it into a serious project under the name Flask, with the first public release occurring on April 1, 2010. The framework emerged from Ronacher's ongoing work within the Pocoo team, which had already produced foundational libraries like Werkzeug—a WSGI utility library initiated in 2007—and Jinja2, a templating engine first released in 2008. These components addressed the need for lightweight, flexible tools in Python web development, contrasting with heavier frameworks like Django. Flask's early motivations centered on creating a simple, unopinionated WSGI-based microframework that avoided unnecessary overhead, allowing developers to build lightweight applications while leveraging extensions for added functionality. This approach was influenced by community discussions around simplifying without sacrificing power, building directly on Werkzeug for request handling and Jinja2 for templating. Flask quickly gained traction through its availability on PyPI and an active repository, attracting developers seeking a minimal yet extensible alternative to full-stack frameworks. Early adoption was driven by its ease of use and integration with the existing Pocoo , leading to widespread experimentation in small-scale projects and prototypes. By 2018, Flask reached version 1.0, marking a stabilized API and solidifying its position as a mature microframework with a robust .

Major Releases and Evolution

Flask achieved a significant milestone with the release of version 1.0 on April 26, 2018, which established long-term stability after nearly eight years of iterative development originating from the Pocoo projects. This version dropped support for Python 2.6 and 3.3, focusing on compatibility with Python 2.7 and 3.4+, while updating minimum dependencies like Werkzeug, Jinja2, and MarkupSafe to their latest stable releases for enhanced security and performance. Version 2.0, released on May 11, 2021, introduced native support for async/await in view functions, enabling better handling of concurrent operations within the WSGI environment, and formally dropped support for Python 2 entirely along with Python 3.5, requiring Python 3.6 or later. This release also aligned with updates across the Pallets , including Werkzeug 2.0 and Jinja 3.0, emphasizing modern Python practices while maintaining core extensibility. In September 2023, Flask 3.0 was released on , removing previously deprecated code such as the internal DispatchingApp and legacy WSGI components, while introducing improved async view support for more seamless integration with asynchronous patterns and configurable providers via the new app. interface for custom . This version enhanced handling by raising explicit errors for endpoint conflicts and deprecating the version attribute in favor of feature detection, promoting cleaner, more maintainable applications. Additionally, it shifted toward async-first recommendations, encouraging developers to adopt ASGI-compatible extensions for high-concurrency scenarios. The 3.1 series began with version 3.1.0 on November 13, 2024, dropping Python 3.8 support and updating dependencies to their latest feature releases, while adding new customization points on the Flask app object for behaviors like and response formatting. It also implemented improved preservation during and testing, allowing better without disrupting application state, with subsequent patches like 3.1.2 in August 2025 addressing minor fixes and security updates. Since April 2016, when the original Pocoo team disbanded, Flask has been governed under the Pallets Projects organization, with lead maintainer David Lord overseeing contributions from a growing community of developers. This structure has ensured active maintenance, including regular security patches and minor releases—such as the four updates in alone—to address vulnerabilities and compatibility issues. Impact of these evolutions includes Flask's adaptation to modern Python features, such as comprehensive type hints introduced progressively from and expanded in later versions for better IDE support and static analysis, all while prioritizing backward compatibility through deprecation cycles rather than abrupt breaks.

Core Components

Werkzeug

Werkzeug serves as the foundational utility library for Flask, handling the core request and response cycle by supplying WSGI-compliant request and response objects that abstract the underlying HTTP protocol details. It includes a routing parser for mapping incoming URLs to application logic and supports for extending functionality, such as adding custom processing layers between the server and the application. These components ensure Flask applications adhere to WSGI standards, enabling seamless interoperability with various web servers. Among its key functionalities, Werkzeug's Router enables flexible routing by converting path patterns into callable rules that match requests and extract parameters, supporting features like variable rules and host-based dispatching without imposing a full framework structure. It manages the WSGI environment through the environ dictionary, which encapsulates server and client data such as headers, query strings, and input streams, while providing built-in to raise appropriate HTTP errors like 404 Not Found or 500 Internal Server Error during processing. This focus on low-level utilities allows developers to build custom dispatching logic atop a stable foundation. In Flask, the application object (Flask instance) wraps Werkzeug's to integrate and request handling directly into the framework's execution flow, where incoming requests are parsed and dispatched to view functions via the underlying url_map. Additionally, Werkzeug's serving module powers Flask's local development server, offering a simple way to run applications with features like auto-reloading for iterative development. Regarding its development, Werkzeug was created by Armin Ronacher in 2007 as a collection of WSGI utilities and has evolved to version 3.1.3 as of November 2024, aligning with Flask 3.x by enhancing support for asynchronous operations through improved handling in request processing. A distinctive aspect of Werkzeug is its strict compliance with WSGI specifications outlined in PEP 333 and PEP 3333, ensuring portability across Python web servers without introducing higher-level abstractions like ORM or templating, which keeps it lightweight and focused on protocol-level tools. This design philosophy emphasizes reusability for any WSGI-compatible project, including Flask's routing system where Werkzeug's and Rule classes underpin URL management.

Jinja2

Jinja2 serves as the default templating engine in Flask, enabling the dynamic generation of and other content by integrating variables, control structures, and template directly into view functions. Through Flask's render_template() function, developers can load and render Jinja2 templates by passing context variables, which Jinja2 processes to produce the final output, such as web pages with embedded data from the application. This integration allows Flask applications to separate presentation logic from , facilitating maintainable codebases for . Key features of Jinja2 enhance its utility within Flask, including sandboxed execution to safely render untrusted templates by restricting access to potentially harmful operations like attribute access or method calls. Auto-escaping prevents (XSS) attacks by automatically escaping characters in output variables unless explicitly marked safe, a default behavior configurable in Flask via file extension detection. Jinja2 also supports filters for data transformation—such as |upper for capitalization or |length for counting—and macros for reusable template snippets, akin to functions in Python. Additionally, extensions enable (i18n) through integration with tools like Babel for extracting messages from templates. The syntax of Jinja2 employs Python-like constructs for clarity and familiarity. Variables are output using double curly braces, as in {{ variable }}, while control structures use tags delimited by {% %}; for example, loops iterate with {% for item in list %}...{% endfor %}, and conditional blocks use {% if condition %}...{% endif %}. Template inheritance promotes modularity through blocks, defined with {% block name %}...{% endblock %}, which child templates can override or extend using {% extends "base.html" %}. Jinja2 supports custom loaders to fetch templates from diverse sources, such as file systems via FileSystemLoader or databases through DictLoader or user-defined classes. In Flask, Jinja2 integration is configurable, with the default template directory set to the TEMPLATES_FOLDER in the application configuration, typically 'templates', allowing customization for project-specific layouts. The MarkupSafe library, bundled with Jinja2, ensures secure string handling by wrapping potentially unsafe content in objects that auto-escape when rendered, preventing injection vulnerabilities. Flask automatically injects global objects like request, session, and url_for() into the Jinja2 environment, streamlining template access to application context without manual passing. Jinja2's evolution includes version 3.1.0, released on March 24, 2022, which enhanced async support originally introduced in 3.0, enabling asynchronous template rendering with enable_async=True in the environment configuration to align with modern asynchronous Flask features like async views. Subsequent updates, such as 3.1.5 in December 2024, refined async iterator handling and added support for synchronous rendering via asyncio.run in async contexts. As of November 2025, Jinja2 has reached version 3.1.6.

Supporting Libraries

Flask relies on several supporting libraries developed under the Pallets Projects umbrella to provide essential utilities for security, data handling, and command-line interactions. These libraries are tightly integrated into Flask's core functionality, enhancing its lightweight design without introducing unnecessary dependencies. MarkupSafe is a library that implements a text object for safely escaping characters in and XML contexts, preventing (XSS) attacks by replacing special characters with their escaped equivalents. It is primarily used internally by Jinja2 for rendering templates, ensuring that untrusted user input is automatically escaped unless explicitly marked as safe. This integration allows developers to build secure web applications without manual escaping in most cases. ItsDangerous provides mechanisms for securely signing serialized data, such as sessions, tokens, and timestamps, to verify integrity and prevent tampering. In Flask, it protects session by generating signed payloads that include a cryptographic signature based on a secret key. For instance, the TimestampSigner class can sign data with an expiration timestamp, allowing validation that the data has not expired or been altered; a conceptual example involves initializing a signer with a secret key and using it to sign and unsign payloads like signer = TimestampSigner('secret-key'); signed = signer.sign('data', max_age=3600). The Flask-Session extension leverages ItsDangerous to manage secure, signed for persistent sessions across requests. Click serves as a composable framework for building command-line interfaces (CLIs), enabling Flask to offer built-in commands like flask run for starting the development server. It allows developers to define custom management scripts, such as database initialization or testing routines, using decorators to create intuitive CLI entry points. This integration simplifies application deployment and maintenance tasks. All these libraries—MarkupSafe, ItsDangerous, and Click—are maintained by the Pallets Projects team, ensuring compatibility with Flask's evolution. As of November 2025, the latest versions are MarkupSafe 3.0.3, ItsDangerous 2.2.0, and Click 8.3.1. In 2025, updates to Flask 3.1 included enhancements for better asynchronous support in related Pallets libraries, such as support for signing key rotation in Flask using ItsDangerous, aligning with modern Python async patterns.

Key Features

Routing and URL Management

Flask employs a flexible routing system powered by Werkzeug, which maps incoming URLs to Python functions known as view functions through the @app.route() decorator. This decorator registers the associated function as an endpoint handler for the specified URL pattern, allowing developers to define how the application responds to HTTP requests. For instance, a basic route for the application's home page can be defined as follows:

python

from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World!'

from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World!'

This setup directs requests to the root (/) to the hello function, which returns a simple response. To handle dynamic content, Flask supports variable rules within route patterns using angle brackets, such as <variable_name>, which capture segments of the path and pass them as arguments to the view function. Converters can be applied to these variables for type validation and conversion; common built-in converters include <int:user_id> for integers, <float:price> for floats, <string:username> for (default), and <path:filename> for path segments that may include slashes. An example route for a might look like:

python

@app.route('/user/<username>') def profile(username): return f'User: {username}' @app.route('/post/<int:post_id>') def show_post(post_id): return f'Post #{post_id}'

@app.route('/user/<username>') def profile(username): return f'User: {username}' @app.route('/post/<int:post_id>') def show_post(post_id): return f'Post #{post_id}'

Here, a request to /user/john passes 'john' to the profile function, while /post/42 ensures post_id is an 42, raising a 404 error for non-integer values. Routes can specify supported HTTP methods beyond the default GET using the methods parameter in the decorator, enabling handling of POST, PUT, DELETE, and others for RESTful operations. For example:

python

@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': # Handle form submission return 'Logged in' return 'Login form'

@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': # Handle form submission return 'Logged in' return 'Login form'

Additionally, the [subdomain](/page/Subdomain) parameter allows routes to match specific subdomains, useful for multi-tenant applications; for instance, @app.route('/', subdomain='api') would handle requests to api.example.com. The strict_slashes parameter controls trailing slash behavior, defaulting to automatic redirection for consistency. For error management within routing, Flask provides the @app.errorhandler() decorator to register custom handlers for HTTP errors or exceptions, such as 404 Not Found. This allows graceful responses instead of default error pages:

python

@app.errorhandler(404) def not_found(error): return 'Page not found', 404

@app.errorhandler(404) def not_found(error): return 'Page not found', 404

Such handlers can be defined for specific status codes or exception classes, ensuring robust handling across the application. Since , routing supports asynchronous view functions using async def, enabling non-blocking I/O operations for improved concurrency in I/O-bound tasks like database queries or external calls. The framework executes these coroutines in a thread with an , without requiring changes to the core routing setup:

python

@app.route('/async') async def async_view(): # Perform async operations, e.g., await some_io() return 'Async response'

@app.route('/async') async def async_view(): # Perform async operations, e.g., await some_io() return 'Async response'

This feature is particularly beneficial for applications handling high concurrency, though it does not accelerate computations. URL management in Flask emphasizes dynamic generation to avoid hard-coded strings, which can lead to maintenance issues if routes change. The url_for() function generates URLs by endpoint name (typically the view function name), accepting keyword arguments for variable values and supporting query strings via additional parameters. For example:

python

from flask import url_for # In a template or view link = url_for('profile', username='john', _external=True) # Produces: http://example.com/user/john query_link = url_for('search', q='flask', page=2) # Produces: /search?q=flask&page=2

from flask import url_for # In a template or view link = url_for('profile', username='john', _external=True) # Produces: http://example.com/user/john query_link = url_for('search', q='flask', page=2) # Produces: /search?q=flask&page=2

This approach automatically escapes special characters and adapts to the application's base URL, including subdomains if configured. Best practices recommend always using url_for for internal links to ensure flexibility and reduce errors from manual URL construction. For RESTful design, developers should align routes with HTTP methods (e.g., GET for retrieval, POST for creation) and use resource-oriented paths (e.g., /users/<id>), promoting scalable and intuitive APIs while leveraging Flask's lightweight routing for such patterns.

Request and Response Handling

Flask processes incoming HTTP requests through its request object, which encapsulates all data sent by the client to the server. This object is automatically available within view functions and is imported as from flask import request. It provides access to various components of the request, such as query parameters, form data, JSON payloads, and uploaded files, enabling developers to extract and validate input efficiently. Key attributes of the request object include request.args, a MultiDict containing URL query arguments parsed from the query string (e.g., ?key=value), accessible via methods like request.args.get('key') to retrieve values safely. For POST or PUT requests with form-encoded data, request.form offers a similar MultiDict for form fields, such as request.form['field']. If the request's Content-Type is application/json, request.json parses and returns the body as a Python dictionary or list. Uploaded files are handled through request.files, another MultiDict that allows processing multipart form data, including file storage and metadata. Additionally, request.method indicates the HTTP method (e.g., 'GET', 'POST'), and request.headers provides an EnvironHeaders object for all request headers. These attributes ensure comprehensive access to request data without manual parsing. The response object in Flask manages outgoing data to the client, including the body, status code, and headers. View functions implicitly return a response: a string converts to a Response object with an mimetype and 200 status, while other types like tuples or dictionaries are handled accordingly. For more control, developers use make_response() to create an explicit Response instance, allowing customization of headers (e.g., response.headers['Custom-Header'] = 'value') and status codes (e.g., response.status_code = 201). This object, built on Werkzeug's response utilities, supports direct manipulation for complex scenarios. Flask employs context locals to manage per-request state without global variables. The g object, short for "global," stores arbitrary data specific to the current request, such as database connections or user info, and is cleared at the end of each request; it is accessed via from flask import g and used like g.user = user. The current_app proxy provides access to the active application instance during request handling, useful for configuration or logger retrieval (e.g., current_app.logger.info('message')), and is automatically bound within application and request contexts. These mechanisms maintain thread-safety and encapsulate request-scoped data. For handling large datasets or , Flask supports response streaming via generators. A view function can yield chunks of data using Response(generator_function, mimetype='text/plain'), preventing exhaustion by sending content incrementally. To preserve request in streamed responses, wrap the generator with stream_with_context(), as in return Response(stream_with_context(generate_data()), mimetype='text/csv'), ensuring access to request and other locals; without it, accessing context-bound objects raises a RuntimeError. This pattern is ideal for CSV exports or live updates but may conflict with certain WSGI middlewares. JSON responses are facilitated by the jsonify() function, which serializes Python objects to and returns a Response with application/json mimetype. Usage is straightforward, as in return jsonify({'key': 'value'}, status=200), supporting additional parameters for status and headers. In Flask 3.x, serialization relies on a configurable provider (default: DefaultJSONProvider), set via app.json_provider_class, allowing customization for non-standard handling. This enables robust development with automatic escaping and indentation options. Error handling in Flask involves raising exceptions to interrupt request processing and return appropriate HTTP responses. The abort() function, imported as from flask import abort, raises a Werkzeug HTTP exception with a specified status code, such as abort(404) for not found errors, which triggers the default error page or a custom handler. Custom exceptions can be defined and registered with @app.errorhandler(CustomError) to return tailored responses, enhancing user feedback for validation failures or internal issues. This system integrates seamlessly with request dispatching for reliable error propagation.

Templating and Rendering

Flask integrates the Jinja2 templating engine to enable dynamic content generation directly within view functions. The core rendering process utilizes the render_template function from the flask module, which loads a specified template file from the application's templates directory and populates it with provided variables. For instance, a view might return render_template('index.html', title='Home Page', items=['item1', 'item2']) to insert the title and items into placeholders within the template. This mechanism inherently supports Jinja2's template inheritance via {% extends %} blocks for reusable layouts and context processors—functions registered with app.context_processor—that automatically add variables like user data to every template's context. Within Flask, Jinja2 templates benefit from an enriched global context that includes the application's config object, allowing access to settings such as {{ config['ENV'] }} for environment-specific rendering. Additional Flask-specific enhancements include built-in filters like |tojson for serializing Python objects to strings while ensuring safety, and the ability to load templates from Python packages using PackageLoader for better organization in larger applications. These features streamline the creation of responsive, data-driven pages without manual string manipulation. For advanced setups, the template search path can be customized by passing a template_folder argument to the Flask constructor, such as Flask(__name__, template_folder='custom/templates'), overriding the default 'templates' subdirectory. Developers can implement custom loaders by replacing app.jinja_loader with a subclass of jinja2.BaseLoader, enabling template sourcing from , filesystems, or other non-local sources. Templating in Flask is commonly applied to produce for web pages, format email content with dynamic insertions, and generate interactive documentation. When a template cannot be located during rendering, Flask raises a jinja2.exceptions.TemplateNotFound exception, typically prompting developers to verify file paths or implement error handlers like @app.errorhandler(TemplateNotFound). Jinja2 itself is a robust, Python-centric template emphasizing through auto-escaping and extensibility via custom filters and tests. For enhanced , particularly in high-traffic scenarios, the Flask-Caching extension can cache rendered template outputs across requests using backends like , though Jinja2 already compiles and caches template internally.

Sessions and Security

Flask sessions enable the storage of user-specific data across multiple requests within a single browser session. The session object behaves like a , allowing developers to set and retrieve values such as user preferences or status using session['key'] = value. This data is persisted client-side as a containing the serialized session contents, which are cryptographically signed to ensure integrity and prevent tampering by the client. The signing mechanism relies on the ItsDangerous library, requiring the application to configure a SECRET_KEY—a random, secret value used for generating signatures. Without a properly set SECRET_KEY, sessions cannot function securely, as unsigned could be forged. To enhance session security, Flask supports configurable cookie attributes that align with modern web standards. The SESSION_COOKIE_HTTPONLY option, when enabled, prevents client-side from accessing the session , mitigating risks from (XSS) attacks that might attempt to steal it. Similarly, SESSION_COOKIE_SAMESITE can be set to 'Lax' or 'Strict' to restrict the cookie's transmission in cross-site requests, providing built-in protection against (CSRF) in certain scenarios. For permanent sessions, which persist beyond the browser's immediate closure, the PERMANENT_SESSION_LIFETIME configuration defaults to 31 days and can be adjusted to control expiration; setting session.permanent = True extends the session accordingly. Developers can further customize session behavior by overriding the session_interface attribute on the Flask app instance, allowing replacement of the default SecureCookieSessionInterface with alternatives like server-side storage backends for larger data needs. Flask incorporates several measures to address common web vulnerabilities directly in its core components. For XSS, the integrated Jinja2 templating engine automatically escapes HTML characters in rendered templates, preventing malicious script injection from user input unless explicitly disabled with the |safe filter. This auto-escaping applies by default to variables in templates, significantly reducing reflected and stored XSS risks. Regarding CSRF, while core Flask does not enforce token validation on forms, it supports secure practices through signed sessions and cookie configurations; full CSRF protection for POST requests is commonly achieved via the @csrf.exempt decorator from the Flask-WTF extension to selectively disable checks on exempt routes like APIs. SQL injection is mitigated by encouraging the use of parameterized queries or object-relational mappers (ORMs) like SQLAlchemy in extensions, as direct string concatenation in database queries is avoided through proper data handling in Werkzeug's request objects. Additional security is provided through response headers and resource controls. Flask does not set the X-Content-Type-Options: nosniff header automatically, but it is recommended to set it manually on responses, forcing browsers to respect the declared content type and preventing MIME-type confusion attacks that could lead to XSS. The MAX_CONTENT_LENGTH configuration limits request body size application-wide, offering a hook for by rejecting oversized payloads early and integrating with extensions for more advanced throttling. Content-Security-Policy (CSP) headers are not auto-set but can be configured manually on responses to restrict resource loading and further block XSS vectors. These features collectively promote secure without requiring external dependencies for basic protections.

Blueprints and Modularity

Flask Blueprints provide a mechanism for structuring web applications into reusable, modular components, enabling developers to group related routes, views, templates, and static files without directly attaching them to the main application instance. Introduced in Flask 0.7, Blueprints act as a template for application sections, allowing operations to be recorded and applied dynamically during registration, which supports at the framework level rather than enforcing a full application . To create a Blueprint, developers instantiate the Blueprint class, specifying a name and import path, optionally with a URL prefix for scoping routes. For example:

python

from flask import [Blueprint](/page/Blueprint) auth_bp = [Blueprint](/page/Blueprint)('auth', __name__, url_prefix='/auth')

from flask import [Blueprint](/page/Blueprint) auth_bp = [Blueprint](/page/Blueprint)('auth', __name__, url_prefix='/auth')

Routes and other components are then defined within the Blueprint using decorators like @auth_bp.route('/login'), which define modular route groups without immediate execution. Registration occurs by calling app.register_blueprint(auth_bp) on the Flask application instance, optionally with parameters like url_prefix to mount the Blueprint under a specific path, such as /auth, ensuring all routes within it are prefixed accordingly. This deferred registration defers initialization until the Blueprint is explicitly attached, preventing premature execution of code during import and facilitating testing and reusability across multiple applications. The primary benefits of Blueprints include enhanced reusability, as a single Blueprint can be registered in different applications or multiple times within the same app with varying configurations; improved organization of resources, where each Blueprint can specify its own template_folder and static_folder for isolated templates and static files; and support for larger-scale applications by promoting a that scales better than monolithic route definitions. For instance, in development, Blueprints can delineate versions like /v1/ and /v2/ or separate user modules such as and administration, while application factories—functions that create and configure the app instance based on environment settings—integrate Blueprints for config-driven instantiation, as in def create_app(config_name): app = Flask(__name__) app.config.from_object(config[config_name]) app.register_blueprint(auth_bp) return app. This approach is particularly valuable for or multi-tenant systems, where reduces and eases maintenance. Advanced features extend Blueprint functionality for more complex scenarios. Blueprints support custom template filters, tests, and context processors scoped to their domain, allowing tailored rendering logic without global pollution. Error handlers can be Blueprint-specific using @bp.errorhandler(404), which only apply to requests routed through that Blueprint, providing granular control over exceptions. Similarly, before- and after-request functions, defined with @bp.before_request or @bp.after_request, execute only for routes within the Blueprint, enabling module-specific middleware like authentication checks or logging. URL construction within Blueprints leverages the url_for function with the Blueprint name, such as url_for('auth.login'), to generate paths that respect prefixes and ensure portability. Despite these advantages, Blueprints have limitations in state management; they do not inherently support cross-Blueprint shared state, requiring the g object for per-request data or third-party extensions for persistent shared resources like databases. This design choice emphasizes loose coupling but necessitates careful planning for inter-module communication in highly integrated applications.

Extensions and Ecosystem

Official Extensions

Flask's official extensions are maintained by the Pallets organization via the Pallets-Eco organization, which coordinates community efforts to sustain key add-ons with core team oversight. This approach keeps the ecosystem vital while limiting the number of directly core-maintained packages, emphasizing collaborative development over a large set. Flask-Session extends the framework's built-in client-side sessions by enabling server-side storage backends such as filesystem, , , or , improving scalability for applications with persistent user data. Installation occurs via pip install Flask-Session, followed by initialization with from flask_session import Session; Session(app). Configuration options include setting app.config['SESSION_TYPE'] = 'redis' for Redis-backed sessions, allowing seamless integration with existing session handling like session['key'] = value. Flask-DebugToolbar provides a debugging overlay for development environments, injecting a sidebar into HTML responses with panels displaying request details, SQL queries (if using an ORM), template rendering times, and configuration values. It is installed using pip install Flask-DebugToolbar and activated via from flask_debugtoolbar import DebugToolbarExtension; DebugToolbarExtension(app). The toolbar requires debug mode (app.debug = True) and is automatically disabled in production. Flask-SQLAlchemy serves as a prominent extension that integrates the SQLAlchemy ORM, enabling declarative models, query building, and session management tailored for Flask applications. Flask's (CLI), integrated since version 0.11 using the Click library, offers built-in commands such as flask run for development servers and flask shell for interactive REPL sessions, rendering a separate Flask-CLI extension unnecessary in current versions. For handling beyond the core flask.[json](/page/JSON) module and jsonify function—which support dumping and parsing with customizable providers—extensions like Flask-OrJSON leverage faster libraries such as orjson for improved performance in API-heavy applications. In the realm of forms and validation, Flask-WTF extends the WTForms library with Flask-specific integrations, including automatic CSRF token generation and protection to safeguard against attacks. Flask-Caching implements caching backends like and to optimize performance in data-intensive scenarios. The official extension set remains concise, with Pallets-Eco overseeing 37 repositories as of November 2025, prioritizing high-impact tools while encouraging broader community contributions. In the Flask 3.x ecosystem, these extensions have received updates for async view compatibility, including ASGI support in backends like for sessions, aligning with modern asynchronous patterns.

Third-Party Extensions

Flask's extensibility has fostered a vibrant community-driven ecosystem, where third-party extensions address specialized needs beyond the core framework. These packages, often hosted on PyPI, integrate seamlessly with Flask's lightweight design to add features like database management, form handling, and authentication without altering the underlying architecture. For database operations, extensions like Flask-Migrate facilitate database schema migrations by leveraging Alembic, allowing developers to version-control changes and apply them across environments. Authentication is commonly handled through extensions like Flask-Login, which manages user sessions, /logout processes, and protected views using Flask's session interface. For API-centric applications, Flask-JWT-Extended offers support for JSON Web Tokens (JWT), enabling secure, stateless with features like token refresh and blacklisting. Additional widely adopted extensions include Flask-RESTful, which simplifies the creation of RESTful APIs by providing resource-based routing and request parsing; and Flask-Mail, for integrating SMTP-based email sending with configurable backends. As of 2025, the Flask ecosystem encompasses over 1,000 extensions listed on PyPI under the "Framework :: Flask" classifier, discoverable through searches. When incorporating these extensions, developers should pin specific versions in dependency files to mitigate conflicts arising from core Flask updates, and test integrations thoroughly to ensure compatibility. Extensions can leverage Flask's Blueprints for modular organization in larger projects.

Practical Usage

Installation and Setup

To install Flask, use the Python package installer pip within a to isolate dependencies and avoid conflicts with system-wide packages. First, create a using the venv module, which is included in Python 3.9 and later—the minimum supported version for Flask 3.x. For example, run python -m venv venv to create the environment, then activate it with source venv/bin/activate on systems or venv\Scripts\activate on Windows. Once activated, install Flask with pip install Flask, which also pulls in core dependencies such as Werkzeug for request handling, Jinja for templating, MarkupSafe, ItsDangerous, Click for the , and Blinker. For reproducible installations across environments, generate a requirements.txt file to pin specific versions, such as Flask==3.1.2 (the latest stable release as of late 2025). Use pip freeze > requirements.txt after installation to capture the exact versions, or employ tools like pip-tools for more advanced dependency resolution and locking, which compile a pinned requirements file from a looser requirements.in specification. A basic Flask application is created by importing the Flask class and instantiating it with the current module name for proper resource loading. The following minimal example defines a single route and runs the development server:

python

from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return '<h1>Hello, World!</h1>' if __name__ == '__main__': app.run(debug=True)

from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return '<h1>Hello, World!</h1>' if __name__ == '__main__': app.run(debug=True)

This code can be saved in a file like app.py and executed with python app.py, starting a local server at http://127.0.0.1:5000.[](https://flask.palletsprojects.com/en/stable/quickstart/) For development, enable debug mode by setting app.debug = True or passing debug=True to app.run(), which provides detailed error pages and automatic reloading on code changes. Additionally, set a secret key for secure session handling with app.secret_key = 'dev' (for testing only) or generate a random value using import os; app.secret_key = os.urandom(24) to sign cookies and data. In production, always use a strong, unique secret key stored securely, such as via environment variables. Organize projects with a standard structure to separate concerns: place application code in an app/ directory (or flaskr/ for -style projects), HTML templates in a templates/ folder, and static assets like CSS or images in a static/ folder, which Flask automatically detects relative to the app instance. For larger applications, adopt the application pattern by defining a function that creates and configures the Flask instance, allowing modular initialization and easier testing. This involves moving the app creation to a function in a separate module, invoked with app = create_app().

Configuration Options

Flask applications manage settings through the app.config object, which is a dictionary subclass provided by the framework to store configuration values. This object allows developers to set, retrieve, and update keys dynamically, such as app.config['KEY'] = value, enabling flexible customization after application instantiation. The config object supports multiple loading methods to accommodate various environments, including direct assignment, loading from Python modules via app.config.from_object('config_module'), from Python files using app.config.from_pyfile('config.cfg'), from environment variables with app.config.from_envvar('CONFIG_PATH') pointing to a config file, or from files via app.config.from_json('config.json'). These methods follow a loading order where later sources override earlier ones, ensuring environment-specific values take precedence. Common configuration keys provided by Flask include built-in options for , , and request handling, each with default values that can be overridden. For instance, DEBUG controls debug mode for detailed error reporting and auto-reloading, defaulting to False and influenced by the FLASK_DEBUG . SECRET_KEY is a required string for cryptographic signing of sessions and in production, with no default and recommended to be a random, secure value. Other keys like TESTING enable test mode behaviors such as disabling error catching, defaulting to False; and MAX_CONTENT_LENGTH limits request body size in bytes, defaulting to None (unlimited). These keys are accessed and set uniformly through app.config, promoting consistency across the application.
KeyTypeDefault ValueDescription
DEBUGboolFalseEnables debug mode for development, including auto-reloading and full traces. Set via FLASK_DEBUG .
TESTINGboolFalseActivates testing configuration, bypassing handlers and CSRF protection.
SECRET_KEYstrNoneSecret key for signing data like sessions; must be set securely in production.
MAX_CONTENT_LENGTHintNoneMaximum size of incoming request bodies in bytes; exceeding it raises an .
A widely adopted pattern for managing configurations involves class-based setups, where a base Config class defines shared defaults, and subclasses like DevelopmentConfig or ProductionConfig override environment-specific values. For example:

python

class Config: SECRET_KEY = 'dev-key' MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB class DevelopmentConfig(Config): DEBUG = True class ProductionConfig(Config): DEBUG = False SECRET_KEY = os.environ.get('SECRET_KEY')

class Config: SECRET_KEY = 'dev-key' MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB class DevelopmentConfig(Config): DEBUG = True class ProductionConfig(Config): DEBUG = False SECRET_KEY = os.environ.get('SECRET_KEY')

The application then loads the appropriate class, such as app.config.from_object(DevelopmentConfig), allowing seamless switching based on runtime conditions. Environment variables can further override these settings by prefixing keys with the application name or using dedicated loaders, ensuring sensitive data like database credentials remains external to source code. For advanced usage, extensions integrate with the config object by defining their own keys, such as Flask-SQLAlchemy's SQLALCHEMY_DATABASE_URI for specifying the database connection string (e.g., 'sqlite:///app.db') and SQLALCHEMY_ENGINE_OPTIONS for engine tweaks like pooling. These are set in app.config before extension initialization, enabling modular configuration without altering core Flask logic. In Flask 3.x, configuration loading retains its established methods, but new app-level customization points allow extensions to hook into broader behaviors, such as response handling, while maintaining compatibility with prior config patterns.

Examples

Basic Application

A minimal Flask application can be created with just a few lines of code, demonstrating the framework's simplicity and core functionality of defining routes and handling requests. The following example, saved as app.py, implements a basic "Hello World" server:

python

from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World!' if __name__ == '__main__': app.run()

from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World!' if __name__ == '__main__': app.run()

This code imports the Flask class from the flask module, which serves as the foundation for the . The app = Flask(__name__) line instantiates the Flask application object, using the Python module name (__name__) to configure the app's root path for locating resources like templates and static files. The @app.route('/') decorator registers the hello function to handle HTTP GET requests to the root path (/), and the function returns a simple string response. Finally, app.run() starts the development server when the script is executed directly. To run the application, execute python app.py in the terminal from the directory containing app.py, which invokes app.run() and launches the server on http://127.0.0.1:5000 by default. Alternatively, after setting the FLASK_APP environment variable to app.py (e.g., export FLASK_APP=app.py on Unix-like systems), use the flask run command for the same effect; this method leverages Flask's built-in CLI and is recommended for most development workflows. Enabling debug mode—via app.run(debug=True) in the code or the --debug flag with flask run—provides automatic reloading on code changes and detailed error pages for troubleshooting. Upon starting, the console logs output similar to * Running on http://127.0.0.1:5000 and * Debug mode: on, confirming the server is active. Accessing http://127.0.0.1:5000/ in a displays the plain text response "Hello, World!" without any formatting, as the function returns a raw string. The server logs incoming requests, such as 127.0.0.1 - - [08/Nov/2025 12:00:00] "GET / HTTP/1.1" 200 -, indicating a successful response with status code . For a simple variation, additional routes can be defined by decorating more functions; for instance, adding @app.route('/about') above a new about function that returns 'About us' allows handling requests to /about independently, showcasing Flask's flexibility without altering the core structure.

Template Rendering Example

Flask facilitates dynamic web page generation through template rendering, leveraging the Jinja2 templating engine to separate presentation logic from application code. In a typical setup, a route handler uses the render_template function from the Flask module to load and render an template file, passing contextual as keyword arguments that become available within the template. Consider extending a basic application with a route that renders a personalized . The following Python code defines a route at the root path:

python

from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html', name='User')

from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html', name='User')

This handler passes a variable name with the value 'User' to the template named 'index.html'. Templates reside in a dedicated templates directory within the application's root folder, a convention that Flask automatically searches during rendering. Within the template file index.html, Jinja2 syntax enables inserting dynamic values and control structures. For instance:

html

<!DOCTYPE html> <html> <head><title>Flask Template</title></head> <body> <h1>Hello, {{ name }}!</h1> {% if name == 'User' %} <p>Welcome, new visitor.</p> {% endif %} </body> </html>

<!DOCTYPE html> <html> <head><title>Flask Template</title></head> <body> <h1>Hello, {{ name }}!</h1> {% if name == 'User' %} <p>Welcome, new visitor.</p> {% endif %} </body> </html>

Here, {{ name }} outputs the passed variable's value, while {% if %} implements conditional logic based on that value. To demonstrate iteration, templates can loop over passed collections, such as a list of items. If the route passes items=['apple', 'banana', 'cherry'] via render_template('index.html', name='User', items=['apple', 'banana', 'cherry']), the template might include:

html

<ul> {% for item in items %} <li>{{ item }}</li> {% endfor %} </ul>

<ul> {% for item in items %} <li>{{ item }}</li> {% endfor %} </ul>

This generates an unordered list of the items dynamically. When accessed via a , the rendered template produces complete output, with variables substituted and logic evaluated server-side before transmission. For example, the above index route yields <h1>Hello, User!</h1><p>Welcome, new visitor.</p> embedded in the full document. If a specified template file is absent from the templates directory, Flask raises a TemplateNotFound exception, typically resulting in a 500 Internal Server Error unless handled explicitly. Templates often integrate static assets like CSS for styling. Flask serves files from a static directory, accessible via the url_for function with the 'static' endpoint. In a template, link a stylesheet as <link rel="stylesheet" href="{{ url_for(" rel="nofollow"static', filename='style.css') }}">, where style.css is placed in the static folder. This generates an absolute , such as /static/style.css, ensuring proper resolution regardless of the application's base path. For interactive features like forms, template rendering supports initial display, but processing submissions follows the POST-redirect-GET pattern to prevent duplicate submissions on page refresh: a POST-handling route processes , flashes messages if needed, and redirects to a GET route that re-renders the template with updated .

Deployment

Server Options

Flask includes a built-in development server powered by the Werkzeug WSGI toolkit, which is intended solely for local testing and during application development. This server can be invoked programmatically via the app.run() method, specifying parameters such as host='0.0.0.0' to bind to all network interfaces and port=5000 for the listening , allowing access from other machines on . Alternatively, the Flask CLI provides the flask run command for launching the server, which supports options like --host and --port to customize binding and settings, as well as environment variables such as FLASK_ENV=development to enable debug mode with automatic reloading on code changes. However, this development server is single-threaded by default, lacks production-grade features like proper handling of concurrent requests, and is explicitly not recommended for deployment in live environments due to potential vulnerabilities and performance limitations. For production use, Flask applications require a robust WSGI-compliant server to handle requests efficiently and support scaling. Gunicorn, a pure-Python WSGI HTTP server, is a popular choice for its simplicity and performance; it can be run with a command like gunicorn -w 4 myapp:app, where -w 4 specifies four worker processes to manage concurrent requests, and myapp:app points to the Flask application instance. Similarly, uWSGI offers advanced configuration options and is suitable for high-load scenarios, invoked via commands such as uwsgi --http :5000 --wsgi-file myapp.py --callable app to serve the application over HTTP on port 5000. These WSGI servers interface with the Flask application object as per the WSGI specification, enabling reliable request processing without the constraints of the development server. To support asynchronous features like WebSockets in Flask applications, ASGI servers can be utilized through WSGI-to-ASGI middleware such as asgiref.WsgiToAsgi, allowing compatibility with servers like , which is designed for handling async protocols. For native ASGI support, frameworks like provide a drop-in compatible alternative to Flask with built-in async capabilities. In testing scenarios, Flask integrates with the pytest framework via its built-in test client for simulating requests without starting a full server, and the pytest-flask plugin enhances this by offering fixtures for , configuration, and live server testing to validate endpoints and behaviors comprehensively.

Production Best Practices

In production environments, Flask applications must prioritize to mitigate common web . Always disable debug mode by setting DEBUG=False in the configuration, as enabling it exposes sensitive information and allows code execution via the . Similarly, generate a strong, unique SECRET_KEY using secure random methods like os.urandom(24) and rotate it periodically or upon incidents—to prevent and forgery attacks. Enforce for all communications by configuring the application behind a that terminates SSL/TLS, ensuring encrypted data transmission and avoiding man-in-the-middle attacks. For vulnerability scanning, integrate tools like Bandit to statically analyze code for issues such as injection flaws or unsafe deserialization before deployment. To optimize performance, implement caching mechanisms using extensions like Flask-Caching, which supports backends such as or to store frequently accessed data and reduce database load in high-traffic scenarios. Employ database connection pooling with libraries like SQLAlchemy's built-in pooling or psycopg2 for , maintaining a pool of 5 connections to minimize overhead from repeated openings and closings, particularly in concurrent environments. Offload static file serving to a dedicated like , which handles efficient delivery of CSS, , and images, bypassing Flask entirely for these requests to improve response times under load. Effective monitoring is essential for maintaining reliability; configure structured logging with libraries like structlog to produce JSON-formatted logs that include context such as request IDs and timestamps, facilitating easier parsing and analysis in tools like ELK Stack. Integrate error tracking services such as Sentry, which captures exceptions, breadcrumbs, and performance metrics from Flask apps, enabling real-time alerts and root-cause analysis in production. For scaling, containerize Flask applications using Docker to ensure consistent environments across development and production, packaging dependencies into images that can be versioned and deployed reproducibly. Orchestrate these containers with for automated scaling, handling deployments, and , supporting horizontal pod autoscaling based on CPU or custom metrics to manage traffic spikes efficiently. Use load balancers, such as those integrated with Kubernetes Ingress or , to distribute incoming requests across multiple instances, preventing single points of failure and balancing load to sustain thousands of concurrent users. As of , leverage Flask's async view support—introduced in —for I/O-bound tasks like calls or file operations, allowing non-blocking execution within WSGI servers to improve performance for concurrent IO-bound tasks without full ASGI migration. Additionally, adopt static type checking with mypy to catch type-related errors early; Flask 3.0+ includes enhanced type hints compatible with mypy's strict mode.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.