feat: docs

This commit is contained in:
David Carmichael
2024-08-16 15:09:07 +01:00
parent caaf17af12
commit bfcc49dd8d
95 changed files with 12960 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
```
Menu = CLI Commands/quart-imp blueprint
Title = Generate a Quart-Imp Blueprint
```
Quart-Imp has its own type of blueprint. It comes with some methods to auto import routes, and nested blueprints etc...
see [ImpBlueprint / Introduction](impblueprint-introduction.html) for more information.
You have the option to generate a regular template rendering blueprint, or a API blueprint that returns a JSON response.
```bash
quart-imp blueprint --help
```
or
```bash
quart-imp api-blueprint --help
```
To generate a Quart-Imp blueprint, run the following command:
```bash
quart-imp blueprint
```
or
```bash
quart-imp api-blueprint
```
After running this command, you will be prompted to enter the location of where you want to create your blueprint:
```text
~ $ quart-imp blueprint
(Creation is relative to the current working directory)
Folder to create blueprint in [Current Working Directory]:
```
As detailed in the prompt, the creation of the blueprint is relative to the current working directory. So to create a
blueprint in the folder `app/blueprints`, you would enter `app/blueprints` in the prompt.
```text
~ $ quart-imp blueprint
(Creation is relative to the current working directory)
Folder to create blueprint in [Current Working Directory]: app/blueprints
```
You will then be prompted to enter a name for your blueprint:
```text
~ $ quart-imp blueprint
...
Name of the blueprint to create [my_new_blueprint]:
```
The default name is 'my_new_blueprint', we will change this to 'admin'
```text
~ $ quart-imp blueprint
...
Name of the blueprint to create [my_new_blueprint]: admin
```
After creating your blueprint, the folder structure will look like this:
```text
app/
├── blueprints
│ └── admin
│ ├── routes
│ │ └── index.py
│ │
│ ├── static
│ │ ├── css
│ │ │ └── water.css
│ │ ├── img
│ │ │ └── quart-imp-logo.png
│ │ └── js
│ │ └── main.js
│ │
│ ├── templates
│ │ └── www
│ │ ├── extends
│ │ │ └── main.html
│ │ ├── includes
│ │ │ ├── footer.html
│ │ │ └── header.html
│ │ └── index.html
│ │
│ └── __init__.py
...
```
This is a self-contained blueprint, so it has its own static, templates and routes folders.
You can now navigate '/admin'
You can streamline this process by specifying the name of the blueprint, the folder to
create it in and the configuration to use, like so:
```bash
quart-imp blueprint -n admin -f app/blueprints
```

View File

@@ -0,0 +1,215 @@
```
Menu = CLI Commands/quart-imp init
Title = Initialising a Quart-Imp Project
```
Quart-Imp has a cli command that deploys a new ready-to-go project.
This project is structured in a way to give you the best idea of
how to use Quart-Imp.
```bash
quart-imp init --help
```
## Create a new project
Make sure you are in the virtual environment, and at the root of your
project folder, then run the following command:
```bash
quart-imp init
```
After running this command, you will be prompted to choose what type of
app you want to deploy:
```text
~ $ quart-imp init
What type of app would you like to create? (minimal, slim, full) [minimal]:
```
See below for the differences between the app types.
After this, you will be prompted to enter a name for your app:
```text
~ $ quart-imp init
...
What would you like to call your app? [app]:
```
'app' is the default name, so if you just press enter, your app will be
called 'app'. You will then see this output:
```text
~ FILES CREATED WILL LOOP OUT HERE ~
===================
Quart app deployed!
===================
Your app has the default name of 'app'
Quart will automatically look for this!
Run: quart run --debug
```
If you called your app something other than 'app', like 'new' for example, you will see:
```text
~ FILES CREATED WILL LOOP OUT HERE ~
===================
Quart app deployed!
===================
Your app has the name of 'new'
Run: quart --app new run --debug
```
As you can see from the output, it gives you instructions on how to start your app,
depending on the name you gave it.
You should see a new folder that has been given the name you specified in
the `quart-imp init` command.
### Additional options
You can also specify a name for your app in the command itself, like so:
```bash
quart-imp init -n my_app
```
This will create a new app called 'my_app'.
The default will be a minimal app, this has no blueprints.
You can also deploy a slim app, that will have one blueprint, like so:
```bash
quart-imp init -n my_app --slim
```
You can also deploy a full app that is setup for multiple blueprints, like so:
```bash
quart-imp init -n my_app --full
```
## init Folder structures
### Minimal app (default)
`quart-imp init --minimal`:
```text
app/
├── resources
│ ├── static
│ │ ├── css
│ │ │ └── water.css
│ │ ├── img
│ │ │ └── quart-imp-logo.png
│ │ └── favicon.ico
│ ├── templates
│ │ └── index.html
│ └── routes.py
└── __init__.py
```
### Slim app
`quart-imp init --slim`:
```text
app/
├── extensions
│ └── __init__.py
├── resources
│ ├── cli
│ │ └── cli.py
│ ├── error_handlers
│ │ └── error_handlers.py
│ ├── static
│ │ ├── css
│ │ │ └── water.css
│ │ ├── img
│ │ │ └── quart-imp-logo.png
│ │ └── favicon.ico
│ └── templates
│ └── error.html
├── www
│ ├── __init__.py
│ ├── routes
│ │ └── index.py
│ ├── static
│ │ ├── css
│ │ │ └── water.css
│ │ ├── img
│ │ │ └── quart-imp-logo.png
│ │ └── js
│ │ └── main.js
│ └── templates
│ └── www
│ ├── extends
│ │ └── main.html
│ ├── includes
│ │ ├── footer.html
│ │ └── header.html
│ └── index.html
└── __init__.py
```
### Full app
`quart-imp init --full`:
```text
app/
├── blueprints
│ └── www
│ ├── __init__.py
│ ├── routes
│ │ └── index.py
│ ├── static
│ │ ├── css
│ │ │ └── water.css
│ │ ├── img
│ │ │ └── quart-imp-logo.png
│ │ └── js
│ │ └── main.js
│ └── templates
│ └── www
│ ├── extends
│ │ └── main.html
│ ├── includes
│ │ ├── footer.html
│ │ └── header.html
│ └── index.html
├── extensions
│ └── __init__.py
├── resources
│ ├── cli
│ │ └── cli.py
│ ├── context_processors
│ │ └── context_processors.py
│ ├── error_handlers
│ │ └── error_handlers.py
│ ├── filters
│ │ └── filters.py
│ ├── routes
│ │ └── routes.py
│ ├── static
│ │ └── favicon.ico
│ └── templates
│ └── error.html
└── __init__.py
```

