diff --git a/.bumpversion.cfg b/.bumpversion.cfg index cacd01d9..9857095f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.5.4 +current_version = 2.5.5 [bumpversion:file:.env] diff --git a/.editorconfig b/.editorconfig index b41370f0..f63e8907 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,10 +1,7 @@ root = true [*.py] -max_line_length = 120 +max_line_length = 100 indent_style = space indent_size = 4 ignore_frosted_errors = E103 -skip = runtests.py,build -balanced_wrapping = true -not_skip = __init__.py diff --git a/.env b/.env index 41780780..a3e832d2 100644 --- a/.env +++ b/.env @@ -11,7 +11,7 @@ fi export PROJECT_NAME=$OPEN_PROJECT_NAME export PROJECT_DIR="$PWD" -export PROJECT_VERSION="2.5.4" +export PROJECT_VERSION="2.5.5" if [ ! -d "venv" ]; then if ! hash pyvenv 2>/dev/null; then diff --git a/.gitignore b/.gitignore index a5b3f21f..99fc0b6f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.egg-info build eggs +.eggs parts var sdist @@ -73,3 +74,6 @@ venv/ # Emacs backup *~ + +# VSCode +/.vscode diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..4d17c9c8 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,6 @@ +[settings] +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses=True +line_length=100 diff --git a/.travis.yml b/.travis.yml index e50d3a4a..ee58dd62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,27 @@ matrix: - os: linux sudo: required python: 3.7 - env: TOXENV=py37-marshmallow2 + env: TOXENV=py37-marshmallow3 + - os: linux + sudo: required + python: 3.7 + env: TOXENV=py37-black + - os: linux + sudo: required + python: 3.7 + env: TOXENV=py37-flake8 + - os: linux + sudo: required + python: 3.7 + env: TOXENV=py37-bandit + - os: linux + sudo: required + python: 3.7 + env: TOXENV=py37-vulture + - os: linux + sudo: required + python: 3.7 + env: TOXENV=py37-isort - os: linux sudo: required python: pypy3.5-6.0 diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md index fced93a8..60a20dfc 100644 --- a/ACKNOWLEDGEMENTS.md +++ b/ACKNOWLEDGEMENTS.md @@ -84,6 +84,8 @@ Documenters - Joshua Crowgey (@jcrowgey) - Antti Kaihola (@akaihola) - Simon Ince (@Simon-Ince) +- Edvard Majakari (@EdvardM) + -------------------------------------------- diff --git a/CHANGELOG.md b/CHANGELOG.md index 604482ef..38626a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,13 @@ Ideally, within a virtual environment. Changelog ========= +### 2.5.5 - June 13, 2019 +- Fixed issue #808: Problems with command line invocation via hug CLI +- Fixed issue #647: Support for arbitrary URL complexity when using CORS middleware +- Fixed issue #805: Added documentation for `map_params` feature +- Added initial automated code cleaning and linting partially satisfying [HOPE-8 -- Style Guideline for Hug](https://github.com/hugapi/HOPE/blob/master/all/HOPE-8--Style-Guide-for-Hug-Code.md#hope-8----style-guide-for-hug-code) +- Implemented [HOPE-20 -- The Zen of Hug](https://github.com/hugapi/HOPE/blob/master/all/HOPE-20--The-Zen-of-Hug.md) + ### 2.5.4 hotfix - May 19, 2019 - Fix issue #798 - Development runner `TypeError` when executing cli diff --git a/Pipfile b/Pipfile new file mode 100644 index 00000000..b9ba84f6 --- /dev/null +++ b/Pipfile @@ -0,0 +1,11 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] + +[dev-packages] + +[requires] +python_version = "3.7" diff --git a/README.md b/README.md index e5d47e06..c0ec61a9 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ As a result of these goals, hug is Python 3+ only and built upon [Falcon's](http [![HUG Hello World Example](https://raw.github.com/hugapi/hug/develop/artwork/example.gif)](https://github.com/hugapi/hug/blob/develop/examples/hello_world.py) - Installing hug =================== @@ -37,9 +36,9 @@ pip3 install hug --upgrade Ideally, within a [virtual environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/). - Getting Started =================== + Build an example API with a simple endpoint in just a few lines. ```py @@ -118,7 +117,6 @@ Then you can access the example from `localhost:8000/v1/echo?text=Hi` / `localho Note: versioning in hug automatically supports both the version header as well as direct URL based specification. - Testing hug APIs =================== @@ -141,7 +139,6 @@ def tests_happy_birthday(): assert response.data is not None ``` - Running hug with other WSGI based servers =================== @@ -155,7 +152,6 @@ uwsgi --http 0.0.0.0:8000 --wsgi-file examples/hello_world.py --callable __hug_w To run the hello world hug example API. - Building Blocks of a hug API =================== @@ -182,7 +178,6 @@ def math(number_1:int, number_2:int): #The :int after both arguments is the Type Type annotations also feed into `hug`'s automatic documentation generation to let users of your API know what data to supply. - **Directives** functions that get executed with the request / response data based on being requested as an argument in your api_function. These apply as input parameters only, and can not be applied currently as output formats or transformations. @@ -242,7 +237,6 @@ def hello(): as shown, you can easily change the output format for both an entire API as well as an individual API call - **Input Formatters** a function that takes the body of data given from a user of your API and formats it for handling. ```py @@ -253,7 +247,6 @@ def my_input_formatter(data): Input formatters are mapped based on the `content_type` of the request data, and only perform basic parsing. More detailed parsing should be done by the Type Annotations present on your `api_function` - **Middleware** functions that get called for every request a hug API processes ```py @@ -272,6 +265,18 @@ You can also easily add any Falcon style middleware using: __hug__.http.add_middleware(MiddlewareObject()) ``` +**Parameter mapping** can be used to override inferred parameter names, eg. for reserved keywords: + +```py +import marshmallow.fields as fields +... + +@hug.get('/foo', map_params={'from': 'from_date'}) # API call uses 'from' +def get_foo_by_date(from_date: fields.DateTime()): + return find_foo(from_date) +``` + +Input formatters are mapped based on the `content_type` of the request data, and only perform basic parsing. More detailed parsing should be done by the Type Annotations present on your `api_function` Splitting APIs over multiple files =================== @@ -314,7 +319,6 @@ Or alternatively - for cases like this - where only one module is being included hug.API(__name__).extend(something, '/something') ``` - Configuring hug 404 =================== @@ -346,7 +350,6 @@ def not_found_handler(): return "Not Found" ``` - Asyncio support =============== @@ -354,6 +357,7 @@ When using the `get` and `cli` method decorator on coroutines, hug will schedule the execution of the coroutine. Using asyncio coroutine decorator + ```py @hug.get() @asyncio.coroutine @@ -362,6 +366,7 @@ def hello_world(): ``` Using Python 3.5 async keyword. + ```py @hug.get() async def hello_world(): @@ -371,9 +376,9 @@ async def hello_world(): NOTE: Hug is running on top Falcon which is not an asynchronous server. Even if using asyncio, requests will still be processed synchronously. - Using Docker =================== + If you like to develop in Docker and keep your system clean, you can do that but you'll need to first install [Docker Compose](https://docs.docker.com/compose/install/). Once you've done that, you'll need to `cd` into the `docker` directory and run the web server (Gunicorn) specified in `./docker/gunicorn/Dockerfile`, after which you can preview the output of your API in the browser on your host machine. @@ -413,7 +418,6 @@ bash-4.3# tree 1 directory, 3 files ``` - Why hug? =================== diff --git a/hug/__init__.py b/hug/__init__.py index e40d8508..2600bd3f 100644 --- a/hug/__init__.py +++ b/hug/__init__.py @@ -34,7 +34,6 @@ from falcon import * from hug import ( - authentication, directives, exceptions, format, @@ -89,10 +88,10 @@ ) from hug.types import create as type +# The following imports must be imported last; in particular, defaults to have access to all modules +from hug import authentication # isort:skip from hug import development_runner # isort:skip -from hug import ( - defaults, -) # isort:skip - must be imported last for defaults to have access to all modules +from hug import defaults # isort:skip try: # pragma: no cover - defaulting to uvloop if it is installed import uvloop diff --git a/hug/_version.py b/hug/_version.py index 8f9fe1d0..e5710554 100644 --- a/hug/_version.py +++ b/hug/_version.py @@ -21,4 +21,4 @@ """ from __future__ import absolute_import -current = "2.5.4" +current = "2.5.5" diff --git a/hug/api.py b/hug/api.py index 0a6c35b1..1bb9f669 100644 --- a/hug/api.py +++ b/hug/api.py @@ -78,6 +78,7 @@ def __init__(self, api): class HTTPInterfaceAPI(InterfaceAPI): """Defines the HTTP interface specific API""" + __slots__ = ( "routes", "versions", @@ -123,10 +124,10 @@ def urls(self): def handlers(self): """Returns all registered handlers attached to this API""" used = [] - for base_url, mapping in self.routes.items(): - for url, methods in mapping.items(): - for method, versions in methods.items(): - for version, handler in versions.items(): + for _base_url, mapping in self.routes.items(): + for _url, methods in mapping.items(): + for _method, versions in methods.items(): + for _version, handler in versions.items(): if not handler in used: used.append(handler) yield handler @@ -179,15 +180,15 @@ def extend(self, http_api, route="", base_url="", **kwargs): self.versions.update(http_api.versions) base_url = base_url or self.base_url - for router_base_url, routes in http_api.routes.items(): + for _router_base_url, routes in http_api.routes.items(): self.routes.setdefault(base_url, OrderedDict()) for item_route, handler in routes.items(): - for method, versions in handler.items(): - for version, function in versions.items(): + for _method, versions in handler.items(): + for _version, function in versions.items(): function.interface.api = self.api self.routes[base_url].setdefault(route + item_route, {}).update(handler) - for sink_base_url, sinks in http_api.sinks.items(): + for _sink_base_url, sinks in http_api.sinks.items(): for url, sink in sinks.items(): self.add_sink(sink, route + url, base_url=base_url) @@ -344,9 +345,11 @@ def handle_404(request, response, *args, **kwargs): return handle_404 def version_router( - self, request, response, api_version=None, versions={}, not_found=None, **kwargs + self, request, response, api_version=None, versions=None, not_found=None, **kwargs ): """Intelligently routes a request to the correct handler based on the version being requested""" + if versions is None: + versions = {} request_version = self.determine_version(request, api_version) if request_version: request_version = int(request_version) @@ -551,9 +554,9 @@ def add_directive(self, directive): def handlers(self): """Returns all registered handlers attached to this API""" - if getattr(self, "_http"): + if getattr(self, "_http", None): yield from self.http.handlers() - if getattr(self, "_cli"): + if getattr(self, "_cli", None): yield from self.cli.handlers() @property diff --git a/hug/decorators.py b/hug/decorators.py index e58ee299..58ba28d2 100644 --- a/hug/decorators.py +++ b/hug/decorators.py @@ -157,7 +157,7 @@ def decorator(middleware_method): class MiddlewareRouter(object): __slots__ = () - def process_response(self, request, response, resource, req_succeeded): + def process_response(self, request, response, resource, _req_succeeded): return middleware_method(request, response, resource) apply_to_api.http.add_middleware(MiddlewareRouter()) @@ -179,7 +179,7 @@ def process_request(self, request, response): self.gen = middleware_generator(request) return self.gen.__next__() - def process_response(self, request, response, resource, req_succeeded): + def process_response(self, request, response, resource, _req_succeeded): return self.gen.send((response, resource)) apply_to_api.http.add_middleware(MiddlewareRouter()) diff --git a/hug/development_runner.py b/hug/development_runner.py index 196f0713..d8407dcc 100644 --- a/hug/development_runner.py +++ b/hug/development_runner.py @@ -76,10 +76,8 @@ def hug( print(str(api.cli)) sys.exit(1) - use_cli_router = slice( - sys.argv.index("-c") if "-c" in sys.argv else sys.argv.index("--command") + 2 - ) - sys.argv[1:] = sys.argv[use_cli_router] + flag_index = (sys.argv.index("-c") if "-c" in sys.argv else sys.argv.index("--command")) + 1 + sys.argv = sys.argv[flag_index:] api.cli.commands[command]() return diff --git a/hug/interface.py b/hug/interface.py index 0539fbfe..42065774 100644 --- a/hug/interface.py +++ b/hug/interface.py @@ -117,7 +117,7 @@ def __init__(self, function, args=None): self.input_transformations[name] = transformer - def __call__(__hug_internal_self, *args, **kwargs): + def __call__(__hug_internal_self, *args, **kwargs): # noqa: N805 """"Calls the wrapped function, uses __hug_internal_self incase self is passed in as a kwarg from the wrapper""" if not __hug_internal_self.is_coroutine: return __hug_internal_self._function(*args, **kwargs) @@ -270,7 +270,7 @@ def validate(self, input_parameters, context): type_handler, input_parameters[key], context=context ) except InvalidTypeData as error: - errors[key] = error.reasons or str(error.message) + errors[key] = error.reasons or str(error) except Exception as error: if hasattr(error, "args") and error.args: errors[key] = error.args[0] @@ -342,7 +342,7 @@ def _rewrite_params(self, params): @staticmethod def cleanup_parameters(parameters, exception=None): - for parameter, directive in parameters.items(): + for _parameter, directive in parameters.items(): if hasattr(directive, "cleanup"): directive.cleanup(exception=exception) @@ -385,7 +385,7 @@ def __call__(self, *args, **kwargs): context = self.api.context_factory(api=self.api, api_version=self.version, interface=self) """Defines how calling the function locally should be handled""" - for requirement in self.requires: + for _requirement in self.requires: lacks_requirement = self.check_requirements(context=context) if lacks_requirement: self.api.delete_context(context, lacks_requirement=lacks_requirement) @@ -604,7 +604,7 @@ def exit_callback(message): args.extend(pass_to_function.pop(self.additional_options, ())) if self.interface.takes_kwargs: add_options_to = None - for index, option in enumerate(unknown): + for option in unknown: if option.startswith("--"): if add_options_to: value = pass_to_function[add_options_to] @@ -959,9 +959,9 @@ def documentation(self, add_to=None, version=None, prefix="", base_url="", url=" def urls(self, version=None): """Returns all URLS that are mapped to this interface""" urls = [] - for base_url, routes in self.api.http.routes.items(): + for _base_url, routes in self.api.http.routes.items(): for url, methods in routes.items(): - for method, versions in methods.items(): + for _method, versions in methods.items(): for interface_version, interface in versions.items(): if interface_version == version and interface == self: if not url in urls: diff --git a/hug/json_module.py b/hug/json_module.py index 5df4f4ae..f881f7f8 100644 --- a/hug/json_module.py +++ b/hug/json_module.py @@ -5,7 +5,7 @@ if HUG_USE_UJSON: import ujson as json - class dumps_proxy: + class dumps_proxy: # noqa: N801 """Proxies the call so non supported kwargs are skipped and it enables escape_forward_slashes to simulate built-in json """ diff --git a/hug/middleware.py b/hug/middleware.py index 61bd0ffb..59f02473 100644 --- a/hug/middleware.py +++ b/hug/middleware.py @@ -153,8 +153,10 @@ class CORSMiddleware(object): __slots__ = ("api", "allow_origins", "allow_credentials", "max_age") def __init__( - self, api, allow_origins: list = ["*"], allow_credentials: bool = True, max_age: int = None + self, api, allow_origins: list = None, allow_credentials: bool = True, max_age: int = None ): + if allow_origins is None: + allow_origins = ["*"] self.api = api self.allow_origins = allow_origins self.allow_credentials = allow_credentials @@ -169,7 +171,7 @@ def match_route(self, reqpath): reqpath = re.sub("^(/v\d*/?)", "/", reqpath) base_url = getattr(self.api.http, "base_url", "") reqpath = reqpath.replace(base_url, "", 1) if base_url else reqpath - if re.match(re.sub(r"/{[^{}]+}", r"/[\\w-]+", route) + "$", reqpath): + if re.match(re.sub(r"/{[^{}]+}", ".+", route) + "$", reqpath, re.DOTALL): return route return reqpath diff --git a/hug/output_format.py b/hug/output_format.py index 1d50d38c..6e60dfb3 100644 --- a/hug/output_format.py +++ b/hug/output_format.py @@ -384,7 +384,7 @@ def output_type(data, request, response): handler = default accepted = [accept_quality(accept_type) for accept_type in accept.split(",")] accepted.sort(key=itemgetter(0)) - for quality, accepted_content_type in reversed(accepted): + for _quality, accepted_content_type in reversed(accepted): if accepted_content_type in handlers: handler = handlers[accepted_content_type] break diff --git a/hug/route.py b/hug/route.py index 91eac8a2..56c3b96c 100644 --- a/hug/route.py +++ b/hug/route.py @@ -27,13 +27,13 @@ from falcon import HTTP_METHODS import hug.api -from hug.routing import CLIRouter as cli -from hug.routing import ExceptionRouter as exception -from hug.routing import LocalRouter as local -from hug.routing import NotFoundRouter as not_found -from hug.routing import SinkRouter as sink -from hug.routing import StaticRouter as static -from hug.routing import URLRouter as http +from hug.routing import CLIRouter as cli # noqa: N813 +from hug.routing import ExceptionRouter as exception # noqa: N813 +from hug.routing import LocalRouter as local # noqa: N813 +from hug.routing import NotFoundRouter as not_found # noqa: N813 +from hug.routing import SinkRouter as sink # noqa: N813 +from hug.routing import StaticRouter as static # noqa: N813 +from hug.routing import URLRouter as http # noqa: N813 class Object(http): diff --git a/hug/routing.py b/hug/routing.py index e215f257..4a8af026 100644 --- a/hug/routing.py +++ b/hug/routing.py @@ -221,13 +221,15 @@ def __init__( versions=any, parse_body=False, parameters=None, - defaults={}, + defaults=None, status=None, response_headers=None, private=False, inputs=None, **kwargs ): + if defaults is None: + defaults = {} super().__init__(**kwargs) if versions is not any: self.route["versions"] = ( diff --git a/hug/this.py b/hug/this.py new file mode 100644 index 00000000..4176df78 --- /dev/null +++ b/hug/this.py @@ -0,0 +1,45 @@ +"""hug/this.py. + +The Zen of Hug + +Copyright (C) 2019 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +""" + +ZEN_OF_HUG = """ +Simple Things should be easy, complex things should be possible. +Complex things done often should be made simple. + +Magic should be avoided. +Magic isn't magic as soon as its mechanics are universally understood. + +Wrong documentation is worse than no documentation. +Everything should be documented. + +All code should be tested. +All tests should be meaningful. + +Consistency is more important than perfection. +It's okay to break consistency for practicality. + +Clarity is more important than performance. +If we do our job right, there shouldn't need to be a choice. + +Interfaces are one honking great idea -- let's do more of those! +""" + +print(ZEN_OF_HUG) diff --git a/requirements/build_common.txt b/requirements/build_common.txt index 848e90ac..567795e0 100644 --- a/requirements/build_common.txt +++ b/requirements/build_common.txt @@ -1,6 +1,5 @@ -r common.txt flake8==3.3.0 -isort==4.2.5 pytest-cov==2.4.0 pytest==4.3.1 python-coveralls==2.9.0 @@ -8,3 +7,4 @@ wheel==0.29.0 PyJWT==1.4.2 pytest-xdist==1.14.0 numpy==1.15.4 + diff --git a/requirements/build_style_tools.txt b/requirements/build_style_tools.txt new file mode 100644 index 00000000..063c2e98 --- /dev/null +++ b/requirements/build_style_tools.txt @@ -0,0 +1,7 @@ +-r build_common.txt +black==19.3b0 +isort==4.3.20 +pep8-naming==0.8.2 +flake8-bugbear==19.3.0 +vulture==1.0 +bandit==1.6.0 diff --git a/scripts/before_install.sh b/scripts/before_install.sh index 51d88bb3..32ab2dbc 100755 --- a/scripts/before_install.sh +++ b/scripts/before_install.sh @@ -18,6 +18,10 @@ echo $TRAVIS_OS_NAME python_minor=5;; py36) python_minor=6;; + py36-marshmallow2) + python_minor=6;; + py36-marshmallow3) + python_minor=6;; py37) python_minor=7;; esac diff --git a/setup.py b/setup.py index a88874a6..a283da29 100755 --- a/setup.py +++ b/setup.py @@ -78,7 +78,7 @@ def list_modules(dirname): setup( name="hug", - version="2.5.4", + version="2.5.5", description="A Python framework that makes developing APIs " "as simple as possible, but no simpler.", long_description=long_description, diff --git a/tests/test_full_request.py b/tests/test_full_request.py index f7f339a8..56866138 100644 --- a/tests/test_full_request.py +++ b/tests/test_full_request.py @@ -39,11 +39,13 @@ def post(body, response): """ -@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="Can't run hug CLI from travis PyPy") +@pytest.mark.skipif( + platform.python_implementation() == "PyPy", reason="Can't run hug CLI from travis PyPy" +) def test_hug_post(tmp_path): - hug_test_file = (tmp_path / "hug_postable.py") + hug_test_file = tmp_path / "hug_postable.py" hug_test_file.write_text(TEST_HUG_API) - hug_server = Popen(['hug', '-f', str(hug_test_file), '-p', '3000']) + hug_server = Popen(["hug", "-f", str(hug_test_file), "-p", "3000"]) time.sleep(5) - requests.post('http://127.0.0.1:3000/test', {'data': 'here'}) + requests.post("http://127.0.0.1:3000/test", {"data": "here"}) hug_server.kill() diff --git a/tests/test_this.py b/tests/test_this.py new file mode 100644 index 00000000..5746cf8d --- /dev/null +++ b/tests/test_this.py @@ -0,0 +1,27 @@ +"""tests/test_this.py. + +Tests the Zen of Hug + +Copyright (C) 2019 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +""" +from hug import this + + +def test_this(): + """Test to ensure this exposes the ZEN_OF_HUG as a string""" + assert type(this.ZEN_OF_HUG) == str diff --git a/tox.ini b/tox.ini index 8b850d5c..298ad013 100644 --- a/tox.ini +++ b/tox.ini @@ -7,9 +7,49 @@ deps= marshmallow2: marshmallow <3.0 marshmallow3: marshmallow >=3.0.0rc5 +whitelist_externals=flake8 +commands=py.test --cov-report html --cov hug -n auto tests + +[testenv:py37-black] +deps= + -rrequirements/build_style_tools.txt + marshmallow >=3.0.0rc5 + +whitelist_externals=flake8 +commands=black --check --verbose -l 100 hug + +[testenv:py37-vulture] +deps= + -rrequirements/build_style_tools.txt + marshmallow >=3.0.0rc5 + +whitelist_externals=flake8 +commands=vulture hug --min-confidence 100 --ignore-names req_succeeded + + +[testenv:py37-flake8] +deps= + -rrequirements/build_style_tools.txt + marshmallow >=3.0.0rc5 + whitelist_externals=flake8 commands=flake8 hug - py.test --cov-report html --cov hug -n auto tests + +[testenv:py37-bandit] +deps= + -rrequirements/build_style_tools.txt + marshmallow >=3.0.0rc5 + +whitelist_externals=flake8 +commands=bandit -r hug/ -ll + +[testenv:py37-isort] +deps= + -rrequirements/build_style_tools.txt + marshmallow >=3.0.0rc5 + +whitelist_externals=flake8 +commands=isort -c --diff --recursive hug [testenv:pywin] deps =-rrequirements/build_windows.txt