Jak naprawić błąd ImportError w Pythonie raz na zawsze

Marek Radoszewski Marek Radoszewski
Backend i Frontend
13.01.2026 9 min
Jak naprawić błąd ImportError w Pythonie raz na zawsze

Jak poradzić sobie z błędem „ImportError: attempted relative import” w projektach Python

ImportError: attempted relative import to komunikat, który potrafi skutecznie wstrzymać pracę nad projektem w Pythonie. Pojawia się zarówno u początkujących, jak i doświadczonych programistów, szczególnie gdy projekt zaczyna dzielić się na moduły i pakiety. Ten artykuł pokazuje, jak naprawić błąd „ImportError: attempted relative import” w projekcie Python pakiety i jak raz na zawsze zrozumieć jego przyczynę.

Skupimy się nie tylko na samym rozwiązaniu, ale też na zrozumieniu mechanizmu działania importów w Pythonie. Dzięki temu nie tylko usuniesz bieżący problem, lecz także zbudujesz solidne fundamenty pod większe aplikacje i biblioteki. To wiedza, która szybko procentuje w codziennej pracy z kodem.

Błąd z importem względnym jest często pierwszym sygnałem, że projekt urósł do rozmiaru, w którym struktura pakietów i sposób uruchamiania plików zaczynają mieć krytyczne znaczenie. Warto w tym momencie zatrzymać się na chwilę i poukładać architekturę, zamiast walczyć z kolejnymi komunikatami w konsoli.

Celem tego artykułu jest przeprowadzenie Cię krok po kroku od zrozumienia, czym jest ImportError: attempted relative import, przez jego najczęstsze źródła, aż po praktyczne metody naprawy. Na koniec poznasz też najlepsze praktyki, które pozwolą unikać tego błędu w przyszłych projektach.

Schemat zależności modułów w projekcie Python, ilustrujący błąd ImportError attempted relative import w strukturze pakietów

Czym jest błąd ImportError: attempted relative import w Pythonie?

ImportError w ogólności oznacza, że Python nie był w stanie znaleźć lub poprawnie załadować modułu, który próbujesz zaimportować. To tak, jakbyś szukał książki na półce, która znajduje się w innym pokoju albo w ogóle nie istnieje. W przypadku komunikatu attempted relative import chodzi jednak o coś bardziej subtelnego: problem z kontekstem importów względnych.

Importy względne to takie, które korzystają z kropek, np. from . import utils albo from ..models import User. Kropka oznacza „ten sam pakiet”, dwie kropki – „pakiet wyżej”. Działa to świetnie, o ile Python wie, w jakim pakiecie aktualnie się znajdujesz. Bez tej informacji nie ma punktu odniesienia, a więc nie da się poprawnie rozstrzygnąć ścieżki.

Jeśli moduł jest „wyrwany” z kontekstu pakietu i uruchamiany jak zwykły skrypt, Python traci informację o jego miejscu w hierarchii. To tak, jakbyś otworzył plik z listą zakupów poza aplikacją Allegro – część odwołań nagle przestaje mieć sens. Python w takiej sytuacji zgłasza właśnie: ImportError: attempted relative import.

W praktyce oznacza to, że problem nie leży w samej składni importu względnego, ale w sposobie uruchomienia modułu. Ten sam kod może działać poprawnie w jednym trybie, a rzucać błąd w innym, mimo identycznych linijek from ..something import something_else.

Skąd się bierze ImportError: attempted relative import? Kontekst uruchamiania

Uruchamianie pliku jako głównego skryptu

Najczęściej błąd pojawia się, gdy plik z importem względnym uruchamiasz wprost komendą:

python moj_skrypt.py

Wtedy Python traktuje ten plik jako skrypt najwyższego poziomu. Ustawia zmienną __name__ na __main__ i nie przypisuje mu żadnego nadrzędnego pakietu. W efekcie:

  • moduł nie jest widziany jako część pakietu,
  • importy względne typu from .coś import coś czy from ..coś import coś nie mają punktu odniesienia,
  • Python nie wie, co oznacza „pakiet wyżej” i zgłasza błąd.

To właśnie brak kontekstu pakietu jest sednem komunikatu attempted relative import. Python widzi, że próbujesz użyć importu względnego, ale nie ma jak go rozwiązać.

Uruchamianie modułu jako części pakietu

Sytuacja zmienia się radykalnie, gdy uruchamiasz ten sam kod jako moduł w pakiecie, używając:

python -m moj_pakiet.moj_modul

W takim trybie Python:

  • szuka moj_pakiet na sys.path,
  • ładuje moduł moj_modul jako element moj_pakiet,
  • zna pełną ścieżkę pakietową, np. moj_pakiet.moj_modul,
  • poprawnie interpretuje . i .. w importach względnych.

To dokładnie ten sam kod, ale inny sposób uruchomienia. Dlatego tak ważne jest, żeby rozumieć, w jakim kontekście działa dany plik i jak Python buduje drzewo pakietów dla Twojego projektu.

Typowy przykład struktury prowadzącej do błędu

Rozważ strukturę:

my_project/
├── __init__.py
├── main.py
└── utils/
    ├── __init__.py
    └── helper.py

W helper.py próbujesz zaimportować coś z main.py:

# utils/helper.py
from ..main import some_function  # import względny do main.py

Jeśli teraz uruchomisz:

cd my_project
python utils/helper.py

otrzymasz ImportError: attempted relative import. Dla Pythona helper.py jest po prostu głównym skryptem (bez pakietu nadrzędnego), więc ..main nie ma sensu. Gdybyś natomiast uruchomił ten moduł w kontekście pakietu, sytuacja wyglądałaby zupełnie inaczej.

Jak naprawić błąd „ImportError: attempted relative import”? (Metody krok po kroku)

Metoda 1: Uruchamiaj moduły jako pakiety za pomocą python -m

To zwykle najczystsze i najbardziej zalecane rozwiązanie. Pozwala zachować importy względne i jednocześnie zapewnia Pythonowi pełny kontekst pakietu. Zamiast uruchamiać plik jak samodzielny skrypt, uruchamiasz go jako moduł w obrębie pakietu.

Jak działa python -m?

Polecenie:

python -m nazwa_pakietu.nazwa_modułu

sprawia, że:

  • Python sprawdza sys.path, szukając katalogu zawierającego nazwa_pakietu,
  • ładuje nazwa_pakietu jako pakiet,
  • uruchamia nazwa_modułu w kontekście tego pakietu,
  • dzięki temu importy względne znają swoje „miejsce w drzewie”.

Przykład z działającym importem względnym

Struktura projektu:

moj_projekt/
├── __init__.py
├── app.py
└── moduly/
    ├── __init__.py
    └── narzedzia.py

W pliku narzedzia.py:

# moj_projekt/moduly/narzedzia.py
from ..app import powitaj  # import względny

W app.py:

# moj_projekt/app.py
def powitaj(imie):
    return f"Witaj, {imie}!"

Niepoprawne uruchomienie:

cd moj_projekt
python moduly/narzedzia.py

To wywołanie zakończy się błędem ImportError: attempted relative import, ponieważ narzedzia.py staje się skryptem najwyższego poziomu.

Poprawne uruchomienie:

cd ..
python -m moj_projekt.moduly.narzedzia

Teraz Python rozpoznaje moj_projekt.moduly.narzedzia jako moduł wewnątrz pakietu moj_projekt, a from ..app import powitaj działa prawidłowo. To najprostszy sposób, aby naprawić błąd, nie zmieniając ani jednego importu w kodzie.

Wskazówka praktyczna

W edytorach takich jak PyCharm czy VS Code możesz skonfigurować konfigurację uruchamiania tak, aby domyślnie używała python -m. Dzięki temu zachowasz importy względne i unikniesz ciągłego ręcznego wpisywania ścieżek pakietowych.

Diagram przedstawiający poprawne uruchamianie modułów Python z użyciem python -m, unikając błędu ImportError attempted relative import

Metoda 2: Zamiana importów względnych na absolutne

Jeżeli dany plik musi być uruchamiany bezpośrednio, np.:

  • to narzędzie CLI,
  • skrypt administracyjny,
  • prosty punkt wejścia do aplikacji,