View File

@@ -0,0 +1,84 @@
```
Menu = Imp/Introduction
Title = Quart-Imp Introduction
```
Quart-Imp is a Quart extension that provides auto import methods for various Quart resources. It will import
blueprints, and other resources. It uses the importlib module to achieve this.
Quart-Imp favors the application factory pattern as a project structure, and is opinionated towards using
Blueprints. However, you can use Quart-Imp without using Blueprints.
Here's an example of a standard Quart-Imp project structure:
```text
app/
├── blueprints/
│ ├── admin/...
│ ├── api/...
│ └── www/...
├── resources/
│ ├── filters/...
│ ├── context_processors/...
│ ├── static/...
│ └── templates/...
└── __init__.py
```
Here's an example of the `app/__init__.py` file:
```python
from quart import Quart
from quart_sqlalchemy import SQLAlchemy
from quart_imp import Imp
from quart_imp.config import QuartConfig, ImpConfig
db = SQLAlchemy()
imp = Imp()
def create_app():
app = Quart(__name__)
QuartConfig(
secret_key="super_secret_key",
app_instance=app,
)
imp.init_app(app, config=ImpConfig(
init_session={"logged_in": False},
))
imp.import_app_resources("resources")
imp.import_blueprints("blueprints")
db.init_app(app)
return app
```
The Quart configuration can be loaded from any standard Quart configuration method, or from the `QuartConfig` class
shown above.
This class contains the standard Quart configuration options found in the Quart documentation.
The `ImpConfig` class is used to configure the `Imp` instance.
The `init_session` option of the `ImpConfig` class is used to set the initial session variables for the Quart app.
This happens before the request is processed.
`ImpConfig` also has the ability to set `SQLALCHEMY_DATABASE_URI` and `SQLALCHEMY_BINDS`
For more information about the configuration setting see
[quart_imp_config-impconfig.md](quart_imp_config-impconfig.html).
`import_app_resources` will walk one level deep into the `resources` folder, and import
all `.py` files as modules.
It will also check for the existence of a `static` and `templates` folder, and register them with the Quart app.
There is a couple of options for `import_app_resources` to control what
is imported, see: [Imp / import_app_resources](imp-import_app_resources.html)
`import_blueprints` expects a folder that contains many Blueprint as Python packages.
It will check each blueprint folder's `__init__.py` file for an instance of a Quart Blueprint or a
Quart-Imp Blueprint. That instant will then be registered with the Quart app.
See more about how importing blueprints work here: [ImpBlueprint / Introduction](impblueprint-introduction.html)

View File

@@ -0,0 +1,106 @@
```
Menu = Imp/import_app_resources
Title = Imp.import_app_resources
```
```python
import_app_resources(
folder: str = "resources",
factories: Optional[List] = None,
static_folder: str = "static",
templates_folder: str = "templates",
files_to_import: Optional[List] = None,
folders_to_import: Optional[List] = None,
) -> None
```
---
Import standard app resources from the specified folder.
This will import any resources that have been set to the Quart app.
Routes, context processors, cli, etc.
**Can only be called once.**
If no static and or template folder is found, the static and or template folder will be set to None in the Quart app
config.
#### Small example of usage:
```python
imp.import_app_resources(folder="resources")
# or
imp.import_app_resources()
# as the default folder is "resources"
```
Folder Structure: `resources`
```text
app
├── resources
│ ├── routes.py
│ ├── app_fac.py
│ ├── static
│ │ └── css
│ │ └── style.css
│ └── templates
│ └── index.html
└── ...
...
```
File: `routes.py`
```python
from quart import current_app as app
from quart import render_template
@app.route("/")
async def index():
return await render_template("index.html")
```
#### How factories work
Factories are functions that are called when importing the app resources. Here's an example:
```python
imp.import_app_resources(
folder="resources",
factories=["development_cli"]
)
```
`["development_cli"]` => `development_cli(app)` function will be called, and the current app will be passed in.
File: `app_fac.py`
```python
def development_cli(app):
@app.cli.command("dev")
def dev():
print("dev cli command")
```
#### Scoping imports
By default, all files and folders will be imported.
To disable this, set `files_to_import` and or
`folders_to_import` to `[None]`.
```python
imp.import_app_resources(scope_import=[None], folders_to_import=[None])
```
To scope the imports, set the `files_to_import` and or `folders_to_import` to a list of files and or folders.
`files_to_import=["cli.py", "routes.py"]` => will only import the files `resources/cli.py`
and `resources/routes.py`
`folders_to_import=["template_filters", "context_processors"]` => will import all files in the folders
`resources/template_filters/*.py` and `resources/context_processors/*.py`

View File

