auの日記

プログラミング初心者の日記。(auはハンドルネームです)

FlaskでPythonを使って上位ディレクトリにあるファイルパスをスマートに通す方法

auです。

追記

あとで気づいたのですが、単体のファイルを実行してもエラーが出て動かなくて、

export FLASK_APP=app.py
flask run --host=0.0.0.0

のように実行すると問題なく動くだけでした。自分への戒めと言う感じでこの記事を残そうと思います。





FlaskをPythonで開発をしていると、「上位ディレクトリにあるファイルのパスを通したいな・・・」ということがあると思います。

調べてみると、sysやosライブラリを使った方法が出てきます。実際に自分もその方法でパスを通しました。しかし、Githubにpushして、インターン先の方にpull request(コードレビュー)をお願いしたところ、「この書き方じゃ全然分からない」と言われてしまい、もっと分かりやすいコードでパスを通さなければならなくなってしまいました。

そこで、今回はsysやosといったライブラリを使わずにパスを通す方法について調べてみました。

こちらの記事がとても分かりやすいです。

d.hatena.ne.jp

思いつく方法でやってみる

folder1の中にあるfile1.pyを、folder1の中のfolder2に入っているfile2.pyでimportしようとしている以下の構造があるとします。

project 
│
├ーfolder1
|   ├ーfolder2
|   |  └file2.py(ここからfile1を呼び出したい!)
|   └file1.py(このファイルが呼び出される) 
└ー app.py

file1.py、file2.pyでは以下のように記述されているとします。

# file1
def hello():
    print('hello world!')
|<<

>|python|
# file2
from .. import file1 # 1つ前の階層のfile1をimport

file1.hello()

一見普通にimportできそうですが、エラーが起きてしまっています。

f:id:program-shoshinsya:20190323153558p:plain

実行した場合

Traceback (most recent call last):
  File "folder1/folder2/file2.py", line 1, in <module>
    from .. import file1
ValueError: attempted relative import beyond top-level package

解決方法

では、どうやってパスを通すかというと、__init__.pyファイルを使用するディレクトリごと作成し、呼び出す側の__init__.pyファイルで読み込みたいファイルをimportします!

実際にやってみましょう。

以下のように、呼ばれるファイルがあるディレクトリと、呼び出すファイルがあるディレクトリの両方に__init__.pyを作成します。つまり、folder1とfolder2の中に__init__.pyを作成します。

project 
│
├ーfolder1
|   ├ーfolder2
|   |  ├ __init__.py(folder2の中で作る)
|   |  └file2.py(ここからfile1を呼び出したい!)
|   ├ー __init.py(folder1の中で作る)
|   └file1.py(このファイルが呼び出される) 
└ー app.py

folder2の__init__.pyに、以下のように記述します。

from .. import file1.py

そして、そのままPythonを実行すると上と同じエラーが起きてしまう・・・

しかし、以下のように実行すると普通に動かすことができました。

export FLASK_APP=app.py
flask run --host=0.0.0.0

パスの通し方の問題なのか、flaskと言うコマンドを使うと、__init__.pyが先に実行されるから問題ないのかは調べてみないとわかりませんが、とりあえず動くだけで、根本的な問題の解決にはなってないと思います。

エンジニアを目指しているので、なぜこうなるのかを理解できるようになるために勉強していこうと思います。