Giter VIP home page Giter VIP logo

hug's Introduction

HUG

PyPI version Build Status Windows Build Status Coverage Status License Join the chat at https://gitter.im/timothycrosley/hug


Read Latest Documentation - Browse GitHub Code Repository


hug aims to make developing Python driven APIs as simple as possible, but no simpler. As a result, it drastically simplifies Python API development.

hug's Design Objectives:

  • Make developing a Python driven API as succinct as a written definition.
  • The framework should encourage code that self-documents.
  • It should be fast. A developer should never feel the need to look somewhere else for performance reasons.
  • Writing tests for APIs written on-top of hug should be easy and intuitive.
  • Magic done once, in an API framework, is better than pushing the problem set to the user of the API framework.
  • Be the basis for next generation Python APIs, embracing the latest technology.

As a result of these goals, hug is Python 3+ only and built upon Falcon's high performance HTTP library

HUG Hello World Example

Supporting hug development

Get professionally supported hug with the Tidelift Subscription

Professional support for hug is available as part of the Tidelift Subscription. Tidelift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools.

Installing hug

Installing hug is as simple as:

pip3 install hug --upgrade

Ideally, within a virtual environment.

Getting Started

Build an example API with a simple endpoint in just a few lines.

# filename: happy_birthday.py
"""A basic (single function) API written using hug"""
import hug


@hug.get('/happy_birthday')
def happy_birthday(name, age:hug.types.number=1):
    """Says happy birthday to a user"""
    return "Happy {age} Birthday {name}!".format(**locals())

To run, from the command line type:

hug -f happy_birthday.py

You can access the example in your browser at: localhost:8000/happy_birthday?name=hug&age=1. Then check out the documentation for your API at localhost:8000/documentation

Parameters can also be encoded in the URL (check out happy_birthday.py for the whole example).

@hug.get('/greet/{event}')
def greet(event: str):
    """Greets appropriately (from http://blog.ketchum.com/how-to-write-10-common-holiday-greetings/)  """
    greetings = "Happy"
    if event == "Christmas":
        greetings = "Merry"
    if event == "Kwanzaa":
        greetings = "Joyous"
    if event == "wishes":
        greetings = "Warm"

    return "{greetings} {event}!".format(**locals())

Which, once you are running the server as above, you can use this way:

curl http://localhost:8000/greet/wishes
"Warm wishes!"

Versioning with hug

# filename: versioning_example.py
"""A simple example of a hug API call with versioning"""
import hug

@hug.get('/echo', versions=1)
def echo(text):
    return text


@hug.get('/echo', versions=range(2, 5))
def echo(text):
    return "Echo: {text}".format(**locals())

To run the example:

hug -f versioning_example.py

Then you can access the example from localhost:8000/v1/echo?text=Hi / localhost:8000/v2/echo?text=Hi Or access the documentation for your API from localhost:8000

Note: versioning in hug automatically supports both the version header as well as direct URL based specification.

Testing hug APIs

hug's http method decorators don't modify your original functions. This makes testing hug APIs as simple as testing any other Python functions. Additionally, this means interacting with your API functions in other Python code is as straight forward as calling Python only API functions. hug makes it easy to test the full Python stack of your API by using the hug.test module:

import hug
import happy_birthday

hug.test.get(happy_birthday, 'happy_birthday', {'name': 'Timothy', 'age': 25}) # Returns a Response object

You can use this Response object for test assertions (check out test_happy_birthday.py ):

def tests_happy_birthday():
    response = hug.test.get(happy_birthday, 'happy_birthday', {'name': 'Timothy', 'age': 25})
    assert response.status == HTTP_200
    assert response.data is not None

Running hug with other WSGI based servers

hug exposes a __hug_wsgi__ magic method on every API module automatically. Running your hug based API on any standard wsgi server should be as simple as pointing it to module_name: __hug_wsgi__.

For Example:

uwsgi --http 0.0.0.0:8000 --wsgi-file examples/hello_world.py --callable __hug_wsgi__

To run the hello world hug example API.

Building Blocks of a hug API

When building an API using the hug framework you'll use the following concepts:

METHOD Decorators get, post, update, etc HTTP method decorators that expose your Python function as an API while keeping your Python method unchanged

@hug.get() # <- Is the hug METHOD decorator
def hello_world():
    return "Hello"

hug uses the structure of the function you decorate to automatically generate documentation for users of your API. hug always passes a request, response, and api_version variable to your function if they are defined params in your function definition.