@@ -0,0 +1,121 @@
```
Menu = Imp/import_blueprint
Title = Imp.import_blueprint
```
```python
import_blueprint(self, blueprint: str) -> None
```
---
Import a specified Quart-Imp or standard Quart Blueprint relative to the Quart app root.
```text
app
├── my_blueprint
│ ├── ...
│ └── __init__.py
├── ...
└── __init__.py
```
File: `app/__init__.py`
```python
from quart import Quart
from quart_imp import Imp
imp = Imp()
def create_app():
app = Quart(__name__)
imp.init_app(app)
imp.import_blueprint("my_blueprint")
return app
```
Quart-Imp Blueprints have the ability to auto import resources, and initialize session variables.
For more information on how Quart-Imp Blueprints work, see the [ImpBlueprint / Introduction](impblueprint-introduction.html)
##### Example of 'my_blueprint' as a Quart-Imp Blueprint:
```text
app
├── my_blueprint
│ ├── routes
│ │ └── index.py
│ ├── static
│ │ └── css
│ │ └── style.css
│ ├── templates
│ │ └── my_blueprint
│ │ └── index.html
│ ├── __init__.py
│ └── config.toml
└── ...
```
File: `__init__.py`
```python
from quart_imp import ImpBlueprint
from quart_imp.config import ImpBlueprintConfig
bp = ImpBlueprint(
__name__,
ImpBlueprintConfig(
enabled=True,
url_prefix="/my-blueprint",
static_folder="static",
template_folder="templates",
static_url_path="/static/my_blueprint",
init_session={"my_blueprint": "session_value"},
),
)
bp.import_resources("routes")
```
File: `routes / index.py`
```python
from .. import bp
@bp.route("/")
async def index():
return "regular_blueprint"
```
##### Example of 'my_blueprint' as a standard Quart Blueprint:
```text
app
├── my_blueprint
│ ├── ...
│ └── __init__.py
└── ...
```
File: `__init__.py`
```python
from quart import Blueprint
bp = Blueprint("my_blueprint", __name__, url_prefix="/my-blueprint")
@bp.route("/")
async def index():
return "regular_blueprint"
```
Both of the above examples will work with `imp.import_blueprint("my_blueprint")`, they will be registered
with the Quart app, and will be accessible via `url_for("my_blueprint.index")`.

View File

@@ -0,0 +1,50 @@
```
Menu = Imp/import_blueprints
Title = Imp.import_blueprints
```
```python
import_blueprints(self, folder: str) -> None
```
---
Import all Quart-Imp or standard Quart Blueprints from a specified folder relative to the Quart app root.
```text
app/
├── blueprints/
│ ├── admin/
│ │ ├── ...
│ │ └── __init__.py
│ ├── www/
│ │ ├── ...
│ │ └── __init__.py
│ └── api/
│ ├── ...
│ └── __init__.py
├── ...
└── __init__.py
```
File: `app/__init__.py`
```python
from quart import Quart
from quart_imp import Imp
imp = Imp()
def create_app():
app = Quart(__name__)
imp.init_app(app)
imp.import_blueprints("blueprints")
return app
```
This will import all Blueprints from the `blueprints` folder using the `Imp.import_blueprint` method.
See [Imp / import_blueprint](imp-import_blueprint.html) for more information.

View File

@@ -0,0 +1,22 @@
```
Menu = Imp/init_app, __init__
Title = Imp.init_app, __init__
```
```python
def init_app(
app: Quart,
config: ImpConfig
) -> None:
# -or-
Imp(
app: Quart,
config: ImpConfig
)
```
---
Initializes the quart app to work with quart-imp.
See [quart_imp_config-impconfig.md](quart_imp_config-impconfig.html) for more information on the `ImpConfig` class.

View File

@@ -0,0 +1,48 @@
```
Menu = Imp/init_session
Title = Imp.init_session
```
```python
init_session() -> None
```
---
Initialize the session variables found in the config. Commonly used in `app.before_request`.
```python
@app.before_request
async def before_request():
imp._init_session()
```
File: `config.toml`
```toml
...
[SESSION]
logged_in = false
...
```
`logged_in` is now available in the session.
```python
@app.route('/get-session-value')
async def login():
print(session['logged_in'])
return "Check Terminal"
```
`Output: False`
Can also be used to reset the values in the session. Here's an example:
```python
@app.route('/logout')
async def logout():
session.clear()
imp._init_session()
return redirect(url_for('index'))
```

View File

@@ -0,0 +1,68 @@
```
Menu = ImpBlueprint/Introduction
Title = Quart-Imp Blueprint Introduction
```
The Quart-Imp Blueprint inherits from the Quart Blueprint class, then adds some additional methods to allow for auto
importing of resources and other nested blueprints.
The Quart-Imp Blueprint requires you to provide the `ImpBlueprintConfig` class as the second argument to the Blueprint.
Here's an example of a Quart-Imp Blueprint structure:
```text
www/
├── nested_blueprints/
│ ├── blueprint_one/
│ │ ├── ...
│ │ └── __init__.py
│ └── blueprint_two/
│ ├── ...
│ └── __init__.py
├── standalone_nested_blueprint/
│ ├── ...
│ └── __init__.py
├── routes/
│ └── index.py
├── static/
│ └── ...
├── templates/
│ └── www/
│ └── index.html
└── __init__.py
```
File: `__init__.py`
```python
from quart_imp import ImpBlueprint
from quart_imp.config import ImpBlueprintConfig
bp = ImpBlueprint(__name__, ImpBlueprintConfig(
enabled=True,
url_prefix="/www",
static_folder="static",
template_folder="templates",
init_session={"logged_in": False},
))
bp.import_resources("routes")
bp.import_nested_blueprints("nested_blueprints")
bp.import_nested_blueprint("standalone_nested_blueprint")
```
The `ImpBlueprintConfig` class is used to configure the Blueprint. It provides a little more flexibility than the
standard Quart Blueprint configuration, like the ability to enable or disable the Blueprint.
`ImpBlueprintConfig`'s `init_session` works the same as `ImpConfig`'s `init_session`, this will add the session data to
the Quart app's session object on initialization of the Quart app.
To see more about configuration see: [quart_imp.config / ImpBlueprintConfig](quart_imp_config-impblueprintconfig.html)
`import_resources` method will walk one level deep into the `routes` folder, and import all `.py` files as modules.
For more information see: [ImpBlueprint / import_resources](impblueprint-import_resources.html)
`import_nested_blueprints` will do the same as `imp.import_blueprints`, but will register the blueprints found as
nested to the current blueprint. For example `www.blueprint_one.index`
`import_nested_blueprint` behaves the same as `import_nested_blueprints`, but will only import a single blueprint.

