賞金將在 6 天后到期。此問題的答案有資格獲得 250聲望賞金。 buhtz想引起對這個問題的更多關注。
也許我的目標和我在這里嘗試做的事情在 unpythonic 的含義上是錯誤的。我愿意就此提出任何建議。
我的目標
- 應用程式 (
myapp) 具有自己的測驗檔案夾。 mypackage帶有自己的測驗檔案夾的包 ( )。- 包的測驗應該可以從應用程式檔案夾和包檔案夾運行。
- 該包具有隱式和顯式組件。后者需要顯式匯入(例如 via
import mypackage.mymoduleB)。 - 可以將包(檔案夾)復制(運送以在其他應用程式中重用?)到其他檔案系統位置,而不會失去其功能和可測驗性。這就是為什么
tests在包檔案夾里面而不是外面。
那是檔案夾樹,其中itest是專案的名稱,myapp是其中的應用程式,if __name__ == '__main__':是mypackag包。
itest
└── myapp
├── myapp.py
├── mypackage
│ ├── __init__.py
│ ├── _mymoduleA.py
│ ├── mymoduleB.py
│ └── tests
│ ├── __init__.py
│ └── test_all.py
└── tests
├── __init__.py
└── test_myapp.py
問題
我可以毫無問題地從應用程式目錄運行單元測驗。
/home/user/tab-cloud/_transfer/itest/myapp $ python3 -m unittest -vvv
test_A (mypackage.tests.test_all.TestAll) ... mymoduleA.foo()
ok
test_B (mypackage.tests.test_all.TestAll) ... mymoduleB.bar()
ok
test_myname (tests.test_myapp.TestMyApp) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
但是當我進入包內時,測驗不會運行(sieh 目標#3)。
/home/user/tab-cloud/_transfer/itest/myapp/mypackage $ python3 -m unittest -vvv
tests.test_all (unittest.loader._FailedTest) ... ERROR
======================================================================
ERROR: tests.test_all (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: tests.test_all
Traceback (most recent call last):
File "/usr/lib/python3.9/unittest/loader.py", line 436, in _find_test_path
module = self._get_module_from_name(name)
File "/usr/lib/python3.9/unittest/loader.py", line 377, in _get_module_from_name
__import__(name)
File "/home/user/tab-cloud/_transfer/itest/myapp/mypackage/tests/test_all.py", line 12, in <module>
from . import mypackage
ImportError: cannot import name 'mypackage' from 'tests' (/home/user/tab-cloud/_transfer/itest/myapp/mypackage/tests/__init__.py)
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
MWE
不,我給你看檔案。為了確保在import從應用程式檔案夾或我使用的包檔案夾importlib(基于外部解決方案)運行時使用正確的包測驗。
三個檔案組成包
這是myapp/mypackage/__init__.py:
# imported implicite via 'mypackage'
from ._mymoduleA import *
# 'mymoduleB' need to be imported explicite
# via 'mypackage.moduleB'
$ cat myapp/mypackage/_mymoduleA.py
def foo():
print('mymoduleA.foo()')
return 1
$ cat myapp/mypackage/mymoduleB.py
def bar():
print('mymoduleB.bar()')
return 2
包的測驗
myapp/mypackage/tests/__init__.py是空的。
這是myapp/mypackage/tests/test_all.py:
import importlib
import unittest
# The package should be able to be tested by itself (run unittest inside the
# package directory) AND from the using application (run unittest in
# application directory).
# Based on: https://stackoverflow.com/a/14050282/4865723
if importlib.util.find_spec('mypackage'):
import mypackage
import mypackage.mymoduleB
else:
from . import mypackage
from mypackage import mymoduleB
class TestAll(unittest.TestCase):
def test_A(self):
self.assertEqual(1, mypackage.foo())
def test_B(self):
self.assertEqual(2, mypackage.mymoduleB.bar())
應用程式
這是cat myapp/myapp.py:
#!/usr/bin/env python3
import mypackage
def myname():
return 'My application!'
if __name__ == '__main__':
print(myname())
mypackage.foo()
try:
mypackage.mymoduleB.bar()
except AttributeError:
# we expecting this
print('Not imported yet: "mymoduleB.bar()"')
# this should work
import mypackage.mymoduleB
mypackage.mymoduleB.bar()
應用程式測驗
myapp/tests/__init__.py是空的。
這是myapp/tests/test_myapp.py:
import unittest
import myapp
class TestMyApp(unittest.TestCase):
def test_myname(self):
self.assertEqual(myapp.myname(), 'My application!')
旁注
請讓我解釋一下我的目標。mypackage應該可以在其他專案中重用。實際上,這意味著我將mypackage檔案夾從一個地方復制到另一個地方。在復制該檔案夾時,我確實希望該tests檔案夾附帶它而無需明確考慮它,因為它位于包檔案夾之外。如果新專案進行單元測驗,則包的測驗應該自動參與該單元測驗(通過discover)。
uj5u.com熱心網友回復:
幾年前我創建了一個匯入庫。它適用于路徑。我用它來創建一個插件系統,我基本上可以安裝和匯入任何庫的多個版本(有一些限制)。
為此,我們得到模塊的當前路徑。然后我們使用路徑匯入包。該庫會自動將正確的路徑添加到 sys.path。
您需要做的就是安裝 pylibimppip install pylibimp并編輯myapp/mypackage/tests/test_all.py
import os
import pylibimp
import unittest
path_tests = os.path.join(os.path.dirname(__file__))
path_mypackage = os.path.dirname(path_tests)
path_myapp = os.path.dirname(path_mypackage)
mypackage = pylibimp.import_module(os.path.join(path_myapp, 'mypackage'), reset_modules=False)
class TestAll(unittest.TestCase):
def test_A(self):
self.assertEqual(1, mypackage.foo())
def test_B(self):
self.assertEqual(2, mypackage.mymoduleB.bar())
我相信背景相當簡單。
import os
import sys
sys.path.insert(0, os.path.abspath('path/to/myapp'))
# Since path is added we can "import mypackage"
mypackage = __import__('mypackage')
sys.path.pop(0) # remove the added path to not mess with other imports
我希望這就是你要找的。
uj5u.com熱心網友回復:
你的目標真的有點不合時宜。但有時,你必須打破規則才能釋放你的心。
您可以通過檢查myapp/mypackage/ init .py中的“ package ”屬性來解決問題,如下所示:
# hint from there: https://stackoverflow.com/questions/65426515/how-to-resolve-attempted-relative-import-with-no-known-parent-package
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
if __package__:
from ._mymoduleA import foo
else:
from _mymoduleA import *
在這種情況下 myapp/mypackage/tests/test_all.py 代碼變得更簡單一些:
import importlib
import unittest
if not importlib.util.find_spec('mypackage'):
from __init__ import *
import mypackage
from mypackage import mymoduleB
class TestAll(unittest.TestCase):
def test_A(self):
self.assertEqual(1, mypackage.foo())
def test_B(self):
self.assertEqual(2, mymoduleB.bar())
所有其他檔案保持不變。
因此,您可以從 /myapp 和 /myapp/mypackage 檔案夾運行測驗。同時,不需要硬編碼任何絕對路徑。該應用程式可以復制到任何其他檔案系統位置。
我希望它對你有用。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/416272.html
標籤:
