An ASGI compliant web server
An ASGI compliant web server which started as a companion project to the
Tonberry ASGI framework.
Just do the usual.
$ pip install qactuar
One process handles all requests using coroutines. Right now this is the only model that Windows supports. With WSL
though you can run the other models just fine.
Forks a new process per request. Within the fork all requests are handled with coroutines.
Creates a pool of processes (by default limited to the number of cpu cores) that the main process will cycle through and
hand off requests to as they come in. Within the fork all requests are handled with coroutines.
During the installation it creates a command line app.
$ qactuar module:app
If your module is in the python path then it will get imported and any ASGI compatible object in the module can be
called. If the apps are set up in the config (see Configuration section below) then you can just start up qactuar
without any arguments.
Command line options can be seen with -h
$ qactuar -h
positional arguments:
str path to a module and variable of an initialised app seperated by a colon; example -> module:app
optional arguments:
-h, --help show this help message and exit
--host str Host to bind to (default: 127.0.0.1)
-p int, --port int Port to bind to (default: 8000)
-s str, --server-type str
Option to set the server concurrency model to async_only, simple_fork or prefork (default: async_only)
--select-sleep-time float
How long to wait in seconds between checking the socket for new connections (default: 0.025)
-r float, --recv-timeout float
How long to wait in seconds for data from an open client connection (default: 0.001)
--recv-bytes int How many bytes to wait for from an open client connection (default: 65536)
--process-pool-size int
PRE-FORK MODE ONLY - How many processes to start up. Recomended size is equal to the number of cpu cores (default: os.cpu_count())
--request-timeout float
How long to wait in seconds for a request to be considrered timed-out (default: 5)
--ssl-cert-path str Path to a certification file for SSL (default: )
--ssl-cert-key str Path to a certification key file for SSL (default: )
--ssl-ciphers str String representing cipher suites to use in the SSLContext (default: EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH)
-a str, --app-dir str
Path to the directory where the module with the app is located (default: .)
-u bool, --use-uvloop bool
Try to use uvloop if it is available (default: True)
-v, --version show program's version number and exit
from tonberry import create_app, expose
from tonberry.content_types import TextPlain
import qactuar
class Root:
@expose.get
async def index(self) -> TextPlain:
return "Hello, world!"
if __name__ == "__main__":
app = create_app(Root)
qactuar.run(app=app)
# other keyword arguments for run() are host, port and conf
For an intuitive but powerful ASGI framework, check out Tonberry!
File, command line and programatic based configurations are supported. For a config file just create an environment
variable QACTUAR_CONFIG
and set the value to the absolute or relative path of a JSON file. This file can overwride
any of the default values listed here. Only the values you wish to override need to be provided.
str
= “127.0.0.1”int
= 8000str
= “async_only” | “prefork” | “simple_fork”float
= 0.025float
= 0.001int
= 65536int
= os.cpu_count()float
= 5str
= “”str
= “”str
= “EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH”str
= “.”bool
= TrueDict[str, str]
= {}Dict[str, Any]
= default_log_setup (see below)The APPS
dictionary takes a route
as the key and a module:app
style string as the value. Multiple applications
can be hosted at the same time by registering each at its own route. A basic example can be seen in the
qactuar_config.json file.
The config is managed in a dataclass object and can be created programmatically. All arguments are optional and are
defined above.
config = qactuar.Config(HOST='0.0.0.0')
qactuar.run(app=app, conf=config)
The LOGS
dictionary is for the logging.config setup as detailed in the
Python documentation. It uses logging.config.dictConfig to set
the logging configs. The loggers used throught the code are qt_server
(used by the parent process), qt_child
(used
in the child processes), qt_access
(used to log each request), qt_exception
(used to log any exceptions)
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"standard": {"format": "{asctime} {levelname} {message}", "style": "{"},
"access": {
"format": "{asctime} ACCESS {host}:{port} {request_id} {method} HTTP/{http_version} {path} {status} {message}",
"style": "{"
},
"exception": {
"format": "{asctime} {levelname} {request_id} {message}",
"style": "{"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "standard",
"stream": "ext://sys.stdout"
},
"access": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "access",
"stream": "ext://sys.stdout"
},
"exception": {
"class": "logging.StreamHandler",
"level": "ERROR",
"formatter": "exception",
"stream": "ext://sys.stderr"
}
},
"loggers": {
"qt_server": {"handlers": ["console"], "level": "DEBUG"},
"qt_child": {"handlers": ["console"], "level": "DEBUG"},
"qt_access": {"handlers": ["access"], "level": "INFO"},
"qt_exception": {"handlers": ["exception"], "level": "ERROR"}
}
}
If changing the LOGS
config then the whole dictionary needs to be replaced. Individual parts of the log config cannot
be changed by themselves.
Included is a utility wrapper to take a Tornado Request Handler and make it work with ASGI. See
tornado_app.py for an example.
Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull
requests.
SemVer is used for versioning. For the versions available, see the
tags on this repository.
This project is licensed under the MIT License - see the LICENSE.txt file for details.