View File

@@ -0,0 +1,78 @@
```
Menu = ImpBlueprint/import_nested_blueprint
Title = ImpBlueprint.import_nested_blueprint
```
```python
import_nested_blueprint(self, blueprint: str) -> None
```
---
Import a specified Quart-Imp or standard Quart Blueprint relative to the Blueprint root.
Works the same as [Imp / import_blueprint](imp-import_blueprint.html) but relative to the Blueprint root.
Blueprints that are imported this way will be scoped to the parent Blueprint that imported them.
`url_for('my_blueprint.my_nested_blueprint.index')`
```text
my_blueprint/
├── routes/...
├── static/...
├── templates/...
├── my_nested_blueprint/
│ ├── routes/
│ │ └── index.py
│ ├── static/...
│ ├── templates/...
│ ├── __init__.py
├── __init__.py
```
File: `my_blueprint/__init__.py`
```python
from quart_imp import ImpBlueprint
from quart_imp.config import ImpBlueprintConfig
bp = ImpBlueprint(__name__, ImpBlueprintConfig(
enabled=True,
static_folder="static",
template_folder="templates",
))
bp.import_resources("routes")
bp.import_nested_blueprint("my_nested_blueprint")
```
File: `my_blueprint/my_nested_blueprint/__init__.py`
```python
from quart_imp import ImpBlueprint
from quart_imp.config import ImpBlueprintConfig
bp = ImpBlueprint(__name__, ImpBlueprintConfig(
enabled=True,
static_folder="static",
template_folder="templates",
))
bp.import_resources("routes")
```
File: `my_blueprint/my_nested_blueprint/routes/index.py`
```python
from quart import render_template
from .. import bp
@bp.route("/")
async def index():
return await render_template(bp.tmpl("index.html"))
```

View File

@@ -0,0 +1,60 @@
```
Menu = ImpBlueprint/import_nested_blueprints
Title = ImpBlueprint.import_nested_blueprints
```
```python
import_nested_blueprints(self, folder: str) -> None
```
---
Will import all the Blueprints from the given folder relative to the Blueprint's root directory.
Uses [Blueprint / import_nested_blueprint](blueprint-import_nested_blueprint.html) to import blueprints from
the specified folder.
Blueprints that are imported this way will be scoped to the parent Blueprint that imported them.
`url_for('my_blueprint.nested_bp_one.index')`
`url_for('my_blueprint.nested_bp_two.index')`
`url_for('my_blueprint.nested_bp_three.index')`
```text
my_blueprint/
├── routes/...
├── static/...
├── templates/...
├── nested_blueprints/
│ │
│ ├── nested_bp_one/
│ │ ├── ...
│ │ ├── __init__.py
│ ├── nested_bp_two/
│ │ ├── ...
│ │ ├── __init__.py
│ └── nested_bp_three/
│ ├── ...
│ ├── __init__.py
├── __init__.py
```
File: `my_blueprint/__init__.py`
```python
from quart_imp import ImpBlueprint
from quart_imp.config import ImpBlueprintConfig
bp = ImpBlueprint(__name__, ImpBlueprintConfig(
enabled=True,
static_folder="static",
template_folder="templates",
))
bp.import_resources("routes")
bp.import_nested_blueprints("nested_blueprints")
```

View File

@@ -0,0 +1,57 @@
```
Menu = ImpBlueprint/import_resources
Title = ImpBlueprint.import_resources
```
```python
import_resources(folder: str = "routes") -> None
```
---
Will import all the resources (cli, routes, filters, context_processors...) from the given folder relative to the
Blueprint's root directory.
```text
my_blueprint
├── user_routes
│ ├── user_dashboard.py
│ └── user_settings.py
├── car_routes
│ ├── car_dashboard.py
│ └── car_settings.py
├── static/...
├── templates/
│ └── my_blueprint/
│ ├── user_dashboard.html
│ └── ...
├── __init__.py
```
File: `my_blueprint/__init__.py`
```python
from quart_imp import ImpBlueprint
from quart_imp.config import ImpBlueprintConfig
bp = ImpBlueprint(__name__, ImpBlueprintConfig(
enabled=True,
static_folder="static",
template_folder="templates",
))
bp.import_resources("user_routes")
bp.import_resources("car_routes")
```
File: `my_blueprint/user_routes/user_dashboard.py`
```python
from quart import render_template
from .. import bp
@bp.route("/user-dashboard")
async def user_dashboard():
return await render_template(bp.tmpl("user_dashboard.html"))
```

View File

@@ -0,0 +1,17 @@
```
Menu = ImpBlueprint/__init__
Title = Quart-Imp Blueprint __init__
```
```python
ImpBlueprint(dunder_name: str, config: ImpBlueprintConfig) -> None
```
---
Initializes the Quart-Imp Blueprint.
`dunder_name` should always be set to `__name__`
`config` is an instance of `ImpBlueprintConfig` that will be used to load the Blueprint's configuration.
See [quart_imp.config / ImpBlueprintConfig](quart_imp_config-impblueprintconfig.html) for more information.

View File

