Falcon est un framework ASGI/WSGI minimaliste permettant de créer des API et des microservices REST critiques, en mettant l'accent sur la fiabilité, l'exactitude et les performances à grande échelle.
Lorsqu'il s'agit de créer des API HTTP, d'autres frameworks vous alourdissent avec des tonnes de dépendances et d'abstractions inutiles. Falcon va droit au but avec un design épuré qui embrasse HTTP et le style architectural REST.
Les applications Falcon fonctionnent avec n'importe quel serveur WSGI ou ASGI et fonctionnent comme un champion sous CPython 3.8+ et PyPy 3.8+.
"Falcon est solide comme le roc et rapide."
"Nous utilisons Falcon en remplacement de [un autre framework] et nous aimons tout simplement les performances (trois fois plus rapides) et la taille de la base de code (facilement la moitié de notre code [d'origine])."
"J'adore #falconframework ! Super propre et simple, j'ai enfin la vitesse et la flexibilité dont j'ai besoin !"
"Falcon a fière allure jusqu'à présent. J'ai réalisé un test rapide pour un de mes petits serveurs et j'ai été environ 40 % plus rapide avec seulement 20 minutes de travail."
"J'ai l'impression de parler enfin de HTTP, sans rien au milieu. Falcon ressemble aux requêtes du backend."
"Le code source de Falcon est si bon que je le préfère presque à la documentation. En gros, il ne peut pas être faux."
"Quel autre framework a intégré la prise en charge de 786 TRY IT NOW ?"
Falcon essaie d'en faire le moins possible tout en restant très efficace.
asyncio
nativeFalcon vous a-t-il aidé à créer une application géniale ? Montrez votre soutien aujourd'hui avec un don unique ou en devenant mécène. Les supporters reçoivent du matériel sympa, la possibilité de promouvoir leur marque auprès des développeurs Python et une assistance prioritaire.
Merci!
La perfection est enfin atteinte, non pas lorsqu'il n'y a plus rien à ajouter, mais lorsqu'il n'y a plus rien à retrancher.
-Antoine de Saint-Exupéry
Nous avons conçu Falcon pour répondre aux besoins exigeants des microservices à grande échelle et des backends d'applications réactifs. Falcon complète les frameworks Web Python plus généraux en offrant des performances, une fiabilité et une flexibilité sans système d'exploitation partout où vous en avez besoin.
Fiable. Nous faisons de grands efforts pour éviter d'introduire des changements radicaux, et lorsque nous le faisons, ils sont entièrement documentés et introduits uniquement (dans l'esprit de SemVer) avec un incrément de version majeur. Le code est rigoureusement testé avec de nombreuses entrées et nous exigeons une couverture à 100 % à tout moment. Falcon n'a aucune dépendance en dehors de la bibliothèque standard, ce qui permet de minimiser la surface d'attaque de votre application tout en évitant les bogues transitifs et les modifications brutales.
Débogable. Falcon évite la magie. Il est facile de savoir quelles entrées mènent à quelles sorties. Les exceptions non gérées ne sont jamais encapsulées ou masquées. Les comportements potentiellement surprenants, tels que l'analyse automatique du corps des requêtes, sont bien documentés et désactivés par défaut. Enfin, en ce qui concerne le framework lui-même, nous veillons à ce que les chemins logiques restent simples et compréhensibles. Tout cela facilite le raisonnement sur le code et le débogage des cas extrêmes dans les déploiements à grande échelle.
Rapide. Même matériel, plus de demandes. Falcon traite les requêtes beaucoup plus rapidement que d'autres frameworks Python populaires comme Django et Flask. Pour une augmentation de vitesse supplémentaire, Falcon se compile avec Cython lorsqu'il est disponible et fonctionne également bien avec PyPy. Vous envisagez de passer à un autre langage de programmation ? Faites d'abord une analyse comparative avec Falcon+PyPy !
Flexible. Falcon vous laisse, en tant que développeur d'API, de nombreuses décisions et détails de mise en œuvre. Cela vous donne une grande liberté pour personnaliser et affiner votre implémentation. Il vous aide également à comprendre vos applications à un niveau plus approfondi, ce qui les rend plus faciles à régler, déboguer et refactoriser sur le long terme. La conception minimaliste de Falcon offre aux membres de la communauté Python un espace pour innover de manière indépendante sur les modules complémentaires et les packages complémentaires de Falcon.
Falcon est utilisé dans le monde entier par un nombre croissant d'organisations, notamment :
Si vous utilisez le framework Falcon pour un projet communautaire ou commercial, pensez à ajouter vos informations à notre wiki sous Qui utilise Falcon ?
Un certain nombre de modules complémentaires, de modèles et de packages complémentaires Falcon sont disponibles pour une utilisation dans vos projets. Nous en avons répertorié plusieurs sur le wiki Falcon comme point de départ, mais vous souhaiterez peut-être également rechercher des ressources supplémentaires dans PyPI.
La communauté Falconry sur Gitter est un endroit idéal pour poser des questions et partager vos idées. Vous pouvez nous trouver en fauconnerie/utilisateur. Nous disposons également d'une salle de fauconnerie/développement pour discuter de la conception et du développement du framework lui-même.
Conformément à notre code de conduite, nous attendons de tous ceux qui participent aux discussions communautaires qu'ils agissent de manière professionnelle et montrent l'exemple en encourageant des discussions constructives. Chaque individu de la communauté est responsable de créer une culture positive, constructive et productive.
PyPy est le moyen le plus rapide d'exécuter votre application Falcon. PyPy3.8+ est pris en charge à partir de PyPy v7.3.7+.
$ pip install falcon
Ou, pour installer la dernière version bêta ou la dernière version candidate, le cas échéant :
$ pip install --pre falcon
Falcon prend également entièrement en charge CPython 3.8+.
La dernière version stable de Falcon peut être installée directement depuis PyPI :
$ pip install falcon
Ou, pour installer la dernière version bêta ou la dernière version candidate, le cas échéant :
$ pip install --pre falcon
Afin de fournir une augmentation de vitesse supplémentaire, Falcon se compile automatiquement avec Cython sous n'importe quel installateur conforme à la PEP 517.
Pour votre commodité, des roues contenant des binaires précompilés sont disponibles auprès de PyPI pour la majorité des plates-formes courantes. Même si une version binaire pour la plate-forme de votre choix n'est pas disponible, pip
choisira une roue purement Python. Vous pouvez également cythoniser Falcon pour votre environnement ; consultez nos documents d'installation pour plus d'informations à ce sujet et sur d'autres options avancées.
Falcon ne nécessite l'installation d'aucun autre package.
Falcon parle WSGI (ou ASGI ; voir aussi ci-dessous). Afin de servir une application Falcon, vous aurez besoin d'un serveur WSGI. Gunicorn et uWSGI sont parmi les plus populaires, mais tout ce qui peut charger une application WSGI fera l'affaire.
$ pip install [gunicorn | uwsgi]
Afin de servir une application Falcon ASGI, vous aurez besoin d'un serveur ASGI. Uvicorn est un choix populaire :
$ pip install uvicorn
Falcon vit sur GitHub, ce qui rend le code facile à parcourir, télécharger, fork, etc. Les demandes de tirage sont toujours les bienvenues ! N'oubliez pas non plus de mettre le projet en vedette s'il vous rend heureux. :)
Une fois que vous avez cloné le dépôt ou téléchargé une archive tar depuis GitHub, vous pouvez installer Falcon comme ceci :
$ cd falcon
$ pip install .
Ou, si vous souhaitez modifier le code, commencez par créer le dépôt principal, clonez le fork sur votre bureau, puis exécutez ce qui suit pour l'installer à l'aide d'un lien symbolique, de sorte que lorsque vous modifiez votre code, les modifications soient automatiquement disponibles pour votre application sans avoir à réinstaller le package :
$ cd falcon
$ FALCON_DISABLE_CYTHON=Y pip install -e .
Vous pouvez tester manuellement les modifications apportées au framework Falcon en basculant vers le répertoire du dépôt cloné, puis en exécutant pytest :
$ cd falcon
$ pip install -r requirements/tests
$ pytest tests
Ou, pour exécuter l'ensemble de tests par défaut :
$ pip install tox && tox
Voir également le fichier tox.ini pour une liste complète des environnements disponibles.
Les docstrings de la base de code Falcon sont assez étendus et nous vous recommandons de garder un REPL en cours d'exécution pendant l'apprentissage du framework afin que vous puissiez interroger les différents modules et classes lorsque vous avez des questions.
Les documents en ligne sont disponibles sur : https://falcon.readthedocs.io
Vous pouvez créer les mêmes documents localement comme suit :
$ pip install tox && tox -e docs
Une fois les documents créés, vous pouvez les consulter en ouvrant la page d'index suivante dans votre navigateur. Sous OS X, c'est aussi simple que :
$ ouvrir docs/_build/html/index.html
Ou sous Linux :
$ xdg-open docs/_build/html/index.html
Voici un exemple simple et artificiel montrant comment créer une application WSGI basée sur Falcon (la version ASGI est incluse plus bas) :
# examples/things.py
# Let's get this party started!
from wsgiref . simple_server import make_server
import falcon
# Falcon follows the REST architectural style, meaning (among
# other things) that you think in terms of resources and state
# transitions, which map to HTTP verbs.
class ThingsResource :
def on_get ( self , req , resp ):
"""Handles GET requests"""
resp . status = falcon . HTTP_200 # This is the default status
resp . content_type = falcon . MEDIA_TEXT # Default is JSON, so override
resp . text = ( ' n Two things awe me most, the starry sky '
'above me and the moral law within me. n '
' n '
' ~ Immanuel Kant n n ' )
# falcon.App instances are callable WSGI apps...
# in larger applications the app is created in a separate file
app = falcon . App ()
# Resources are represented by long-lived class instances
things = ThingsResource ()
# things will handle all requests to the '/things' URL path
app . add_route ( '/things' , things )
if __name__ == '__main__' :
with make_server ( '' , 8000 , app ) as httpd :
print ( 'Serving on port 8000...' )
# Serve until process is killed
httpd . serve_forever ()
Vous pouvez exécuter l'exemple ci-dessus directement à l'aide du serveur wsgiref inclus :
$ pip install falcon
$ python things.py
Puis dans un autre terminal :
$ curl localhost:8000/things
La version ASGI de l'exemple est similaire :
# examples/things_asgi.py
import falcon
import falcon . asgi
# Falcon follows the REST architectural style, meaning (among
# other things) that you think in terms of resources and state
# transitions, which map to HTTP verbs.
class ThingsResource :
async def on_get ( self , req , resp ):
"""Handles GET requests"""
resp . status = falcon . HTTP_200 # This is the default status
resp . content_type = falcon . MEDIA_TEXT # Default is JSON, so override
resp . text = ( ' n Two things awe me most, the starry sky '
'above me and the moral law within me. n '
' n '
' ~ Immanuel Kant n n ' )
# falcon.asgi.App instances are callable ASGI apps...
# in larger applications the app is created in a separate file
app = falcon . asgi . App ()
# Resources are represented by long-lived class instances
things = ThingsResource ()
# things will handle all requests to the '/things' URL path
app . add_route ( '/things' , things )
Vous pouvez exécuter la version ASGI avec uvicorn ou tout autre serveur ASGI :
$ pip install falcon uvicorn
$ uvicorn things_asgi:app
Voici un exemple plus complexe qui montre la lecture des en-têtes et des paramètres de requête, la gestion des erreurs et l'utilisation des corps de requête et de réponse. Notez que cet exemple suppose que le package de requêtes a été installé.
(Pour l'application ASGI équivalente, voir : Un exemple plus complexe (ASGI)).
# examples/things_advanced.py
import json
import logging
import uuid
from wsgiref import simple_server
import falcon
import requests
class StorageEngine :
def get_things ( self , marker , limit ):
return [{ 'id' : str ( uuid . uuid4 ()), 'color' : 'green' }]
def add_thing ( self , thing ):
thing [ 'id' ] = str ( uuid . uuid4 ())
return thing
class StorageError ( Exception ):
@ staticmethod
def handle ( ex , req , resp , params ):
# TODO: Log the error, clean up, etc. before raising
raise falcon . HTTPInternalServerError ()
class SinkAdapter :
engines = {
'ddg' : 'https://duckduckgo.com' ,
'y' : 'https://search.yahoo.com/search' ,
}
def __call__ ( self , req , resp , engine ):
url = self . engines [ engine ]
params = { 'q' : req . get_param ( 'q' , True )}
result = requests . get ( url , params = params )
resp . status = str ( result . status_code ) + ' ' + result . reason
resp . content_type = result . headers [ 'content-type' ]
resp . text = result . text
class AuthMiddleware :
def process_request ( self , req , resp ):
token = req . get_header ( 'Authorization' )
account_id = req . get_header ( 'Account-ID' )
challenges = [ 'Token type="Fernet"' ]
if token is None :
description = ( 'Please provide an auth token '
'as part of the request.' )
raise falcon . HTTPUnauthorized ( title = 'Auth token required' ,
description = description ,
challenges = challenges ,
href = 'http://docs.example.com/auth' )
if not self . _token_is_valid ( token , account_id ):
description = ( 'The provided auth token is not valid. '
'Please request a new token and try again.' )
raise falcon . HTTPUnauthorized ( title = 'Authentication required' ,
description = description ,
challenges = challenges ,
href = 'http://docs.example.com/auth' )
def _token_is_valid ( self , token , account_id ):
return True # Suuuuuure it's valid...
class RequireJSON :
def process_request ( self , req , resp ):
if not req . client_accepts_json :
raise falcon . HTTPNotAcceptable (
description = 'This API only supports responses encoded as JSON.' ,
href = 'http://docs.examples.com/api/json' )
if req . method in ( 'POST' , 'PUT' ):
if 'application/json' not in req . content_type :
raise falcon . HTTPUnsupportedMediaType (
title = 'This API only supports requests encoded as JSON.' ,
href = 'http://docs.examples.com/api/json' )
class JSONTranslator :
# NOTE: Normally you would simply use req.media and resp.media for
# this particular use case; this example serves only to illustrate
# what is possible.
def process_request ( self , req , resp ):
# req.stream corresponds to the WSGI wsgi.input environ variable,
# and allows you to read bytes from the request body.
#
# See also: PEP 3333
if req . content_length in ( None , 0 ):
# Nothing to do
return
body = req . stream . read ()
if not body :
raise falcon . HTTPBadRequest ( title = 'Empty request body' ,
description = 'A valid JSON document is required.' )
try :
req . context . doc = json . loads ( body . decode ( 'utf-8' ))
except ( ValueError , UnicodeDecodeError ):
description = ( 'Could not decode the request body. The '
'JSON was incorrect or not encoded as '
'UTF-8.' )
raise falcon . HTTPBadRequest ( title = 'Malformed JSON' ,
description = description )
def process_response ( self , req , resp , resource , req_succeeded ):
if not hasattr ( resp . context , 'result' ):
return
resp . text = json . dumps ( resp . context . result )
def max_body ( limit ):
def hook ( req , resp , resource , params ):
length = req . content_length
if length is not None and length > limit :
msg = ( 'The size of the request is too large. The body must not '
'exceed ' + str ( limit ) + ' bytes in length.' )
raise falcon . HTTPContentTooLarge (
title = 'Request body is too large' , description = msg )
return hook
class ThingsResource :
def __init__ ( self , db ):
self . db = db
self . logger = logging . getLogger ( 'thingsapp.' + __name__ )
def on_get ( self , req , resp , user_id ):
marker = req . get_param ( 'marker' ) or ''
limit = req . get_param_as_int ( 'limit' ) or 50
try :
result = self . db . get_things ( marker , limit )
except Exception as ex :
self . logger . error ( ex )
description = ( 'Aliens have attacked our base! We will '
'be back as soon as we fight them off. '
'We appreciate your patience.' )
raise falcon . HTTPServiceUnavailable (
title = 'Service Outage' ,
description = description ,
retry_after = 30 )
# NOTE: Normally you would use resp.media for this sort of thing;
# this example serves only to demonstrate how the context can be
# used to pass arbitrary values between middleware components,
# hooks, and resources.
resp . context . result = result
resp . set_header ( 'Powered-By' , 'Falcon' )
resp . status = falcon . HTTP_200
@ falcon . before ( max_body ( 64 * 1024 ))
def on_post ( self , req , resp , user_id ):
try :
doc = req . context . doc
except AttributeError :
raise falcon . HTTPBadRequest (
title = 'Missing thing' ,
description = 'A thing must be submitted in the request body.' )
proper_thing = self . db . add_thing ( doc )
resp . status = falcon . HTTP_201
resp . location = '/%s/things/%s' % ( user_id , proper_thing [ 'id' ])
# Configure your WSGI server to load "things.app" (app is a WSGI callable)
app = falcon . App ( middleware = [
AuthMiddleware (),
RequireJSON (),
JSONTranslator (),
])
db = StorageEngine ()
things = ThingsResource ( db )
app . add_route ( '/{user_id}/things' , things )
# If a responder ever raises an instance of StorageError, pass control to
# the given handler.
app . add_error_handler ( StorageError , StorageError . handle )
# Proxy some things to another service; this example shows how you might
# send parts of an API off to a legacy system that hasn't been upgraded
# yet, or perhaps is a single cluster that all data centers have to share.
sink = SinkAdapter ()
app . add_sink ( sink , r'/search/(?P<engine>ddg|y)Z' )
# Useful for debugging problems in your API; works with pdb.set_trace(). You
# can also use Gunicorn to host your app. Gunicorn can be configured to
# auto-restart workers when it detects a code change, and it also works
# with pdb.
if __name__ == '__main__' :
httpd = simple_server . make_server ( '127.0.0.1' , 8000 , app )
httpd . serve_forever ()
Encore une fois, ce code utilise wsgiref, mais vous pouvez également exécuter l'exemple ci-dessus en utilisant n'importe quel serveur WSGI, tel que uWSGI ou Gunicorn. Par exemple:
$ pip install requests gunicorn
$ gunicorn things:app
Sous Windows, vous pouvez exécuter Gunicorn et uWSGI via WSL, ou vous pouvez essayer Waitress :
$ pip install requests waitress
$ waitress-serve --port=8000 things:app
Pour tester cet exemple, ouvrez un autre terminal et exécutez :
$ http localhost:8000/1/things authorization:custom-token
Vous pouvez également afficher la configuration de l'application à partir de la CLI via le script falcon-inspect-app
fourni avec le framework :
falcon-inspect-app things_advanced:app
Voici la version ASGI de l'application ci-dessus. Notez qu'il utilise le package httpx à la place des requêtes.
# examples/things_advanced_asgi.py
import json
import logging
import uuid
import falcon
import falcon . asgi
import httpx
class StorageEngine :
async def get_things ( self , marker , limit ):
return [{ 'id' : str ( uuid . uuid4 ()), 'color' : 'green' }]
async def add_thing ( self , thing ):
thing [ 'id' ] = str ( uuid . uuid4 ())
return thing
class StorageError ( Exception ):
@ staticmethod
async def handle ( ex , req , resp , params ):
# TODO: Log the error, clean up, etc. before raising
raise falcon . HTTPInternalServerError ()
class SinkAdapter :
engines = {
'ddg' : 'https://duckduckgo.com' ,
'y' : 'https://search.yahoo.com/search' ,
}
async def __call__ ( self , req , resp , engine ):
url = self . engines [ engine ]
params = { 'q' : req . get_param ( 'q' , True )}
async with httpx . AsyncClient () as client :
result = await client . get ( url , params = params )
resp . status = result . status_code
resp . content_type = result . headers [ 'content-type' ]
resp . text = result . text
class AuthMiddleware :
async def process_request ( self , req , resp ):
token = req . get_header ( 'Authorization' )
account_id = req . get_header ( 'Account-ID' )
challenges = [ 'Token type="Fernet"' ]
if token is None :
description = ( 'Please provide an auth token '
'as part of the request.' )
raise falcon . HTTPUnauthorized ( title = 'Auth token required' ,
description = description ,
challenges = challenges ,
href = 'http://docs.example.com/auth' )
if not self . _token_is_valid ( token , account_id ):
description = ( 'The provided auth token is not valid. '
'Please request a new token and try again.' )
raise falcon . HTTPUnauthorized ( title = 'Authentication required' ,
description = description ,
challenges = challenges ,
href = 'http://docs.example.com/auth' )
def _token_is_valid ( self , token , account_id ):
return True # Suuuuuure it's valid...
class RequireJSON :
async def process_request ( self , req , resp ):
if not req . client_accepts_json :
raise falcon . HTTPNotAcceptable (
description = 'This API only supports responses encoded as JSON.' ,
href = 'http://docs.examples.com/api/json' )
if req . method in ( 'POST' , 'PUT' ):
if 'application/json' not in req . content_type :
raise falcon . HTTPUnsupportedMediaType (
description = 'This API only supports requests encoded as JSON.' ,
href = 'http://docs.examples.com/api/json' )
class JSONTranslator :
# NOTE: Normally you would simply use req.get_media() and resp.media for
# this particular use case; this example serves only to illustrate
# what is possible.
async def process_request ( self , req , resp ):
# NOTE: Test explicitly for 0, since this property could be None in
# the case that the Content-Length header is missing (in which case we
# can't know if there is a body without actually attempting to read
# it from the request stream.)
if req . content_length == 0 :
# Nothing to do
return
body = await req . stream . read ()
if not body :
raise falcon . HTTPBadRequest ( title = 'Empty request body' ,
description = 'A valid JSON document is required.' )
try :
req . context . doc = json . loads ( body . decode ( 'utf-8' ))
except ( ValueError , UnicodeDecodeError ):
description = ( 'Could not decode the request body. The '
'JSON was incorrect or not encoded as '
'UTF-8.' )
raise falcon . HTTPBadRequest ( title = 'Malformed JSON' ,
description = description )
async def process_response ( self , req , resp , resource , req_succeeded ):
if not hasattr ( resp . context , 'result' ):
return
resp . text = json . dumps ( resp . context . result )
def max_body ( limit ):
async def hook ( req , resp , resource , params ):
length = req . content_length
if length is not None and length > limit :
msg = ( 'The size of the request is too large. The body must not '
'exceed ' + str ( limit ) + ' bytes in length.' )
raise falcon . HTTPContentTooLarge (
title = 'Request body is too large' , description = msg )
return hook
class ThingsResource :
def __init__ ( self , db ):
self . db = db
self . logger = logging . getLogger ( 'thingsapp.' + __name__ )
async def on_get ( self , req , resp , user_id ):
marker = req . get_param ( 'marker' ) or ''
limit = req . get_param_as_int ( 'limit' ) or 50
try :
result = await self . db . get_things ( marker , limit )
except Exception as ex :
self . logger . error ( ex )
description = ( 'Aliens have attacked our base! We will '
'be back as soon as we fight them off. '
'We appreciate your patience.' )
raise falcon . HTTPServiceUnavailable (
title = 'Service Outage' ,
description = description ,
retry_after = 30 )
# NOTE: Normally you would use resp.media for this sort of thing;
# this example serves only to demonstrate how the context can be
# used to pass arbitrary values between middleware components,
# hooks, and resources.
resp . context . result = result
resp . set_header ( 'Powered-By' , 'Falcon' )
resp . status = falcon . HTTP_200
@ falcon . before ( max_body ( 64 * 1024 ))
async def on_post ( self , req , resp , user_id ):
try :
doc = req . context . doc
except AttributeError :
raise falcon . HTTPBadRequest (
title = 'Missing thing' ,
description = 'A thing must be submitted in the request body.' )
proper_thing = await self . db . add_thing ( doc )
resp . status = falcon . HTTP_201
resp . location = '/%s/things/%s' % ( user_id , proper_thing [ 'id' ])
# The app instance is an ASGI callable
app = falcon . asgi . App ( middleware = [
# AuthMiddleware(),
RequireJSON (),
JSONTranslator (),
])
db = StorageEngine ()
things = ThingsResource ( db )
app . add_route ( '/{user_id}/things' , things )
# If a responder ever raises an instance of StorageError, pass control to
# the given handler.
app . add_error_handler ( StorageError , StorageError . handle )
# Proxy some things to another service; this example shows how you might
# send parts of an API off to a legacy system that hasn't been upgraded
# yet, or perhaps is a single cluster that all data centers have to share.
sink = SinkAdapter ()
app . add_sink ( sink , r'/search/(?P<engine>ddg|y)Z' )
Vous pouvez exécuter la version ASGI avec n'importe quel serveur ASGI, tel que uvicorn :
$ pip install falcon httpx uvicorn
$ uvicorn things_advanced_asgi:app
Merci de votre intérêt pour le projet ! Nous acceptons les demandes de tirage de développeurs de tous niveaux. Pour commencer, créez simplement la branche master sur GitHub sur votre compte personnel, puis clonez la branche dans votre environnement de développement.
Si vous souhaitez contribuer mais n'avez pas encore quelque chose en tête, nous vous invitons à jeter un œil aux problèmes répertoriés sous notre prochaine étape. Si vous en voyez un sur lequel vous aimeriez travailler, veuillez laisser un commentaire rapide afin que nous ne nous retrouvions pas avec des efforts en double. Merci d'avance!
Veuillez noter que tous les contributeurs et responsables de ce projet sont soumis à notre code de conduite.
Avant de soumettre une pull request, veuillez vous assurer que vous avez ajouté/mis à jour les tests appropriés (et que tous les tests existants réussissent toujours avec vos modifications), et que votre style de codage suit PEP 8 et ne provoque pas de plaintes de pyflakes.
Les messages de validation doivent être formatés à l'aide des conventions AngularJS.
Les commentaires suivent le guide de style de Google, avec l'exigence supplémentaire de préfixer les commentaires en ligne en utilisant votre pseudo GitHub et un préfixe approprié :
Les principaux responsables du projet Falcon sont :
N'hésitez pas à nous contacter si vous avez des questions ou si vous avez simplement besoin d'un peu d'aide pour commencer. Vous pouvez nous retrouver en fauconnerie/dev sur Gitter.
Voir aussi : CONTRIBUTION.md
Copyright 2013-2024 par les contributeurs individuels et corporatifs, comme indiqué dans les fichiers sources individuels.
Sous licence Apache, version 2.0 (la « Licence » ); vous ne pouvez utiliser aucune partie du framework Falcon sauf en conformité avec la licence. Les contributeurs acceptent de concéder sous la même licence leur travail. Vous pouvez obtenir une copie de la licence sur http://www.apache.org/licenses/LICENSE-2.0
Sauf disposition contraire de la loi applicable ou accord écrit, le logiciel distribué sous la licence est distribué « EN L'ÉTAT », SANS GARANTIE OU CONDITION D'AUCUNE SORTE, expresse ou implicite. Consultez la licence pour connaître la langue spécifique régissant les autorisations et les limitations en vertu de la licence.