diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 59dbdba8..44bd7b6e 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.5.0 +current_version = 2.5.1 [bumpversion:file:.env] diff --git a/.env b/.env index 518df410..76bc30c5 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.0" +export PROJECT_VERSION="2.5.1" if [ ! -d "venv" ]; then if ! hash pyvenv 2>/dev/null; then diff --git a/CHANGELOG.md b/CHANGELOG.md index 02e9b265..0f221963 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ Ideally, within a virtual environment. Changelog ========= +### 2.5.1 hotfix - May 9, 2019 +- Fixed issue #784 - POST requests broken on 2.5.0 +- Optimizations and simplification of async support, taking advantadge of Python3.4 deprecation. +- Fix issue #785: Empty query params are not ignored on 2.5.0 +- Added support for modifying falcon API directly on startup +- Initial `black` formatting of code base, in preperation for CI enforced code formatting + ### 2.5.0 - May 4, 2019 - Updated to latest Falcon: 2.0.0 - Added support for Marshmallow 3 diff --git a/CODING_STANDARD.md b/CODING_STANDARD.md index 2a949a9f..dfe6f117 100644 --- a/CODING_STANDARD.md +++ b/CODING_STANDARD.md @@ -2,7 +2,7 @@ Coding Standard ========= Any submission to this project should closely follow the [PEP 8](https://www.python.org/dev/peps/pep-0008/) coding guidelines with the exceptions: -1. Lines can be up to 120 characters long. +1. Lines can be up to 100 characters long. 2. Single letter or otherwise nondescript variable names are prohibited. Standards for new hug modules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f3da0b3b..a9f1f004 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ Account Requirements: Base System Requirements: -- Python3.3+ +- Python3.5+ - Python3-venv (included with most Python3 installations but some Ubuntu systems require that it be installed separately) - bash or a bash compatible shell (should be auto-installed on Linux / Mac) - [autoenv](https://github.com/kennethreitz/autoenv) (optional) diff --git a/benchmarks/http/bobo_test.py b/benchmarks/http/bobo_test.py index eb1f4ed4..843e6ab0 100644 --- a/benchmarks/http/bobo_test.py +++ b/benchmarks/http/bobo_test.py @@ -1,9 +1,9 @@ import bobo -@bobo.query('/text', content_type='text/plain') +@bobo.query("/text", content_type="text/plain") def text(): - return 'Hello, world!' + return "Hello, world!" app = bobo.Application(bobo_resources=__name__) diff --git a/benchmarks/http/bottle_test.py b/benchmarks/http/bottle_test.py index 8a8d7314..49159922 100644 --- a/benchmarks/http/bottle_test.py +++ b/benchmarks/http/bottle_test.py @@ -3,6 +3,6 @@ app = bottle.Bottle() -@app.route('/text') +@app.route("/text") def text(): - return 'Hello, world!' + return "Hello, world!" diff --git a/benchmarks/http/cherrypy_test.py b/benchmarks/http/cherrypy_test.py index e100b679..5dd8e6b0 100644 --- a/benchmarks/http/cherrypy_test.py +++ b/benchmarks/http/cherrypy_test.py @@ -2,10 +2,9 @@ class Root(object): - @cherrypy.expose def text(self): - return 'Hello, world!' + return "Hello, world!" app = cherrypy.tree.mount(Root()) diff --git a/benchmarks/http/falcon_test.py b/benchmarks/http/falcon_test.py index 6f709d61..18891612 100644 --- a/benchmarks/http/falcon_test.py +++ b/benchmarks/http/falcon_test.py @@ -2,12 +2,11 @@ class Resource(object): - def on_get(self, req, resp): resp.status = falcon.HTTP_200 - resp.content_type = 'text/plain' - resp.body = 'Hello, world!' + resp.content_type = "text/plain" + resp.body = "Hello, world!" app = falcon.API() -app.add_route('/text', Resource()) +app.add_route("/text", Resource()) diff --git a/benchmarks/http/flask_test.py b/benchmarks/http/flask_test.py index e585dd31..4da0554a 100644 --- a/benchmarks/http/flask_test.py +++ b/benchmarks/http/flask_test.py @@ -3,6 +3,6 @@ app = flask.Flask(__name__) -@app.route('/text') +@app.route("/text") def text(): - return 'Hello, world!' + return "Hello, world!" diff --git a/benchmarks/http/hug_test.py b/benchmarks/http/hug_test.py index 929d3166..7ac32e54 100644 --- a/benchmarks/http/hug_test.py +++ b/benchmarks/http/hug_test.py @@ -1,9 +1,9 @@ import hug -@hug.get('/text', output_format=hug.output_format.text, parse_body=False) +@hug.get("/text", output_format=hug.output_format.text, parse_body=False) def text(): - return 'Hello, World!' + return "Hello, World!" app = hug.API(__name__).http.server() diff --git a/benchmarks/http/muffin_test.py b/benchmarks/http/muffin_test.py index cf9c9985..88710fcb 100644 --- a/benchmarks/http/muffin_test.py +++ b/benchmarks/http/muffin_test.py @@ -1,8 +1,8 @@ import muffin -app = muffin.Application('web') +app = muffin.Application("web") -@app.register('/text') +@app.register("/text") def text(request): - return 'Hello, World!' + return "Hello, World!" diff --git a/benchmarks/http/pyramid_test.py b/benchmarks/http/pyramid_test.py index b6404f26..76b4364f 100644 --- a/benchmarks/http/pyramid_test.py +++ b/benchmarks/http/pyramid_test.py @@ -2,14 +2,14 @@ from pyramid.config import Configurator -@view_config(route_name='text', renderer='string') +@view_config(route_name="text", renderer="string") def text(request): - return 'Hello, World!' + return "Hello, World!" config = Configurator() -config.add_route('text', '/text') +config.add_route("text", "/text") config.scan() app = config.make_wsgi_app() diff --git a/benchmarks/http/tornado_test.py b/benchmarks/http/tornado_test.py index c703bb41..a8e06097 100755 --- a/benchmarks/http/tornado_test.py +++ b/benchmarks/http/tornado_test.py @@ -6,12 +6,10 @@ class TextHandler(tornado.web.RequestHandler): def get(self): - self.write('Hello, world!') + self.write("Hello, world!") -application = tornado.web.Application([ - (r"/text", TextHandler), -]) +application = tornado.web.Application([(r"/text", TextHandler)]) if __name__ == "__main__": application.listen(8000) diff --git a/benchmarks/internal/argument_populating.py b/benchmarks/internal/argument_populating.py index da38303e..c5becfa7 100644 --- a/benchmarks/internal/argument_populating.py +++ b/benchmarks/internal/argument_populating.py @@ -3,11 +3,10 @@ from hug.decorators import auto_kwargs from hug.introspect import generate_accepted_kwargs -DATA = {'request': None} +DATA = {"request": None} class Timer(object): - def __init__(self, name): self.name = name @@ -26,25 +25,25 @@ def my_method_with_kwargs(name, request=None, **kwargs): pass -with Timer('generate_kwargs'): - accept_kwargs = generate_accepted_kwargs(my_method, ('request', 'response', 'version')) +with Timer("generate_kwargs"): + accept_kwargs = generate_accepted_kwargs(my_method, ("request", "response", "version")) for test in range(100000): my_method(test, **accept_kwargs(DATA)) -with Timer('auto_kwargs'): +with Timer("auto_kwargs"): wrapped_method = auto_kwargs(my_method) for test in range(100000): wrapped_method(test, **DATA) -with Timer('native_kwargs'): +with Timer("native_kwargs"): for test in range(100000): my_method_with_kwargs(test, **DATA) -with Timer('no_kwargs'): +with Timer("no_kwargs"): for test in range(100000): my_method(test, request=None) diff --git a/docker/template/__init__.py b/docker/template/__init__.py index 2ec76b39..3d060614 100644 --- a/docker/template/__init__.py +++ b/docker/template/__init__.py @@ -2,6 +2,6 @@ from handlers import birthday, hello -@hug.extend_api('') +@hug.extend_api("") def api(): return [hello, birthday] diff --git a/docker/template/handlers/hello.py b/docker/template/handlers/hello.py index e6b2f4eb..be28cdf3 100644 --- a/docker/template/handlers/hello.py +++ b/docker/template/handlers/hello.py @@ -2,5 +2,5 @@ @hug.get("/hello") -def hello(name: str="World"): +def hello(name: str = "World"): return "Hello, {name}".format(name=name) diff --git a/examples/authentication.py b/examples/authentication.py index 0796f3e5..26d7d4d7 100644 --- a/examples/authentication.py +++ b/examples/authentication.py @@ -1,4 +1,4 @@ -'''A basic example of authentication requests within a hug API''' +"""A basic example of authentication requests within a hug API""" import hug import jwt @@ -9,10 +9,10 @@ # on successful authentication. Naturally, this is a trivial demo, and a much # more robust verification function is recommended. This is for strictly # illustrative purposes. -authentication = hug.authentication.basic(hug.authentication.verify('User1', 'mypassword')) +authentication = hug.authentication.basic(hug.authentication.verify("User1", "mypassword")) -@hug.get('/public') +@hug.get("/public") def public_api_call(): return "Needs no authentication" @@ -21,9 +21,9 @@ def public_api_call(): # Directives can provide computed input parameters via an abstraction # layer so as not to clutter your API functions with access to the raw # request object. -@hug.get('/authenticated', requires=authentication) +@hug.get("/authenticated", requires=authentication) def basic_auth_api_call(user: hug.directives.user): - return 'Successfully authenticated with user: {0}'.format(user) + return "Successfully authenticated with user: {0}".format(user) # Here is a slightly less trivial example of how authentication might @@ -40,10 +40,10 @@ def __init__(self, user_id, api_key): def api_key_verify(api_key): - magic_key = '5F00832B-DE24-4CAF-9638-C10D1C642C6C' # Obviously, this would hit your database + magic_key = "5F00832B-DE24-4CAF-9638-C10D1C642C6C" # Obviously, this would hit your database if api_key == magic_key: # Success! - return APIUser('user_foo', api_key) + return APIUser("user_foo", api_key) else: # Invalid key return None @@ -52,15 +52,15 @@ def api_key_verify(api_key): api_key_authentication = hug.authentication.api_key(api_key_verify) -@hug.get('/key_authenticated', requires=api_key_authentication) # noqa +@hug.get("/key_authenticated", requires=api_key_authentication) # noqa def basic_auth_api_call(user: hug.directives.user): - return 'Successfully authenticated with user: {0}'.format(user.user_id) + return "Successfully authenticated with user: {0}".format(user.user_id) def token_verify(token): - secret_key = 'super-secret-key-please-change' + secret_key = "super-secret-key-please-change" try: - return jwt.decode(token, secret_key, algorithm='HS256') + return jwt.decode(token, secret_key, algorithm="HS256") except jwt.DecodeError: return False @@ -68,17 +68,19 @@ def token_verify(token): token_key_authentication = hug.authentication.token(token_verify) -@hug.get('/token_authenticated', requires=token_key_authentication) # noqa +@hug.get("/token_authenticated", requires=token_key_authentication) # noqa def token_auth_call(user: hug.directives.user): - return 'You are user: {0} with data {1}'.format(user['user'], user['data']) + return "You are user: {0} with data {1}".format(user["user"], user["data"]) -@hug.post('/token_generation') # noqa +@hug.post("/token_generation") # noqa def token_gen_call(username, password): """Authenticate and return a token""" - secret_key = 'super-secret-key-please-change' - mockusername = 'User2' - mockpassword = 'Mypassword' - if mockpassword == password and mockusername == username: # This is an example. Don't do that. - return {"token" : jwt.encode({'user': username, 'data': 'mydata'}, secret_key, algorithm='HS256')} - return 'Invalid username and/or password for user: {0}'.format(username) + secret_key = "super-secret-key-please-change" + mockusername = "User2" + mockpassword = "Mypassword" + if mockpassword == password and mockusername == username: # This is an example. Don't do that. + return { + "token": jwt.encode({"user": username, "data": "mydata"}, secret_key, algorithm="HS256") + } + return "Invalid username and/or password for user: {0}".format(username) diff --git a/examples/cli.py b/examples/cli.py index 1d7a1d09..efb6a094 100644 --- a/examples/cli.py +++ b/examples/cli.py @@ -3,10 +3,10 @@ @hug.cli(version="1.0.0") -def cli(name: 'The name', age: hug.types.number): +def cli(name: "The name", age: hug.types.number): """Says happy birthday to a user""" return "Happy {age} Birthday {name}!\n".format(**locals()) -if __name__ == '__main__': +if __name__ == "__main__": cli.interface.cli() diff --git a/examples/cli_object.py b/examples/cli_object.py index 74503519..00026539 100644 --- a/examples/cli_object.py +++ b/examples/cli_object.py @@ -1,20 +1,20 @@ import hug -API = hug.API('git') +API = hug.API("git") -@hug.object(name='git', version='1.0.0', api=API) +@hug.object(name="git", version="1.0.0", api=API) class GIT(object): """An example of command like calls via an Object""" @hug.object.cli - def push(self, branch='master'): - return 'Pushing {}'.format(branch) + def push(self, branch="master"): + return "Pushing {}".format(branch) @hug.object.cli - def pull(self, branch='master'): - return 'Pulling {}'.format(branch) + def pull(self, branch="master"): + return "Pulling {}".format(branch) -if __name__ == '__main__': +if __name__ == "__main__": API.cli() diff --git a/examples/cors_middleware.py b/examples/cors_middleware.py index 77156d18..9600534a 100644 --- a/examples/cors_middleware.py +++ b/examples/cors_middleware.py @@ -4,6 +4,6 @@ api.http.add_middleware(hug.middleware.CORSMiddleware(api, max_age=10)) -@hug.get('/demo') +@hug.get("/demo") def get_demo(): - return {'result': 'Hello World'} + return {"result": "Hello World"} diff --git a/examples/cors_per_route.py b/examples/cors_per_route.py index 7d2400b1..75c553f9 100644 --- a/examples/cors_per_route.py +++ b/examples/cors_per_route.py @@ -2,5 +2,5 @@ @hug.get() -def cors_supported(cors: hug.directives.cors="*"): +def cors_supported(cors: hug.directives.cors = "*"): return "Hello world!" diff --git a/examples/docker_compose_with_mongodb/app.py b/examples/docker_compose_with_mongodb/app.py index e4452e35..88f2466e 100644 --- a/examples/docker_compose_with_mongodb/app.py +++ b/examples/docker_compose_with_mongodb/app.py @@ -2,29 +2,28 @@ import hug -client = MongoClient('db', 27017) -db = client['our-database'] -collection = db['our-items'] +client = MongoClient("db", 27017) +db = client["our-database"] +collection = db["our-items"] -@hug.get('/', output=hug.output_format.pretty_json) +@hug.get("/", output=hug.output_format.pretty_json) def show(): """Returns a list of items currently in the database""" items = list(collection.find()) # JSON conversion chokes on the _id objects, so we convert # them to strings here for i in items: - i['_id'] = str(i['_id']) + i["_id"] = str(i["_id"]) return items -@hug.post('/new', status_code=hug.falcon.HTTP_201) +@hug.post("/new", status_code=hug.falcon.HTTP_201) def new(name: hug.types.text, description: hug.types.text): """Inserts the given object as a new item in the database. Returns the ID of the newly created item. """ - item_doc = {'name': name, 'description': description} + item_doc = {"name": name, "description": description} collection.insert_one(item_doc) - return str(item_doc['_id']) - + return str(item_doc["_id"]) diff --git a/examples/docker_nginx/api/__main__.py b/examples/docker_nginx/api/__main__.py index 9f43342d..c16b1689 100644 --- a/examples/docker_nginx/api/__main__.py +++ b/examples/docker_nginx/api/__main__.py @@ -11,4 +11,4 @@ def base(): @hug.get("/add", examples="num=1") def add(num: hug.types.number = 1): - return {"res" : num + 1} + return {"res": num + 1} diff --git a/examples/docker_nginx/setup.py b/examples/docker_nginx/setup.py index 4a3a4c4f..252029ef 100644 --- a/examples/docker_nginx/setup.py +++ b/examples/docker_nginx/setup.py @@ -4,37 +4,26 @@ from setuptools import setup setup( - name = "app-name", - version = "0.0.1", - description = "App Description", - url = "https://github.com/CMoncur/nginx-gunicorn-hug", - author = "Cody Moncur", - author_email = "cmoncur@gmail.com", - classifiers = [ + name="app-name", + version="0.0.1", + description="App Description", + url="https://github.com/CMoncur/nginx-gunicorn-hug", + author="Cody Moncur", + author_email="cmoncur@gmail.com", + classifiers=[ # 3 - Alpha # 4 - Beta # 5 - Production/Stable "Development Status :: 3 - Alpha", - "Programming Language :: Python :: 3.6" + "Programming Language :: Python :: 3.6", ], - packages = [], - + packages=[], # Entry Point - entry_points = { - "console_scripts": [] - }, - + entry_points={"console_scripts": []}, # Core Dependencies - install_requires = [ - "hug" - ], - + install_requires=["hug"], # Dev/Test Dependencies - extras_require = { - "dev": [], - "test": [], - }, - + extras_require={"dev": [], "test": []}, # Scripts - scripts = [] + scripts=[], ) diff --git a/examples/file_upload_example.py b/examples/file_upload_example.py index 69e0019e..10efcece 100644 --- a/examples/file_upload_example.py +++ b/examples/file_upload_example.py @@ -17,9 +17,10 @@ import hug -@hug.post('/upload') + +@hug.post("/upload") def upload_file(body): """accepts file uploads""" #
is a simple dictionary of {filename: b'content'} - print('body: ', body) - return {'filename': list(body.keys()).pop(), 'filesize': len(list(body.values()).pop())} + print("body: ", body) + return {"filename": list(body.keys()).pop(), "filesize": len(list(body.values()).pop())} diff --git a/examples/force_https.py b/examples/force_https.py index cbff5d05..ade7ea50 100644 --- a/examples/force_https.py +++ b/examples/force_https.py @@ -10,4 +10,4 @@ @hug.get() def my_endpoint(): - return 'Success!' + return "Success!" diff --git a/examples/happy_birthday.py b/examples/happy_birthday.py index c91a872e..ee80b378 100644 --- a/examples/happy_birthday.py +++ b/examples/happy_birthday.py @@ -2,12 +2,13 @@ import hug -@hug.get('/happy_birthday', examples="name=HUG&age=1") +@hug.get("/happy_birthday", examples="name=HUG&age=1") def happy_birthday(name, age: hug.types.number): """Says happy birthday to a user""" return "Happy {age} Birthday {name}!".format(**locals()) -@hug.get('/greet/{event}') + +@hug.get("/greet/{event}") def greet(event: str): """Greets appropriately (from http://blog.ketchum.com/how-to-write-10-common-holiday-greetings/) """ greetings = "Happy" diff --git a/examples/hello_world.py b/examples/hello_world.py index c7c91c10..6b0329fd 100644 --- a/examples/hello_world.py +++ b/examples/hello_world.py @@ -4,4 +4,4 @@ @hug.get() def hello(request): """Says hellos""" - return 'Hello Worlds for Bacon?!' + return "Hello Worlds for Bacon?!" diff --git a/examples/html_serve.py b/examples/html_serve.py index 38b54cb7..d8b994e6 100644 --- a/examples/html_serve.py +++ b/examples/html_serve.py @@ -6,10 +6,10 @@ DIRECTORY = os.path.dirname(os.path.realpath(__file__)) -@hug.get('/get/document', output=hug.output_format.html) +@hug.get("/get/document", output=hug.output_format.html) def nagiosCommandHelp(**kwargs): """ Returns command help document when no command is specified """ - with open(os.path.join(DIRECTORY, 'document.html')) as document: + with open(os.path.join(DIRECTORY, "document.html")) as document: return document.read() diff --git a/examples/image_serve.py b/examples/image_serve.py index ba0998d0..27f726dd 100644 --- a/examples/image_serve.py +++ b/examples/image_serve.py @@ -1,7 +1,7 @@ import hug -@hug.get('/image.png', output=hug.output_format.png_image) +@hug.get("/image.png", output=hug.output_format.png_image) def image(): """Serves up a PNG image.""" - return '../artwork/logo.png' + return "../artwork/logo.png" diff --git a/examples/marshmallow_example.py b/examples/marshmallow_example.py index 09fc401c..8bbda223 100644 --- a/examples/marshmallow_example.py +++ b/examples/marshmallow_example.py @@ -22,15 +22,17 @@ from marshmallow.validate import Range, OneOf -@hug.get('/dateadd', examples="value=1973-04-10&addend=63") -def dateadd(value: fields.DateTime(), - addend: fields.Int(validate=Range(min=1)), - unit: fields.Str(validate=OneOf(['minutes', 'days']))='days'): +@hug.get("/dateadd", examples="value=1973-04-10&addend=63") +def dateadd( + value: fields.DateTime(), + addend: fields.Int(validate=Range(min=1)), + unit: fields.Str(validate=OneOf(["minutes", "days"])) = "days", +): """Add a value to a date.""" value = value or dt.datetime.utcnow() - if unit == 'minutes': + if unit == "minutes": delta = dt.timedelta(minutes=addend) else: delta = dt.timedelta(days=addend) result = value + delta - return {'result': result} + return {"result": result} diff --git a/examples/multi_file_cli/api.py b/examples/multi_file_cli/api.py index 179f6385..f7242804 100644 --- a/examples/multi_file_cli/api.py +++ b/examples/multi_file_cli/api.py @@ -8,10 +8,10 @@ def echo(text: hug.types.text): return text -@hug.extend_api(sub_command='sub_api') +@hug.extend_api(sub_command="sub_api") def extend_with(): - return (sub_api, ) + return (sub_api,) -if __name__ == '__main__': +if __name__ == "__main__": hug.API(__name__).cli() diff --git a/examples/multi_file_cli/sub_api.py b/examples/multi_file_cli/sub_api.py index dd21eda0..20ffa319 100644 --- a/examples/multi_file_cli/sub_api.py +++ b/examples/multi_file_cli/sub_api.py @@ -3,4 +3,4 @@ @hug.cli() def hello(): - return 'Hello world' + return "Hello world" diff --git a/examples/multiple_files/api.py b/examples/multiple_files/api.py index f26c160b..e3a1b29f 100644 --- a/examples/multiple_files/api.py +++ b/examples/multiple_files/api.py @@ -3,7 +3,7 @@ import part_2 -@hug.get('/') +@hug.get("/") def say_hi(): """This view will be at the path ``/``""" return "Hi from root" diff --git a/examples/multiple_files/part_1.py b/examples/multiple_files/part_1.py index 50fb4a71..2a460865 100644 --- a/examples/multiple_files/part_1.py +++ b/examples/multiple_files/part_1.py @@ -4,4 +4,4 @@ @hug.get() def part1(): """This view will be at the path ``/part1``""" - return 'part1' + return "part1" diff --git a/examples/multiple_files/part_2.py b/examples/multiple_files/part_2.py index 4c8dfdad..350ce8f6 100644 --- a/examples/multiple_files/part_2.py +++ b/examples/multiple_files/part_2.py @@ -4,4 +4,4 @@ @hug.get() def part2(): """This view will be at the path ``/part2``""" - return 'Part 2' + return "Part 2" diff --git a/examples/override_404.py b/examples/override_404.py index 1b3371a8..261d6f14 100644 --- a/examples/override_404.py +++ b/examples/override_404.py @@ -3,9 +3,9 @@ @hug.get() def hello_world(): - return 'Hello world!' + return "Hello world!" @hug.not_found() def not_found(): - return {'Nothing': 'to see'} + return {"Nothing": "to see"} diff --git a/examples/pil_example/pill.py b/examples/pil_example/pill.py index 910c6e52..3f9a8a67 100644 --- a/examples/pil_example/pill.py +++ b/examples/pil_example/pill.py @@ -2,8 +2,8 @@ from PIL import Image, ImageDraw -@hug.get('/image.png', output=hug.output_format.png_image) +@hug.get("/image.png", output=hug.output_format.png_image) def create_image(): - image = Image.new('RGB', (100, 50)) # create the image - ImageDraw.Draw(image).text((10, 10), 'Hello World!', fill=(255, 0, 0)) + image = Image.new("RGB", (100, 50)) # create the image + ImageDraw.Draw(image).text((10, 10), "Hello World!", fill=(255, 0, 0)) return image diff --git a/examples/quick_server.py b/examples/quick_server.py index 423158ec..5fe9b1bf 100644 --- a/examples/quick_server.py +++ b/examples/quick_server.py @@ -3,8 +3,8 @@ @hug.get() def quick(): - return 'Serving!' + return "Serving!" -if __name__ == '__main__': +if __name__ == "__main__": hug.API(__name__).http.serve() diff --git a/examples/quick_start/first_step_1.py b/examples/quick_start/first_step_1.py index 3cc8dbea..971ded8f 100644 --- a/examples/quick_start/first_step_1.py +++ b/examples/quick_start/first_step_1.py @@ -5,5 +5,4 @@ @hug.local() def happy_birthday(name: hug.types.text, age: hug.types.number, hug_timer=3): """Says happy birthday to a user""" - return {'message': 'Happy {0} Birthday {1}!'.format(age, name), - 'took': float(hug_timer)} + return {"message": "Happy {0} Birthday {1}!".format(age, name), "took": float(hug_timer)} diff --git a/examples/quick_start/first_step_2.py b/examples/quick_start/first_step_2.py index e24b12de..ee314ae5 100644 --- a/examples/quick_start/first_step_2.py +++ b/examples/quick_start/first_step_2.py @@ -2,9 +2,8 @@ import hug -@hug.get(examples='name=Timothy&age=26') +@hug.get(examples="name=Timothy&age=26") @hug.local() def happy_birthday(name: hug.types.text, age: hug.types.number, hug_timer=3): """Says happy birthday to a user""" - return {'message': 'Happy {0} Birthday {1}!'.format(age, name), - 'took': float(hug_timer)} + return {"message": "Happy {0} Birthday {1}!".format(age, name), "took": float(hug_timer)} diff --git a/examples/quick_start/first_step_3.py b/examples/quick_start/first_step_3.py index 8f169dc5..33a02c92 100644 --- a/examples/quick_start/first_step_3.py +++ b/examples/quick_start/first_step_3.py @@ -3,13 +3,12 @@ @hug.cli() -@hug.get(examples='name=Timothy&age=26') +@hug.get(examples="name=Timothy&age=26") @hug.local() def happy_birthday(name: hug.types.text, age: hug.types.number, hug_timer=3): """Says happy birthday to a user""" - return {'message': 'Happy {0} Birthday {1}!'.format(age, name), - 'took': float(hug_timer)} + return {"message": "Happy {0} Birthday {1}!".format(age, name), "took": float(hug_timer)} -if __name__ == '__main__': +if __name__ == "__main__": happy_birthday.interface.cli() diff --git a/examples/return_400.py b/examples/return_400.py index 9129a440..859d4c09 100644 --- a/examples/return_400.py +++ b/examples/return_400.py @@ -1,6 +1,7 @@ import hug from falcon import HTTP_400 + @hug.get() def only_positive(positive: int, response): if positive < 0: diff --git a/examples/secure_auth_with_db_example.py b/examples/secure_auth_with_db_example.py index f2d3922a..e1ce207e 100644 --- a/examples/secure_auth_with_db_example.py +++ b/examples/secure_auth_with_db_example.py @@ -7,7 +7,7 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -db = TinyDB('db.json') +db = TinyDB("db.json") """ Helper Methods @@ -21,8 +21,8 @@ def hash_password(password, salt): :param salt: :return: Hex encoded SHA512 hash of provided password """ - password = str(password).encode('utf-8') - salt = str(salt).encode('utf-8') + password = str(password).encode("utf-8") + salt = str(salt).encode("utf-8") return hashlib.sha512(password + salt).hexdigest() @@ -32,7 +32,7 @@ def gen_api_key(username): :param username: :return: Hex encoded SHA512 random string """ - salt = str(os.urandom(64)).encode('utf-8') + salt = str(os.urandom(64)).encode("utf-8") return hash_password(username, salt) @@ -51,8 +51,8 @@ def authenticate_user(username, password): logger.warning("User %s not found", username) return False - if user['password'] == hash_password(password, user.get('salt')): - return user['username'] + if user["password"] == hash_password(password, user.get("salt")): + return user["username"] return False @@ -67,9 +67,10 @@ def authenticate_key(api_key): user_model = Query() user = db.search(user_model.api_key == api_key)[0] if user: - return user['username'] + return user["username"] return False + """ API Methods start here """ @@ -89,30 +90,19 @@ def add_user(username, password): user_model = Query() if db.search(user_model.username == username): - return { - 'error': 'User {0} already exists'.format(username) - } + return {"error": "User {0} already exists".format(username)} - salt = hashlib.sha512(str(os.urandom(64)).encode('utf-8')).hexdigest() + salt = hashlib.sha512(str(os.urandom(64)).encode("utf-8")).hexdigest() password = hash_password(password, salt) api_key = gen_api_key(username) - user = { - 'username': username, - 'password': password, - 'salt': salt, - 'api_key': api_key - } + user = {"username": username, "password": password, "salt": salt, "api_key": api_key} user_id = db.insert(user) - return { - 'result': 'success', - 'eid': user_id, - 'user_created': user - } + return {"result": "success", "eid": user_id, "user_created": user} -@hug.get('/api/get_api_key', requires=basic_authentication) +@hug.get("/api/get_api_key", requires=basic_authentication) def get_token(authed_user: hug.directives.user): """ Get Job details @@ -123,34 +113,26 @@ def get_token(authed_user: hug.directives.user): user = db.search(user_model.username == authed_user)[0] if user: - out = { - 'user': user['username'], - 'api_key': user['api_key'] - } + out = {"user": user["username"], "api_key": user["api_key"]} else: # this should never happen - out = { - 'error': 'User {0} does not exist'.format(authed_user) - } + out = {"error": "User {0} does not exist".format(authed_user)} return out # Same thing, but authenticating against an API key -@hug.get(('/api/job', '/api/job/{job_id}/'), requires=api_key_authentication) +@hug.get(("/api/job", "/api/job/{job_id}/"), requires=api_key_authentication) def get_job_details(job_id): """ Get Job details :param job_id: :return: """ - job = { - 'job_id': job_id, - 'details': 'Details go here' - } + job = {"job_id": job_id, "details": "Details go here"} return job -if __name__ == '__main__': +if __name__ == "__main__": add_user.interface.cli() diff --git a/examples/sink_example.py b/examples/sink_example.py index 7b2614fa..aadd395f 100644 --- a/examples/sink_example.py +++ b/examples/sink_example.py @@ -6,6 +6,6 @@ import hug -@hug.sink('/all') +@hug.sink("/all") def my_sink(request): - return request.path.replace('/all', '') + return request.path.replace("/all", "") diff --git a/examples/smtp_envelope_example.py b/examples/smtp_envelope_example.py index 9c1c6b82..ffa1db51 100644 --- a/examples/smtp_envelope_example.py +++ b/examples/smtp_envelope_example.py @@ -4,9 +4,8 @@ @hug.directive() class SMTP(object): - def __init__(self, *args, **kwargs): - self.smtp = envelopes.SMTP(host='127.0.0.1') + self.smtp = envelopes.SMTP(host="127.0.0.1") self.envelopes_to_send = list() def send_envelope(self, envelope): @@ -19,12 +18,12 @@ def cleanup(self, exception=None): self.smtp.send(envelope) -@hug.get('/hello') +@hug.get("/hello") def send_hello_email(smtp: SMTP): envelope = envelopes.Envelope( - from_addr=(u'me@example.com', u'From me'), - to_addr=(u'world@example.com', u'To World'), - subject=u'Hello', - text_body=u"World!" + from_addr=(u"me@example.com", u"From me"), + to_addr=(u"world@example.com", u"To World"), + subject=u"Hello", + text_body=u"World!", ) smtp.send_envelope(envelope) diff --git a/examples/sqlalchemy_example/demo/api.py b/examples/sqlalchemy_example/demo/api.py index 2e63b410..b0617535 100644 --- a/examples/sqlalchemy_example/demo/api.py +++ b/examples/sqlalchemy_example/demo/api.py @@ -6,40 +6,28 @@ from demo.validation import CreateUserSchema, DumpSchema, unique_username -@hug.post('/create_user2', requires=basic_authentication) -def create_user2( - db: SqlalchemySession, - data: CreateUserSchema() -): - user = TestUser( - **data - ) +@hug.post("/create_user2", requires=basic_authentication) +def create_user2(db: SqlalchemySession, data: CreateUserSchema()): + user = TestUser(**data) db.add(user) db.flush() return dict() -@hug.post('/create_user', requires=basic_authentication) -def create_user( - db: SqlalchemySession, - username: unique_username, - password: hug.types.text -): - user = TestUser( - username=username, - password=password - ) +@hug.post("/create_user", requires=basic_authentication) +def create_user(db: SqlalchemySession, username: unique_username, password: hug.types.text): + user = TestUser(username=username, password=password) db.add(user) db.flush() return dict() -@hug.get('/test') +@hug.get("/test") def test(): - return '' + return "" -@hug.get('/hello') +@hug.get("/hello") def make_simple_query(db: SqlalchemySession): for word in ["hello", "world", ":)"]: test_model = TestModel() @@ -49,7 +37,7 @@ def make_simple_query(db: SqlalchemySession): return " ".join([obj.name for obj in db.query(TestModel).all()]) -@hug.get('/hello2') +@hug.get("/hello2") def transform_example(db: SqlalchemySession) -> DumpSchema(): for word in ["hello", "world", ":)"]: test_model = TestModel() @@ -59,6 +47,6 @@ def transform_example(db: SqlalchemySession) -> DumpSchema(): return dict(users=db.query(TestModel).all()) -@hug.get('/protected', requires=basic_authentication) +@hug.get("/protected", requires=basic_authentication) def protected(): - return 'smile :)' + return "smile :)" diff --git a/examples/sqlalchemy_example/demo/app.py b/examples/sqlalchemy_example/demo/app.py index 321f6052..748ca17d 100644 --- a/examples/sqlalchemy_example/demo/app.py +++ b/examples/sqlalchemy_example/demo/app.py @@ -19,7 +19,7 @@ def delete_context(context: SqlalchemyContext, exception=None, errors=None, lack @hug.local(skip_directives=False) def initialize(db: SqlalchemySession): - admin = TestUser(username='admin', password='admin') + admin = TestUser(username="admin", password="admin") db.add(admin) db.flush() diff --git a/examples/sqlalchemy_example/demo/authentication.py b/examples/sqlalchemy_example/demo/authentication.py index 1cd1a447..09aabb6d 100644 --- a/examples/sqlalchemy_example/demo/authentication.py +++ b/examples/sqlalchemy_example/demo/authentication.py @@ -7,8 +7,7 @@ @hug.authentication.basic def basic_authentication(username, password, context: SqlalchemyContext): return context.db.query( - context.db.query(TestUser).filter( - TestUser.username == username, - TestUser.password == password - ).exists() + context.db.query(TestUser) + .filter(TestUser.username == username, TestUser.password == password) + .exists() ).scalar() diff --git a/examples/sqlalchemy_example/demo/context.py b/examples/sqlalchemy_example/demo/context.py index 46d5661a..e20a3126 100644 --- a/examples/sqlalchemy_example/demo/context.py +++ b/examples/sqlalchemy_example/demo/context.py @@ -10,7 +10,6 @@ class SqlalchemyContext(object): - def __init__(self): self._db = session_factory() diff --git a/examples/sqlalchemy_example/demo/directives.py b/examples/sqlalchemy_example/demo/directives.py index 921e287e..3f430133 100644 --- a/examples/sqlalchemy_example/demo/directives.py +++ b/examples/sqlalchemy_example/demo/directives.py @@ -6,6 +6,5 @@ @hug.directive() class SqlalchemySession(Session): - - def __new__(cls, *args, context: SqlalchemyContext=None, **kwargs): + def __new__(cls, *args, context: SqlalchemyContext = None, **kwargs): return context.db diff --git a/examples/sqlalchemy_example/demo/models.py b/examples/sqlalchemy_example/demo/models.py index ceb40a3b..25b2445f 100644 --- a/examples/sqlalchemy_example/demo/models.py +++ b/examples/sqlalchemy_example/demo/models.py @@ -5,13 +5,15 @@ class TestModel(Base): - __tablename__ = 'test_model' + __tablename__ = "test_model" id = Column(Integer, primary_key=True) name = Column(String) class TestUser(Base): - __tablename__ = 'test_user' + __tablename__ = "test_user" id = Column(Integer, primary_key=True) username = Column(String) - password = Column(String) # do not store plain password in the database, hash it, see porridge for example + password = Column( + String + ) # do not store plain password in the database, hash it, see porridge for example diff --git a/examples/sqlalchemy_example/demo/validation.py b/examples/sqlalchemy_example/demo/validation.py index c8371162..c54f0711 100644 --- a/examples/sqlalchemy_example/demo/validation.py +++ b/examples/sqlalchemy_example/demo/validation.py @@ -11,11 +11,9 @@ @hug.type(extend=hug.types.text, chain=True, accept_context=True) def unique_username(value, context: SqlalchemyContext): if context.db.query( - context.db.query(TestUser).filter( - TestUser.username == value - ).exists() + context.db.query(TestUser).filter(TestUser.username == value).exists() ).scalar(): - raise ValueError('User with a username {0} already exists.'.format(value)) + raise ValueError("User with a username {0} already exists.".format(value)) return value @@ -26,22 +24,19 @@ class CreateUserSchema(Schema): @validates_schema def check_unique_username(self, data): if self.context.db.query( - self.context.db.query(TestUser).filter( - TestUser.username == data['username'] - ).exists() + self.context.db.query(TestUser).filter(TestUser.username == data["username"]).exists() ).scalar(): - raise ValueError('User with a username {0} already exists.'.format(data['username'])) + raise ValueError("User with a username {0} already exists.".format(data["username"])) class DumpUserSchema(ModelSchema): - @property def session(self): return self.context.db class Meta: model = TestModel - fields = ('name',) + fields = ("name",) class DumpSchema(Schema): diff --git a/examples/static_serve.py b/examples/static_serve.py index 126b06e3..eac7019a 100644 --- a/examples/static_serve.py +++ b/examples/static_serve.py @@ -9,6 +9,7 @@ tmp_dir_object = None + def setup(api=None): """Sets up and fills test directory for serving. @@ -28,13 +29,16 @@ def setup(api=None): # populate directory a with text files file_list = [ - ["hi.txt", """Hi World!"""], - ["hi.html", """Hi World!"""], - ["hello.html", """ + ["hi.txt", """Hi World!"""], + ["hi.html", """Hi World!"""], + [ + "hello.html", + """ pop-up - """], - ["hi.js", """alert('Hi World')""" ] + """, + ], + ["hi.js", """alert('Hi World')"""], ] for f in file_list: @@ -42,16 +46,16 @@ def setup(api=None): fo.write(f[1]) # populate directory b with binary file - image = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\n\x00\x00\x00\n\x08\x02\x00\x00\x00\x02PX\xea\x00\x00\x006IDAT\x18\xd3c\xfc\xff\xff?\x03n\xc0\xc4\x80\x170100022222\xc2\x85\x90\xb9\x04t3\x92`7\xb2\x15D\xeb\xc6\xe34\xa8n4c\xe1F\x120\x1c\x00\xc6z\x12\x1c\x8cT\xf2\x1e\x00\x00\x00\x00IEND\xaeB`\x82' + image = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\n\x00\x00\x00\n\x08\x02\x00\x00\x00\x02PX\xea\x00\x00\x006IDAT\x18\xd3c\xfc\xff\xff?\x03n\xc0\xc4\x80\x170100022222\xc2\x85\x90\xb9\x04t3\x92`7\xb2\x15D\xeb\xc6\xe34\xa8n4c\xe1F\x120\x1c\x00\xc6z\x12\x1c\x8cT\xf2\x1e\x00\x00\x00\x00IEND\xaeB`\x82" with open(os.path.join(dir_b, "smile.png"), mode="wb") as fo: - fo.write(image) + fo.write(image) -@hug.static('/static') +@hug.static("/static") def my_static_dirs(): """Returns static directory names to be served.""" global tmp_dir_object if tmp_dir_object == None: setup() - return(tmp_dir_object.name,) + return (tmp_dir_object.name,) diff --git a/examples/streaming_movie_server/movie_server.py b/examples/streaming_movie_server/movie_server.py index b78bf693..443c83af 100644 --- a/examples/streaming_movie_server/movie_server.py +++ b/examples/streaming_movie_server/movie_server.py @@ -5,4 +5,4 @@ @hug.get(output=hug.output_format.mp4_video) def watch(): """Watch an example movie, streamed directly to you from hug""" - return 'movie.mp4' + return "movie.mp4" diff --git a/examples/test_happy_birthday.py b/examples/test_happy_birthday.py index ce3a165c..e7bb6c69 100644 --- a/examples/test_happy_birthday.py +++ b/examples/test_happy_birthday.py @@ -2,15 +2,17 @@ import happy_birthday from falcon import HTTP_400, HTTP_404, HTTP_200 + def tests_happy_birthday(): - response = hug.test.get(happy_birthday, 'happy_birthday', {'name': 'Timothy', 'age': 25}) + response = hug.test.get(happy_birthday, "happy_birthday", {"name": "Timothy", "age": 25}) assert response.status == HTTP_200 assert response.data is not None + def tests_season_greetings(): - response = hug.test.get(happy_birthday, 'greet/Christmas') + response = hug.test.get(happy_birthday, "greet/Christmas") assert response.status == HTTP_200 assert response.data is not None assert str(response.data) == "Merry Christmas!" - response = hug.test.get(happy_birthday, 'greet/holidays') + response = hug.test.get(happy_birthday, "greet/holidays") assert str(response.data) == "Happy holidays!" diff --git a/examples/unicode_output.py b/examples/unicode_output.py index e48a5c5e..f0a408ad 100644 --- a/examples/unicode_output.py +++ b/examples/unicode_output.py @@ -5,4 +5,4 @@ @hug.get() def unicode_response(): """An example endpoint that returns unicode data nested within the result object""" - return {'data': ['Τη γλώσσα μου έδωσαν ελληνική']} + return {"data": ["Τη γλώσσα μου έδωσαν ελληνική"]} diff --git a/examples/use_socket.py b/examples/use_socket.py index 129b0bcf..d64e52d8 100644 --- a/examples/use_socket.py +++ b/examples/use_socket.py @@ -5,22 +5,24 @@ import time -http_socket = hug.use.Socket(connect_to=('www.google.com', 80), proto='tcp', pool=4, timeout=10.0) -ntp_service = hug.use.Socket(connect_to=('127.0.0.1', 123), proto='udp', pool=4, timeout=10.0) +http_socket = hug.use.Socket(connect_to=("www.google.com", 80), proto="tcp", pool=4, timeout=10.0) +ntp_service = hug.use.Socket(connect_to=("127.0.0.1", 123), proto="udp", pool=4, timeout=10.0) EPOCH_START = 2208988800 + + @hug.get() def get_time(): """Get time from a locally running NTP server""" - time_request = '\x1b' + 47 * '\0' + time_request = "\x1b" + 47 * "\0" now = struct.unpack("!12I", ntp_service.request(time_request, timeout=5.0).data.read())[10] return time.ctime(now - EPOCH_START) @hug.get() -def reverse_http_proxy(length: int=100): +def reverse_http_proxy(length: int = 100): """Simple reverse http proxy function that returns data/html from another http server (via sockets) only drawback is the peername is static, and currently does not support being changed. Example: curl localhost:8000/reverse_http_proxy?length=400""" diff --git a/examples/versioning.py b/examples/versioning.py index 9c6a110d..c7b3004d 100644 --- a/examples/versioning.py +++ b/examples/versioning.py @@ -2,21 +2,21 @@ import hug -@hug.get('/echo', versions=1) +@hug.get("/echo", versions=1) def echo(text): return text -@hug.get('/echo', versions=range(2, 5)) # noqa +@hug.get("/echo", versions=range(2, 5)) # noqa def echo(text): - return 'Echo: {text}'.format(**locals()) + return "Echo: {text}".format(**locals()) -@hug.get('/unversioned') +@hug.get("/unversioned") def hello(): - return 'Hello world!' + return "Hello world!" -@hug.get('/echo', versions='6') +@hug.get("/echo", versions="6") def echo(text): - return 'Version 6' + return "Version 6" diff --git a/examples/write_once.py b/examples/write_once.py index 796d161e..b61ee09a 100644 --- a/examples/write_once.py +++ b/examples/write_once.py @@ -6,8 +6,8 @@ @hug.local() @hug.cli() @hug.get() -def top_post(section: hug.types.one_of(('news', 'newest', 'show'))='news'): +def top_post(section: hug.types.one_of(("news", "newest", "show")) = "news"): """Returns the top post from the provided section""" - content = requests.get('https://news.ycombinator.com/{0}'.format(section)).content - text = content.decode('utf-8') - return text.split('