Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pythonけものみち(という名のオレオレFWを作った言い訳)

 Pythonけものみち(という名のオレオレFWを作った言い訳)

筑波大学enPiTの夏合宿LTで使用した資料です。拙作オレオレFWの紹介をしています。

[リポジトリ]
https://github.com/notchman8600/dora-person

[リリースサイト]
https://dora.notchman.tech/

notch_man

August 23, 2023
Tweet

More Decks by notch_man

Other Decks in Technology

Transcript

  1. 2 ⾃⼰紹介 クラウドソーシングサービスなどを 開発しています notch_man twitter: @notch_man8600 • 認定スクラムマスター(CSM) •

    ラボのシステム開発の全責任を負う(⾟い) • 学類パンフに載ったけど留年したよ(笑) • 現場で都合良く使われています [概要] • 2020年3⽉ ⾹川⾼専卒業 • 2021年4⽉ 筑波⼤編⼊ • enPiT2021(受講)&2022〜(メンター) [略歴]
  2. 7 ヤバい物体1 request = { "data": 1, => int "user_id":

    "example-user-id", => str "password": "password", "method": "add", } router = { "/add": lambda a, b: a + b, "/sub": lambda a, b: a - b, } if "method" in request: print(router[request["method"]](request["data"], 2)) try: print(request["1"]) # もしパスワードカラムみたいなものがあったら削除しよう request.pop("password") except Exception as e: pass print(request["user_id"])
  3. 8 可愛らしいdictの例 • key-valueの型が揺れる ◦ 静的解析で追跡しにくい ◦ 自由に値を書き換える • 気軽に抽象クラスでエラーを掴んでしまう

    ◦ エラータイプが分からん ◦ passの恐ろしさ • 無い物が生まれたり、あるものが無くなったり • ありとあらゆることが⾃由にできる
  4. 9 サービス開発の⼿段として抑えて欲しい点 • とりあえず、3年は生かしてくれ ◦ 3年は大幅には壊れない技術選定 ◦ メンテナンスし続けられる技術選定 ◦ ブロークンに変わる可能性の低い技術選定

    • コードの中と外に頼れるものであるか? • コードの中に頼れるか ◦ コメントアウト、型宣言・型ヒント、 docstring • コードの外に頼れるか ◦ テスト、静的解析、フォーマッタ...
  5. 13 Ore1_python • 目指したもの ◦ 適当にグワッとやってグワッと動く ◦ DBはpython-mysql-connectorを使いたい ◦ 環境変数の出し入れでフィーチャートグルが使える簡便さ

    ◦ サバイブ性(主観) • いい加減な保守でも死ににくいコード ◦ 突然Slackが飛んで来たときに勘と雑なメトリクスで原因特定しやすい ◦ 3年後に見ても思い出しやすい(IDEが頑張ってくれるやつ) • 最悪、Router周りは気合いで書き直せるレベルのもの ◦ データは長生きである
  6. 14 ここで鎮静剤投与 • オレオレFWは家でやれ ◦ 現場でやると同僚に殴られます ◦ そういうときは🍺で黙らせる...(嘘です) • ただ、こういう変な趣味が役立つことがあります

    • 世の中の70%くらいの課題は市販のFWで何とかなります ◦ 残りの30%がどうもならんのよ • 綺麗事が通じない案件ではオレオレが思わぬ活躍を(後任は死ぬ)
  7. 15 Ore1_python • 作った物 ◦ ルーター ◦ Req/ResのDTO ◦ Controller

    ◦ Store • 作らなかった物 ◦ FastAPIで何とかなるところ ◦ ルーティング周り ◦ ロギング ▪ newrelicに投げました • 正直、FastAPIでええやん? ◦ starletteとpydanticが死んだらどうするんや...? ◦ 脱出を見込める何かにしておきたい ◦ オレオレで全部作る必要は無い(重要)
  8. 16 ルーター • エンドポイントを定義 ◦ ルーティング周りはFastAPI • 値を受け付けてラップして結果を返す責務 • 認証・認可の検証

    • リクエストのバリデーション(pydantic+dto) ◦ 最低限のvalidationはpydantic ◦ 値の整合性やキャストなど細かいところは自前で実装 • 後はControllerに渡す(DTOしか知らない世界) • FastAPIの知識はここで終わり(重要)
  9. 17 可能性を収束させる • 無限の可能性のある冒険をするな! • APIのレスポンスとかは生辞書がそのまま帰ってくる ◦ ex.) res.body.json() =>

    {“message”:”hello world”} • 我々はAPI仕様書やDDL(DBスキーマ)を見なければならない ◦ それはつらくない? • こういう自由な物体(あるいは何が入ってるか分からないもの)は ロジック側で適切なモデルに詰めて可能性を収束させる ◦ 余計なnullチェックを回避 ◦ 綺麗な値が入ることを(ある程度)保証できる • この動画を視てください ◦ https://www.youtube.com/watch?v=jjHwxWg2sWQ
  10. @self.dora_router.get("/reset") async def reset_vote_count(request: Request): try: token = request.cookies.get("access_token") if

    token_str is None: raise HTTPException(status_code=400, detail="Bad Request") # 中略 payload = decode(token, SECRET_KEY, algorithms=[ALGORITHM]) github_id: str = payload.get("github_name") if github_id != "notchman8600": raise HTTPException(status_code=403, detail="君にその権限はない") except ExpiredSignatureError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Access token is expired", headers={"WWW-Authenticate": "Bearer"}, ) except PyJWTError: raise HTTPException(status_code=401, detail="Unauthorized") err = self.dora_controller.reset_count() if err is not None: raise HTTPException(status_code=500, detail="Internal Server Error") return RedirectResponse(url="/vote", status_code=303)
  11. 19 コントローラー • リクエストオブジェクトを受け取りレスポンスを作る起点 ◦ 個人的にはActionの方が好きですが、検索キーワード的に MVCとか意識して... • Actionオブジェクトは単一の値 or

    DTOを許容する ◦ ユーザーの送信データは基本的に危なっかしいので valid -> DTO ◦ まあ、そうならないケースもあるしその辺りを統一する必要はあるのか? ▪ よくを言えば全てDTOで包んだ方が良いのが単に面倒 ▪ 逃げ道のアピールですよ(言い訳) • APIコールはControllerから直接(1カ所しか無いし5行だからね) • レスポンスはdtoで包んで可能性を収束させる ◦ ロジックはinvalidなデータをviewへ流すな!
  12. 20 ストア • アクセスカウンタと立候補者データを保存 • redisとmysqlのインスタンスを注入 ◦ python-mysql-connector ◦ redis

    • 責務が少ないしインフラレベルでストア層を分離する意味は分からなかった ので単一クラスで実装 • この辺りを分けるかはどうかは理性が問われる
  13. 21 その他 • エラー周りは全て例外を上げて終了 • newrelicでログは全て監視 ◦ まあ、ラピッドで立ち上げたし細かいロギングは苦情が出たら ...😇 •

    例外は大体ハンドラで掴まえてるのであそこにロガーを挿せばおk ◦ 実はこの設計はヤバいのでController層である程度理性ある対応をした方が良い ◦ 救えないエラーだけ例外を投げてハンドラ側のログ処理に委ねる設計が良い • まあ、この辺りは時間がないので割愛(言い訳) • テストはローカルのシェルスクリプトでE2E ◦ 成功体験だけできればいいのでは?