1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】CORSエラー等のバグは循環importかも【Flask】

Last updated at Posted at 2025-05-27

超初歩的なミスですが、関連情報が見つからないので共有しておきます。

TL;DR

循環参照ダメ、絶対(CORS, Flaskに限らず)
linterを入れよう

やりたかっとこと・エラー

  • フロント Next.js (※今回フロント側は一切関係ないです)
  • バックエンド Flask
    で、Next側からFlask側のAPIを叩きたいが、flask runで動かすとCORSエラー。
    本番環境では動作するが、dev環境ではエラーになる。

そもCORSとは?

Cross Origin Resource Sharing(オリジン間リソース共有)。
通常はオリジン(ドメインやポート)が異なる外部サーバーからのリクエストは弾かれるが、CORSを設定することで選択的にリクエストを通すことが可能。
Flaskにおいては、flask_corsモジュールでCORS機能が提供されています。

ダメだったプログラム

簡素化しています。

ディレクトリ
app
├── api
│   ├── routes.py
│   └── services.py
├── __init__.py
└── config.py
/app/__init__.py
from app import create_app
from flask_cors import CORS

app = Flask(__name__)

CORS(app, origins=[
    FRONT_URI,
    FROT_DEV_URI
], supports_credentials=True)


if __name__ == '__main__':
    app.run(debug=True)
/app/api/routes.py
from . import api

@api.route("/news-summary", methods=["GET"])
def get_news_summary():
    return hogehoge()
/app/api/__init__.py
from flask import Blueprint

api = Blueprint('api', __name__)

from . import routes

原因

 はい、循環参照です。
 気づいたときはもうアホかと思いましたが、routes.pyと__init__.pyで相互にimportしているせいでおかしくなっていました。

/app/api/routes.py
from . import api
/app/api/__init__.py
from . import routes

 本番環境の場合とdev環境のCORS以外の機能は正常に動いていたので、余計に解決が遅れました。

参考: ChatGPTによる説明「Pythonにおいて循環importは未定義動作ですか?」

ChatGPT:
Pythonにおいて循環インポート(circular import)は**未定義動作(undefined behavior)**とは言いませんが、意図しないエラーやバグを引き起こす可能性があるため注意が必要な動作です。

循環インポートとは?
例えば、以下のようにモジュールAとBが互いにインポートし合っている状態です:

# module_a.py
from module_b import func_b
def func_a():
    print("A")
    func_b()

# module_b.py
from module_a import func_a
def func_b():
    print("B")
    func_a()

Pythonの挙動(仕様上の動作)
Pythonのインポートは実行時に行われ、モジュールは最初にインポートされた時点で一度だけ評価(=実行)されます。

循環インポートがあると、片方のモジュールの定義がまだ終わっていない状態でもう片方から参照されることがあり、その結果、AttributeError や ImportError が発生することがあります。

これは 未定義動作(undefined behavior) ではなく、Pythonの明示的な仕様の中で起こる定義済みの挙動ですが、開発者の意図と食い違いやすく、バグの温床になります。

対処方法
循環インポートが問題になる場合、以下のような対処が推奨されます:

インポートの位置を関数内に移動する:

def func_a():
    from module_b import func_b
    print("A")
    func_b()

依存関係を見直し、設計を分離・整理する:

共通の機能を別モジュールに切り出すなど。

遅延インポート(lazy import)や型ヒントのための if TYPE_CHECKING を使う:

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from module_b import SomeClass

結論
Pythonにおける循環インポートは「未定義動作」ではなく、「仕様上は定義されているが、予期しないエラーを引き起こすリスクが高いため、避けるべき構造」です。コード設計や依存関係の整理を通じて、循環参照を避けることが推奨されます。

解決法

 __init__.pyに切り分ける意味があまりない分量に感じたので、routes.pyに統合したら動きました。

/app/api/routes.py
api = Blueprint('api', __name__)

@api.route("/news-summary", methods=["GET"])
def get_news_summary():
    return hogehoge()

 たぶんもっとベターな構成はありますがとりあえず。
 あと同じことを絶対に今後もやらかすので、戒めのためlinter(flake8)を導入しました。

所感

 そもそも循環importでエラーが起きないことを知らなかったので、Pythonが嫌いな理由が一つ増えそう(でも好き)でしたが、linterがあるので耐えです。
 何につけてもネットの記事を丸コピするのはよくないですね。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?