Problem PATH z pytest „ImportError: Brak modułu o nazwie YadaYadaYada”

231

Użyłem easy_install, aby zainstalować pytest na komputerze Mac i zacząłem pisać testy dla projektu o strukturze plików podobnej do następującej:

repo/
repo/app.py
repo/settings.py
repo/models.py
repo/tests/
repo/tests/test_app.py

uruchom py.testw katalogu repo, wszystko zachowuje się tak, jak można się spodziewać

ale kiedy próbuję tego samego na Linuksie lub Windowsie (oba mają na sobie pytest 2.2.3), szczeka za każdym razem, gdy natrafi na pierwszy import czegoś z mojej ścieżki aplikacji. Powiedz na przykładfrom app import some_def_in_app

Czy muszę edytować moją PATH, aby uruchomić py.test na tych systemach? Czy ktoś tego doświadczył?

MattoTodd
źródło
4
Oto sposób, aby to naprawić za pomocą setuptools.
ederag
4
Sprawdź odpowiedź @hoefling i rozważ zmianę zaakceptowanej, jeśli SO pozwala na to po tak długim czasie: znacznie lepiej!
Davide,

Odpowiedzi:

91

Tak, folder źródłowy nie znajduje się w ścieżce Pythona, jeśli przejdziesz cddo katalogu testów.

Masz 2 możliwości:

  1. Dodaj ścieżkę ręcznie do plików testowych, mniej więcej tak:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. Uruchom testy z env var PYTHONPATH=../.

Not_a_Golfer
źródło
11
kiedy cdidę do katalogu? uruchamiam py.testz mojego katalogu głównego. chyba że się mylę i masz na myśli, gdy pytający przechodzi przez moje foldery
MattoTodd,
jeśli to był cdproblem, czy nie trafiłbym go również na Maca?
MattoTodd,
Och, źle odczytałem i pomyślałem, że to nie działa z katalogu testów. nadal sztuczka z sugestii 1 zadziałałaby. Używam tylko Linuksa, więc nie mogę wyjaśnić zachowania w innych systemach operacyjnych.
Not_a_Golfer
czy masz taki import na wszystkich swoich plikach test.py?
MattoTodd,
4
tak, ale moja struktura katalogów jest zwykle nieco inna - zwykle trzymam / src i / test w katalogu głównym.
Not_a_Golfer
275

Nie jestem pewien, dlaczego py.test nie dodaje bieżącego katalogu do samej PYTHONPATH, ale oto obejście (do wykonania z katalogu głównego repozytorium):

python -m pytest tests/

Działa, ponieważ Python dodaje dla ciebie bieżący katalog w PYTHONPATH.

Bezskrzydły ptak
źródło
2
Wymaga przepisania importów względnych na import absolutny, jeśli masz kod do uruchamiania aplikacji nie na poziomie, z którego wykonujesz polecenie. Na przykład: project/test/all-my-testsi z project/src/app.pypowodu tej zmiany należy wywołać app.pypośrednio, używając __main__.pypliku w project/src, aby można było użyć wywołania python -m src. O ile wiem, dość niechlujne rzeczy.
Zelphir Kaltstahl
3
@Zelphir: Używanie importu bezwzględnego jest zalecaną praktyką. Habnabit's ma dobry artykuł na temat najlepszych praktyk w zakresie pakowania: blog.habnab.it/blog/2013/07/21/python-packages-and-you , a PEP8 mówi, że „domyślny import względny nigdy nie powinien być używany i został usunięty w Pythonie 3. ” Zobacz: python.org/dev/peps/pep-0008 .
Apteryx,
1
@Apteryx Masz na myśli „bezwzględny projekt”, prawda? Ponieważ rzeczy takie /home/user/dev/projectxyz/src ...byłyby naprawdę złe i w większości przypadków nie działałyby na innych komputerach. Myślę, że miałem na myśli to, że zawsze muszę zapisywać cały katalog główny projektu do ścieżki modułu, nawet jeśli moduł znajduje się w tym samym folderze, co plik. Nie wiedziałem, że jest to uważane za najlepszą praktykę, więc to przydatna informacja, dzięki. Zgadzam się z większością pep8, chociaż wciąż nie jest doskonały.
Zelphir Kaltstahl
1
@Zelphir, tak, o to mi chodziło. Uważam, że termin import bezwzględny w Pythonie zawsze odnosi się do „bezwzględny dla projektu”. Zobacz: python.org/dev/peps/pep-0328/#rationale-for-absolute-imports . W rzeczywistości jestem prawie pewien, że nie możesz importować z losowych, bezwzględnych lokalizacji ścieżek, przynajmniej przy użyciu domyślnego mechanizmu „importowania”.
Apteryx,
4
Dodałem __init__.pyw testach, które rozwiązały problem. Teraz mogę użyćpytest
Kiran Kumar Kotari
154