możesz rozważyć zamianę importów względnych na importy absolutne. Wtedy import nie zależy od bieżącego kontekstu pakietu, tylko od pełnej ścieżki modułu w projekcie.

Na czym polega import absolutny?

Import absolutny zaczyna się od nazwy głównego pakietu, np.:

from moj_projekt.app import powitaj

Zamiast:

from ..app import powitaj

Python szuka modułu moj_projekt.app na ścieżkach z sys.path. Warunkiem powodzenia jest to, żeby katalog nadrzędny względem moj_projekt znajdował się na tej liście.

Przykład zamiany importu

Struktura:

moj_projekt/
├── __init__.py
├── app.py
└── moduly/
    ├── __init__.py
    └── narzedzia.py

Oryginalny kod z importem względnym:

# moj_projekt/moduly/narzedzia.py
from ..app import powitaj  # import względny – problem przy bezpośrednim uruchomieniu

Zamieniamy na import absolutny:

# moj_projekt/moduly/narzedzia.py
from moj_projekt.app import powitaj  # import absolutny

Następnie uruchamiamy skrypt z katalogu nadrzędnego:

projekt_glowny/
├── moj_projekt/
│   ├── __init__.py
│   ├── app.py
│   └── moduly/
│       ├── __init__.py
│       └── narzedzia.py
└── inna_rzecz.py

Będąc w projekt_glowny/:

python moj_projekt/moduly/narzedzia.py

projekt_glowny trafia na sys.path, więc Python może znaleźć moj_projekt.app. W ten sposób import absolutny działa nawet przy bezpośrednim uruchamianiu pliku.

O czym pamiętać przy importach absolutnych?

  • Pliki uruchamiaj zawsze z właściwego katalogu nadrzędnego.
  • Utrzymuj spójną strukturę pakietów i jasną nazwę głównego pakietu.
  • To podejście jest bardzo czytelne w bibliotekach i większych aplikacjach, ale może wymagać dyscypliny przy uruchamianiu skryptów.

Metoda 3: Ręczna modyfikacja sys.path (rozwiązanie specjalne)

Trzecia metoda polega na ręcznym dodaniu katalogu projektu do sys.path. To podejście bywa używane w skryptach pomocniczych, testach lub kodzie eksperymentalnym. Nie jest jednak polecane jako główna strategia dla produkcyjnych aplikacji.

Jak dodać katalog projektu do sys.path?

Przykładowy skrypt:

# Moj_projekt/nieco_inny_skrypt.py
import sys
import os

# dodaj katalog główny projektu do sys.path
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if project_root not in sys.path:
    sys.path.insert(0, project_root)

# teraz można używać importów absolutnych
from moj_projekt.moduly.narzedzia import powitaj
from moj_projekt.app import powitaj as powitaj_z_app

print(powitaj("Świat"))
print(powitaj_z_app("Programistę"))

Co się tutaj dzieje:

  • os.path.dirname(__file__) zwraca katalog bieżącego pliku,
  • os.path.join(..., '..') przechodzi o poziom wyżej – do katalogu projektu,
  • sys.path.insert(0, ...) dodaje tę ścieżkę na początek listy wyszukiwania modułów.

Dzięki temu możesz korzystać z importów absolutnych nawet wtedy, gdy normalnie Python nie widziałby Twojego projektu na sys.path.

Kiedy warto użyć tej metody?

  • W małych skryptach narzędziowych, które mają być uruchamiane z różnych miejsc.
  • W jednorazowych eksperymentach i prototypach.
  • W testach, gdy nie masz jeszcze uporządkowanej struktury pakietów.

Staraj się jednak unikać tej metody w głównym kodzie produkcyjnym. Nadmierna manipulacja sys.path może prowadzić do trudnych w diagnozie konfliktów i nieprzewidywalnych zachowań.

Najlepsze praktyki: jak unikać ImportError: attempted relative import w przyszłości

1. Zawsze dodawaj pliki __init__.py do katalogów pakietów

