Falcon は、ミッションクリティカルな REST API とマイクロサービスを構築するための最小限の ASGI/WSGI フレームワークであり、大規模な信頼性、正確性、パフォーマンスに重点を置いています。
HTTP API を構築する場合、他のフレームワークでは大量の依存関係と不必要な抽象化が重荷になります。 Falcon は、HTTP と REST アーキテクチャ スタイルを採用したクリーンな設計で核心を突いています。
Falcon アプリはあらゆる WSGI または ASGI サーバーで動作し、CPython 3.8 以降および PyPy 3.8 以降で優れた動作をします。
「ファルコンは盤石で速い。」
「私たちは[別のフレームワーク]の代わりにFalconを使用してきましたが、パフォーマンス(3倍高速)とコードベースのサイズ([元の]コードの軽く半分)が気に入っています。」
「#falconframework が大好きです! 非常にクリーンでシンプルで、ついに必要なスピードと柔軟性を手に入れました!」
「これまでのところ、Falcon は素晴らしいようです。私の小さなサーバー用に簡単なテストをハッキングしたところ、わずか 20 分の作業で最大 40% 高速になりました。」
「中間に何もなく、ついに HTTP について話しているだけのような気がします。Falcon はバックエンドのリクエストのように見えます。」
「Falcon のソース コードは非常に優れているので、ドキュメントよりもソース コードの方が好きです。基本的に間違っているはずがありません。」
「786 TRY IT NOW のサポートが統合されているフレームワークが他にありますか?」
Falcon は、高い効果を維持しながら、できる限り少ないことをしようとします。
asyncio
サポートFalcon は素晴らしいアプリの作成に役立ちましたか? 1 回限りの寄付またはパトロンになって、今すぐサポートを示してください。サポーターはクールなギア、Python 開発者にブランドを宣伝する機会、優先サポートを手に入れることができます。
ありがとう!
完璧とは、加えるものがなくなったときではなく、取り除くものがなくなったときに最終的に達成されるのです。
- アントワーヌ・ド・サン=テグジュペリ
私たちは、大規模なマイクロサービスと応答性の高いアプリ バックエンドの厳しいニーズをサポートするように Falcon を設計しました。 Falcon は、必要な場所にベアメタルのパフォーマンス、信頼性、柔軟性を提供することで、より一般的な Python Web フレームワークを補完します。
信頼性のある。私たちは破壊的変更の導入を避けるためにあらゆる努力を払っており、破壊的変更を導入する場合は完全に文書化され、(SemVer の精神に基づいて) メジャー バージョンの増分でのみ導入されます。コードは多数の入力を使用して厳密にテストされており、常に 100% のカバレッジが必要です。 Falcon には標準ライブラリ以外の依存関係がないため、推移的なバグや重大な変更を回避しながら、アプリの攻撃対象領域を最小限に抑えることができます。
デバッグ可能。ファルコンは魔法を避けます。どの入力がどの出力につながるかを簡単に判断できます。未処理の例外はカプセル化またはマスクされることはありません。リクエスト本文の自動解析など、潜在的に驚くべき動作については十分に文書化されており、デフォルトでは無効になっています。最後に、フレームワーク自体に関しては、ロジック パスをシンプルで理解しやすいものに保つよう注意しています。これらすべてにより、コードの推論と、大規模な展開におけるエッジ ケースのデバッグが容易になります。
速い。同じハードウェアでも、より多くのリクエストが可能です。 Falcon は、Django や Flask などの他の人気のある Python フレームワークよりも大幅に速くリクエストを処理します。さらに速度を向上させるために、Falcon は利用可能な場合は Cython でコンパイルし、PyPy でも適切に動作します。別のプログラミング言語への移行を検討していますか?まずはFalcon+PyPyでベンチマーク!
フレキシブル。 Falcon は、多くの決定と実装の詳細を API 開発者に任せます。これにより、実装を非常に自由にカスタマイズおよび調整できるようになります。また、アプリをより深いレベルで理解するのにも役立ち、長期的にはアプリの調整、デバッグ、リファクタリングが容易になります。 Falcon のミニマリストなデザインは、Python コミュニティのメンバーが Falcon のアドオンや補完的なパッケージを独自に革新するためのスペースを提供します。
Falcon は、世界中で次のような組織で使用されています。
コミュニティまたは商用プロジェクトで Falcon フレームワークを使用している場合は、Wiki の「Falcon を使用している人は?」に情報を追加することを検討してください。
プロジェクトでは、多数の Falcon アドオン、テンプレート、および補完的なパッケージを使用できます。出発点として、これらのいくつかを Falcon wiki にリストしましたが、追加のリソースについては PyPI を検索することもできます。
Gitter の Falconry コミュニティは、質問したりアイデアを共有したりするのに最適な場所です。鷹狩り/ユーザーで私たちを見つけることができます。また、フレームワーク自体の設計と開発について話し合うための鷹狩り/開発ルームもあります。
当社の行動規範によれば、コミュニティのディスカッションに参加するすべての人が専門的に行動し、模範を示して建設的なディスカッションを促進することを期待しています。コミュニティ内の各個人は、前向きで建設的で生産的な文化を創造する責任があります。
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 は PEP 517 準拠のインストーラーで Cython を使用して自動的にコンパイルします。
便宜上、ほとんどの一般的なプラットフォーム用に、コンパイル済みのバイナリを含むホイールを PyPI から入手できます。選択したプラットフォーム用のバイナリ ビルドが利用できない場合でも、 pip
純粋な Python ホイールを選択します。ご使用の環境に合わせて Falcon を cythonize することもできます。このオプションおよびその他の高度なオプションの詳細については、インストールに関するドキュメントを参照してください。
Falcon では、他のパッケージをインストールする必要はありません。
Falcon は WSGI (または ASGI、以下も参照) を話します。 Falcon アプリを提供するには、WSGI サーバーが必要です。 Gunicorn と uWSGI は最も人気のあるものの一部ですが、WSGI アプリをロードできるものなら何でも使えます。
$ pip install [gunicorn | uwsgi]
Falcon ASGI アプリを提供するには、ASGI サーバーが必要です。ユビコーンは人気のある選択肢です。
$ pip install uvicorn
Falcon は GitHub 上に存在するため、コードの参照、ダウンロード、フォークなどが簡単になります。プル リクエストはいつでも歓迎です。また、プロジェクトが満足できる場合は、必ずスターを付けてください。 :)
リポジトリのクローンを作成するか、GitHub から tarball をダウンロードしたら、次のように 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 コード ベースの docstring は非常に広範であるため、フレームワークを学習しながら REPL を実行し続けて、質問があったときにさまざまなモジュールやクラスをクエリできるようにすることをお勧めします。
オンライン ドキュメントは https://falcon.readthedocs.io から入手できます。
次のように、同じドキュメントをローカルで構築できます。
$ pip install tox && tox -e docs
ドキュメントが構築されたら、ブラウザで次のインデックス ページを開いてドキュメントを表示できます。 OS X では次のように簡単です。
$ open docs/_build/html/index.html
または Linux の場合:
$ xdg-open docs/_build/html/index.html
以下は、Falcon ベースの WSGI アプリの作成方法を示す、単純で人為的な例です (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
ここでは、ヘッダーとクエリ パラメーターの読み取り、エラーの処理、リクエストと応答の本文の操作を示す、より複雑な例を示します。この例では、requests パッケージがインストールされていることを前提としていることに注意してください。
(同等の 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 を使用していますが、uWSGI や Gunicorn などの WSGI サーバーを使用して上記の例を実行することもできます。例えば:
$ pip install requests gunicorn
$ gunicorn things:app
Windows では、WSL 経由で Gunicorn と uWSGI を実行できます。あるいは、Waittress を試してみることもできます。
$ pip install requests waitress
$ waitress-serve --port=8000 things:app
この例をテストするには、別のターミナルを開いて次を実行します。
$ http localhost:8000/1/things authorization:custom-token
フレームワークにバンドルされているfalcon-inspect-app
スクリプトを使用して、CLI からアプリケーション構成を表示することもできます。
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 バージョンは、uvicorn などの任意の ASGI サーバーで実行できます。
$ pip install falcon httpx uvicorn
$ uvicorn things_advanced_asgi:app
プロジェクトにご興味をお持ちいただきありがとうございます。あらゆるスキル レベルの開発者からのプル リクエストを歓迎します。開始するには、GitHub 上のマスター ブランチを個人アカウントにフォークし、そのフォークを開発環境に複製するだけです。
貢献したいがまだ何も考えていない場合は、次のマイルストーンの下にリストされている問題を検討することをお勧めします。取り組みたいものが見つかった場合は、作業が重複してしまうことのないよう、簡単なコメントを残してください。前もって感謝します!
このプロジェクトのすべての貢献者および管理者は、当社の行動規範の対象となることに注意してください。
プル リクエストを送信する前に、適切なテストを追加/更新していること (および、既存のすべてのテストが変更を加えても合格していること)、およびコーディング スタイルが PEP 8 に従っていて、pyflake がエラーを発生させないことを確認してください。
コミットメッセージは、AngularJS の規則を使用してフォーマットする必要があります。
コメントは Google のスタイル ガイドに従いますが、GitHub ニックネームと適切な接頭辞を使用してインライン コメントの前に付けるという追加の要件があります。
Falcon プロジェクトの中心的なメンテナーは次のとおりです。
ご質問がある場合、または開始にあたり少しサポートが必要な場合はお気軽にお問い合わせください。 Gitter の falconry/dev で私たちを見つけることができます。
参照: CONTRIBUTING.md
個々のソース ファイルに記載されているように、個人および企業の寄稿者による著作権は 2013 ~ 2024 年に帰属します。
Apache License バージョン 2.0 (「ライセンス」) に基づいてライセンスされています。ライセンスに準拠する場合を除き、Falcon フレームワークのいかなる部分も使用することはできません。寄稿者は、同じライセンスに基づいて自分の作品をライセンス供与することに同意します。ライセンスのコピーは http://www.apache.org/licenses/LICENSE-2.0 で入手できます。
適用される法律で義務付けられている場合または書面による同意がない限り、ライセンスに基づいて配布されるソフトウェアは、明示または黙示を問わず、いかなる種類の保証や条件もなく、「現状のまま」で配布されます。ライセンスに基づく許可と制限を規定する特定の言語については、ライセンスを参照してください。