1

I. 서문 : 응용 프로그램 디렉토리 구조 및 모듈이 게시물의 끝에 나열됩니다.패키지 가져 오기 오류로 Python 실행 Unittest

II. 문제 문 : PYTHONPATH가 설정되어 있지 않으면 응용 프로그램이 실행되지만 unittest가 으로 실패합니다. ImportError : 모듈이 models.transactions가 아닌입니다. 이것은 거래를 app.py에서 가져 오려고 할 때 발생합니다. PYTHONPATH가 /sandbox/app으로 설정된 경우 응용 프로그램과 unittest가 오류없이 실행됩니다. 솔루션에 대한 제약 조건은 PYTHONPATH가 설정되어서는 안되며 sys.path를 프로그래밍 방식으로 수정하면 안됩니다.

III. 세부 정보 : PYTHONPATH가 설정되고 test_app.py이 패키지 /sandbox$ python -m unittest tests.test_app으로 실행되는 경우를 고려하십시오. 코드 전체에 뿌려 __main__ 의 인쇄 문에서 상대 :

models : app.models.transactions 
models : models.transactions 
resources: app.resources.transactions 
app  : app.app 
test  : tests.test_app 

유닛 테스트에 먼저 응용 프로그램을 가져오고하는 것은 너무 app.models.transactions있다. 앱 의 다음 가져 오기 시도는 resources.transactions입니다. 가져올 때 models.transactions을 가져오고 을 입력하면 __name__app.resources.transactions입니다. 그 다음에 app.app 가져 오기가 이어지고 마지막으로 unittest 모듈 tests.test.app이옵니다. PYTHONPATH를 설정하면 응용 프로그램이 models.transactions를 확인할 수 있습니다!

하나의 해결책은 models.transactionsresources.transaction 안에 넣는 것입니다. 그러나 문제를 해결할 다른 방법이 있습니까?

models : models.transactions 
resources: resources.transactions 
app  : __main__ 

이 예상되며, 더 수입은 /sandbox/app 또는 옆 위에있는 시도되고 있지 : 응용 프로그램이 __main__의 인쇄 문을 실행

완성도를 들어

이 있습니다.

IV. 부록

A.1 디렉토리 구조 :

|-- sandbox 
    |-- app 
     |-- models 
      |-- __init__.py 
      |-- transactions.py 
     |-- resources 
      |-- __init__.py 
      |-- transactions.py   
     |-- __init__.py 
     |-- app.py 
    |-- tests 
     |-- __init__.py 
     |-- test_app.py 

A.2 모듈 :

(1) 응용 프로그램 :

from flask import Flask 
from models.transactions import TransactionsModel 
from resources.transactions import Transactions 
print '  app  : ', __name__ 
def create_app(): 
    app = Flask(__name__) 
    return app 
app = create_app() 
if __name__ == '__main__': 
    app.run(host='127.0.0.1', port=5000, debug=True) 

(2) models.transactions

print '  model : ', __name__ 
class TransactionsModel: 
    pass 

(3) resources.transactions :

from models.transactions import TransactionsModel 
print '  resources: ', __name__ 
class Transactions: 
    pass 

(4) 테스트.test_app

import unittest 
from app.app import create_app 
from app.resources.transactions import Transactions 
print '  test  : ', __name__ 
class DonationTestCase(unittest.TestCase): 
    def setUp(self): 
     pass 
    def tearDown(self): 
     pass 
    def test_transactions_get_with_none_ids(self): 
     self.assertEqual(0, 0) 
if __name__ == '__main__': 
    unittest.main() 

답변

1

Flask 문서가 응용 프로그램을 패키지로 실행하고 있다고 말하면서, 환경 변수 : FLASK_APP을 설정하는 것이 중요합니다. 그런 다음 응용 프로그램은 프로젝트 루트 인 $ python -m flask run에서 실행됩니다. 이제 가져 오기에는 app.models.transactions과 같은 응용 프로그램 루트가 포함됩니다. unittest가 프로젝트 루트의 패키지와 같은 방식으로 실행되기 때문에 모든 가져 오기도 여기서 해결됩니다.