conftest rozwiązanie

Najmniej inwazyjne rozwiązanie polega na dodaniu pustego pliku o nazwie conftest.pyw repo/katalogu:

$ touch repo/conftest.py

Otóż ​​to. Nie ma potrzeby pisania niestandardowego kodu w celu manipulacji sys.pathlub pamiętaj, aby przeciągać PYTHONPATHlub umieszczać __init__.pyw katalogach, w których nie należy.

Katalog projektu następnie:

repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
     └── test_app.py

Wyjaśnienie

pytestszuka conftestmodułów w kolekcji testowej, aby zebrać niestandardowe zaczepy i urządzenia, a aby zaimportować z nich niestandardowe obiekty, pytestdodaje katalog nadrzędny conftest.pydosys.path (w tym przypadku repokatalogu).

Inne struktury projektu

Jeśli masz inną strukturę projektu, umieść conftest.pykatalog w katalogu głównym pakietu (ten, który zawiera pakiety, ale sam nie jest pakietem, więc nie zawiera an __init__.py), na przykład:

repo
├── conftest.py
├── spam
   ├── __init__.py
   ├── bacon.py
   └── egg.py
├── eggs
   ├── __init__.py
   └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

src układ

Chociaż tego podejścia można użyć z srcukładem (umieść conftest.pyw katalogu src):

repo
├── src
   ├── conftest.py
   ├── spam
      ├── __init__.py
      ├── bacon.py
      └── egg.py
   └── eggs 
       ├── __init__.py
       └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

uwaga, że ​​dodanie w srccelu PYTHONPATHzłagodzenia znaczenia i zalet srcukładu! Skończysz na testowaniu kodu z repozytorium, a nie zainstalowanego pakietu. Jeśli musisz to zrobić, może wcale nie potrzebujesz srcreż.

Dokąd się udać?

Oczywiście conftestmoduły to nie tylko niektóre pliki, które pomagają w odkrywaniu kodu źródłowego; w tym miejscu mają miejsce wszystkie udoskonalenia pytestframeworku specyficzne dla projektu i dostosowanie pakietu testowego. pytestma wiele informacji o conftestmodułach rozrzuconych po ich dokumentach ; zacznij od conftest.py: lokalnych wtyczek dla poszczególnych katalogów

Ponadto SO ma doskonałe pytanie dotyczące conftestmodułów: W py.test, do czego służą pliki conftest.py?

hoefling
źródło
2
@ aaa90210 chociaż nie mogę odtworzyć twojego problemu (importowanie z poufnej wersji w katalogu głównym działa na dowolnym poziomie), nigdy nie powinieneś importować z poufnych plików, ponieważ jest to nazwa zastrzeżona pytesti zdecydowanie odradzam to . W ten sposób sadzisz nasiona na przyszłe błędy. Utwórz kolejny moduł o nazwie utils.pyi umieść tam kod do ponownego użycia w testach.
hoefling 15.01.19
4
Kciuki w górę! To jedyne rozwiązanie, które działa dobrze dla mnie.
130
4
powinna być absolutnie przyjętą odpowiedzią - dziękuję!
Martin Peck,
2
Logicznie rzecz biorąc, conftest.pynie należy do kodu aplikacji i, imo, umieszczenie go pod src/jest nieprawidłowe.
Nik O'Lai
2
Ta odpowiedź powinna być stałym nagłówkiem w SO. Wielkie dzięki.
Rigoberta Raviolini
119

Miałem ten sam problem. Naprawiłem to, dodając pusty __init__.pyplik do mojego testskatalogu.

Aron Curzon
źródło
77
Zauważ, że NIE jest to zalecane przez py.test: avoid “__init__.py” files in your test directories. This way your tests can run easily against an installed version of mypkg, independently from the installed package if it contains the tests or not.SRC: pytest.org/latest/goodpractises.html
K.-Michael Aye
24
Przybyłem tutaj z tym samym pytaniem i stwierdziłem, że usunięcie __init__.pyz mojego katalogu testów rozwiązało to dla mnie.
101
3
@mafro Nie widzę problemu? Testy nie muszą być kodem do importowania, są one znalezione przez testera. Tylko kod DO TESTOWANIA powinien być zainstalowanym pakietem / modułem, a nie testami.
K.-Michael Aye,
5
Dodanie do __init__.pypodkatalogów test/sprawia, że ​​import absolutny działa w celu uruchomienia określonych testów w tym podkatalogu w stosunku do przyszłych modułów. Dzięki.
Bryce Guinta,
7
proszę bardzo : doc.pytest.org/en/latest/goodpractices.html bardzo łatwe do znalezienia w Google.
K.-Michael Aye
46

Uruchom pytestsię jako moduł z: python -m pytest tests

