Falcon هو إطار عمل ASGI/WSGI مبسط لبناء واجهات برمجة تطبيقات REST والخدمات الصغيرة ذات المهام الحرجة، مع التركيز على الموثوقية والصحة والأداء على نطاق واسع.
عندما يتعلق الأمر ببناء واجهات برمجة تطبيقات HTTP، فإن أطر العمل الأخرى تثقل كاهلك بالكثير من التبعيات والتجريدات غير الضرورية. يتجه Falcon إلى المطاردة بتصميم نظيف يحتضن النمط المعماري HTTP وREST.
تعمل تطبيقات Falcon مع أي خادم WSGI أو ASGI، وتعمل كالبطل تحت CPython 3.8+ وPyPy 3.8+.
"فالكون صلب كالصخر وسريع."
"لقد كنا نستخدم Falcon كبديل لـ [إطار عمل آخر] ونحن ببساطة نحب الأداء (أسرع بثلاث مرات) وحجم قاعدة التعليمات البرمجية (بسهولة نصف الكود [الأصلي] الخاص بنا)."
"أنا أحب #falconframework! نظيف وبسيط للغاية، وأخيرًا حصلت على السرعة والمرونة التي أحتاجها!"
"يبدو Falcon رائعًا حتى الآن. لقد قمت بإجراء اختبار سريع لخادم صغير خاص بي وكان أسرع بنسبة 40% تقريبًا خلال 20 دقيقة فقط من العمل."
"أشعر وكأنني أتحدث عن HTTP أخيرًا، دون أي شيء في المنتصف. يبدو أن Falcon يشبه طلبات الواجهة الخلفية."
"الكود المصدري لـ Falcon جيد جدًا، وأنا أفضّله تقريبًا على التوثيق. ولا يمكن أن يكون خطأ في الأساس."
"ما هو الإطار الآخر الذي يحتوي على دعم متكامل لـ 786، جربه الآن؟"
يحاول Falcon أن يفعل أقل قدر ممكن مع الحفاظ على فعاليته العالية.
asyncio
الأصليهل ساعدك Falcon في إنشاء تطبيق رائع؟ أظهر دعمك اليوم من خلال التبرع لمرة واحدة أو من خلال أن تصبح راعيًا. يحصل المؤيدون على معدات رائعة، وفرصة للترويج لعلامتهم التجارية لمطوري Python، والدعم ذي الأولوية.
شكرًا!
لا يتم الوصول إلى الكمال في النهاية عندما لا يكون هناك ما يمكن إضافته، ولكن عندما لا يكون هناك ما يمكن حذفه.
- أنطوان دو سانت إكزوبيري
لقد صممنا Falcon لدعم الاحتياجات المطلوبة للخدمات الصغيرة واسعة النطاق والواجهات الخلفية للتطبيقات سريعة الاستجابة. يكمل Falcon أطر عمل ويب Python الأكثر عمومية من خلال توفير الأداء المعدني والموثوقية والمرونة أينما كنت في حاجة إليها.
موثوق. نحن نبذل قصارى جهدنا لتجنب إدخال تغييرات جذرية، وعندما نفعل ذلك، يتم توثيقها بالكامل ويتم تقديمها فقط (بروح SemVer) مع زيادة الإصدار الرئيسي. يتم اختبار الكود بدقة باستخدام العديد من المدخلات ونحتاج إلى تغطية بنسبة 100% في جميع الأوقات. ليس لدى Falcon أي تبعيات خارج المكتبة القياسية، مما يساعد على تقليل سطح الهجوم لتطبيقك مع تجنب الأخطاء العابرة وكسر التغييرات.
قابل للتصحيح. فالكون يتجنب السحر. من السهل معرفة أي المدخلات تؤدي إلى أي مخرجات. لا يتم تغليف الاستثناءات غير المعالجة أو إخفائها أبدًا. يتم توثيق السلوكيات التي من المحتمل أن تكون مفاجئة، مثل التحليل التلقائي لنص الطلب، بشكل جيد ويتم تعطيلها افتراضيًا. أخيرًا، عندما يتعلق الأمر بالإطار نفسه، فإننا نحرص على إبقاء المسارات المنطقية بسيطة ومفهومة. كل هذا يجعل من السهل التفكير في التعليمات البرمجية وتصحيح أخطاء حالات الحافة في عمليات النشر واسعة النطاق.
سريع. نفس الأجهزة، المزيد من الطلبات. يستجيب Falcon للطلبات بشكل أسرع بكثير من أطر عمل Python الشائعة الأخرى مثل Django وFlask. لتعزيز السرعة الإضافية، يجمع Falcon نفسه مع Cython عند توفره، ويعمل أيضًا بشكل جيد مع PyPy. هل تفكر في الانتقال إلى لغة برمجة أخرى؟ المعيار مع Falcon + PyPy أولاً!
مرن. يترك Falcon الكثير من القرارات وتفاصيل التنفيذ لك، كمطور واجهة برمجة التطبيقات (API). يمنحك هذا الكثير من الحرية لتخصيص وضبط التنفيذ الخاص بك. كما أنه يساعدك على فهم تطبيقاتك على مستوى أعمق، مما يسهل ضبطها وتصحيح أخطائها وإعادة تصميمها على المدى الطويل. يوفر تصميم Falcon البسيط مساحة لأعضاء مجتمع Python للابتكار بشكل مستقل في إضافات Falcon والحزم التكميلية.
يتم استخدام فالكون حول العالم من قبل عدد متزايد من المنظمات، بما في ذلك:
إذا كنت تستخدم إطار عمل Falcon لمشروع مجتمعي أو تجاري، فيرجى التفكير في إضافة معلوماتك إلى الويكي الخاص بنا ضمن من يستخدم Falcon؟
يتوفر عدد من إضافات Falcon والقوالب والحزم التكميلية للاستخدام في مشاريعك. لقد أدرجنا العديد منها في Falcon wiki كنقطة بداية، ولكن قد ترغب أيضًا في البحث في PyPI للحصول على موارد إضافية.
يعد مجتمع الصقارة في Gitter مكانًا رائعًا لطرح الأسئلة ومشاركة أفكارك. يمكنك أن تجدنا في الصقارة/المستخدم. لدينا أيضًا غرفة للصقارة/للتطوير لمناقشة تصميم الإطار نفسه وتطويره.
وفقًا لقواعد السلوك الخاصة بنا، نتوقع من كل من يشارك في المناقشات المجتمعية أن يتصرف بشكل احترافي، وأن يكون قدوة في تشجيع المناقشات البناءة. كل فرد في المجتمع مسؤول عن خلق ثقافة إيجابية وبناءة ومنتجة.
PyPy هي أسرع طريقة لتشغيل تطبيق Falcon الخاص بك. يتم دعم PyPy3.8+ اعتبارًا من PyPy v7.3.7+.
$ pip install falcon
أو لتثبيت أحدث إصدار تجريبي أو إصدار مرشح، إن وجد:
$ pip install --pre falcon
يدعم Falcon أيضًا CPython 3.8+ بشكل كامل.
يمكن تثبيت أحدث إصدار ثابت من Falcon مباشرةً من PyPI:
$ pip install falcon
أو لتثبيت أحدث إصدار تجريبي أو إصدار مرشح، إن وجد:
$ pip install --pre falcon
من أجل توفير زيادة إضافية في السرعة، يقوم Falcon تلقائيًا بتجميع نفسه مع Cython ضمن أي برنامج تثبيت متوافق مع PEP 517.
من أجل راحتك، تتوفر العجلات التي تحتوي على ثنائيات مجمعة مسبقًا من PyPI لغالبية الأنظمة الأساسية الشائعة. حتى في حالة عدم توفر البنية الثنائية للنظام الأساسي الذي تختاره، ستختار pip
عجلة بايثون خالصة. يمكنك أيضًا cythonize Falcon من أجل بيئتك؛ راجع مستندات التثبيت الخاصة بنا للحصول على مزيد من المعلومات حول هذا والخيارات المتقدمة الأخرى.
لا يتطلب Falcon تثبيت أي حزم أخرى.
يتحدث Falcon WSGI (أو ASGI؛ انظر أيضًا أدناه). لكي تتمكن من خدمة تطبيق Falcon، ستحتاج إلى خادم WSGI. يعد Gunicorn وuWSGI من أكثر التطبيقات شيوعًا، ولكن أي شيء يمكنه تحميل تطبيق WSGI سيفي بالغرض.
$ pip install [gunicorn | uwsgi]
من أجل خدمة تطبيق Falcon ASGI، ستحتاج إلى خادم ASGI. Uvicorn هو خيار شائع:
$ pip install uvicorn
يعيش Falcon على GitHub، مما يجعل من السهل تصفح الكود وتنزيله وتقسيمه وما إلى ذلك. نرحب دائمًا بطلبات السحب! وتذكر أيضًا أن تقوم بتمييز المشروع بنجمة إذا كان ذلك يجعلك سعيدًا. :)
بمجرد استنساخ الريبو أو تنزيل tarball من GitHub، يمكنك تثبيت Falcon بهذه الطريقة:
$ cd falcon
$ pip install .
أو، إذا كنت تريد تحرير الكود، فقم أولاً بتقسيم الريبو الرئيسي، ثم انسخ التفرع على سطح المكتب لديك، ثم قم بتشغيل ما يلي لتثبيته باستخدام الارتباط الرمزي، بحيث عندما تقوم بتغيير الكود الخاص بك، ستكون التغييرات متاحة تلقائيًا لك تطبيقك دون الحاجة إلى إعادة تثبيت الحزمة:
$ cd falcon
$ FALCON_DISABLE_CYTHON=Y pip install -e .
يمكنك اختبار التغييرات يدويًا على إطار عمل Falcon عن طريق التبديل إلى دليل الريبو المستنسخ ثم تشغيل pytest:
$ cd falcon
$ pip install -r requirements/tests
$ pytest tests
أو لتشغيل مجموعة الاختبارات الافتراضية:
$ pip install tox && tox
راجع أيضًا ملف tox.ini للحصول على قائمة كاملة بالبيئات المتوفرة.
تعد سلاسل المستندات الموجودة في قاعدة كود Falcon واسعة جدًا، ونوصي بالاحتفاظ بـ REPL قيد التشغيل أثناء تعلم إطار العمل حتى تتمكن من الاستعلام عن الوحدات والفئات المختلفة عندما تكون لديك أسئلة.
المستندات عبر الإنترنت متاحة على: https://falcon.readthedocs.io
يمكنك إنشاء نفس المستندات محليًا على النحو التالي:
$ pip install tox && tox -e docs
بمجرد إنشاء المستندات، يمكنك عرضها عن طريق فتح صفحة الفهرس التالية في متصفحك. في نظام التشغيل OS X، الأمر بسيط مثل:
$ افتح docs/_build/html/index.html
أو على نظام لينكس:
$ xdg-open docs/_build/html/index.html
فيما يلي مثال بسيط ومبتكر يوضح كيفية إنشاء تطبيق WSGI يستند إلى Falcon (تم تضمين إصدار ASGI في الأسفل):
# 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 ()
يمكنك تشغيل المثال أعلاه مباشرة باستخدام خادم wsgiref المضمن:
$ pip install falcon
$ python things.py
ثم في محطة أخرى:
$ curl localhost:8000/things
إصدار ASGI للمثال مشابه:
# 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 )
يمكنك تشغيل إصدار ASGI باستخدام uvicorn أو أي خادم ASGI آخر:
$ pip install falcon uvicorn
$ uvicorn things_asgi:app
فيما يلي مثال أكثر تعقيدًا يوضح قراءة الرؤوس ومعلمات الاستعلام ومعالجة الأخطاء والعمل مع أجسام الطلب والاستجابة. لاحظ أن هذا المثال يفترض أنه تم تثبيت حزمة الطلبات.
(للحصول على تطبيق ASGI المكافئ، راجع: مثال أكثر تعقيدًا (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 ()
مرة أخرى، يستخدم هذا الرمز wsgiref، ولكن يمكنك أيضًا تشغيل المثال أعلاه باستخدام أي خادم WSGI، مثل uWSGI أو Gunicorn. على سبيل المثال:
$ pip install requests gunicorn
$ gunicorn things:app
على نظام التشغيل Windows، يمكنك تشغيل Gunicorn وuWSGI عبر WSL، أو يمكنك تجربة Waitress:
$ pip install requests waitress
$ waitress-serve --port=8000 things:app
لاختبار هذا المثال، افتح محطة أخرى وقم بتشغيل:
$ http localhost:8000/1/things authorization:custom-token
يمكنك أيضًا عرض تكوين التطبيق من واجهة سطر الأوامر عبر البرنامج النصي falcon-inspect-app
المرفق مع إطار العمل:
falcon-inspect-app things_advanced:app
إليك إصدار ASGI من التطبيق من الأعلى. لاحظ أنه يستخدم حزمة httpx بدلاً من الطلبات.
# 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' )
يمكنك تشغيل إصدار ASGI مع أي خادم ASGI، مثل uvicorn:
$ pip install falcon httpx uvicorn
$ uvicorn things_advanced_asgi:app
شكرا لاهتمامك بالمشروع! نحن نرحب بطلبات السحب من المطورين من جميع مستويات المهارة. للبدء، ما عليك سوى تفرع الفرع الرئيسي على GitHub إلى حسابك الشخصي ثم استنساخ التفرع في بيئة التطوير الخاصة بك.
إذا كنت ترغب في المساهمة ولكن ليس لديك أي شيء في ذهنك بالفعل، فنحن ندعوك لإلقاء نظرة على المشكلات المدرجة ضمن معلمنا التالي. إذا رأيت واحدًا ترغب في العمل عليه، فيرجى ترك تعليق سريع حتى لا ينتهي بنا الأمر بجهد مكرر. شكرا مقدما!
يرجى ملاحظة أن جميع المساهمين والمشرفين على هذا المشروع يخضعون لقواعد السلوك الخاصة بنا.
قبل إرسال طلب سحب، يرجى التأكد من أنك قمت بإضافة/تحديث الاختبارات المناسبة (وأن جميع الاختبارات الموجودة لا تزال تجتاز تغييراتك)، وأن أسلوب الترميز الخاص بك يتبع PEP 8 ولا يتسبب في شكوى pyflakes.
يجب تنسيق رسائل الالتزام باستخدام اصطلاحات AngularJS.
تتبع التعليقات دليل أسلوب Google، مع المتطلبات الإضافية المتمثلة في إضافة بادئة للتعليقات المضمنة باستخدام لقب GitHub الخاص بك والبادئة المناسبة:
المشرفون الأساسيون على مشروع فالكون هم:
من فضلك لا تتردد في التواصل معنا إذا كانت لديك أي أسئلة، أو كنت تحتاج فقط إلى القليل من المساعدة للبدء. يمكنك أن تجدنا في الصيد بالصقور/ديف على جيتر.
أنظر أيضا: CONTRIBUTING.md
حقوق الطبع والنشر 2013-2024 مملوكة للمساهمين من الأفراد والشركات كما هو مذكور في ملفات المصدر الفردية.
مرخص بموجب ترخيص Apache، الإصدار 2.0 ("الترخيص")؛ لا يجوز لك استخدام أي جزء من إطار عمل Falcon إلا وفقًا للترخيص. يوافق المساهمون على ترخيص عملهم بموجب نفس الترخيص. يمكنك الحصول على نسخة من الترخيص على http://www.apache.org/licenses/LICENSE-2.0
ما لم يكن ذلك مطلوبًا بموجب القانون المعمول به أو تم الاتفاق عليه كتابيًا، يتم توزيع البرامج الموزعة بموجب الترخيص على أساس "كما هي"، دون ضمانات أو شروط من أي نوع، سواء كانت صريحة أو ضمنية. راجع الترخيص لمعرفة الأذونات والقيود التي تحكم اللغة المحددة بموجب الترخيص.