diff --git a/.travis.yml b/.travis.yml index e50d3a4a..9fbf9433 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,23 @@ 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: pypy3.5-6.0 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/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/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..6124414d 100644 --- a/hug/middleware.py +++ b/hug/middleware.py @@ -89,7 +89,7 @@ def process_request(self, request, response): data = self.store.get(sid) request.context.update({self.context_name: data}) - def process_response(self, request, response, resource, req_succeeded): + def process_response(self, request, response, resource, _req_succeeded): """Save request context in coupled store object. Set cookie containing a session ID.""" sid = request.cookies.get(self.cookie_name, None) if sid is None or not self.store.exists(sid): @@ -138,7 +138,7 @@ def process_request(self, request, response): ) ) - def process_response(self, request, response, resource, req_succeeded): + def process_response(self, request, response, resource, _req_succeeded): """Logs the basic data returned by the API""" self.logger.info(self._generate_combined_log(request, response)) @@ -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 @@ -174,7 +176,7 @@ def match_route(self, reqpath): return reqpath - def process_response(self, request, response, resource, req_succeeded): + def process_response(self, request, response, resource, _req_succeeded): """Add CORS headers to the response""" response.set_header("Access-Control-Allow-Credentials", str(self.allow_credentials).lower()) 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/requirements/build_common.txt b/requirements/build_common.txt index 848e90ac..bd32ae27 100644 --- a/requirements/build_common.txt +++ b/requirements/build_common.txt @@ -8,3 +8,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..a24c2023 --- /dev/null +++ b/requirements/build_style_tools.txt @@ -0,0 +1,6 @@ +-r build_common.txt +black==19.3b0 +pep8-naming==0.8.2 +flake8-bugbear==19.3.0 +vulture==1.0 +bandit==1.6.0 \ No newline at end of file diff --git a/tox.ini b/tox.ini index 8b850d5c..4393037b 100644 --- a/tox.ini +++ b/tox.ini @@ -7,9 +7,41 @@ 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 + + +[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:pywin] deps =-rrequirements/build_windows.txt