Stefano Messina
źródło
3
To wydaje się działającym rozwiązaniem, ale czy ktoś może wyjaśnić DLACZEGO? Wolę naprawić podstawową przyczynę niż po prostu użyć python -m pytestbez żadnego wyjaśnienia innego niż „ponieważ to działa”
Janne Enberg,
4
Dzieje się tak, gdy na przykład hierarchia projektu: package/src package/testsi testsimportujesz z src. Wykonanie jako moduł będzie traktować importowanie jako bezwzględne niż w stosunku do lokalizacji wykonania.
Stefano Messina
1
To rozwiązanie pomogło mi, dzięki! Przyczyną tego był konflikt w wersji Python. test pytest działa dla wcześniejszej wersji Pythona. W mojej sytuacji moja wersja Pythona to 3.7.1, python -m testy pytest działają, ale nie testy pytest.
Ruxi Zhang
1
Z Pytest „Uruchamianie pytest z python -m pytest [...] zamiast pytest [...] daje prawie równoważne zachowanie, z tym wyjątkiem, że poprzednie wywołanie doda bieżący katalog do sys.path.”
Moad Ennagi,
37

Możesz uruchomić PYTHONPATH w katalogu głównym projektu

PYTHONPATH=. py.test

Lub użyj instalacji pip jako importu do edycji

pip install -e .   # install package using setup.py in editable mode
Ford Guo
źródło
3
Nie działało to dla mnie, gdy testkatalog nie był w srcstrukturze katalogów i wywołanie z katalogu zawierającego zarówno katalog, jak testi srckatalog.
Zelphir Kaltstahl
21

Stworzyłem to jako odpowiedź na twoje pytanie i moje własne zamieszanie. Mam nadzieję, że to pomoże. Zwróć uwagę na PYTHONPATH zarówno w wierszu poleceń py.test, jak i na stronie tox.ini.

https://github.com/jeffmacdonald/pytest_test

W szczególności: musisz powiedzieć py.test i toks, gdzie znaleźć moduły, które dołączasz.

Z py.test możesz to zrobić:

PYTHONPATH=. py.test

I z toks, dodaj to do swojego tox.ini:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}
Jeff MacDonald
źródło
1
Czy mógłbyś krótko wyjaśnić projekt, który połączyłeś?
JF Meier
1
Może to tylko ja, ale plik README w projekcie jest dość szczegółowy, a mój komentarz dotyczący stackoverflow mówi, dlaczego utworzyłem repo.
Jeff MacDonald
5
Chociaż nie jest to absolutnie konieczne, zwykle zasadą jest umieszczenie głównej treści odpowiedzi w samej odpowiedzi, ponieważ zapewnia ona, że ​​odpowiedź będzie zrozumiała za X lat, kiedy połączony zasób może już dawno zniknąć.
JF Meier
:) No cóż. To dla ciebie internet.
Jeff MacDonald
9

Miałem ten sam problem w Flask.

Kiedy dodałem:

__init__.py

do folderu testów problem zniknął :)

Prawdopodobnie aplikacja nie mogła rozpoznać testów folderów jako modułu

użytkownik13037517
źródło
8

Naprawiłem to, usuwając najwyższy poziom __init__.pyw folderze nadrzędnym moich źródeł.

Gonzalo
źródło
1
Naprawiłem to dla mnie. Czy ktoś może to wyjaśnić?
aboger
to właśnie tutaj to naprawiło. zdecydowanie chciałbym zobaczyć wyjaśnienie tego, jeśli ktoś to ma
king_wayne,
dodałem init .py, ale wciąż napotykałem te same problemy, ale to rozwiązanie również dla mnie działało.
Abhijit
7