Type Annotations functions that optionally are attached to your methods arguments to specify how the argument is validated and converted into a Python type

@hug.get()
def math(number_1:int, number_2:int): #The :int after both arguments is the Type Annotation
    return number_1 + number_2

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.

@hug.get()
def test_time(hug_timer):
    return {'time_taken': float(hug_timer)}

Directives may be accessed via an argument with a hug_ prefix, or by using Python 3 type annotations. The latter is the more modern approach, and is recommended. Directives declared in a module can be accessed by using their fully qualified name as the type annotation (ex: module.directive_name).

Aside from the obvious input transformation use case, directives can be used to pipe data into your API functions, even if they are not present in the request query string, POST body, etc. For an example of how to use directives in this way, see the authentication example in the examples folder.

Adding your own directives is straight forward:

@hug.directive()
def square(value=1, **kwargs):
    '''Returns passed in parameter multiplied by itself'''
    return value * value

@hug.get()
@hug.local()
def tester(value: square=10):
    return value

tester() == 100

For completeness, here is an example of accessing the directive via the magic name approach:

@hug.directive()
def multiply(value=1, **kwargs):
    '''Returns passed in parameter multiplied by itself'''
    return value * value

@hug.get()
@hug.local()
def tester(hug_multiply=10):
    return hug_multiply

tester() == 100

Output Formatters a function that takes the output of your API function and formats it for transport to the user of the API.

@hug.default_output_format()
def my_output_formatter(data):
    return "STRING:{0}".format(data)

@hug.get(output=hug.output_format.json)
def hello():
    return {'hello': 'world'}

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.

