Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Réaliser une API avec Python 3

Python

Python 2 et Python 3

Librairies et Frameworks

The Zen of Python, by Tim Peters

19 principes pour expliquer la philosophie du langage

# Easter egg plein de bon sens !
import this

Hello World

hello_world.py
print("Hello World!")

bash
$ python3 hello_world.py

Les fonctions sont des objets

def my_function():
    """
    Some function docstring.
    """
    print("my_function")

print(my_function)
(out) <function my_function at 0x7fa904ea7620>

Les classes sont des objets

class MyClass:
    """
    Some class docstring.
    """
    def __init__(self, value):
        self.value = value

    def move(self):
        """
        Some method docstring.
        """
        print("Moving %s ..." % self.value)

print(MyClass)
(out) <class '__main__.MyClass'>

Les classes sont modifiables

# On crée une instance de MyClass
my_instance = MyClass(42)

# On appelle la méthode move
my_instance.move()
(out) Moving 42 ... # Tout va bien

# On défini une fonction jump
def jump(self):
    print("Jumping %s ..." % self.value)

# On redéfini la méthode move
MyClass.move = jump

# Et on appelle move
my_instance.move()
(out) Jumping 42 ... # Suprise !

L'introspection

import inspect

def maximum(arg1, arg2):
    """
    Return the maximum value.
    """
    return arg2 if arg2 > arg1 else arg1

# On récupère la signature
inspect.signature(maximum)
(out) <Signature (arg1, arg2)>

# la docstring
inspect.getdoc(maximum)
(out) Return the maximum value.

# et même les sources !
inspect.getsource(maximum)
(out) def maximum(arg1, arg2):\n        """\n        Return the maximum value.\n        """\n        return arg2 if arg2 > arg1 else arg1\n

Les objets dynamiques

class Class42:
    def __init__(self):
        self._data = {}

    def __setattr__(self, key, value):
        if key == "_data":
            super().__setattr__(key, value)
        else:
            self._data[key] = value * 42

    def __getattr__(self, item):
        return self._data.get(item)

    @property
    def total(self):
        return sum(self._data.values())

o = Class42()
o.first = 1
o.second = 2
print("o: (%s, %s, %s)" % (o.first, o.second, o.total))
(out) o: (42, 84, 126)

Les callables

Tout peut devenir fonction

class ClassCallable:
    def __call__(self, *args, **kwargs):
        print("args: %s; kwargs: %s; " % (args, kwargs))

callable = ClassCallable()
callable(1, 2, arg3=3, arg4=4)
(out) args: (1, 2); kwargs: {'arg4': 4, 'arg3': 3};

Les annotations décorateurs

def double_first(func):
    def double_func(a, b):
        return func(a*2, b)
    return double_func


@double_first
def decorated_sum(a, b):
    return a + b
# decorated_sum = double_first(decorated_sum)

print(decorated_sum(3, 4))
(out) 10

Les annotations décorateurs

class add_second:
    def __init__(self, value):
        self.value = value

    def __call__(self, func):
        def add_func(a, b):
            return func(a, b + self.value)
        return add_func


@double_first
@add_second(2)
def decorated_product(a, b):
    return a * b
# decorated_sum = add_second(2)(double_first(decorated_sum))

print(decorated_product(2, 3))
(out) 20

Type Hints (Python 3.5)

from typing import Dict

# Let's pass in a dictionary
def greeting(names: Dict[str, int]) -> str:
    keys = names.keys()
    print('Hello, {}'.format(', '.join(keys)))

greeting({'jane': 10, 'john': 11, 'judy': 12})

Python est idéal pour implémenter de la magie

hug exploite python 3

Pour implémenter et documenter une API

"""First hug API (local, command-line, and HTTP access)"""
import hug


@hug.cli()
@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 %s Birthday %s!' % (age, name)}


if __name__ == '__main__':
    happy_birthday.interface.cli()

Mais une API, c'est quoi ?

Application Programming Interface

wikipedia
ensemble normalisé de classes, de méthodes ou de fonctions qui sert de façade par laquelle un logiciel offre des services à d'autres logiciels

Pas de protocole particulier !

hug permet d'écrire une API

hug est performant

Benchmark Pycnic

Getting started

1 seul code => 3 cibles

getting_started.py
"""First hug API (local, command-line, and HTTP access)"""
import hug


@hug.get(examples='name=Hug&age=1')
@hug.cli()
@hug.local()
def happy_birthday(name: hug.types.text, age: hug.types.number, \
                   timer: hug.directives.Timer=-1):
    """Says happy birthday to a user"""
    return {'message': 'Happy {0} Birthday {1}!'.format(age, name),
            'took': float(timer)}

Versioning

Plusieurs versions dans la même application

versioning.py
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())

@hug.get('/unversioned')
def hello():
    return 'Hello world!'

Type Annotations & Validation

Les données sont automatiquement validées

validation.py
import datetime as dt

import hug
from marshmallow import fields
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'):
    """Add a value to a date."""
    value = value or dt.datetime.utcnow()
    if unit == 'minutes':
        delta = dt.timedelta(minutes=addend)
    else:
        delta = dt.timedelta(days=addend)
        result = value + delta
    return {'result': result}

Directives

import hug

@hug.directive()
def basic(default='hi', **kwargs):
    return default + ' there!'

@hug.local()
def english(greeting: basic='hello'):
    return greeting

@hug.local()
def american(greeting: basic):
    return greeting

assert english() == 'hello there!'
assert american() == 'hi there!'
assert english('OMG!') == 'OMG!'

Output & Input Formatters

output_image.py
import hug

@hug.get('/image.png', output=hug.output_format.png_image)
def image():
    """Serves up a PNG image."""
    return '../logo.png'
output_pil.py
import hug
from PIL import Image, ImageDraw

@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))
    return image
    # voir duck-typing
output_video.py
import hug

@hug.get(output=hug.output_format.mp4_video)
def watch():
    """Watch an example movie, streamed directly to you from hug"""
    return 'movie.mp4'

Authentification

import hug

verify = hug.authentication.verify('User1', 'mypassword')
authentication = hug.authentication.basic(verify)

@hug.get('/authenticated', requires=authentication)
def basic_auth_api_call(user: hug.directives.user):
    return 'Successfully authenticated with user: {0}'.format(user)

Points forts

Limites

Use a spacebar or arrow keys to navigate