- 技術 💻
attempted relative importの原因と直し方|18通り再現
結論
ImportError: attempted relative import with no known parent package は、from .utils import hello のような相対インポートを含むファイルを、パッケージの一部としてではなく「ただのファイル」として実行したときに出る Python のエラーです。多くは python app/main.py の形で直接実行したことが原因で、プロジェクトのいちばん上のフォルダから python -m app.main と実行すると直ります。コードを書き換えるより先に、実行の仕方を見直すのが近道です。
この記事でわかること
- エラー文の意味と、
__name__・__package__・sys.path[0]が実行の仕方でどう変わるか(実際の出力つき) - python ファイル名・cd してからの実行・
__main__.py・pytest・uvicorn・VS Code の実行ボタンなど18通りの原因の再現と、直したあとの出力 - beyond top-level package との違い、
-m・絶対インポート・pyproject.toml での入口づくりの選び方と判断表
この記事で実際に動かしたもの:相対インポートのエラーを起こすプロジェクト10個(合わせて18通りの実行の仕方)と、直したあとの実行を Python 3.13 で動かし、エラー文は 3.9〜3.15.0b4 の6つの版で比べ、pytest 9.1.1・uvicorn 0.54.0・Flask 3.1.3・pip の編集可能インストール・uv run でも試しました。
flowchart TD
A{実行の仕方} -->|python a/b.py| B[no known<br>parent package]
A -->|python -m a.b| C{点の数}
C -->|多すぎ| D[beyond<br>top-level]
C -->|合う| E[動く]
B --> F[上から<br>-m a.b]
D --> F
やりたいことごとに、いちばん早い方法を先にまとめます。
| やりたいこと | いちばん早い方法 | 詳しい節 |
|---|---|---|
| attempted relative import with no known parent package を今すぐ直したい | プロジェクトのいちばん上のフォルダで python -m app.main(.py は付けない、区切りは点) |
直し方1:python -m |
| エラーの意味を知りたい | 「親のパッケージが分からないので、点で始まるインポートを解決できない」という意味 | エラー文の意味 |
| attempted relative import beyond top-level package が出る | 実行しているフォルダが1段深い。もう1段上に移動してから -m で実行する |
beyond top-level package |
| ファイルを直接実行したい(実行ボタンで動かしたい) | そのファイルだけ絶対インポート from app.utils import … にし、実行の設定を「モジュール」にする |
直し方2・VS Code・PyCharm |
| pytest で出る | tests/__init__.py を置くか、テストは絶対インポートにして python -m pytest |
pytest・uvicorn |
| uvicorn main:app で出る | 1つ上のフォルダで uvicorn api.main:app |
pytest・uvicorn |
| どこから実行しても動くようにしたい | pyproject.toml の [project.scripts] に入口を書き、pip install -e . か uv run |
直し方3:インストール |
| sys.path に足したのに直らない | sys.path は相対インポートの親を決めないため。-m で実行する |
やってはいけない直し方 |
確認環境:Ubuntu 24.04.5 LTS の上で Python 3.13.18 を主に使い、相対インポートを含む小さなプロジェクト10個(基本形・2段のパッケージ・__main__.py・__init__.py なし・sys.path の書き換え・try/except・pytest・uvicorn・Flask・src レイアウト)を作業用の場所に作り、実行の仕方を変えてトレースバックの原文と直したあとの出力を取りました。エラー文の版による違いは、同じプロジェクトを Python 3.9.25・3.14.6・3.15.0b4(uv 0.11.32 で作業用の場所に導入)と 3.11.18・3.12.3 で実行して比べています。テストは pytest 9.1.1、Web アプリは uvicorn 0.54.0(127.0.0.1 だけで待ち受け、curl で応答を確かめて停止)、インストールは作業用の仮想環境への pip install -e .(setuptools)と uv run で試しました。相対インポートの決まりは Python 公式チュートリアル「モジュール」と言語リファレンス「インポートシステム」、-m と -P はコマンドラインのドキュメント、__package__ の扱いはデータモデルのドキュメント、pytest のインポートの仕組みは pytest 公式ドキュメント、VS Code と PyCharm の実行の設定は各公式ドキュメントで確かめました。Windows・Mac・VS Code・PyCharm は手元で動かしていないため、公式ドキュメントに書かれている範囲だけを載せています。このエラーの文言と起きる条件は OS に依存しない Python 本体の動作です。図は mermaid で描画を確かめています(確認日 2026年11月6日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「attempted relative importの原因と直し方|18通り再現」
ImportError: attempted relative import with no known parent packageの意味(エラー文の読み方)
この節では、エラー文の日本語訳と、トレースバックのどこを見れば原因の行と実行の仕方が分かるのかが分かります。
「ImportError: attempted relative import with no known parent package」を日本語にすると「親のパッケージが分からない状態で、相対インポートが試みられました」です。相対インポートとは、from .utils import hello や from ..config import VALUE のように、先頭に点(.)が付いたインポートのことです。点1つは「自分と同じパッケージ」、点2つは「1つ上のパッケージ」を表します。点を数えるための基準になる「自分が属するパッケージ」が分からないと、このエラーになります。
手元で、次の形のプロジェクトを作りました。app フォルダには空の __init__.py があり、パッケージとして正しい形です。
projA/
└── app/
├── __init__.py
├── main.py
└── utils.py
# app/utils.py
def hello():
return "hello from utils"
# app/main.py
import sys
print("__name__ =", repr(__name__))
print("__package__=", repr(__package__))
print("__spec__ =", None if __spec__ is None else __spec__.name)
print("sys.path[0]=", repr(sys.path[0]))
from .utils import hello
print(hello())
projA の中で python app/main.py と実行すると、次のように止まりました。
$ python app/main.py
__name__ = '__main__'
__package__= None
__spec__ = None
sys.path[0]= '/srv/s4/r17/t307/s/projA/app'
Traceback (most recent call last):
File "/srv/s4/r17/t307/s/projA/app/main.py", line 6, in <module>
from .utils import hello
ImportError: attempted relative import with no known parent package
トレースバックは、次の順番で読むと直す場所が決まります。
最後の行の文言を見る
no known parent package なら「親が分からない」、beyond top-level package なら「点が多すぎて、いちばん上より上に出た」です。直し方が違います。
File の行で、点で始まる import があるファイルを見る
上の例では app/main.py の6行目です。このファイルが「どう実行されたか」が原因の中心です。
先頭が <frozen runpy> かどうかを見る
トレースバックに <frozen runpy> の行があれば -m で実行しています。無ければファイル名で直接実行しています。
自分で確かめる:__package__ を表示する
エラーの行より前に print(__name__, __package__) を1行足して実行します。None か ''(空文字)なら、相対インポートは必ず失敗します。
Python のバージョンによるエラー文の違い
同じプロジェクトを6つの版で実行して、エラー文を比べました。文言は 3.9 から 3.15.0b4 まで同じでした。
| 版 | python app/main.py |
cd pkg して python -m sub.job |
python -m app.main |
|---|---|---|---|
| 3.9.25 | no known parent package | beyond top-level package | 動く |
| 3.11.18 | no known parent package | beyond top-level package | 動く |
| 3.12.3 | no known parent package | beyond top-level package | 動く |
| 3.13.18 | no known parent package | beyond top-level package | 動く |
| 3.14.6 | no known parent package | beyond top-level package | 動く |
| 3.15.0b4 | no known parent package | beyond top-level package | 動く |
原因は「ファイルの実行の仕方」:__name__・__package__・sys.path[0]を比べる
この節では、同じファイルでも実行の仕方によって相対インポートが動いたり動かなかったりする理由が、実際の値で分かります。
Python 公式チュートリアルは、相対インポートについて「現在のモジュールのパッケージ名に基づく」「メインモジュールはパッケージを持たないので、アプリケーションのメインモジュールとして使うモジュールは常に絶対インポートを使わなければならない」と説明しています。言語リファレンスも、ファイルを直接実行したときは __main__.__spec__ が常に None になり、-m で実行したときにモジュールの情報が入ると書いています。手元で、同じ app/main.py を4通りで実行した値です。
| 実行の仕方 | __package__ |
__spec__ |
sys.path[0] |
結果 |
|---|---|---|---|---|
projA で python app/main.py | None | None | projA/app | no known parent package |
app で python main.py | None | None | projA/app | no known parent package |
app で python -m main | '' | main | projA/app | no known parent package |
projA で python -m app.main | 'app' | app.main | projA | hello from utils |
動いたのは最後の1つだけです。python -m app.main では、Python が「app パッケージの main モジュール」として読み込むので、__package__ が 'app' になり、.utils を app.utils と解決できます。__name__ はどの場合も '__main__' で、if __name__ == "__main__": のブロックも動きます。
ファイルとして実行(python app/main.py)
__package__ も __spec__ も None になる
sys.path の先頭はファイルのあるフォルダ(app)
点で始まるインポートは必ず失敗する
モジュールとして実行(python -m app.main)
__package__ は 'app'、__spec__ は app.main
sys.path の先頭は今いるフォルダ(projA)
.utils を app.utils として解決できる
__init__.py が無いフォルダでも動くのか
上位の解説には「__init__.py が無いことが原因」とするものもありますが、手元の Python 3.13 では、__init__.py の無い app フォルダでも python -m app.main で相対インポートが動きました。__init__.py の無いフォルダは「名前空間パッケージ」として読み込まれます。
$ python -m app.main # app/__init__.py なし
app hello (no __init__.py)
_NamespacePath ['/srv/s4/r17/t307/s/projD/app']
__file__ of app: None
$ python app/main.py
ImportError: attempted relative import with no known parent package
つまり、このエラーの直接の原因は __init__.py の有無ではなく実行の仕方です。ただし、公式チュートリアルは「__init__.py はディレクトリをパッケージとして扱わせるために必要(名前空間パッケージを使う場合を除く)」と書いており、名前空間パッケージは発展的な機能という位置づけです。pytest は __init__.py を手がかりにテストのパッケージを決めるため(後の節)、自分のパッケージには空の __init__.py を置いておくほうが、ツールとの食い違いが起きにくくなります。言葉の整理は関連記事「モジュールとは|プログラムでの意味・パッケージとの違いを3言語で実例解説」で扱っています。
相対インポートのエラーの原因一覧(18通り)
この節では、手元で再現した18通りの実行の仕方と、それぞれのエラー文・直し方が一覧で分かります。
| No. | 原因(実行の仕方) | 手元で出たエラー文(最後の行) | 直し方 |
|---|---|---|---|
| 1 | python app/main.py | no known parent package | python -m app.main |
| 2 | cd app して python main.py | no known parent package | 1つ上に戻って -m |
| 3 | cd app して python -m main | no known parent package | 1つ上に戻って -m app.main |
| 4 | python -m app/main.py | No module named 'app/main' | 区切りを点にし .py を外す |
| 5 | python -m app.main.py | 一度動いたあと __path__ attribute not found | .py を外す |
| 6 | python tool(__main__.py のあるフォルダ) | no known parent package | python -m tool |
| 7 | python -c・対話モード・標準入力 | no known parent package | 絶対インポートで書く |
| 8 | cd pkg して python -m sub.job(中は ..) | beyond top-level package | もう1段上から -m pkg.sub.job |
| 9 | 点が多すぎる(...outside) | beyond top-level package | 点を減らす・絶対インポート |
| 10 | python pkg/sub/job.py(中は ..) | no known parent package | python -m pkg.sub.job |
| 11 | pytest で tests に __init__.py が無い | no known parent package | tests/__init__.py を置く |
| 12 | python tests/test_ops.py | no known parent package | pytest で実行する |
| 13 | app の中で uvicorn main:app | no known parent package | 上で uvicorn api.main:app |
| 14 | runpy.run_path("app/main.py") | no known parent package | runpy.run_module("app.main") |
| 15 | sys.path に親を足してから相対インポート | no known parent package | 絶対インポートにするか -m |
| 16 | src レイアウトをインストールせずに実行 | no known parent package/No module named 'mytool' | pip install -e .・uv run |
| 17 | パッケージの中で from utils import を -m 実行 | No module named 'utils' | from .utils か from app.utils |
| 18 | try/except で相対と絶対を切り替え | 本当の原因が隠れ No module named 'utils' | 切り替えをやめ、実行の仕方を決める |
17 と 18 は、このエラーを直そうとして点を消したり try/except を足したりしたあとに出る、別の形のエラーです。No module named 全般の原因は関連記事「ModuleNotFoundErrorの原因と直し方|No module named」で扱っています。
原因1〜7:python ファイル名で直接実行した(cd・-mの書き間違い・__main__.py・-c)
この節では、いちばん多い「ファイルとして実行した」ことによる7通りの原因と、それぞれの直したあとの出力が分かります。
原因1〜2:python ファイル名で実行した・cd してから実行した
原因1は最初の節の例そのものです。原因2の cd app してから python main.py も、ファイルとして実行している点は同じで、__package__ は None でした。どちらも、projA(app フォルダが見える場所)に移動して python -m app.main にすると直ります。
$ cd projA
$ python -m app.main
__name__ = '__main__'
__package__= 'app'
__spec__ = app.main
sys.path[0]= '/srv/s4/r17/t307/s/projA'
hello from utils
原因3:app の中に入って python -m main と実行した
-m を付けても、app の中から main だけを指定すると、main はどのパッケージにも属さない「いちばん上のモジュール」として読み込まれます。__package__ は空文字 '' になり、同じエラーでした。トレースバックに <frozen runpy> の行が入るのが原因1との見分け方です。
$ cd app
$ python -m main
__package__= ''
Traceback (most recent call last):
File "<frozen runpy>", line 203, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/srv/s4/r17/t307/s/projA/app/main.py", line 6, in <module>
from .utils import hello
ImportError: attempted relative import with no known parent package
app の中にいるまま python -m app.main と書くと、今度は No module named 'app' になりました。-m は今いるフォルダを sys.path の先頭に足すので、app の中からは app フォルダ自身が見えないためです。
原因4〜5:-m にファイルのパスや .py を書いた
公式のコマンドラインのドキュメントは、-m の引数は「モジュール名なので、拡張子(.py)を付けてはいけない」と書いています。パスの形で書くと、次のように別のエラーになりました。
$ python -m app/main.py
/usr/local/bin/python3: Error while finding module specification for 'app/main.py' (ModuleNotFoundError: No module named 'app/main'). Try using 'app/main' instead of 'app/main.py' as the module name.
$ python -m app.main.py
__name__ = 'app.main'
__package__= 'app'
__spec__ = app.main
sys.path[0]= '/srv/s4/r17/t307/s/projA'
hello from utils
/usr/local/bin/python3: Error while finding module specification for 'app.main.py' (ModuleNotFoundError: __path__ attribute not found on 'app.main' while trying to find 'app.main.py'). Try using 'app.main' instead of 'app.main.py' as the module name.
app.main.py の形は注意が要ります。Python は「app.main パッケージの中の py モジュール」を探すために、先に app.main を読み込むので、main.py の中身が一度実行されてからエラーになりました(__name__ が 'app.main' と表示されているのがその証拠です)。ファイルに書き込みや送信の処理があると、それも1回動きます。区切りは / ではなく .、末尾の .py は付けない、と覚えます。
原因6:__main__.py のあるフォルダをそのまま実行した
パッケージに __main__.py を置くと、python -m パッケージ名 でそのパッケージを実行できます。公式ドキュメントは、パッケージ名を渡すと <pkg>.__main__ がメインモジュールとして実行されると書いています。ところが、フォルダ名をそのまま python tool と渡すと、フォルダが sys.path に足されて __main__.py が単独で実行されるため、相対インポートは失敗しました。
# tool/__main__.py
from .core import run
print("__main__.py:", __package__, run())
$ python -m tool
__main__.py: tool tool.core.run()
$ python tool
Traceback (most recent call last):
File "<frozen runpy>", line 203, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/srv/s4/r17/t307/s/projC/tool/__main__.py", line 1, in <module>
from .core import run
ImportError: attempted relative import with no known parent package
$ python tool/__main__.py
ImportError: attempted relative import with no known parent package
原因7:python -c・対話モード・標準入力で相対インポートした
言語リファレンスは、対話モード・-c・標準入力からの実行でも __main__.__spec__ が None になると書いています。手元でも同じエラーでした。Jupyter のセルのように、ファイルではない場所で書いたコードでは、相対インポートではなく from app.utils import hello のように絶対インポートで書きます。
$ python -c "from .app import utils"
Traceback (most recent call last):
File "<string>", line 1, in <module>
from .app import utils
ImportError: attempted relative import with no known parent package
$ echo 'from . import utils' | python
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: attempted relative import with no known parent package
attempted relative import beyond top-level packageの原因と直し方
この節では、似たエラー「beyond top-level package」が出る条件と、no known parent package との違いが分かります。
「attempted relative import beyond top-level package」は「いちばん上のパッケージより上へ相対インポートしようとした」という意味です。親のパッケージは分かっているものの、点の数が、Python が知っているパッケージの深さより多いときに出ます。手元で、次の2段のパッケージを作りました。
projB/
└── pkg/
├── __init__.py
├── config.py # VALUE = "pkg.config"
└── sub/
├── __init__.py
└── job.py # from ..config import VALUE
$ cd pkg
$ python -m sub.job
Traceback (most recent call last):
File "<frozen runpy>", line 203, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/srv/s4/r17/t307/s/projB/pkg/sub/job.py", line 1, in <module>
from ..config import VALUE
ImportError: attempted relative import beyond top-level package
$ cd ..
$ python -m pkg.sub.job
job: __main__ VALUE = pkg.config
pkg の中から sub.job と指定すると、Python にとってのいちばん上は sub です。.. は sub のさらに上を指すので「上へ出すぎ」になります。projB から pkg.sub.job と指定すれば、いちばん上が pkg になり、..config を pkg.config と解決できました。
実行場所が正しくても、点が多すぎれば同じエラーです。pkg/sub/deep.py に from ...outside import X(点3つ)と書いて python -m pkg.sub.deep を実行すると、beyond top-level package になりました。点3つは pkg のさらに上を指すからです。プロジェクトの外のモジュールは相対インポートでは読めないので、インストールするか絶対インポートで書きます。なお、同じ job.py を python pkg/sub/job.py とファイルで実行すると、文言は no known parent package に変わりました。
1no known parent package
親のパッケージ自体が分からない。ファイルとして実行した、またはいちばん上のモジュールとして読んだ。
2beyond top-level package
親は分かるが、点の数が深さより多い。実行場所が1段深いか、点の書きすぎ。
3見分け方
-m で実行していて beyond なら、まず1段上のフォルダから実行し直す。
直し方1:python -m パッケージ.モジュール で実行する(Windows・Mac・Linux)
この節では、いちばん手早く確実な直し方である -m での実行を、OS ごとの書き方と、実行するフォルダの決め方とあわせて説明します。
コードは書き換えず、実行の仕方だけを変えます。手順は次のとおりです。
- 1いちばん上のパッケージのフォルダを決める相対インポートで最も上まで上がる先のフォルダ(例では app・pkg)
- 2そのフォルダが見える1つ上に移動するls(Windows は dir)で app が表示される場所。たいていはプロジェクトのルート
- 3パスの / を . に変え、.py を外して -m で渡すapp/main.py なら app.main、pkg/sub/job.py なら pkg.sub.job
- 4__package__ を表示して確かめる'app' のようにパッケージ名が出れば相対インポートは解決できる
| OS | エラーになる書き方 | 直した書き方 |
|---|---|---|
| Linux | python3 app/main.py | python3 -m app.main(手元で確認) |
| Mac | python3 app/main.py | python3 -m app.main(Python 本体の同じ動作) |
| Windows | py app\main.py | py -m app.main(公式ドキュメントに py -m の例あり) |
| 仮想環境 | 有効化したあとの python app/main.py | 有効化したあとの python -m app.main |
| uv | uv run app/main.py | uv run python -m app.main |
Windows の Python ドキュメントには、既定の Python に引数を渡す例として py -m this が載っています。-m の後ろはモジュール名なので、Windows でも区切りは \ ではなく . です。仮想環境の作り方は関連記事「uv venvとuvの使い方|仮想環境の作成・有効化とpipとの速さ比較」で扱っています。
移動しないで実行したいとき(PYTHONPATH)
どうしても app の中にいるまま実行したいときは、親のフォルダを PYTHONPATH で教えると -m が使えます。手元では、app の中で PYTHONPATH=.. python -m app.main と実行すると hello from utils と表示されました。一方、Python 3.11 で入った -P(PYTHONSAFEPATH)を付けると、-m でも今いるフォルダが sys.path に足されなくなるため、projA で python -P -m app.main は No module named 'app' になりました。社内の実行スクリプトで -P を使っている場合は、PYTHONPATH かインストールで場所を教えます。
直し方2:絶対インポートに書き換える(相対インポートとの使い分け)
この節では、ファイルを直接実行したいときの絶対インポートへの書き換えと、相対・絶対の使い分けの基準が分かります。
公式チュートリアルのとおり、メインモジュールとして直接実行するファイルでは絶対インポートを使います。パッケージ名から書くのが絶対インポートです。
# 相対インポート(-m で実行すれば動く)
from .utils import hello
# 絶対インポート(パッケージ名から書く)
from app.utils import hello
ただし、絶対インポートにしただけで python app/main.py が動くとは限りません。ファイルとして実行すると sys.path の先頭は app フォルダになり、app フォルダの1つ上が見えないため、from app.utils は No module named 'app' になります。-m で実行するか、インストールしてパッケージを見えるようにするのが前提です。
上位の解説でよく見る「点を外して from utils import hello にする」書き換えは、ファイルとして実行したときだけ動き、-m で実行すると逆に壊れました(原因17)。
$ python app/main.py # 中身は from utils import hello
hi
$ python -m app.main
Traceback (most recent call last):
File "<frozen runpy>", line 203, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/srv/s4/r17/t307/s/projF/app/main.py", line 1, in <module>
from utils import hello
ModuleNotFoundError: No module named 'utils'
app フォルダの中で動いているときだけ utils が見えている状態なので、ほかのファイルから import app.main したときやテストのときに壊れます。パッケージの中のファイルでは、from .utils か from app.utils のどちらかにそろえます。
| 書き方 | 向いている場面 | 注意 |
|---|---|---|
from .utils import hello | パッケージの中どうしの読み込み。パッケージ名を変えても書き換えが要らない | 直接実行するファイルでは使えない |
from app.utils import hello | 入口のファイル・テスト・スクリプト。どこから読まれても同じ意味 | app が sys.path から見える必要がある |
from utils import hello | パッケージに入れない単独のスクリプトどうし | パッケージの中では -m で壊れる |
import の基本と sys.path の探し方は関連記事「Python importの使い方|自作モジュールとエラー対処」で扱っています。インポートを直していて「cannot import name」が出た場合は循環インポートの可能性があり、関連記事「cannot import nameの直し方|Pythonの循環インポート」で扱っています。
直し方3:pyproject.tomlでインストールして入口のコマンドを作る(pip install -e・uv run)
この節では、どのフォルダから実行しても相対インポートが動くようにする、インストールと入口のコマンドの作り方が実物で分かります。
パッケージを src フォルダの下に置く「src レイアウト」では、インストールしないとパッケージ自体が見えません。手元の projI で、インストール前の実行結果は次のとおりでした。
projI/
├── pyproject.toml
└── src/
└── mytool/
├── __init__.py
├── cli.py # from .core import greet
└── core.py
$ python src/mytool/cli.py
ImportError: attempted relative import with no known parent package
$ python -m mytool.cli
/usr/local/bin/python3: Error while finding module specification for 'mytool.cli' (ModuleNotFoundError: No module named 'mytool')
pyproject.toml の [project.scripts] に「コマンド名 = "モジュール:関数"」の形で入口を書きます。
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "mytool"
version = "0.1.0"
[project.scripts]
mytool = "mytool.cli:main"
# src/mytool/cli.py
from .core import greet
def main():
print("cli:", __name__, greet())
if __name__ == "__main__":
main()
作業用の仮想環境に pip install -e .(編集可能インストール。ソースを書き換えると、入れ直さずに反映される)で入れると、5.0秒で終わり、どこからでも動きました。
$ pip install -q -e .
$ mytool
cli: mytool.cli greet from mytool.core
$ python -m mytool.cli
cli: __main__ greet from mytool.core
$ cd /
$ python -m mytool.cli
cli: __main__ greet from mytool.core
作られた mytool コマンドの中身は、次のように from mytool.cli import main と絶対インポートで読み込んで main() を呼ぶだけのものでした。入口のファイルが絶対インポート、パッケージの中が相対インポート、という使い分けになっています。
#!/dev/shm/t307/venv/bin/python3
import sys
from mytool.cli import main
if __name__ == '__main__':
sys.argv[0] = sys.argv[0].removesuffix('.exe')
sys.exit(main())
uv を使う場合は、同じ pyproject.toml のまま uv run mytool で、ビルドと仮想環境への導入が自動で行われました。
$ uv run mytool
Building mytool @ file:///srv/s4/r17/t307/s/projI
Built mytool @ file:///srv/s4/r17/t307/s/projI
Installed 1 package in 1ms
cli: mytool.cli greet from mytool.core
$ uv run python -m mytool.cli
cli: __main__ greet from mytool.core
上位の解説には python setup.py install を使う方法もありますが、今のパッケージングでは pyproject.toml と pip(または uv)でのインストールが標準の形です。
pytest・uvicornで出るattempted relative importの直し方
この節では、テストの実行と Web アプリの起動で、このエラーが出る条件と直し方が分かります。
原因11〜12:pytest でテストを集める段階で出る
次のテストを用意しました。tests フォルダには __init__.py がありません。
# tests/test_ops.py
from .helpers import make
from calc.ops import add
def test_add():
assert add(make()["a"], 2) == 3
$ python -m pytest -q
==================================== ERRORS ====================================
______________________ ERROR collecting tests/test_ops.py ______________________
ImportError while importing test module '/srv/s4/r17/t307/s/projG/tests/test_ops.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/local/lib/python3.13/importlib/__init__.py:88: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
tests/test_ops.py:1: in <module>
from .helpers import make
E ImportError: attempted relative import with no known parent package
=========================== short test summary info ============================
ERROR tests/test_ops.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
1 error in 0.09s
pytest 公式ドキュメントによると、既定の読み込み方(prepend)では、テストファイルから上へ向かって __init__.py を含む最後のフォルダを探し、その1つ上を sys.path の先頭に入れて、パッケージ名つきで読み込みます。__init__.py が無いと test_ops は単独のモジュールとして読まれ、相対インポートの親がありません。tests/__init__.py(空ファイル)を置くと通りました。
$ touch tests/__init__.py
$ python -m pytest -q
. [100%]
1 passed in 0.01s
$ pytest -q
. [100%]
1 passed in 0.01s
テストファイルを python tests/test_ops.py と直接実行すると、__init__.py があっても no known parent package でした。テストは pytest から実行します。
--import-mode=importlib を付けると、手元では相対インポートは通ったものの、sys.path が変わらないため今度は ModuleNotFoundError: No module named 'calc' になりました。pyproject.toml に次のように pythonpath を書くと、tests に __init__.py が無くても通りました。
[tool.pytest.ini_options]
pythonpath = ["."]
addopts = ["--import-mode=importlib"]
$ pytest -q # tests/__init__.py なし・上の設定あり
1 passed in 0.00s
公式ドキュメントは、python -m pytest と pytest の違いは「前者は今いるフォルダを sys.path に足す」ことだけと説明しており、既定は今後も prepend のままとしています。pytest の使い方全体は関連記事「pytest 使い方|導入からfixture・mock・カバレッジまで実行結果つき」で扱っています。
- 1tests に __init__.py があるか相対インポートを使うテストでは、既定の prepend で必要
- 2テストの import は絶対インポートかfrom calc.ops import add の形なら、tests の構成に左右されにくい
- 3pytest と python -m pytest で結果が違わないか違うなら sys.path の問題。pythonpath かインストールで固定する
- 4テストファイルを直接実行していないかpython tests/test_x.py は必ず no known parent package
原因13:uvicorn main:app をフォルダの中で実行した
FastAPI などで使う uvicorn は、main:app の main を今いるフォルダからモジュールとして読み込みます。api フォルダの中で実行すると main はいちばん上のモジュールになり、from .settings import MSG が失敗しました。
$ cd api
$ uvicorn main:app
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/srv/s4/r17/t307/s/projH/api/main.py", line 1, in <module>
from .settings import MSG
ImportError: attempted relative import with no known parent package
1つ上のフォルダで uvicorn api.main:app --host 127.0.0.1 --port 18307 と起動すると、curl で 200 が返りました。
INFO: Uvicorn running on http://127.0.0.1:18307 (Press CTRL+C to quit)
INFO: 127.0.0.1:47130 - "GET / HTTP/1.1" 200 OK
ツールによって違いがある点にも注意します。同じ形の Flask 3.1.3 のアプリ(web/main.py に from .settings import MSG)は、web フォルダの中で flask --app main routes と実行してもルートの一覧が表示され、エラーになりませんでした。Flask は __init__.py をたどってパッケージ名を補うためで、uvicorn は今いるフォルダからそのまま読み込みます。どのツールでも通るように、モジュール名はパッケージ名から書き(web.main・api.main:app)、プロジェクトのルートで起動する形にそろえておきます。
VS Code・PyCharmの実行ボタンで出るときの設定
この節では、エディターの実行ボタンで相対インポートのエラーが出る理由と、公式ドキュメントにある設定項目が分かります。
VS Code の Python のクイックスタートは、エディター右上の再生ボタン「Run Python File in Terminal」で、開いているファイルをターミナルで実行すると説明しています。これはファイルとしての実行(原因1)なので、相対インポートを含むファイルではこのエラーになります。VS Code・PyCharm は手元で動かしていないため、公式ドキュメントに書かれている項目だけを載せます。
| エディター | 設定項目(公式ドキュメントの記述) | 使い方 |
|---|---|---|
| VS Code(デバッグ) | launch.json の module:「-m 引数と同じように、デバッグするモジュール名を指定する」 | "program" の代わりに "module": "app.main" と書く |
| VS Code(デバッグ) | launch.json の cwd:省略すると ${workspaceFolder} | app が見えるフォルダを開いておく |
| VS Code(実行) | python.terminal.executeInFileDir:ファイルのあるフォルダで実行するか(既定 false) | 相対インポートの解決にはならない。-m はターミナルで打つ |
| VS Code(解析) | python.analysis.extraPaths:インポート解決の追加の探す場所 | エディターの赤線を消すだけ。実行時の動作は変わらない |
| PyCharm | 実行構成の「Script path/Module name」の切り替え | Module name を選び app.main を入れる |
| PyCharm | Working directory・Add content roots/source roots to PYTHONPATH | プロジェクトのルートを作業フォルダにする |
VS Code の launch.json は、たとえば次の形になります(module の属性は公式ドキュメントの説明どおり。Flask の例でも "module": "flask" が使われています)。
{
"version": "0.2.0",
"configurations": [
{
"name": "app.main をモジュールとして実行",
"type": "debugpy",
"request": "launch",
"module": "app.main",
"cwd": "${workspaceFolder}"
}
]
}
エディターの「赤い波線」と実行時のエラーは別物です。python.analysis.extraPaths で波線が消えても、実行ボタンがファイルとして実行している限り、このエラーは残ります。
やってはいけない直し方:sys.path・__package__・try/exceptで隠す
この節では、検索するとよく見つかるものの、直らない・別の問題を生む書き換えを、実際の結果で説明します。
原因15:sys.path に親のフォルダを足しても相対インポートは直らない
# app/main.py
import sys, pathlib
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))
print("__package__ =", repr(__package__))
from .utils import hello
$ python app/main.py
__package__ = None
Traceback (most recent call last):
File "/srv/s4/r17/t307/s/projE/app/main.py", line 4, in <module>
from .utils import hello
ImportError: attempted relative import with no known parent package
sys.path は「どこを探すか」を決めるもので、「自分がどのパッケージに属するか」(__package__)は変わりません。同じ書き換えのあとに from app.utils import hello(絶対インポート)にすると absolute: hello from utils と動きましたが、ファイルの場所に依存するコードがあちこちに残ります。
__package__ を自分で書き換える
__package__ = "app" を代入してから相対インポートする方法(PEP 366 の仕組みを使うもの)は、手元の 3.13.18・3.14.6・3.15.0b4 ではどれも警告なしで動きました。しかし、データモデルのドキュメントは、__package__ を 3.13 で非推奨とし、3.15 で「インポートの仕組みや標準ライブラリから設定も参照もされなくなる」と書いています。将来の版で動かなくなる前提の書き方なので、使いません。
原因18:try/except で相対と絶対を切り替えると、本当の原因が隠れる
try:
from .utils import hello
except ImportError:
from utils import hello
この形は、python app/main.py でも python -m app.main でも動きます。ところが、utils.py の中で入っていないライブラリを import していると、表示は次のようになりました。
$ python -m app.main4
Traceback (most recent call last):
File "/srv/s4/r17/t307/s/projJ/app/main4.py", line 2, in <module>
from .utils import hello
File "/srv/s4/r17/t307/s/projJ/app/utils.py", line 1, in <module>
import requests_not_installed
ModuleNotFoundError: No module named 'requests_not_installed'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<frozen runpy>", line 203, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/srv/s4/r17/t307/s/projJ/app/main4.py", line 5, in <module>
from utils import hello
ModuleNotFoundError: No module named 'utils'
ModuleNotFoundError は ImportError の仲間なので except に捕まり、最後の行は「utils が無い」になります。本当の原因(ライブラリが入っていない)はトレースバックの上のほうに埋もれます。
相対インポートのエラーを消したいとき
python -m パッケージ.モジュール に決める。どこからでも動かすなら pyproject.toml でインストールする直すのはコードではなく、起動の仕方
よくあるエラー/つまずき
この節では、相対インポートまわりで出るエラー文の原文と、意味・直し方を一覧で確かめられます。
| エラー文(原文) | 意味 | 直し方 |
|---|---|---|
ImportError: attempted relative import with no known parent package | 親のパッケージが分からない | 上のフォルダから python -m a.b |
ImportError: attempted relative import beyond top-level package | 点が多く、いちばん上より上に出た | もう1段上から -m・点を減らす |
No module named 'app/main'(-m の後ろ) | -m にファイルのパスを書いた | app.main と点で書く |
__path__ attribute not found on 'app.main' | -m に .py を付けた(中身は1回動く) | .py を外す |
No module named 'app'(-m で) | app が見えないフォルダで実行した・-P | app が見える場所へ移動・PYTHONPATH |
No module named 'utils'(-m で) | パッケージの中で点なしの import | from .utils か from app.utils |
ERROR collecting tests/… と no known parent | pytest が tests をパッケージと見ない | tests/__init__.py・pythonpath |
import .utils と書いて SyntaxError | 相対インポートは from の形だけ | from . import utils |
最後の行の決まりは言語リファレンスに「相対インポートは from <> import <> の形しか使えない」と書かれています。.moduleY は式として使えないためです。
判断表:attempted relative importの直し方の選び方
この節では、状況ごとにどの直し方を選ぶかを1つの表で決められます。
| 状況 | 選ぶ直し方 | 理由 |
|---|---|---|
| 手元で1回動かしたい | python -m app.main | コードを変えずに済む |
| beyond top-level package が出た | 1段上のフォルダから -m | いちばん上のパッケージを正しく伝える |
| 実行ボタンで動かしたい | VS Code は launch.json の module、PyCharm は Module name | エディターが -m と同じ実行をする |
| 入口のファイル1つだけを直接実行したい | そのファイルだけ絶対インポート+実行場所を固定 | メインモジュールは絶対インポートが公式の方針 |
| チームで配る・どこからでも実行したい | pyproject.toml の [project.scripts]+pip install -e ./uv run | 実行場所に左右されない |
| pytest で出た | tests/__init__.py、またはテストを絶対インポートにして pythonpath を設定 | pytest の読み込み方に合わせる |
| uvicorn・Flask で出た | 1つ上のフォルダで api.main:app の形で指定 | モジュール名をパッケージ名から書く |
Jupyter・-c で書いている | 絶対インポート | ファイルではないので親のパッケージが無い |
よくある質問
この節では、attempted relative import with no known parent package について検索されることの多い疑問に答えます。
Q. attempted relative import with no known parent package の日本語の意味は?
A. 「親のパッケージが分からない状態で、相対インポート(点で始まる import)が試みられました」という意味です。ファイルとして直接実行したときに出ることがほとんどです。
Q. __init__.py を置いたのに直らないのはなぜ?
A. 原因が実行の仕方だからです。手元では __init__.py があっても python app/main.py はエラーになり、無くても python -m app.main は動きました。-m で実行し直します。
Q. python -m で実行すると No module named になる
A. 実行しているフォルダが1段深い可能性が高いです。ls(Windows は dir)で、指定したパッケージのフォルダが表示される場所から実行します。.py を付けていないか、区切りが点になっているかも確かめます。
Q. python -m app.main.py と書いたら一度動いてからエラーになった
A. Python が app.main を読み込んだあとに、その中の py を探してエラーになるためです。main.py は1回実行されているので、ファイルの書き込みなどがあれば実行済みです。.py を外します。
Q. 相対インポートと絶対インポートはどちらを使うべき?
A. パッケージの中どうしは相対インポートでも絶対インポートでも動きます。直接実行する入口のファイル、テスト、Jupyter では絶対インポートにします。迷ったら、チームで片方にそろえるのが混乱が少ない方法です。
Q. beyond top-level package と no known parent package は何が違う?
A. no known parent package は親のパッケージ自体が分からない、beyond top-level package は親は分かるが点の数が多すぎる、という違いです。後者は -m で実行しているのに出るので、1段上のフォルダから実行し直すのが先です。
Q. VS Code の実行ボタンで出るのをなくしたい
A. 再生ボタンはファイルとして実行するので、デバッグ用の launch.json に "module": "app.main" を書いて、その構成で実行します。python.analysis.extraPaths はエディターの波線を消すだけで、実行時のエラーは変わりません。
Q. pytest だと通るのに python ファイル名だと失敗する
A. pytest は __init__.py をたどってパッケージ名つきで読み込むので相対インポートが解決できますが、python ファイル名ではパッケージ名がありません。テストは pytest から、アプリは -m か入口のコマンドから実行します。
Q. sys.path.append で直せる?
A. 相対インポートは直りません。sys.path は探す場所を変えるだけで、__package__ は None のままでした。絶対インポートと組み合わせれば動きますが、ファイルの場所に依存するので、-m かインストールを選びます。
Q. Windows ではどう実行する?
A. 公式ドキュメントにある py -m の形で、py -m app.main と実行します。-m の後ろはモジュール名なので、区切りは \ ではなく点です。
まとめ
この節では、記事の要点を5つに絞って振り返ります。
- 意味:attempted relative import with no known parent package は「親のパッケージが分からないので、点で始まる import を解決できない」。原因はコードより実行の仕方。
- いちばんの直し方:パッケージのフォルダが見える場所で
python -m app.main。区切りは点、.pyは付けない。Windows はpy -m app.main。 - beyond top-level package:点が多すぎるか、実行場所が1段深い。1つ上のフォルダから
-mで実行し直す。 - ツール別:pytest は
tests/__init__.pyか pythonpath、uvicorn はapi.main:app、VS Code は launch.json のmodule、PyCharm は Module name。 - やってはいけない:sys.path の書き換え・
__package__の代入・try/except の切り替えで隠さない。配るなら pyproject.toml の[project.scripts]で入口を作る。
参考・出典
この節では、この記事で確かめた公式の資料が分かります(確認日 2026年11月6日)。
- Python 公式チュートリアル「モジュール」パッケージ内参照:https://docs.python.org/3/tutorial/modules.html
- Python 言語リファレンス「インポートシステム」パッケージ相対インポート・__main__ に関する注意点:https://docs.python.org/3/reference/import.html
- Python 言語リファレンス「データモデル」module.__package__:https://docs.python.org/3/reference/datamodel.html
- Python 公式ドキュメント「コマンドラインと環境」-m・-P・PYTHONSAFEPATH:https://docs.python.org/3/using/cmdline.html
- Python 公式ドキュメント「Windows で Python を使う」:https://docs.python.org/3/using/windows.html
- PEP 328 – Imports: Multi-Line and Absolute/Relative:https://peps.python.org/pep-0328/
- PEP 366 – Main module explicit relative imports:https://peps.python.org/pep-0366/
- pytest 公式ドキュメント「pytest import mechanisms and sys.path/PYTHONPATH」:https://docs.pytest.org/en/stable/explanation/pythonpath.html
- VS Code 公式ドキュメント「Python debugging in VS Code」:https://code.visualstudio.com/docs/python/debugging
- VS Code 公式ドキュメント「Python settings reference」:https://code.visualstudio.com/docs/python/settings-reference
- VS Code 公式ドキュメント「Quick Start Guide for Python in VS Code」:https://code.visualstudio.com/docs/python/python-quick-start
- PyCharm 公式ドキュメント「Run/debug configuration: Python」:https://www.jetbrains.com/help/pycharm/run-debug-configuration-python.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「attempted relative importの原因と直し方|18通り再現」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。