# Version 21.12

# 概要

このリリースは、バージョン21のリリースサイクルの最終リリースとなります。バージョン21は今後長期サポートに入り、2023年12月までの2年間サポートされる予定です。

# 知っておきたい

詳しくはChangelog (opens new window)をご覧ください。注目すべき新機能・不具合、アップグレード対象...

# アプリケーション名とブループリント名の厳格化

v21.6](./v21.6.md#stricter-application-and-blueprint-names-and-deprecation) では、アプリケーションとブループリントの名前は新しい制限に適合することが求められました。この変更は、起動時に強制されるようになりました。

以下の制約に従った名前でなければいけません。

  1. 英数字(a-zA-Z0-9)のみ使用可能です。
  2. ハイフン(-)またはアンダースコア(_)を含むことができます。
  3. 文字またはアンダースコア (a-zA-Z_) で始まる必要があります。

# 厳格なアプリケーションとブループリントのプロパティ

SanicBlueprint オブジェクトのプロパティを直接設定できる古い寛容さは、非推奨となり、今後は許可されなくなりました。必ず ctx オブジェクトを使用してください。

app = Sanic("MyApp")
app.ctx.db = Database()

# 削除

以下の非推奨機能は廃止されました。

  • sanic.exceptions.abort
  • sanic.views.CompositionView
  • sanic.response.StreamingHTTPResponse

# ストリーミング応答のアップグレード (まだの場合)

レスポンスメソッド sanic.response.stream非推奨 となり、v22.6 で削除される予定です。まだ旧式のストリーミングレスポンスを使用している場合は、アップグレードをお願いします。

旧 - 非推奨

async def sample_streaming_fn(response):
    await response.write("foo,")
    await response.write("bar")
@app.route("/")
async def test(request: Request):
    return stream(sample_streaming_fn, content_type="text/csv")

現在

async def sample_streaming_fn(response):
    await response.write("foo,")
    await response.write("bar")
@app.route("/")
async def test(request: Request):
    response = await request.respond(content_type="text/csv")
    await response.send("foo,")
    await response.send("bar")

# CLIのオーバーホールとMOTD(本日のメッセージ)について

SanicのCLIはかなり大規模なアップグレードを受けました。これは app.run() と同等にするために多くの新機能を追加しています。また、新しい MOTD 表示も含まれており、実行中の環境について素早く、一目でわかるハイライトを提供します。MOTDはTTYを意識しているので、サーバーのログではあまり冗長になりません。これは主にアプリケーション開発時の利便性を目的としています。

$ sanic --help
usage: sanic [-h] [--version] [--factory] [-s] [-H HOST] [-p PORT] [-u UNIX] [--cert CERT] [--key KEY] [--tls DIR] [--tls-strict-host]
             [-w WORKERS | --fast] [--access-logs | --no-access-logs] [--debug] [-d] [-r] [-R PATH] [--motd | --no-motd] [-v]
             [--noisy-exceptions | --no-noisy-exceptions]
             module
   ▄███ █████ ██      ▄█▄      ██       █   █   ▄██████████
  ██                 █   █     █ ██     █   █  ██
   ▀███████ ███▄    ▀     █    █   ██   ▄   █  ██
               ██  █████████   █     ██ █   █  ▄▄
  ████ ████████▀  █         █  █       ██   █   ▀██ ███████
 To start running a Sanic application, provide a path to the module, where
 app is a Sanic() instance:
     $ sanic path.to.server:app
 Or, a path to a callable that returns a Sanic() instance:
     $ sanic path.to.factory:create_app --factory
 Or, a path to a directory to run as a simple HTTP server:
     $ sanic ./path/to/static --simple
Required
========
  Positional:
    module                         Path to your Sanic app. Example: path.to.server:app
                                   If running a Simple Server, path to directory to serve. Example: ./
Optional
========
  General:
    -h, --help                     show this help message and exit
    --version                      show program's version number and exit
  Application:
    --factory                      Treat app as an application factory, i.e. a () -> <Sanic app> callable
    -s, --simple                   Run Sanic as a Simple Server, and serve the contents of a directory
                                   (module arg should be a path)
  Socket binding:
    -H HOST, --host HOST           Host address [default 127.0.0.1]
    -p PORT, --port PORT           Port to serve on [default 8000]
    -u UNIX, --unix UNIX           location of unix socket
  TLS certificate:
    --cert CERT                    Location of fullchain.pem, bundle.crt or equivalent
    --key KEY                      Location of privkey.pem or equivalent .key file
    --tls DIR                      TLS certificate folder with fullchain.pem and privkey.pem
                                   May be specified multiple times to choose multiple certificates
    --tls-strict-host              Only allow clients that send an SNI matching server certs
  Worker:
    -w WORKERS, --workers WORKERS  Number of worker processes [default 1]
    --fast                         Set the number of workers to max allowed
    --access-logs                  Display access logs
    --no-access-logs               No display access logs
  Development:
    --debug                        Run the server in debug mode
    -d, --dev                      Currently is an alias for --debug. But starting in v22.3, 
                                   --debug will no longer automatically trigger auto_restart. 
                                   However, --dev will continue, effectively making it the 
                                   same as debug + auto_reload.
    -r, --reload, --auto-reload    Watch source directory for file changes and reload on changes
    -R PATH, --reload-dir PATH     Extra directories to watch and reload on changes
  Output:
    --motd                         Show the startup display
    --no-motd                      No show the startup display
    -v, --verbosity                Control logging noise, eg. -vv or --verbosity=2 [default 0]
    --noisy-exceptions             Output stack traces for all exceptions
    --no-noisy-exceptions          No output stack traces for all exceptions

# サーバーの実行モードと debug に来る変更点

現在、DEVPRODUCTION の2つの実行モードがあります。デフォルトでは、Sanicサーバーは PRODUCTION モードで実行されます。これはデプロイメントを想定している。

現在、DEVモードは古いSanicのバージョンで debug=True が動作する方式と非常によく似ています。しかし、v22.3では しかし、v22.3では、debug=Trueは自動リロードを有効にしなくなりました**。デバッグと自動リロードを行いたい場合は、DEVモードを有効にする必要があります。

開発

$ sanic server:app --dev
app.run(debug=True, auto_reload=True)

運営

$ sanic server:app
app.run()

v22.3から、PRODUCTIONモードはデフォルトでアクセスログを有効にしなくなりました。

変更点の概要は以下の通りです。

フラグ モード トレースバック ログ アクセスログ 再読込 最大ワーカー
--debug DEBUG はい DEBUG はい ^1
PROD いいえ INFO ^2 ^3
--dev DEBUG はい DEBUG はい はい
--fast はい
  • ^1 --debug で自動リロードを非推奨とし、22.3 で削除する。
  • ^2 22.3以降ではWARNINGに移行します。
  • ^3 22.3以降は「いいえ」

# 最大許容ワーカー数

fast を使用すると、許可された最大数のワーカーを簡単にスピンアップさせることができます。

$ sanic server:app --fast
app.run(fast=True)

# ファーストクラスのSanic Extensionsサポート

Sanic Extensions は、特に API 開発者のために意図された多くの追加機能を提供します。パッケージが環境にある限り、追加設定なしで、それが提供するすべての機能を簡単に実装できるようになりました。これらの機能は以下の通りです。

  • HEADOPTIONSTRACE のエンドポイントの自動作成
  • CORS保護
  • 定義済み、エンドポイント固有のレスポンスシリアライザー
  • ルートハンドラへの依存性注入
  • RedocやSwaggerによるOpenAPIドキュメンテーション
  • リクエストのクエリ引数とボディ入力の検証

Sanicと一緒にインストールするのが望ましいですが、パッケージ単体でインストールすることも可能です。

$ pip install sanic[ext]
$ pip install sanic sanic-ext

その後、追加の設定は必要ありません。Sanic Extensionsはアプリケーションにアタッチされ、これ以上の構成は必要なく、すべての追加機能を提供します。

もし、動作方法を変更したり、追加の設定を提供したい場合は、app.extendを使用してSanic Extensionsを変更することができます。以下の例は同等です。Config オブジェクトは、IDE 開発に役立つ型アノテーションを提供するためのものです。

# This is optional, not required
app = Sanic("MyApp")
app.extend(config={"oas_url_prefix": "/apidocs"})
# This is optional, not required
app = Sanic("MyApp")
app.config.OAS_URL_PREFIX = "/apidocs"
# This is optional, not required
from sanic_ext import Config
app = Sanic("MyApp")
app.extend(config=Config(oas_url_prefix="/apidocs"))

# コンテキストに応じた例外処理

v21.9 では、アプリケーション全体で一貫して例外を発生させる能力を単純化するために、例外にデフォルトメッセージを追加しました。

class TeapotError(SanicException):
    status_code = 418
    message = "Sorry, I cannot brew coffee"
raise TeapotError

しかし、これには2つのことが欠けていました。

  1. ダイナミックで予測可能なメッセージ・フォーマット
  2. エラーメッセージに追加のコンテキストを追加する機能(これについては後ほど詳しく説明します)

現在のリリースでは、エラーメッセージを書くときにコンテキストを提供するために、Sanic例外が発生したときに追加情報を持つことができます。

class TeapotError(SanicException):
    status_code = 418
    @property
    def message(self):
        return f"Sorry {self.extra['name']}, I cannot make you coffee"
raise TeapotError(extra={"name": "Adam"})

新機能として、例外インスタンスに extra メタを渡すことができるようになりました。この extra 情報オブジェクトは、 PRODUCTION モードでは抑制されますが、 DEVELOPMENT モードでは表示されます。

PRODUCTION

image

DEVELOPMENT

image

2.の話に戻ります: エラーメッセージにコンテキストを追加する機能

これは、マイクロサービスや、エラーメッセージをJSON形式で返すつもりのAPIを作るときに特に有効です。この使用例では、クライアントに詳細を返すために、解析可能なエラーメッセージだけでなく、例外の周りに何らかのコンテキストを持たせたいと考えています。

raise TeapotError(context={"foo": "bar"})

これは、(利用可能であれば)常にエラーで渡されるようにしたい情報です。以下のような感じです。

PRODUCTION

{
  "description": "I'm a teapot",
  "status": 418,
  "message": "Sorry Adam, I cannot make you coffee",
  "context": {
    "foo": "bar"
  }
}

DEVELOPMENT

{
  "description": "I'm a teapot",
  "status": 418,
  "message": "Sorry Adam, I cannot make you coffee",
  "context": {
    "foo": "bar"
  },
  "extra": {
    "name": "Adam",
    "more": "lines",
    "complex": {
      "one": "two"
    }
  },
  "path": "/",
  "args": {},
  "exceptions": [
    {
      "type": "TeapotError",
      "exception": "Sorry Adam, I cannot make you coffee",
      "frames": [
        {
          "file": "handle_request",
          "line": 83,
          "name": "handle_request",
          "src": ""
        },
        {
          "file": "/tmp/p.py",
          "line": 17,
          "name": "handler",
          "src": "raise TeapotError("
        }
      ]
    }
  ]
}

# バックグランドタスクの管理

app.add_task メソッドを使用してバックグラウンドタスクを作成するとき、オプションで name キーワード引数を渡すことができるようになり、フェッチやキャンセルができるようになりました。

app.add_task(dummy, name="dummy_task")
task = app.get_task("dummy_task")
app.cancel_task("dummy_task")

# 定義におけるルートコンテキストのkwargs

ルートが定義されるとき、ctx_ というプレフィックスを持つキーワード引数をいくつでも追加することができます。これらの値は、ルートの ctx オブジェクトに注入されます。

@app.get("/1", ctx_label="something")
async def handler1(request):
    ...
@app.get("/2", ctx_label="something")
async def handler2(request):
    ...
@app.get("/99")
async def handler99(request):
    ...
@app.on_request
async def do_something(request):
    if request.route.ctx.label == "something":
        ...

# ブループリントはいつでも登録可能

Sanicの以前のバージョンでは、ブループリントをアプリケーションにアタッチできるタイミングに厳密な順序がありました。もし、app.blueprint(bp)を、すべてのオブジェクトをBlueprintインスタンスにアタッチする前に実行すると、それらは見逃されることになります。

現在では、いつでもブループリントをアタッチすることができ、それにアタッチされたすべてのものがスタートアップ時に含まれます。

# うるさい例外 (すべての例外をログに強制的に記録)

新しい設定値として NOISY_EXCEPTIONS があります。この値が False (デフォルト) の場合、Sanic はすべての SanicExceptionquiet プロパティを尊重します。つまり、quiet=True の例外はログ出力に表示されません。

しかし、 NOISY_EXCEPTIONS=True に設定すると、 quiet の値に関係なく、すべての例外がログに記録されるようになります。

これはデバッグをするときに便利です。

app.config.NOISY_EXCEPTIONS = True

# シグナルイベントを Enum で表す

便宜上、すべてのビルトインシグナル値を持つ Enum が存在します。

from sanic.signals import Event
@app.signal(Event.HTTP_LIFECYCLE_BEGIN)
async def connection_opened(conn_info):
    ...

# 環境変数のカスタム型キャスト

デフォルトでは、Sanic は環境変数を config インスタンスに適用する際に、 int, float, または bool 値に変換します。これを独自のコンバータで拡張することができる。

app = Sanic(..., config=Config(converters=[UUID]))

# uvloop を設定値で無効にする

設定値によって uvloop の使用を制御することができます。

app.config.USE_UVLOOP = False

# 複数のTLS証明書でSanicサーバーを実行する

Sanicは、複数のTLS証明書を使用して実行することができます。

app.run(
    ssl=[
        "/etc/letsencrypt/live/example.com/",
        "/etc/letsencrypt/live/mysite.example/",
    ]
)

# ニュース

# 近日公開予定: SanicでPythonのWeb開発

コア開発者の一人である@ahopkins (opens new window)によるSanicに関する本が近日発売予定です。

詳しくはsanicbook.com (opens new window)でご覧ください。

ウェブアプリケーションのパフォーマンスとスケーラビリティを向上させるために、Sanicを使った作業の実践的な知識を身につけましょう。その一方で、アプリを大幅にオーバーエンジニアリングすることなく、変化するビジネスニーズに合わせてカスタマイズする方法を学び、開発スキルをレベルアップします。

書籍の収益の一部はSanic Community Organizationに入り、Sanicの開発・運営資金として活用されます。つまり、この本を買うことも、Sanicを支援する方法のひとつなのです。

# ドキュメントのダークモード

まだお気づきでないようですが、このSanicのウェブサイトはネイティブのダークモードでご利用いただけるようになりました。ページの右上にあるテーマを切り替えることができます。

# 謝礼

今回のリリースに参加された皆さん、ありがとうございました。 👏

@adarsharegmi (opens new window) @ahopkins (opens new window) @ashleysommer (opens new window) @ChihweiLHBird (opens new window) @cnicodeme (opens new window) @kianmeng (opens new window) @meysam81 (opens new window) @nuxion (opens new window) @prryplatypus (opens new window) @realDragonium (opens new window) @SaidBySolo (opens new window) @sjsadowski (opens new window) @Tronic (opens new window) @Varriount (opens new window) @vltr (opens new window) @whos4n3 (opens new window)

そして、ドキュメントの同期と中国語への翻訳を維持してくれた@miss85246 (opens new window)@ZinkLu (opens new window) には、多大なる感謝を捧げます。


もし、このプロジェクトを楽しんでいただけるなら、ぜひ貢献をご検討ください。もちろんコードの貢献は大歓迎ですが、どんな形の貢献も大歓迎です。ドキュメントを書いたり、使用例を紹介したり、会話に参加してあなたの声を伝えたり、もし可能であれば、金銭的な貢献 (opens new window)も検討してみてください。

MIT Licensed
Copyright © 2018-present Sanic Community Organization

~ Made with ❤️ and ☕️ ~