문제의 핵심은 다음과 같이 설명 할 수 있습니다. test_app.평 옆으로 수입에 액세스해야하지만 같은 스크립트로 실행되는 경우 :이 __name__ == __main__에게 있습니다

/sandbox/test$ python test_app.py 

. 즉, from models.transactions import TransactionsModel과 같은 가져 오기는 옆으로 있고 계층 구조에서 더 낮지 않으므로 해결되지 않습니다. test_app.py 패키지로 실행할 수있는이 문제를 해결하려면 다음

/sandbox$ python unittest -m test.test_app 

-m 스위치가이 작업을 수행하기 위해 파이썬을 알 것입니다. 이제 패키지는 에서 실행 중이기 때문에 app.model에 액세스 할 수 있습니다.

from app.models.transactions import TransactionsModel 

이 응용 프로그램의 수입은 지금 기준으로해야합니다, 테스트 실행을하려면 : test_app.py의 수입은 같은 것을이 변경 내용을 반영가되어야합니다. 예를 들어 app.resources :

from ..models.transactions import TransactionsModel 

따라서 테스트가 성공적으로 끝났지 만 응용 프로그램이 실행되면 실패합니다! 이것이 문제의 핵심입니다. 응용 프로그램이 /sandbox/app$ python app.py에서 스크립트로 실행되면이 상대 가져 오기 ..models.transactions에 도달하고 프로그램이 최상위 수준에서 가져 오기를 시도하는 오류를 반환합니다. 하나를 고치고 다른 하나를 끊으십시오.

어떻게 PYTHONPATH를 설정하지 않고도이 문제를 해결할 수 있습니까? 가능한 솔루션은 __init__ .py 패키지의 조건부를 사용하여 조건부 가져 오기를 수행하는 것입니다. 이것은 resources 패키지의 모습의 예는 다음과 같습니다 극복하기

if __name__ == 'resources': 
    from models.transactions import TransactionsModel 
    from controllers.transactions import get_transactions 
elif __name__ == 'app.resources': 
    from ..models.transactions import TransactionsModel 
    from ..controllers.transactions import get_transactions 

마지막 장애물은 우리가이 resources.py에 들어갔습니다 얻는 방법이다. __init__ .py에서 수행 된 가져 오기는 해당 파일에 바인딩되며 resources.py에서 사용할 수 없습니다. 일반적으로 하나 resources.py에서 다음과 같은 수입이 포함됩니다 : 다시,

import resources 

를, 그것은 resources 또는 app.resources입니까? 어려움이 방금 우리를 향해 나아 갔던 것처럼 보입니다.

from importlib import import_module 
import_module(__name__) 

사용할 수있는 다른 방법이 있습니다 : importlib가 제공하는 툴은 예를 들어, 다음은 올바른 가져 오기를 만들 것이다, 여기에 도움이 될 수 있습니다. 예 :

TransactionsModel = getattr(import_module(__name__), 'TransactionsModel') 

이렇게하면 현재 상황에서 오류가 수정되었습니다.

또 다른 직접적인 해결책은 모듈 자체에서 절대 가져 오기를 사용하는 것입니다. resources.pysys.path.append(app_root) 으로 PYTHONPATH 변경에 관한

models_root = os.path.join(os.path.dirname(__file__), '..', 'models') 
fp, file_path, desc = imp.find_module(module_name, [models_root]) 
TransactionsModel = imp.load_module(module_name, fp, file_path, 
    desc).TransactionsModel 
TransactionType = imp.load_module(module_name, fp, file_path, 
    desc).TransactionType 

그냥 참고 사항 : 자원 예를 들어. 이것은 잘 작동하며 필요한 곳에 몇 줄의 코드가 있습니다. 또한 실행 파일의 경로 만 변경하고 완료되면 되돌립니다. unittest의 유스 케이스처럼 보입니다. 애플리케이션이 다른 환경으로 옮겨지면 문제가 될 수 있습니다.

+0