Zacząłem dostawać dziwne ConftestImportFailure: ImportError('No module named ...błędy, gdy przypadkowo dodałem __init__.pyplik do mojego katalogu src (który nie miał być pakietem Pythona, tylko kontenerem całego źródła).

jbasko
źródło
3

Otrzymałem ten błąd z powodu czegoś jeszcze prostszego (można nawet powiedzieć, że jest trywialny). Nie zainstalowałem pytestmodułu. Więc prosty apt install python-pytestto dla mnie naprawił.

„pytest” zostałoby wymienione w pliku setup.py jako zależność testowa. Upewnij się, że zainstalowałeś również wymagania testowe.

krak
źródło
3

Miałem podobny problem. pytestnie rozpoznałem modułu zainstalowanego w środowisku, w którym pracowałem.

Rozwiązałem go, instalując się pytestw tym samym środowisku.

nocibambi
źródło
Mimo że używałem pytest z venv, miałem również zainstalowany globalnie, co dało mi ten błąd. Po odinstalowaniu wersji globalnej i zainstalowaniu w venv działało.
Markus Ressel
2

Dla mnie problem został tests.pywygenerowany przez Django wraz z testskatalogiem. Usunięcie tests.pyrozwiązało problem.

Paweł Mucha
źródło
2

Wystąpił ten błąd, ponieważ nieprawidłowo użyłem importu względnego. W przykładzie OP test_app.py powinien importować funkcje przy użyciu np

from repo.app import *

Jakkolwiek pliki __init__.py są swobodnie rozproszone wokół struktury plików, to nie działa i tworzy rodzaj ImportError, chyba że pliki i pliki testowe znajdują się w tym samym katalogu.

from app import *

Oto przykład tego, co miałem do czynienia z jednym z moich projektów:

Oto struktura mojego projektu:

microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py

Aby uzyskać dostęp do activity_indicator.py z test_activity_indicator.py musiałem:

  • uruchom test_activity_indicatory.py z poprawnym importem względnym:
    from microbit.activity_indicator.activity_indicator import *
  • umieść pliki __init__.py w całej strukturze projektu:
    microbit/
    microbit/__init__.py
    microbit/activity_indicator/__init__.py
    microbit/activity_indicator/activity_indicator.py
    microbit/tests/__init__.py
    microbit/tests/test_activity_indicator.py
Oppy
źródło
0

Bardzo często testy były przerywane z powodu niemożności zaimportowania modułu. Po badaniach dowiedziałem się, że system patrzy na plik w niewłaściwym miejscu i możemy łatwo rozwiązać problem, kopiując plik zawierający moduł w ten sam folder, jak podano, w celu prawidłowego zaimportowania. Inną propozycją rozwiązania byłaby zmiana deklaracji importu i pokazanie MutPy poprawnej ścieżki urządzenia. Jednak ze względu na fakt, że wiele jednostek może mieć tę zależność, co oznacza, że ​​musimy zatwierdzić zmiany również w ich deklaracjach, wolimy po prostu przenieść jednostkę do folderu.

Baydaa
źródło
Istnieją inne odpowiedzi, które dostarczają pytania OP, i zostały opublikowane jakiś czas temu. Publikując odpowiedź, upewnij się, że dodałeś nowe rozwiązanie lub znacznie lepsze wyjaśnienie, szczególnie w przypadku odpowiedzi na starsze pytania. Czasami lepiej opublikować komentarz do konkretnej odpowiedzi.
help-info.de
Jako dodatek do komentarza z @ help-info.de: Oto link do przewodnika odpowiadającego na pytania: stackoverflow.com/help/how-to-answer
ręka NOD
0

Zgodnie z postem Dirka Avery'ego na Medium (i obsługiwanym przez moje osobiste doświadczenia), jeśli używasz środowiska wirtualnego do swojego projektu, nie możesz użyć ogólnosystemowej instalacji pytest; musisz zainstalować go w środowisku wirtualnym i użyć tej instalacji.

W szczególności, jeśli masz zainstalowany w obu miejscach, to po prostu uruchomienie pytestpolecenia nie będzie działać, ponieważ będzie korzystało z instalacji systemu. Jak opisano w innych odpowiedziach, jednym z prostych rozwiązań jest uruchomienie python -m pytestzamiast pytest; działa to, ponieważ używa środowiska pytest w środowisku. Alternatywnie możesz po prostu odinstalować systemową wersję pytest; po reaktywacji środowiska wirtualnego pytestpolecenie powinno działać.

Einhaender
źródło
Do tej pory działało tylko dla mnie python -m pytest tests/.
Nik O'Lai
0

Miałem ten sam problem, postępując zgodnie z samouczkiem Flask i znalazłem odpowiedź na oficjalnych dokumentach Pytest. To trochę zmienia sposób, w jaki ja (i myślę, że wielu innych) jestem przyzwyczajony do robienia rzeczy.

Musisz utworzyć setup.pyplik w katalogu głównym projektu z co najmniej dwoma następującymi wierszami:

from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())

gdzie PACKAGENAME to nazwa Twojej aplikacji. Następnie musisz zainstalować go za pomocą pip:

pip install -e .

-eFlaga mówi pip aby nienaruszony pakiet w edytowalny lub „rozwijać” tryb. Więc przy następnym uruchomieniu pytestpowinna znaleźć twoją aplikację w standardzie PYTHONPATH.

Luis Lezcano Airaldi
źródło
0

Moje rozwiązanie:

utwórz conftest.pyplik w testkatalogu zawierającym:

import os
import sys
sys.path.insert(0,os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/code/")

Spowoduje to dodanie folderu będącego przedmiotem zainteresowania do ścieżki Pythona bez modyfikowania każdego pliku testowego , ustawiania zmiennej env lub bałagania za pomocą ścieżek bezwzględnych / względnych.

David Burba
źródło