Skip to content

Commit

Permalink
beta release for 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
unknown authored and TheClockTwister committed Mar 12, 2021
2 parents 08c2849 + 0849746 commit 09bffbf
Show file tree
Hide file tree
Showing 25 changed files with 812 additions and 54 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ Python/*.egg-info
*.rar

docs/_build/

sandbox\.py

*.log
128 changes: 83 additions & 45 deletions Aeros/WebServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@

import functools
import inspect
from typing import Union, List, Tuple
from quart import Quart
import hashlib
import uvicorn
from logging import INFO
from colorama import init
import sys

from .caching.server import Cache
from .compression import Base
from .Request import EasyRequest
import logging
import logging.handlers


class WebServer(Quart):
Expand All @@ -24,6 +25,8 @@ class WebServer(Quart):
directly from the Python code itself, making it easier to integrate in higher-level scripts and
applications without calling os.system() od subprocess.Popen(). """

__log_format_std = '[%(asctime)s] %(levelname)s: %(message)s' # for instance logger "quart.app"

def __init__(self, import_name: str, host: str = "0.0.0.0", port: int = 80, include_server_header: bool = True,
logging_level: int = INFO, cache: Cache = None, compression: Base = None,
global_headers: dict = {}, *args, **kwargs):
Expand All @@ -37,6 +40,10 @@ def __init__(self, import_name: str, host: str = "0.0.0.0", port: int = 80, incl
self._cache = cache
self._compression = compression

h = logging.StreamHandler(sys.stdout) # make own logging format the same as uvicorn uses
h.setFormatter(logging.Formatter(self.__log_format_std))
self.logger.handlers = [h]

self._global_headers = global_headers
if include_server_header:
self._global_headers['server'] = self._global_headers.get('server', 'Aeros 2.0.0 (uvicorn)') # change server header to Aeros
Expand Down Expand Up @@ -66,30 +73,6 @@ async def decorated_function(*args2, **kwargs2):
def clear_cache(self):
self._cache.clear()

def __make_config(self, **kwargs) -> dict:
""" Initializes all features such as `Compression` and `Cache` that are handled by Aeros.
This method is called when a server is going to be run, weather it be via uvicorn single thread
or multicore via the filename and variable name.
Returns:
dict: The config used by uvicorn to run the server instance
"""

# Initialize extra features just in case the user replaced them with their own instances
if issubclass(self._cache.__class__, Cache):
self._cache.init_app(self)
else:
self.logger.info("Caching is disabled")

if issubclass(self._compression.__class__, Base):
self._compression.init_app(self)
else:
self.logger.info("Compression is disabled")

kwargs["headers"] = kwargs.get("headers", self._global_headers) # inject default value if none present

return kwargs

def _get_own_instance_path(self) -> str:
"""
Retrieves the file and variable name of this instance to be used in the uvicorn CLI.
Expand Down Expand Up @@ -120,41 +103,96 @@ def _get_own_instance_path(self) -> str:

def run_server(self,
host: str = None, port: int = None, log_level: int = None, # overrides for __init__
use_colors: bool = True, access_log: bool = True,
access_log_file: str = None, access_log_to_std: bool = True,
soft_limit: int = 32, hard_limit: int = 42,
**kwargs
) -> None:
""" Generates the necessary config and runs the server instance. Possible keyword arguments are the same as supported by the
`uvicorn.run()` method that passes it to `Config(app, **kwargs)`.
`uvicorn.run()` method as they are passed into `Config(app, **kwargs)`. This method also configures features like caching
and compression that are not default in Quart or Flask and unique to Aeros or require third-party modules to be configured.
"""
self.logger.setLevel(log_level if log_level else self.log_level)
if use_colors:
init() # init terminal on Windows just to make sure sequences are displayed correctly as colors

config = self.__make_config(
host=host if host else self._host,
port=port if port else self._port,
log_level=log_level if log_level else self.log_level,
use_colors=use_colors, access_log=access_log,
limit_concurrency=soft_limit + 1, backlog=hard_limit,
**kwargs
)
instance = self
if kwargs.get('suppress_deprecation_warning', False):
del kwargs['suppress_deprecation_warning']
else:
self.logger.warning('The usage of run_server() is deprecated. Use run() instead.') # delete, otherwise it gets into the config

# Initialize extra features just in case the user replaced them with their own instances
if issubclass(self._cache.__class__, Cache):
self._cache.init_app(self)
self.logger.info("Caching is enabled")
else:
self.logger.info("Caching is disabled")

if issubclass(self._compression.__class__, Base):
self._compression.init_app(self)
self.logger.info("Compression is enabled")
else:
self.logger.info("Compression is disabled")

# create uvicorn configuration ------------------------------------------------------------------------------------------------

config = {
'app': self,
'host': host if host else self._host,
'port': port if port else self._port,
'headers': kwargs.get('headers', self._global_headers),

# try multi-core execution
'log_level': log_level if log_level else self.log_level,
'access_log': bool(access_log_file) or access_log_to_std,

'limit_concurrency': soft_limit + 1,
'backlog': hard_limit,

**kwargs
}

# set logging information
self.logger.setLevel(log_level if log_level else self.log_level) # re-set log level for instance logger if changed
config['log_config'] = { # logging configuration for uvicorn.run()
'version': 1, 'disable_existing_loggers': True,
'formatters': {
'default': {'()': 'logging.Formatter', 'fmt': self.__log_format_std},
'access_file': {'()': 'uvicorn.logging.AccessFormatter', 'fmt': '[%(asctime)s] %(client_addr)s | "%(request_line)s" | %(status_code)s'},
'access_std': {'()': 'uvicorn.logging.AccessFormatter', 'fmt': '[%(asctime)s] ACCESS: %(client_addr)s | "%(request_line)s" | %(status_code)s'}
},
'handlers': {
'default': {'formatter': 'default', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout'},
'error': {'formatter': 'default', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stderr'}
},
'loggers': {
'uvicorn': {'level': log_level, 'handlers': ['default']},
'uvicorn.access': {'level': 'INFO', 'propagate': False, 'handlers': []}
}
}

if access_log_to_std:
config['log_config']['handlers']['access_std'] = {'formatter': 'access_std', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout'}
config['log_config']['loggers']['uvicorn.access']['handlers'].append('access_std')
if access_log_file:
config['log_config']['handlers']['access_file'] = {'formatter': 'access_file', 'class': 'logging.FileHandler', 'filename': access_log_file}
config['log_config']['loggers']['uvicorn.access']['handlers'].append('access_file')

# try multi-core execution
if config.get('workers', None):
self.logger.debug(f"{self.__class__.__name__}.run_server() multi-core execution is a beta feature. You can also use uvicorn CLI directly.")
instance_path = self._get_own_instance_path()
if instance_path is not None:
# if instance found, run multi-core by replacing instance with path
instance = instance_path
config['app'] = instance_path
self.logger.info(f"Starting in multi-thread mode...")
else:
# if instance not found, delete worker count and launch single-thread
del config['workers']
self.logger.info(f"Starting in single-thread mode...")

x = config.get('app')
del config['app']
print(x)

self.logger.info(f"Starting in {'multi-thread' if type(instance) == str else 'single-thread'} mode...")
uvicorn.run(x, **config)

uvicorn.run(instance, **config)
def run(self, *args, **kwargs):
return self.run_server(*args, **kwargs, suppress_deprecation_warning=True)

def route(self, *args, **kwargs):
def new_route_decorator(func):
Expand Down
18 changes: 18 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Contributing
When contributing to this repository, please first discuss the change you wish
to make via issue before submitting a merge request.

## Code Style
Please assure that cour contribution complies to the following code style before
asking to merge a merge request (you may just set it to WIP and correct code style).

### Required
- All variable names must be in English
- Code documentation must be in English
- All code must be readable (no binaries)

### Optional
- [PEP8 convention](https://www.python.org/dev/peps/pep-0008/#prescriptive-naming-conventions)
- A blank line at the end of each file
- Docstrings for every class, method and function
- Unit tests for good coverage
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
![](https://raw.githubusercontent.com/TheClockTwister/Aeros/master/img/header.png)

<p align="center">
<img src="https://img.shields.io/pypi/pyversions/Aeros?label=Python%20Version&style=flat-square">
<img src="https://img.shields.io/pypi/v/Aeros?label=PyPi%20Version&style=flat-square"/>
<img src="https://img.shields.io/pypi/format/Aeros?label=PyPi%20Format&style=flat-square"/>
<img src="https://img.shields.io/pypi/dm/Aeros?label=Downloads&style=flat-square"/>
<img src="https://img.shields.io/github/repo-size/TheClockTwister/Aeros?label=Repo%20Size&style=flat-square">
<img src="https://img.shields.io/pypi/pyversions/Aeros?label=Python%20Version">
<img src="https://img.shields.io/pypi/v/Aeros?label=PyPi%20Version"/>
<img src="https://img.shields.io/github/repo-size/TheClockTwister/Aeros?label=Repo%20Size">
<img src="https://img.shields.io/pypi/format/Aeros?label=PyPi%20Format"/>
<img src="https://readthedocs.org/projects/aeros/badge/?version=latest"/>
<img src="https://img.shields.io/pypi/dm/Aeros?label=Downloads"/>
</p>

# Python package documentation
# Python package [documentation](https://aeros.readthedocs.io/en/latest/)

[Aeros](https://pypi.org/project/Aeros/) is an all-in-one ASGI (Asynchronous Server Gateway Interface) package containing wrappers for widely used Web and API functions, as well as
custom-written helper functions which make backend development a lot easier and require way less code than a native implementation using the featured packages would.
Expand Down Expand Up @@ -56,7 +57,6 @@ A detailed overview of pros and cons can be found here:
| Concurrent requests | 64 | 1 | ? |
| Worker threads | 8 | 1 | ? |


The following graph shows the overall performance improvement since version 1.0.6. The replacement of Aeros backend in version 2.0.0 is clearly visible as a boot in single-thread
performance.

Expand Down
File renamed without changes
File renamed without changes
Empty file added doc/header.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added doc/versions.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/workers.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Binary file added docs/_static/check_mark.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 09bffbf

Please sign in to comment.