@hug.default_input_format("application/json")
def my_input_formatter(data):
    return ('Results', hug.input_format.json(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

@hug.request_middleware()
def process_data(request, response):
    request.env['SERVER_NAME'] = 'changed'

@hug.response_middleware()
def process_data(request, response, resource):
    response.set_header('MyHeader', 'Value')

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:

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

hug enables you to organize large projects in any manner you see fit. You can import any module that contains hug decorated functions (request handling, directives, type handlers, etc) and extend your base API with that module.

For example:

something.py

import hug

@hug.get('/')
def say_hi():
    return 'hello from something'

Can be imported into the main API file:

__init__.py

import hug
from . import something

@hug.get('/')
def say_hi():
    return "Hi from root"

@hug.extend_api('/something')
def something_api():
    return [something]

Or alternatively - for cases like this - where only one module is being included per a URL route:

#alternatively
hug.API(__name__).extend(something, '/something')

Configuring hug 404

By default, hug returns an auto generated API spec when a user tries to access an endpoint that isn't defined. If you would not like to return this spec you can turn off 404 documentation:

From the command line application:

hug -nd -f {file} #nd flag tells hug not to generate documentation on 404

Additionally, you can easily create a custom 404 handler using the hug.not_found decorator:

@hug.not_found()
def not_found_handler():
    return "Not Found"

This decorator works in the same manner as the hug HTTP method decorators, and is even version aware:

@hug.not_found(versions=1)
def not_found_handler():
    return ""

@hug.not_found(versions=2)
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

@hug.get()
@asyncio.coroutine
def hello_world():
    return "Hello"

Using Python 3.5 async keyword.

@hug.get()
async def hello_world():
    return "Hello"

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.

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.

$ cd ./docker
# This will run Gunicorn on port 8000 of the Docker container.
$ docker-compose up gunicorn

# From the host machine, find your Dockers IP address.
# For Windows & Mac:
$ docker-machine ip default

# For Linux:
$ ifconfig docker0 | grep 'inet' | cut -d: -f2 | awk '{ print $1}' | head -n1

By default, the IP is 172.17.0.1. Assuming that's the IP you see, as well, you would then go to http://172.17.0.1:8000/ in your browser to view your API.

You can also log into a Docker container that you can consider your work space. This workspace has Python and Pip installed so you can use those tools within Docker. If you need to test the CLI interface, for example, you would use this.

$ docker-compose run workspace bash

On your Docker workspace container, the ./docker/templates directory on your host computer is mounted to /src in the Docker container. This is specified under services > app of ./docker/docker-compose.yml.

bash-4.3# cd /src
bash-4.3# tree
.
├── __init__.py
└── handlers
    ├── birthday.py
    └── hello.py

1 directory, 3 files

Security contact information

hug takes security and quality seriously. This focus is why we depend only on thoroughly tested components and utilize static analysis tools (such as bandit and safety) to verify the security of our code base. If you find or encounter any potential security issues, please let us know right away so we can resolve them.

To report a security vulnerability, please use the Tidelift security contact. Tidelift will coordinate the fix and disclosure.

Why hug?

HUG simply stands for Hopefully Useful Guide. This represents the project's goal to help guide developers into creating well written and intuitive APIs.


Thanks and I hope you find this hug helpful as you develop your next Python API!

~Timothy Crosley

hug's People

Contributors

alisaifee avatar allyjweir avatar banteg avatar bernhardreiter avatar brandonhoffman avatar chelseadole avatar cmoncur avatar cwenner avatar dangitall avatar edvardm avatar erwinhaasnoot avatar gemedet avatar ghostofgoes avatar jamesmco avatar jay-tyler avatar jj avatar johnlam avatar kirklg avatar rodcloutier avatar spock avatar stigok avatar svenstaro avatar tbekolay avatar thorbjornwolf avatar timothycrosley avatar ulmentflam avatar vortec avatar vytas7 avatar waddlelikeaduck avatar wagner-intevation avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hug's Issues

More authorization scaffolding or access to request object?

I am writing an API that needs to change output based on which user is logged in. I really like what I've been able to do with hug so far, but I'm not really seeing a clean way to do this at this point. Ideally, the functions would be able to access some kind of session information.

I know auth is implemented as nicely wrapped falcon middleware at the moment, so this may be rather tricky to pull off plumbing wise. If you arrive at a high-levl solution and are open to contributions, I'd be happy to contribute, as I do have a pressing use case for this.

Semantic versioning support

Right now hug only supports versioning on a straight numeric value, this works fairly well in most cases where no breaking changes happen between major releases, however it can occasionally fall apart - which unfortunately could lead to the hug API version not staying at all in sync with the codes semantic version. It would be beneficial to support semantic versioning routes natively in hug. One potential way to do this would be to allow a length 3 tuple to be passed into the tuple in addition to a straight int. A convenience named tuple object could be added to make usage explicit:

@hug.get(versions=hug.version(major=3, minor=2))
def endpoint():
   pass

thanks @vortec for the idea!

PEP 257 docstrings

The coding standards document indicates that the code in this project should conform to PEP 8, which specifies PEP 257 docstrings. My pull request #176 changed the files that it touched. I would be happy to take care of converting the rest of the project if you like.

Seamless micro-services

It would be really cool if you could use hug APIs locally via Python, in a way that made changing to remote usage during production use as simple as a single line / config change.

support for multiple chained transformers

It would be cool to be able to pass in a list of transformers instead of just a single transformer. A quick and consistent way to implement would be a transform.multiple transformation function that took a list of transforming functions.

-nd/--no-404-documentation flag not working

Traceback:
Traceback (most recent call last): File "/home/rye/.virtualenvs/hug/bin/hug", line 11, in <module> sys.exit(run.terminal()) File "/home/rye/.virtualenvs/hug/lib/python3.5/site-packages/hug/run.py", line 171, in terminal **server_arguments) TypeError: server() got an unexpected keyword argument 'sink'

Tested on a very basic Hug-app with only a single get-handler.

Add optional support for writing functions that conform to pep 484

https://www.python.org/dev/peps/pep-0484/ provides an optional way to write functions that enable type linters etc to extract information in a standard way from annotations. Hug should be expanded to support users who choose to follow this pep.

  • Add an optional transform parameter on the decorator that can be used instead of the -> syntax
  • Ensure that if the -> annotation points to the same type, nothing happens at all on output
  • If transform is set to False, nothing should happen even if an annotation is specified that does not have the same type

python3.2 package falcon/request.py invalid syntax problem

My Ubuntu is 14.04 and I just install the latest Python version and setup_tools.

I install hug with sudo pip3 install hug --upgrade successfully. But When I try to import hug in REPL, I got such error:

Python 3.2.3 (default, Jun 18 2015, 21:46:42)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import hug
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.2/dist-packages/hug/__init__.py", line 32, in <module>
    from hug import (authentication, directives, documentation, exceptions, format,
  File "/usr/local/lib/python3.2/dist-packages/hug/authentication.py", line 25, in <module>
    from falcon import HTTPUnauthorized
  File "/usr/local/lib/python3.2/dist-packages/falcon/__init__.py", line 32, in <module>
    from falcon.api import API, DEFAULT_MEDIA_TYPE  # NOQA
  File "/usr/local/lib/python3.2/dist-packages/falcon/api.py", line 22, in <module>
    from falcon.request import Request, RequestOptions
  File "/usr/local/lib/python3.2/dist-packages/falcon/request.py", line 38
    DEFAULT_ERROR_LOG_FORMAT = (u'{0:%Y-%m-%d %H:%M:%S} [FALCON] [ERROR]'
                                                                        ^
SyntaxError: invalid syntax

It also happened when I directly import in a Python file.

Add instructions for contributing using a Windows based machine

It would be useful to list instructions on getting hug setup for development / contributing from a Windows based machine. Since I do not own any computers running Windows personally, this is an area where I could really use another contributor's help to ensure the instructions are correct.

How to set CORS on per endpoint basis?

Hi,

Any idea on how to enable CORS or set Header on per end point basis ? If I use middleware it will be for all request, won't it ?

Thanks in advance

Combining multiple API files fails, when no directives are defined

If the example for splitting APIs across multiple files is followed an error occurs when no directives are defined, due to incorrect default in the code:

(testhug) william@Williams-MBP:~/projects/testhug/multi$ hug -f __init__.py
Traceback (most recent call last):
File "/Users/william/projects/testhug/bin/hug", line 11, in <module>
    sys.exit(run.terminal())
File "/Users/william/projects/testhug/lib/python3.4/site-packages/hug/run.py", line 171, in terminal
    api = server(importlib.machinery.SourceFileLoader(file_name.split(".")[0], file_name).load_module(),
File "<frozen importlib._bootstrap>", line 539, in _check_name_wrapper
File "<frozen importlib._bootstrap>", line 1614, in load_module
File "<frozen importlib._bootstrap>", line 596, in _load_module_shim
File "<frozen importlib._bootstrap>", line 1220, in load
File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
File "<frozen importlib._bootstrap>", line 1129, in _exec
File "<frozen importlib._bootstrap>", line 1471, in exec_module
File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
File "__init__.py", line 10, in <module>
    @hug.extend_api('/thing')
File "/Users/william/projects/testhug/lib/python3.4/site-packages/hug/decorators.py", line 194, in decorator
    module.__hug__.extend(api, route)
File "/Users/william/projects/testhug/lib/python3.4/site-packages/hug/decorators.py", line 97, in extend
    for directive in getattr(module.__hug__, '_directives', ()).values():
AttributeError: 'tuple' object has no attribute ‘values’

This needs to be fixed ASAP and prior to the next release. A test should be added to guarantee this wont happen again.

Example for extend_api decorator

I whipped up a quick example from looking through the decorators.py source:

__init__.py

import hug
from . import something

@hug.get('/')
def say_hi():
    return "Hi from root"

@hug.extend_api('/something')
def something_api():
    return [something]

#alternatively
__hug__.extend(something, '/something')

something.py

import hug

@hug.get('/')
def say_hi():
    return 'hello from something'

Support for running from a script

I want to be able to run/debug my services in eclipse/pydev. I faked it by doing this in my hug API file:

if __name__ == '__main__':
    import sys
    from hug import run
    # run.terminal() gets its args from sys.argv so give it what it expects
    sys.argv[0] = "hug"
    sys.argv.extend(['-f', __file__])
    run.terminal()

and it seems to work.

Is there a better way that I have missed?

How to import modules with undecorated functions?

Is there a way to import modules/libraries that are not decorated with @hug? I must be missing something obvious?

Here what I'm getting:

C:\Users\mike\PycharmProjects\hugs\person> hug -f .\app.py
Traceback (most recent call last):
  File "C:\Users\mike\AppData\Local\Programs\Python\Python35\lib\runpy.py", line 170, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Users\mike\AppData\Local\Programs\Python\Python35\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Users\mike\AppData\Local\Programs\Python\Python35\Scripts\hug.exe\__main__.py", line 9, in <module>
  File "C:\Users\mike\AppData\Local\Programs\Python\Python35\lib\site-packages\hug\run.py", line 147, in terminal
    api = server(importlib.machinery.SourceFileLoader(file_name.split(".")[0], file_name).load_module())
  File "<frozen importlib._bootstrap_external>", line 383, in _check_name_wrapper
  File "<frozen importlib._bootstrap_external>", line 804, in load_module
  File "<frozen importlib._bootstrap_external>", line 663, in load_module
  File "<frozen importlib._bootstrap>", line 268, in _load_module_shim
  File "<frozen importlib._bootstrap>", line 693, in _load
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 660, in exec_module
  File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
  File ".\app.py", line 2, in <module>
    import person
ImportError: No module named 'person'

Thanks!

Support automatic reload on file change

Split off from here: #31. Ideally, hug would automatically restart a dev API when the files used to create it changed. This would streamline the user workflow when using hug.

Use of output formatter seems to affect required parameters

I don't know why the presence of an output formatter would affect making sure required parameters are present...


'''
Both of these have a single parameter named "data".
If you call the one without the output formatter, it correctly notices the missing parameter.
If you call the one with the output formatter, it raises a server error.

'''
import hug

@hug.get('/without_output_formatter')
def with_output_formatter(data):
    ''' 
    If called without the required parameter, gives you ...
HTTP/1.0 400 Bad Request
Date: Mon, 01 Feb 2016 04:09:15 GMT
Server: WSGIServer/0.2 CPython/3.4.3+
content-length: 55
content-type: application/json

{
    "errors": {
        "data": "Required parameter not supplied"
    }
}

    '''
    return 'Without Output Formatter'

@hug.get('/with_output_formatter',output=hug.output_format.svg_xml_image)
def without_output_formatter(data):
    ''' 
    If called without the required parameter, gives you ...
HTTP/1.0 500 Internal Server Error
Content-Length: 59
Content-Type: text/plain
Date: Mon, 01 Feb 2016 04:09:09 GMT
Server: WSGIServer/0.2 CPython/3.4.3+

A server error occurred.  Please contact the administrator.


    ''' 
    return 'With Output Formatter'


Kudos. And a half-baked idea for an application.

Kudos on your work! I'll be watching to see where it goes.

The half-baked idea I'd like to work on sometime (when I get time):
Up till now, hypermedia APIs have not gotten as much as attention as they deserve, arguably because they are designed for a theoretical client that doesn't actually exist. What if this client did exist in the form of code completion and validation engines for multiple languages? By existing only as a helper for the developer, it might avoid the mess of CORBA and SOAP.

Hug could make this easy if it used the HTTP Link Header to return type information. For example, it could return the following for the /users API endpoint:

{
  "@context": "http://schema.org/",
  "@type": "Person",
  "@typedef": "http://www.janedoe.com/typedefs/person.d.ts"
}

With http://www.janedoe.com/typedefs/person.d.ts being something like this (using Typescript to describe JSON because JSON Schema is unwieldy):

interface Person
{
    name: string;
    jobTitle?: string;
    telephone?: string;
    url?: string;
}

And a sample response body for a GET request:

{
  "name": "Jane Doe",
  "jobTitle": "Professor",
  "telephone": "(425) 123-4567",
  "url": "http://www.janedoe.com"
}

Related:

Add support for Content-Type:"application/json; charset=UTF-8" style header

I'm having some problem in a toy app, I'm experiencing a server error in Firefox but not in Chrome (same client).

Diving in the code I noticed the problem is related with the dictionary lookup in HugAPI.input_format.

return getattr(self, '_input_format', {}).get(content_type, hug.defaults.input_format.get(content_type, None))

Whenever the content type is composed nothing matches.

An easy workaround is to strip after the ; but generally a real fix requires an encoding argument floating around, with an UTF-8 default.

I'd like to help, you'd like to discuss an approach to solve the problem?

Contributing

Hey, great work!
Do you have plans for future features? I would like to help the project. Maybe you could create issues tagged as bugs or enhancements.

Ability to set serializer using -> annotation

Currently, you can specify how data that comes in is processed using annotation type handlers. It would be cool if you could specify how outputted data should be structured (in Python) using the return or -> annotation. Just like input types this should be separate from it's content_type / output_format

Add `output_invalid` route option

It would be useful to allow easily switching the output format on case of a validation error.
To enable this we should add an output_invalid argument to the HTTPRouter object that will be used on validation errors if specified, and will default to the overall output_format if not.

Issues testing an API imported from another file

The testing facilities included in hug are really nice. However, when writing tests against your own APIs declared in separate files, there appear to be two small issues.

First, the README seems to indicate that you can test by passing in the module with your API to hug.test.get or a similar function. However, it appears that you actually need to pass your_module.__hug__ as the first argument to hug.test.get. I'm not sure if this is just outdated documentation or a legitimate bug.

Second, assuming the above is the correct way to test a module, py.test seems to hang after finishing the tests. This appears to only be an issue when importing APIs from another module. Declaring API endpoints inline seems to work, but this is obviously not feasible when writing tests against your nicely structure set of API files ;) I'm guessing that there is some "server" thread that is not exiting after the tests?

Retrieving request body?

First off, like this API simple and strait to the point.

I've used hug to build a simple REST API layer to another api, I would like to log the request body and parameters to be used at a later date for application data analytics (pretty graphs!) but there doesn't seem to be a clear way to this indicated by documentation, something as simple as this:

import hug
@hug.get('/version')
def get_version();
    info(response.body);

Would suffice, is the response functions only available in the application middleware?

Length hug type

There should be a hug.length type that allows validating against the length of a passed in field and transforming it to a specific Python type (defaulting to string). This would allow quickly building endpoints that accept for instance names under a curtain string length:

@hug.post()
def user(name: hug.length(0, 10)):
    pass # logic goes here

Enable pass through validation

hug should (optionally) allow the user to configure a route to raise on validation errors, instead of collecting the errors into an error dict. This would be the quickest to process option, allow short-circuit validation, and allow the errors to pass to their overall exception handlers.

Newlines are replaced with \n

When I use this method:

@hug.get()
def newline():
    s = """this is a string
with a newline
"""
    return s

I get the following output from http://localhost:8000/newline:

"this is a string\nwith a newline\n"

Is it possible to prevent the newline being replaced?

Custom overall validation function

It would beneficial to have a secondary custom validator that could be set for API endpoints. This would allow easy support for things such as verifying one field or another was present within the scope of the validation process.

CONTRIBUTING.MD

There should be a document at the root of the repository that explains the process and concerns that should be taken when contributing to hug, to encourage more contributors.

Architecture overview

There should be an overview of the architecture and thoughts behind it somewhere within the hug repo. This will also help new contributors get a since of how to contribute and what files serve what purposes within the code base.

Allow creating API objects explicitly

hug internally creates a 'hug' singleton object on every module where hug routes or utilities are defined. It would be neat if you could do this explicitly as well as implicitly. For example:

api = hug.API(__name__)

api == __hug__

this could enable cleaner usage in some circumstances.

Ability to route separate from handlers

It would be useful to enable routing to be done separate from the handler functions to better support alternative directory structures and workflows:

api.py

import hug
import library

api = hug.API(__name__)
api.get('/api_call_1')(library.function_1)
api.class_based('/api_calls_2')(library.class_1)

That would also allow easy routing of existing internal Python only APIs without code changes

It's important to note, however, that this would need to be done in addition to the default behavior of decorating functions, and would not be replacing it.

Move documentation creating functionality to API singleton

The function that creates documentation should be moved from it's own module to live inside of the hug API singleton. This would allow easily retrieving documentation from other modules for introspection reasons, as well as easy customization of the documentation produced.

Doesn't seem to work with Python 2.x, only with Python3.x

It uses a mock module that doesn't seem to exist in Python 2. When using Python3 it works fine.
Is this project supposed to be Python3 only?

Traceback (most recent call last):
  File "main.py", line 1, in <module>
    import hug
  File "/usr/local/lib/python2.7/dist-packages/hug/__init__.py", line 32, in <module>
    from hug import (authentication, directives, documentation, exceptions, format,
  File "/usr/local/lib/python2.7/dist-packages/hug/test.py", line 26, in <module>
    from unittest import mock
ImportError: cannot import name mock

Tried with version 1.9.3 installed using pip.

request_middleware Question

I've just come across hug as I'm writing a new API and it looks simple, but very effective :)

I'm currently adding authentication using request_middleware as this:

import hug
from falcon import HTTP_FORBIDDEN

@hug.request_middleware()
def process_data(request, response):
        if 'X-API-Key' not in request.headers:
                response.body='403 Forbidden'
                response.status=HTTP_FORBIDDEN

@hug.get('/test', versions=1)
def test(text):
        print(text)
        return {'text': text}

This works fine, and returns a 403, however it continues to process the endpoint (I see the text on the console).

Is there a (sane) way to stop this from happening?

doco says port 8080 but default is 8000

The initial example in the doco says
localhost:8080/happy_birthday?name=Hug&age=1
but the default is 8000 causing initial confusion.
Not a biggie but might confuse newcomers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.