Każdy katalog, który ma być traktowany jako pakiet, powinien zawierać plik __init__.py, nawet pusty. Choć nowsze wersje Pythona dopuszczają tzw. niejawne pakiety, jawne __init__.py daje jasność i kompatybilność. Ułatwia też narzędziom analitycznym i IDE poprawne rozpoznanie struktury projektu.

2. Oddziel logikę biznesową od plików startowych

Dobra architektura projektu zakłada, że:

  • logika (funkcje, klasy, modele) żyje w modułach pakietu,
  • pliki uruchamiane bezpośrednio (main.py, run.py, skrypty CLI) są tylko cienką warstwą startową.

Dzięki temu większość modułów będzie uruchamiana jako część pakietu, a nie jako skrypty najwyższego poziomu. Importy względne i absolutne staną się stabilne i przewidywalne, a ryzyko błędu ImportError: attempted relative import mocno spadnie.

3. Preferuj importy absolutne w kodzie bibliotek

Gdy tworzysz bibliotekę lub moduł, który ma być używany w wielu projektach, importy absolutne są zwykle czytelniejsze i mniej problematyczne. Od razu widać:

  • z którego pakietu pochodzi dany moduł,
  • jak wygląda pełna ścieżka,
  • gdzie szukać definicji.

Importy względne są wygodne wewnątrz jednego pakietu, ale nadmierne ich użycie może utrudniać refaktoryzację i przenoszenie modułów między pakietami.

4. Uruchamiaj moduły pakietów przez python -m

Zamiast:

python my_package/my_module.py

zacznij konsekwentnie używać:

python -m my_package.my_module

To standardowy i poprawny sposób uruchamiania modułów w kontekście pakietu. Dzięki temu:

  • unikniesz błędów z importami względnymi,
  • zachowasz spójność w sposobie uruchamiania,
  • łatwiej będzie Ci konfigurować środowisko w różnych IDE i narzędziach.

5. Testuj strukturę pakietów na wczesnym etapie

Im wcześniej zweryfikujesz, czy:

  • importy działają poprawnie,
  • ścieżki pakietów są spójne,
  • pliki startowe są dobrze umiejscowione,

tym mniej czasu stracisz na późniejsze porządki. W miarę rozrostu projektu każdy drobny błąd w strukturze może być coraz trudniejszy do naprawienia. Warto więc regularnie sprawdzać, czy nie pojawiają się ostrzeżenia lub błędy związane z importami.

Podsumowanie: jak trwale poradzić sobie z ImportError: attempted relative import

Błąd ImportError: attempted relative import jest sygnałem, że Python nie zna kontekstu pakietu dla modułu, który próbujesz uruchomić. Najczęściej wynika to z uruchamiania pliku bezpośrednio (python plik.py), mimo że ten plik używa importów względnych.

Kluczowe wnioski:

  • Importy względne działają tylko wtedy, gdy moduł jest uruchamiany jako część pakietu.
  • Uruchamianie skryptu jako głównego (__main__) pozbawia go informacji o nadrzędnym pakiecie.
  • Najlepszym i najczystszym rozwiązaniem jest uruchamianie modułów przez python -m w ramach struktury pakietów.
  • Alternatywnie możesz:
  • przejść na importy absolutne, dbając o poprawny sys.path,
  • w specjalnych przypadkach ręcznie modyfikować sys.path.
  • Dobre praktyki struktury pakietów, __init__.py, oddzielenie logiki od punktów wejścia i konsekwentne używanie python -m pozwolą Ci w przyszłości uniknąć tego problemu.

Kiedy zrozumiesz, jak Python widzi Twój projekt jako drzewo pakietów, błąd ImportError: attempted relative import przestanie być uciążliwym zagadką, a stanie się prostym sygnałem: „brakuje właściwego kontekstu uruchomienia”. Dzięki temu możesz świadomie projektować strukturę swoich aplikacji i spokojniej rozwijać kolejne moduły.

Marek Radoszewski

Autor

Marek Radoszewski

Freelance developer i tech blogger od 7 lat. Pracował przy projektach dla klientów z Polski, UK i USA. Na blogu pisze o praktycznych aspektach programowania, narzędziach i tym, jak skutecznie rozwijać karierę jako niezależny programista.

Wróć do kategorii Backend i Frontend