"그것이 효과가있다"는 사실은이 코드가 "읽기 쉽지 않다"는 사실에 대해 가중되어야합니다. 즉, 다른 사람들은 코드를 유지 관리하는 사람들이 여기에서하는 모든 것을 이해하는데 어려움을 겪습니다 (v. "옳은 일"을하는 "래퍼 (wrapper)"쉘 스크립트를 제공하는 것). 또한 더 많은 테스트/모듈을 추가 할 때마다이 기능이 빠르게 다루기 힘들어지는 느낌을받습니다. – Marco

+0

물론입니다! 파이썬 경로, sys.path 등을 변경할 수없는 제약이없는 한, 수입을 처리하는 방법이 아닙니다. 결국, 위의 첫 번째 단락에서 언급했듯이, 이것을 처리하는 방법은 FLASK_APP를 설정하는 것입니다 , app & unittest를 패키지로 실행하십시오. – Aaron

0

TL; DR :

from models.transactions import TransactionsModel 
from resources.transactions import Transactions 

당신이 말할 때 버전

from app.models.transactions import TransactionsModel 
from app.resources.transactions import Transactions 

이상을 :

당신은, 당신은이 개 수입을 변경해야 할 수있는 경우

If the PYTHONPATH is not set the application runs...

어떻게 앱을 실행하나요? 나도 app.py의 수입 (최상위 app 모듈 누락) 잘못된 때문에 ... 같은

cd sandbox/app 
python app.py 

뭔가를 추측 그렇지 않으면 app 똑같이 실패 실행하고 있습니다. 그 . (

python tests/test_app.py 

요점은 현재를 :

IMO 당신은 또한 tests 모듈 (즉, 당신이 tests/__init__.py을 삭제할 수 있습니다)하고 그냥 테스트를 실행하기 위해 (stricly)이 필요하지 않습니다 디렉토리) 기본 PYTHONPATH에 포함 거기 모듈이로드되었는지 출신으로 항상/수입 검색 - 귀하의 테스트가 실행될 때 어떤 경우에 from app.app import create_appapp.py의 첫 번째 행 :

from models.transactions import TransactionsModel 

은 오류를 발생시킵니다 (./models 모듈/디렉토리 없음). 당신이 유일한 다른 옵션은 것 (PYTHONPATH을 수정하거나 명시 적으로 제외하는 sys.path 조작이 아닌) 내가 생각할 수있는 유일한 옵션은 app.py 응용 프로그램 모듈의 수입을 변경할 수 없습니다, 또는 다른 제약하는 경우

당신이 당신의 케이크를 가지고 그것을 먹을 수없는, 즉

from app import create_app 
from resources.transactions import Transactions 

:) : 일 수 :

cd sandbox/app 
python ../tests/test_app.py 

하지만 당신은 당신의 단위 테스트, 당신의 수입에, 변경해야 할 것 파이썬 조회 경로를 변경하지 않고 모든 모듈을 동일한 위치 (.)에서 시작하여 일관성있게 유지해야합니다.

+0

응용 프로그램은 패키지가 아닌 스크립트로 실행되며, 이는/sandbox/app에서 python app.py로 수행됩니다. app.models.transactions와 같은 가져 오기 사용은 해결되지 않습니다. 나는 그것을 시도했다. 두 번째 요점은 test_app가 python -m unittest test.test_app와 같은/sandbox에서 패키지로 실행된다는 것입니다. 이를 통해 테스트에서 옆쪽 가져 오기에 액세스 할 수 있으며이 테스트를 실행하는 일반적인 방법입니다. 이것은 체인 된 가져 오기를 제외하고는 작동합니다. – Aaron

+0

"스크립트로 실행"v. "패키지로 실행 중"(후자를 가정해도 차이가 없음)은 차이가없는 구별법입니다. 테스트에서 가져온 것을 표시된대로 변경하면 어떻게됩니까? '/ sandbox/app' with'python ../ tests/test_app.py' 이게 작동할까요? – Marco

+0

내가하려고하는 점은 경로 시스템을 둘러싼 제약 속에서는 모듈/패키지가'.'에서 해결되어야한다는 것입니다. 파이썬이 여기에서 가져 오기를 해결하려고 시도하는 곳입니다. 분명히 환경의 lib 폴더와 다른 것들은 이미'$ PYTHONPATH' 또는'sys.path'에 있습니다.) – Marco