Skip to content

Commit

Permalink
Merge pull request #810 from hugapi/develop
Browse files Browse the repository at this point in the history
2.5.5 Release
  • Loading branch information
timothycrosley authored Jun 14, 2019
2 parents 3f51f0d + ddcc792 commit 8f71844
Show file tree
Hide file tree
Showing 29 changed files with 247 additions and 67 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 2.5.4
current_version = 2.5.5

[bumpversion:file:.env]

Expand Down
5 changes: 1 addition & 4 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*.egg-info
build
eggs
.eggs
parts
var
sdist
Expand Down Expand Up @@ -73,3 +74,6 @@ venv/

# Emacs backup
*~

# VSCode
/.vscode
6 changes: 6 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=100
22 changes: 21 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions ACKNOWLEDGEMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ Documenters
- Joshua Crowgey (@jcrowgey)
- Antti Kaihola (@akaihola)
- Simon Ince (@Simon-Ince)
- Edvard Majakari (@EdvardM)



--------------------------------------------
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
11 changes: 11 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]

[dev-packages]

[requires]
python_version = "3.7"
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
===================

Expand All @@ -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
Expand Down Expand Up @@ -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
===================

Expand All @@ -141,7 +139,6 @@ def tests_happy_birthday():
assert response.data is not None
```


Running hug with other WSGI based servers
===================

Expand All @@ -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
===================

Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
===================
Expand Down Expand Up @@ -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
===================

Expand Down Expand Up @@ -346,14 +350,14 @@ def not_found_handler():
return "Not Found"
```


Asyncio support
===============

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
Expand All @@ -362,6 +366,7 @@ def hello_world():
```

Using Python 3.5 async keyword.

```py
@hug.get()
async def hello_world():
Expand All @@ -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.
Expand Down Expand Up @@ -413,7 +418,6 @@ bash-4.3# tree
1 directory, 3 files
```


Why hug?
===================

Expand Down
7 changes: 3 additions & 4 deletions hug/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from falcon import *

from hug import (
authentication,
directives,
exceptions,
format,
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion hug/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
"""
from __future__ import absolute_import

current = "2.5.4"
current = "2.5.5"
25 changes: 14 additions & 11 deletions hug/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def __init__(self, api):

class HTTPInterfaceAPI(InterfaceAPI):
"""Defines the HTTP interface specific API"""

__slots__ = (
"routes",
"versions",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions hug/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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())
Expand Down
6 changes: 2 additions & 4 deletions hug/development_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading

0 comments on commit 8f71844

Please sign in to comment.