@@ -0,0 +1,44 @@
```
Menu = ImpBlueprint/tmpl
Title = ImpBlueprint.tmpl
```
```python
tmpl(template: str) -> str
```
---
Scopes the template lookup to the name of the blueprint (this takes from the `__name__` attribute of the Blueprint).
Due to the way Quart templating works, and to avoid template name collisions.
It is standard practice to place the name of the Blueprint in the template path,
then to place any templates under that folder.
```text
my_blueprint/
├── routes/
│ └── index.py
├── static/...
├── templates/
│ └── my_blueprint/
│ └── index.html
├── __init__.py
```
File: `my_blueprint/routes/index.py`
```python
from quart import render_template
from .. import bp
@bp.route("/")
async def index():
return await render_template(bp.tmpl("index.html"))
```
`bp.tmpl("index.html")` will output `"my_blueprint/index.html"`.

121
docs/_md/v1/__index__.md Normal file
View File

@@ -0,0 +1,121 @@
# Welcome to the Quart-Imp Documentation
## What is Quart-Imp?
Quart-Imp's main purpose is to help simplify the importing of blueprints, and resources. It has a few extra
features built in to help with securing pages and password authentication.
## Install Quart-Imp
```bash
pip install quart-imp
```
## Getting Started
To get started right away, you can use the CLI commands to create a new Quart-Imp project.
```bash
quart-imp init
```
### Minimal Quart-Imp Setup
Run the following command to create a minimal Quart-Imp project.
```bash
quart-imp init -n app --minimal
```
See [CLI Commands / quart-imp init](cli_commands-quart-imp_init.html) for more information.
### The minimal structure
#### Folder Structure
```text
app/
├── resources/
│ ├── static/...
│ ├── templates/
│ │ └── index.html
│ └── index.py
└── __init__.py
```
File: `app/__init__.py`
```python
from quart import Quart
from quart_imp import Imp
from quart_imp.config import QuartConfig, ImpConfig
imp = Imp()
def create_app():
app = Quart(__name__, static_url_path="/")
QuartConfig(
secret_key="secret_key",
app_instance=app
)
imp.init_app(app, ImpConfig())
imp.import_app_resources()
# Takes argument 'folder' default folder is 'resources'
return app
```
File: `app/resources/routes.py`
```python
from quart import current_app as app
from quart import render_template
@app.route("/")
async def index():
return await render_template("index.html")
```
File: `app/resources/templates/index.html`
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Quart-Imp</title>
</head>
<body>
<h1>Quart-Imp</h1>
</body>
</html>
```
---
Setting up a virtual environment is recommended.
**Linux / Darwin**
```bash
python3 -m venv venv
```
```bash
source venv/bin/activate
```
**Windows**
```bash
python -m venv venv
```
```text
.\venv\Scripts\activate
```

43
docs/_md/v1/__menu__.md Normal file
View File

@@ -0,0 +1,43 @@
- CLI Commands
- quart-imp init
- quart-imp blueprint
- Imp
- Introduction
- init_app, __init__
- init_session
- import_app_resources
- import_blueprint
- import_blueprints
- ImpBlueprint
- Introduction
- __init__
- init_session
- import_resources
- import_nested_blueprint
- import_nested_blueprints
- tmpl
- quart_imp.config
- QuartConfig
- ImpConfig
- ImpBlueprintConfig
- DatabaseConfig
- SQLDatabaseConfig
- SQLiteDatabaseConfig
- quart_imp.security
- login_check
- permission_check
- pass_function_check
- api_login_check
- include_csrf
- quart_imp.auth
- encrypt_password
- authenticate_password
- generate_password
- generate_salt
- generate_csrf_token
- generate_private_key
- generate_email_validator
- generate_numeric_validator
- generate_alphanumeric_validator
- is_email_address_valid
- is_username_valid

View File

@@ -0,0 +1,55 @@
```
Menu = quart_imp.auth/authenticate_password
Title = authenticate_password - quart_imp.auth
```
```python
from quart_imp.auth import authenticate_password
```
```python
authenticate_password(
input_password: str,
database_password: str,
database_salt: str,
encryption_level: int = 512,
pepper_length: int = 1,
pepper_position: t.Literal["start", "end"] = "end"
) -> bool
```
---
For use in password hashing.
To be used alongside the [quart_imp.auth / encrypt_password](quart_imp_auth-encrypt_password.html) function.
Takes the plain input password, the stored hashed password along with the stored salt
and will try every possible combination of pepper values to find a match.
**Note:**
- You must know the pepper length used to hash the password.
- You must know the position of the pepper used to hash the password.
- You must know the encryption level used to hash the password.
#### Authentication Scenario:
```
Plain password: "password"
Generated salt: "^%$*" (randomly generated)
Generated pepper (length 1): "A" (randomly generated)
Pepper position: "end"
```
```python
input_password = "password"
database_password = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0..." # pulled from database
database_salt = "^%$*" # pulled from database
authenticate_password(
input_password,
database_password,
database_salt
) # >>> True
```

View File

@@ -0,0 +1,53 @@
```
Menu = quart_imp.auth/encrypt_password
Title = encrypt_password - quart_imp.auth
```
```python
from quart_imp.auth import encrypt_password
```
```python
encrypt_password(
password: str,
salt: str,
encryption_level: int = 512,
pepper_length: int = 1,
pepper_position: t.Literal["start", "end"] = "end"
) -> str
```
---
For use in password hashing.
To be used alongside the [quart_imp.auth / authenticate_password](quart_imp_auth-authenticate_password.html) function.
Takes the plain password, applies a pepper, salts it, then produces a digested sha512 or sha256 if specified.
Can set the encryption level to 256 or 512, defaults to 512.
Can set the pepper length, defaults to 1. Max is 3.
Can set the pepper position, "start" or "end", defaults to "end".
**Note:**
- You must inform the authenticate_password function of the pepper length used to hash the password.
- You must inform the authenticate_password function of the position of the pepper used to hash the password.
- You must inform the authenticate_password function of the encryption level used to hash the password.
#### Encryption Scenario:
```
Plain password: "password"
Generated salt: "^%$*" (randomly generated)
Generated pepper (length 1): "A" (randomly generated)
Pepper position: "end"
```
1. Pepper is added to the end of the plain password: "passwordA"
2. Salt is added to the end of the peppered password: "passwordA^%$*"
3. Password is hashed: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0..."
4. Salt and hashed password are then stored in the database.

View File

@@ -0,0 +1,24 @@
```
Menu = quart_imp.auth/generate_alphanumeric_validator
Title = generate_alphanumeric_validator - quart_imp.auth
```
```python
from quart_imp.auth import generate_alphanumeric_validator
```
```python
generate_alphanumeric_validator(length: int = 8) -> str
```
---
Generates a random alphanumeric string of the given length.
(letters are capitalized)
##### Example:
```python
generate_alphanumeric_validator(8) # >>> 'A1B2C3D4'
```

View File

@@ -0,0 +1,26 @@
```
Menu = quart_imp.auth/generate_csrf_token
Title = generate_csrf_token - quart_imp.auth
```
```python
from quart_imp.auth import generate_csrf_token
```
```python
generate_csrf_token() -> str
```
---
Generates a SHA1 using the current date and time.
For use in Cross-Site Request Forgery.
Also used by the [quart_imp.security / csrf_protect](quart_imp_security-include_csrf.html) decorator.
##### Example:
```python
generate_csrf_token() # >>> 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0'
```

View File

@@ -0,0 +1,27 @@
```
Menu = quart_imp.auth/generate_email_validator
Title = generate_email_validator - quart_imp.auth
```
```python
from quart_imp.auth import generate_email_validator
```
```python
generate_email_validator() -> str
```
---
Uses `generate_alphanumeric_validator` with a length of 8 to
generate a random alphanumeric value for the specific use of
validating accounts via email.
See [quart_imp.auth / generate_alphanumeric_validator](quart_imp_auth-generate_alphanumeric_validator.html)
for more information.
##### Example:
```python
generate_email_validator() # >>> 'A1B2C3D4'
```

View File

@@ -0,0 +1,27 @@
```
Menu = quart_imp.auth/generate_numeric_validator
Title = generate_numeric_validator - quart_imp.auth
```
```python
from quart_imp.auth import generate_numeric_validator
```
```python
generate_numeric_validator(length: int) -> int
```
---
Generates random choice between 1 * (length) and 9 * (length).
If the length is 4, it will generate a number between 1111 and 9999.
For use in MFA email, or unique filename generation.
##### Example:
```python
generate_numeric_validator(4) # >>> 1234
```

View File

@@ -0,0 +1,26 @@
```
Menu = quart_imp.auth/generate_password
Title = generate_password - quart_imp.auth
```
```python
from quart_imp.auth import generate_password
```
```python
generate_password(style: str = "mixed", length: int = 3) -> str
```
---
Generates a password of (length) characters.
The Default length is 3.
Style options: "animals", "colors", "mixed" - defaults to "mixed"
##### Example:
```python
generate_password(style="animals", length=3) # >>> 'Cat-Goat-Pig12'
```

View File

@@ -0,0 +1,38 @@
```
Menu = quart_imp.auth/generate_private_key
Title = generate_private_key - quart_imp.auth
```
```python
from quart_imp.auth import generate_private_key
```
```python
generate_private_key(hook: t.Optional[str]) -> str
```
---
Generates a sha256 private key from a passed in hook value.
If no hook is passed in, it will generate a hook using datetime.now() and a
random number between 1 and 1000.
```python
@app.route('/register', methods=['GET', 'POST'])
async def register():
if request.method == "POST":
...
salt = generate_salt()
password = request.form.get('password')
encrypted_password = encrypt_password(password, salt)
...
user = User(
username=username,
email=email,
password=encrypted_password,
salt=salt,
private_key=generate_private_key(hook=username)
)
...
```

View File

@@ -0,0 +1,46 @@
```
Menu = quart_imp.auth/generate_salt
Title = generate_salt - quart_imp.auth
```
```python
from quart_imp.auth import generate_salt
```
```python
generate_salt(length: int = 4) -> str
```
---
Generates a string of (length) characters of punctuation.
The Default length is 4.
For use in password hashing and storage of passwords in the database.
##### Example:
```python
generate_salt() # >>> '*!$%'
```
```python
@app.route('/register', methods=['GET', 'POST'])
async def register():
if request.method == "POST":
...
salt = generate_salt()
password = request.form.get('password')
encrypted_password = encrypt_password(password, salt)
...
user = User(
username=username,
email=email,
password=encrypted_password,
salt=salt
)
...
```

View File

@@ -0,0 +1,46 @@
```
Menu = quart_imp.auth/is_email_address_valid
Title = is_email_address_valid - quart_imp.auth
```
```python
from quart_imp.auth import is_email_address_valid
```
```python
is_email_address_valid(
email_address: str
) -> bool
```
---
Checks if an email address is valid.
Is not completely RFC 5322 compliant, but it is good enough for most use cases.
Here are examples of mistakes that it will not catch:
##### Valid but fails:
```text
email@[123.123.123.123]
“email”@example.com
very.unusual.“@”.unusual.com@example.com
very.“(),:;<>[]”.VERY.“very@\\ "very”.unusual@strange.example.com
```
##### Invalid but passes:
```text
email@example.com (Joe Smith)
email@111.222.333.44444
```
##### Example:
```python
is_email_address_valid('hello@example.com') # >>> True
is_email_address_valid('hello@hello@example.com') # >>> False
```

View File

@@ -0,0 +1,58 @@
```
Menu = quart_imp.auth/is_username_valid
Title = is_username_valid - quart_imp.auth
```
```python
from quart_imp.auth import is_username_valid
```
```python
is_username_valid(
username: str,
allowed: t.Optional[t.List[t.Literal["all", "dot", "dash", "under"]]] = None
) -> bool
```
---
Checks if a username is valid.
Valid usernames can only include letters,
numbers, ., -, and _ but cannot begin or end with
the last three mentioned.
##### Example "all":
```python
is_username_valid("username", allowed=["all"])
```
Output:
```text
username : WILL PASS : True
user.name : WILL PASS : True
user-name : WILL PASS : True
user_name : WILL PASS : True
_user_name : WILL PASS : False
```
##### Example "dot", "dash":
```python
is_username_valid("username", allowed=["dot", "dash"])
```
Output:
```text
username : WILL PASS : True
user.name : WILL PASS : True
user-name : WILL PASS : True
user-name.name : WILL PASS : True
user_name : WILL PASS : False
_user_name : WILL PASS : False
.user.name : WILL PASS : False
```

View File

@@ -0,0 +1,33 @@
```
Menu = quart_imp.config/ImpBlueprintConfig
Title = ImpBlueprintConfig - quart_imp.config
```
```python
from quart_imp.config import ImpBlueprintConfig
```
```python
ImpBlueprintConfig(
enabled: bool = False,
url_prefix: str = None,
subdomain: str = None,
url_defaults: dict = None,
static_folder: t.Optional[str] = None,
template_folder: t.Optional[str] = None,
static_url_path: t.Optional[str] = None,
root_path: str = None,
cli_group: str = None,
init_session: dict = None
)
```
---
A class that holds a Quart-Imp blueprint configuration.
Most of these values are passed to the `Blueprint` class when the blueprint is registered.
The `enabled` argument is used to enable or disable the blueprint. This is useful for feature flags.
`init_session` is used to set the session values in the main `before_request` function.

View File

@@ -0,0 +1,32 @@
```
Menu = quart_imp.config/ImpConfig
Title = ImpConfig - quart_imp.config
```
```python
from quart_imp.config import ImpConfig
```
```python
ImpConfig(
init_session: t.Optional[t.Dict[str, t.Any]] = None,
)
```
---
The `ImpConfig` class is used to set the initial session data
that the application will use.
```python
imp_config = ImpConfig(
init_session={"key": "value"},
)
def create_app():
app = Quart(__name__)
QuartConfig(debug=True, app_instance=app)
imp.init_app(app, imp_config)
...
```

View File

@@ -0,0 +1,59 @@
```
Menu = quart_imp.config/QuartConfig
Title = QuartConfig - quart_imp.config
```
```python
from quart_imp.config import QuartConfig
```
```python
QuartConfig(
debug: t.Optional[bool] = None,
propagate_exceptions: t.Optional[bool] = None,
trap_http_exceptions: t.Optional[bool] = None,
trap_bad_request_errors: t.Optional[bool] = None,
secret_key: t.Optional[str] = None,
session_cookie_name: t.Optional[str] = None,
session_cookie_domain: t.Optional[str] = None,
session_cookie_path: t.Optional[str] = None,
session_cookie_httponly: t.Optional[bool] = None,
session_cookie_secure: t.Optional[bool] = None,
session_cookie_samesite: t.Optional[t.Literal["Lax", "Strict"]] = None,
permanent_session_lifetime: t.Optional[int] = None,
session_refresh_each_request: t.Optional[bool] = None,
use_x_sendfile: t.Optional[bool] = None,
send_file_max_age_default: t.Optional[int] = None,
error_404_help: t.Optional[bool] = None,
server_name: t.Optional[str] = None,
application_root: t.Optional[str] = None,
preferred_url_scheme: t.Optional[str] = None,
max_content_length: t.Optional[int] = None,
templates_auto_reload: t.Optional[bool] = None,
explain_template_loading: t.Optional[bool] = None,
max_cookie_size: t.Optional[int] = None,
app_instance: t.Optional["Quart"] = None
)
```
---
A class that holds a Quart configuration values.
You can set the configuration values to the app instance by either passing the app instance to the `app_instance`
parameter or by calling the `apply_config` method on the `QuartConfig` instance.
```python
def create_app():
app = Quart(__name__)
QuartConfig(debug=True, app_instance=app)
return app
```
or
```python
def create_app():
app = Quart(__name__)
config = QuartConfig(debug=True)
config.apply_config(app)
return app
```

View File

@@ -0,0 +1,47 @@
```
Menu = quart_imp.security/api_login_check
Title = api_login_check - quart_imp.security
```
```python
from quart_imp.security import api_login_check
```
```python
api_login_check(
session_key: str,
values_allowed: t.Union[t.List[t.Union[str, int, bool]], str, int, bool],
fail_json: t.Optional[t.Dict[str, t.Any]] = None
)
```
`@api_login_check(...)`
---
A decorator that is used to secure API routes that return JSON responses.
`session_key` The session key to check for.
`values_allowed` A list of or singular value(s) that the session key must contain.
`fail_json` JSON that is returned on failure. `{"error": "You are not logged in."}` by default.
##### Example:
```python
@bp.route("/api/resource", methods=["GET"])
@api_login_check('logged_in', True)
async def api_page():
...
```
##### Example of defined fail_json:
```python
@bp.route("/api/resource", methods=["GET"])
@api_login_check('logged_in', True, fail_json={"failed": "You need to be logged in."})
async def api_page():
...
```

View File

@@ -0,0 +1,48 @@
```
Menu = quart_imp.security/include_csrf
Title = include_csrf - quart_imp.security
```
```python
from quart_imp.security import include_csrf
```
```python
include_csrf(
session_key: str = "csrf",
form_key: str = "csrf",
abort_code: int = 401
)
```
`@include_csrf(...)`
---
A decorator that handles CSRF protection.
On a **GET** request, a CSRF token is generated and stored in the session key
specified by the session_key parameter.
On a **POST** request, the form_key specified is checked against the session_key
specified.
- If they match, the request is allowed to continue.
- If no match, the response will be abort(abort_code), default 401.
```python
@bp.route("/admin", methods=["GET", "POST"])
@include_csrf(session_key="csrf", form_key="csrf")
async def admin_page():
...
# You must pass in the CSRF token from the session into the template.
# Then add <input type="hidden" name="csrf" value="{{ csrf }}"> to the form.
return await render_template("admin.html", csrf=session.get("csrf"))
```
Form key:
```html
<input type="hidden" name="csrf" value="{{ csrf }}">
```

View File

@@ -0,0 +1,66 @@
```
Menu = quart_imp.security/login_check
Title = login_check - quart_imp.security
```
```python
from quart_imp.security import login_check
```
```python
login_check(
session_key: str,
values_allowed: t.Union[t.List[t.Union[str, int, bool]], str, int, bool],
fail_endpoint: t.Optional[str] = None,
pass_endpoint: t.Optional[str] = None,
endpoint_kwargs: t.Optional[t.Dict[str, t.Union[str, int]]] = None,
message: t.Optional[str] = None,
message_category: str = "message"
)
```
`@login_check(...)`
---
A decorator that checks if the specified session key exists and contains the specified value.
`session_key` The session key to check for.
`values_allowed` A list of or singular value(s) that the session key must contain.
`fail_endpoint` The endpoint to redirect to if the session key does not exist or does not contain the specified values.
`endpoint_kwargs` A dictionary of keyword arguments to pass to the redirect endpoint.
`message` If a message is specified, a flash message is shown.
`message_category` The category of the flash message.
##### Example of a route that requires a user to be logged in:
```python
@bp.route("/admin", methods=["GET"])
@login_check(
'logged_in',
True,
fail_endpoint='blueprint.login_page',
message="Login needed"
)
async def admin_page():
...
```
##### Example of a route that if the user is already logged in, redirects to the specified endpoint:
```python
@bp.route("/login-page", methods=["GET"])
@login_check(
'logged_in',
True,
pass_endpoint='blueprint.admin_page',
message="Already logged in"
)
async def login_page():
...
```

View File

@@ -0,0 +1,114 @@
```
Menu = quart_imp.security/pass_function_check
Title = pass_function_check - quart_imp.security
```
```python
from quart_imp.security import pass_function_check
```
```python
def pass_function_check(
function: t.Callable,
predefined_args: t.Optional[t.Dict] = None,
fail_endpoint: t.Optional[str] = None,
pass_endpoint: t.Optional[str] = None,
endpoint_kwargs: t.Optional[t.Dict[str, t.Union[str, int]]] = None,
message: t.Optional[str] = None,
message_category: str = "message",
fail_on_missing_kwargs: bool = False,
with_app_context: bool = False,
)
```
**NOTE: This was added mostly as an experimental feature, but ended up being useful in some cases.**
A decorator that takes the result of a function and checks if it is True or False.
URL variables from `@route` will be read by this decorator.
To use URL variables in your passed in function,
make sure your functions argument(s) name(s) match the name(s) of the URL variable(s).
**Example:**
```python
def check_if_number(value):
if isinstance(value, int):
return True
return False
@bp.route("/admin-page/<int:value>", methods=["GET"])
@login_check('logged_in', True, 'blueprint.login_page') # can be mixed with login_check
@pass_function_check(
check_if_number,
predefined_args=None,
fail_endpoint='www.index',
message="Failed message"
)
async def admin_page():
...
@bp.route("/admin-page/<int:value>", methods=["GET"])
@login_check('logged_in', True, 'blueprint.login_page') # can be mixed with login_check
@pass_function_check(
check_if_number,
predefined_args={'value': 10},
fail_endpoint='www.index',
message="Failed message"
)
async def admin_page_overwrite():
...
```
**Advanced use case:**
Here's an example of accessing quart.session from within the passed in function. including the
`with_app_context` parameter, the function will be called with `app_context()`.
```python
from quart import current_app
from quart import session
...
def check_if_number(number=1, session_=None):
if session_:
print(session_)
try:
int(number)
return True
except ValueError:
return False
@bp.route("/pass-func-check-with-url-var/<number>", methods=["GET"])
@pass_function_check(
check_if_number,
predefined_args={'number': 10, 'session_': session},
fail_endpoint="www.index",
with_app_context=True
)
async def admin_page_overwrite_with_session():
...
```
If you pass in a predefined arg that has the same key name as a session variable that exists, the value
of that predefined arg will be replaced with the session variable value.
```python
session['car'] = 'Toyota'
...
def check_function(car):
if car == 'Toyota':
return True
return False
...
@bp.route("/pass-func-check-with-url-var/<number>", methods=["GET"])
@pass_function_check(
check_function,
predefined_args={'car': session},
...
```
This will pass, as pass_function_check will replace the value of the predefined arg 'car' with the value
of the session variable 'car'.

View File

@@ -0,0 +1,57 @@
```
Menu = quart_imp.security/permission_check
Title = permission_check - quart_imp.security
```
```python
from quart_imp.security import permission_check
```
```python
permission_check(
session_key: str,
values_allowed: t.Union[t.List[t.Union[str, int, bool]], str, int, bool],
fail_endpoint: t.Optional[str] = None,
endpoint_kwargs: t.Optional[t.Dict[str, t.Union[str, int]]] = None,
message: t.Optional[str] = None,
message_category: str = "message"
)
```
`@permission_check(...)`
---
A decorator that checks if the specified session key exists and its value(s) match the specified value(s).
`session_key` The session key to check for.
`values_allowed` A list of or singular value(s) that the session key must contain.
`fail_endpoint` The endpoint to redirect to if the session key does not exist or does not contain the specified values.
`endpoint_kwargs` A dictionary of keyword arguments to pass to the redirect endpoint.
`message` If a message is specified, a flash message is shown.
`message_category` The category of the flash message.
##### Example:
```python
@bp.route("/admin-page", methods=["GET"])
@login_check(
'logged_in',
True,
'blueprint.login_page'
) # can be mixed with login_check
@permission_check(
'permissions',
['admin'],
fail_endpoint='www.index',
message="Failed message"
)
async def admin_page():
...
```