From 7c180376d64d0cdb35fd0d0b7aaee5646215d79a Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Mon, 21 Jun 2021 14:53:09 +0300 Subject: [PATCH] Add Simple Server and Coverage action (#2168) * Add Simple Server and CodeCov action * Remove token * Codecov to tox.ini * fix tox * Set coverage location * Add ignore to codecov * Try glob ignore * Setup CodeClimate * Allow coverage check to run * Change coverage check * Add codeclimate exclusions --- .codeclimate.yml | 12 ++++++ .coveragerc | 7 ++- .github/workflows/coverage.yml | 40 +++++++++++++++++ .github/workflows/pr-python37.yml | 5 ++- .github/workflows/pr-python38.yml | 3 ++ .github/workflows/pr-python39.yml | 3 ++ .gitignore | 1 + Makefile | 3 ++ codecov.yml | 14 ------ sanic/__main__.py | 72 +++++++++++++++++++------------ sanic/simple.py | 21 +++++++++ tox.ini | 19 +++++++- 12 files changed, 156 insertions(+), 44 deletions(-) create mode 100644 .codeclimate.yml create mode 100644 .github/workflows/coverage.yml delete mode 100644 codecov.yml create mode 100644 sanic/simple.py diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 00000000..506005ac --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,12 @@ +exclude_patterns: + - "sanic/__main__.py" + - "sanic/reloader_helpers.py" + - "sanic/simple.py" + - "sanic/utils.py" + - ".github/" + - "changelogs/" + - "docker/" + - "docs/" + - "examples/" + - "hack/" + - "scripts/" diff --git a/.coveragerc b/.coveragerc index 60831593..ac33bfaf 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,7 +1,12 @@ [run] branch = True source = sanic -omit = site-packages, sanic/utils.py, sanic/__main__.py +omit = + site-packages + sanic/__main__.py + sanic/reloader_helpers.py + sanic/simple.py + sanic/utils.py [html] directory = coverage diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 00000000..56a98398 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,40 @@ +name: Coverage check +# on: +# push: +# branches: +# - main +# tags: +# - "!*" # Do not execute on tags +# paths: +# - sanic/* +# - tests/* +# pull_request: +# paths: +# - "!*.MD" +on: [push, pull_request] +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: [3.9] + os: [ubuntu-latest] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies 🔨 + run: | + python -m pip install --upgrade pip + pip install tox + - uses: paambaati/codeclimate-action@v2.5.3 + if: always() + env: + CC_TEST_REPORTER_ID: ${{ secrets.CODECLIMATE }} + with: + coverageCommand: tox -e coverage diff --git a/.github/workflows/pr-python37.yml b/.github/workflows/pr-python37.yml index 2a99c409..80ade1e0 100644 --- a/.github/workflows/pr-python37.yml +++ b/.github/workflows/pr-python37.yml @@ -6,6 +6,9 @@ on: push: branches: - main + paths: + - sanic/* + - tests/* jobs: testPy37: @@ -14,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: -# os: [ubuntu-latest, macos-latest] + # os: [ubuntu-latest, macos-latest] os: [ubuntu-latest] config: - { python-version: 3.7, tox-env: py37 } diff --git a/.github/workflows/pr-python38.yml b/.github/workflows/pr-python38.yml index 7e7b6f09..c630f0e0 100644 --- a/.github/workflows/pr-python38.yml +++ b/.github/workflows/pr-python38.yml @@ -6,6 +6,9 @@ on: push: branches: - main + paths: + - sanic/* + - tests/* jobs: testPy38: diff --git a/.github/workflows/pr-python39.yml b/.github/workflows/pr-python39.yml index 1665d7f6..8b46d2c8 100644 --- a/.github/workflows/pr-python39.yml +++ b/.github/workflows/pr-python39.yml @@ -6,6 +6,9 @@ on: push: branches: - main + paths: + - sanic/* + - tests/* jobs: testPy39: diff --git a/.gitignore b/.gitignore index 502fcebc..1972c53e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ .coverage .coverage.* coverage +coverage.xml .tox settings.py .idea/* diff --git a/Makefile b/Makefile index ab50a1a0..a005b842 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,9 @@ test: clean test-coverage: clean python setup.py test --pytest-args="--cov sanic --cov-report term --cov-append " +view-coverage: + sanic ./coverage --simple + install: python setup.py install diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index c615d563..00000000 --- a/codecov.yml +++ /dev/null @@ -1,14 +0,0 @@ -codecov: - require_ci_to_pass: no -coverage: - precision: 3 - round: nearest - status: - project: - default: - target: auto - threshold: 0.5% - patch: - default: - target: auto - threshold: 0.75% diff --git a/sanic/__main__.py b/sanic/__main__.py index 63cd7688..027bf879 100644 --- a/sanic/__main__.py +++ b/sanic/__main__.py @@ -3,6 +3,7 @@ import sys from argparse import ArgumentParser, RawTextHelpFormatter from importlib import import_module +from pathlib import Path from typing import Any, Dict, Optional from sanic_routing import __version__ as __routing_version__ # type: ignore @@ -11,6 +12,7 @@ from sanic import __version__ from sanic.app import Sanic from sanic.config import BASE_LOGO from sanic.log import error_logger +from sanic.simple import create_simple_server class SanicArgumentParser(ArgumentParser): @@ -37,13 +39,28 @@ def main(): action="version", version=f"Sanic {__version__}; Routing {__routing_version__}", ) + parser.add_argument( + "--factory", + action="store_true", + help=( + "Treat app as an application factory, " + "i.e. a () -> callable" + ), + ) + parser.add_argument( + "-s", + "--simple", + dest="simple", + action="store_true", + help="Run Sanic as a Simple Server (module arg should be a path)\n ", + ) parser.add_argument( "-H", "--host", dest="host", type=str, default="127.0.0.1", - help="host address [default 127.0.0.1]", + help="Host address [default 127.0.0.1]", ) parser.add_argument( "-p", @@ -51,7 +68,7 @@ def main(): dest="port", type=int, default=8000, - help="port to serve on [default 8000]", + help="Port to serve on [default 8000]", ) parser.add_argument( "-u", @@ -62,7 +79,7 @@ def main(): help="location of unix socket\n ", ) parser.add_argument( - "--cert", dest="cert", type=str, help="location of certificate for SSL" + "--cert", dest="cert", type=str, help="Location of certificate for SSL" ) parser.add_argument( "--key", dest="key", type=str, help="location of keyfile for SSL\n " @@ -70,14 +87,6 @@ def main(): parser.add_bool_arguments( "--access-logs", dest="access_log", help="display access logs" ) - parser.add_argument( - "--factory", - action="store_true", - help=( - "Treat app as an application factory, " - "i.e. a () -> callable\n " - ), - ) parser.add_argument( "-w", "--workers", @@ -103,7 +112,12 @@ def main(): help="Extra directories to watch and reload on changes\n ", ) parser.add_argument( - "module", help="path to your Sanic app. Example: path.to.server:app" + "module", + help=( + "Path to your Sanic app. Example: path.to.server:app\n" + "If running a Simple Server, path to directory to serve. " + "Example: ./\n" + ), ) args = parser.parse_args() @@ -112,25 +126,29 @@ def main(): if module_path not in sys.path: sys.path.append(module_path) - delimiter = ":" if ":" in args.module else "." - module_name, app_name = args.module.rsplit(delimiter, 1) + if args.simple: + path = Path(args.module) + app = create_simple_server(path) + else: + delimiter = ":" if ":" in args.module else "." + module_name, app_name = args.module.rsplit(delimiter, 1) - if app_name.endswith("()"): - args.factory = True - app_name = app_name[:-2] + if app_name.endswith("()"): + args.factory = True + app_name = app_name[:-2] - module = import_module(module_name) - app = getattr(module, app_name, None) - if args.factory: - app = app() + module = import_module(module_name) + app = getattr(module, app_name, None) + if args.factory: + app = app() - app_type_name = type(app).__name__ + app_type_name = type(app).__name__ - if not isinstance(app, Sanic): - raise ValueError( - f"Module is not a Sanic app, it is a {app_type_name}. " - f"Perhaps you meant {args.module}.app?" - ) + if not isinstance(app, Sanic): + raise ValueError( + f"Module is not a Sanic app, it is a {app_type_name}. " + f"Perhaps you meant {args.module}.app?" + ) if args.cert is not None or args.key is not None: ssl: Optional[Dict[str, Any]] = { "cert": args.cert, diff --git a/sanic/simple.py b/sanic/simple.py new file mode 100644 index 00000000..c575917e --- /dev/null +++ b/sanic/simple.py @@ -0,0 +1,21 @@ +from pathlib import Path + +from sanic import Sanic +from sanic.exceptions import SanicException +from sanic.response import redirect + + +def create_simple_server(directory: Path): + if not directory.is_dir(): + raise SanicException( + "Cannot setup Sanic Simple Server without a path to a directory" + ) + + app = Sanic("SimpleServer") + app.static("/", directory, name="main") + + @app.get("/") + def index(_): + return redirect(app.url_for("main", filename="index.html")) + + return app diff --git a/tox.ini b/tox.ini index dac8dacb..590dc25a 100644 --- a/tox.ini +++ b/tox.ini @@ -76,6 +76,23 @@ deps = docutils pygments gunicorn==20.0.4 - commands = make docs-test + +[testenv:coverage] +usedevelop = True +deps = + sanic-testing>=0.6.0 + coverage==5.3 + pytest==5.2.1 + pytest-cov + pytest-sanic + pytest-sugar + pytest-benchmark + chardet==3.* + beautifulsoup4 + gunicorn==20.0.4 + uvicorn + websockets>=9.0 +commands = + pytest tests --cov=./sanic --cov-report=xml