"""Tests that run once""" import io import os import sys import imp import shutil import tempfile import subprocess import contextlib import datetime import json # Third-party dependency import six from nose.tools import ( assert_raises, ) PYTHON = sys.version_info[0] # e.g. 2 or 3 try: long except NameError: # Python 3 compatibility long = int def _pyside2_commit_date(): """Return the commit date of PySide2""" import PySide2 if hasattr(PySide2, '__build_commit_date__'): commit_date = PySide2.__build_commit_date__ datetime_object = datetime.datetime.strptime( commit_date[: commit_date.rfind('+')], '%Y-%m-%dT%H:%M:%S' ) return datetime_object else: # Returns None if no __build_commit_date__ is available return None @contextlib.contextmanager def captured_output(): new_out, new_err = six.StringIO(), six.StringIO() old_out, old_err = sys.stdout, sys.stderr try: sys.stdout, sys.stderr = new_out, new_err yield sys.stdout, sys.stderr finally: sys.stdout, sys.stderr = old_out, old_err def CustomWidget(parent=None): """ Wrap CustomWidget class into a function to avoid global Qt import """ from Qt import QtWidgets class Widget(QtWidgets.QWidget): pass return Widget(parent) self = sys.modules[__name__] qwidget_ui = u"""\ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Form</class> <widget class="QWidget" name="Form"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>507</width> <height>394</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> <widget class="QLineEdit" name="lineEdit"/> </item> <item row="1" column="0"> <widget class="QLabel" name="label"> <property name="text"> <string>TextLabel</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QLineEdit" name="lineEdit_2"/> </item> </layout> </widget> <resources/> <connections> <connection> <sender>lineEdit</sender> <signal>textChanged(QString)</signal> <receiver>label</receiver> <slot>setText(QString)</slot> <hints> <hint type="sourcelabel"> <x>228</x> <y>23</y> </hint> <hint type="destinationlabel"> <x>37</x> <y>197</y> </hint> </hints> </connection> </connections> </ui> """ qmainwindow_ui = u"""\ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>238</width> <height>44</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLineEdit" name="lineEdit"/> </item> </layout> </widget> </widget> <resources/> <connections/> </ui> """ qdialog_ui = u"""\ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Dialog</class> <widget class="QDialog" name="Dialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>186</width> <height>38</height> </rect> </property> <property name="windowTitle"> <string>Dialog</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLineEdit" name="lineEdit"/> </item> </layout> </widget> <resources/> <connections/> </ui> """ qdockwidget_ui = u"""\ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>DockWidget</class> <widget class="QDockWidget" name="DockWidget"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>169</width> <height>60</height> </rect> </property> <property name="windowTitle"> <string>DockWidget</string> </property> <widget class="QWidget" name="dockWidgetContents"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLineEdit" name="lineEdit"/> </item> </layout> </widget> </widget> <resources/> <connections/> </ui> """ qcustomwidget_ui = u"""\ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>238</width> <height>44</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="CustomWidget" name="customwidget"> </widget> </widget> <customwidgets> <customwidget> <class>CustomWidget</class> <extends>QWidget</extends> <header>tests.h</header> </customwidget> </customwidgets> <resources/> <connections/> </ui> """ def setup(): """Module-wide initialisation This function runs once, followed by teardown() below once all tests have completed. """ self.tempdir = tempfile.mkdtemp() def saveUiFile(filename, ui_template): filename = os.path.join(self.tempdir, filename) with io.open(filename, "w", encoding="utf-8") as f: f.write(ui_template) return filename self.ui_qwidget = saveUiFile("qwidget.ui", qwidget_ui) self.ui_qmainwindow = saveUiFile("qmainwindow.ui", qmainwindow_ui) self.ui_qdialog = saveUiFile("qdialog.ui", qdialog_ui) self.ui_qdockwidget = saveUiFile("qdockwidget.ui", qdockwidget_ui) self.ui_qcustomwidget = saveUiFile("qcustomwidget.ui", qcustomwidget_ui) def teardown(): shutil.rmtree(self.tempdir) def binding(binding): """Isolate test to a particular binding When used, tests inside the if-statement are run independently with the given binding. Without this function, a test is run once for each binding. """ return os.getenv("QT_PREFERRED_BINDING") == binding @contextlib.contextmanager def ignoreQtMessageHandler(msgs): """A context that ignores specific qMessages for all bindings Args: msgs: list of message strings to ignore """ from Qt import QtCompat def messageOutputHandler(msgType, logContext, msg): if msg in msgs: return sys.stderr.write("{0}\n".format(msg)) QtCompat.qInstallMessageHandler(messageOutputHandler) try: yield finally: QtCompat.qInstallMessageHandler(None) def test_environment(): """Tests require all bindings to be installed (except PySide on py3.5+)""" if sys.version_info <= (3, 4): # PySide is not available for Python > 3.4 imp.find_module("PySide") imp.find_module("PySide2") imp.find_module("PyQt4") imp.find_module("PyQt5") def test_load_ui_returntype(): """load_ui returns an instance of QObject""" import sys from Qt import QtWidgets, QtCore, QtCompat app = QtWidgets.QApplication(sys.argv) obj = QtCompat.loadUi(self.ui_qwidget) assert isinstance(obj, QtCore.QObject) app.exit() def test_load_ui_baseinstance(): """Tests to see if the baseinstance loading loads a QWidget on properly""" import sys from Qt import QtWidgets, QtCompat app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QWidget() QtCompat.loadUi(self.ui_qwidget, win) assert hasattr(win, 'lineEdit'), "loadUi could not load instance to win" app.exit() def test_load_ui_signals(): """Tests to see if the baseinstance connects signals properly""" import sys from Qt import QtWidgets, QtCompat app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QWidget() QtCompat.loadUi(self.ui_qwidget, win) win.lineEdit.setText('Hello') assert str(win.label.text()) == 'Hello', "lineEdit signal did not fire" app.exit() def test_load_ui_mainwindow(): """Tests to see if the baseinstance loading loads a QMainWindow properly""" import sys from Qt import QtWidgets, QtCompat app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QMainWindow() QtCompat.loadUi(self.ui_qmainwindow, win) assert hasattr(win, 'lineEdit'), \ "loadUi could not load instance to main window" app.exit() def test_load_ui_dialog(): """Tests to see if the baseinstance loading loads a QDialog properly""" import sys from Qt import QtWidgets, QtCompat app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QDialog() QtCompat.loadUi(self.ui_qdialog, win) assert hasattr(win, 'lineEdit'), \ "loadUi could not load instance to main window" app.exit() def test_load_ui_dockwidget(): """Tests to see if the baseinstance loading loads a QDockWidget properly""" import sys from Qt import QtWidgets, QtCompat app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QDockWidget() QtCompat.loadUi(self.ui_qdockwidget, win) assert hasattr(win, 'lineEdit'), \ "loadUi could not load instance to main window" app.exit() def test_load_ui_customwidget(): """Tests to see if loadUi loads a custom widget properly""" import sys from Qt import QtWidgets, QtCompat app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QMainWindow() QtCompat.loadUi(self.ui_qcustomwidget, win) # Ensure that the derived class was properly created # and not the base class (in case of failure) custom_class_name = getattr(win, "customwidget", None).__class__.__name__ excepted_class_name = CustomWidget(win).__class__.__name__ assert custom_class_name == excepted_class_name, \ "loadUi could not load custom widget to main window" app.exit() def test_load_ui_invalidpath(): """Tests to see if loadUi successfully fails on invalid paths""" import sys from Qt import QtWidgets, QtCompat app = QtWidgets.QApplication(sys.argv) assert_raises(IOError, QtCompat.loadUi, 'made/up/path') app.exit() def test_load_ui_invalidxml(): """Tests to see if loadUi successfully fails on invalid ui files""" import sys invalid_xml = os.path.join(self.tempdir, "invalid.ui") with io.open(invalid_xml, "w", encoding="utf-8") as f: f.write(u""" <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0" garbage </ui> """) from xml.etree import ElementTree from Qt import QtWidgets, QtCompat app = QtWidgets.QApplication(sys.argv) assert_raises(ElementTree.ParseError, QtCompat.loadUi, invalid_xml) app.exit() def test_load_ui_existingLayoutOnDialog(): """Tests to see if loading a ui onto a layout in a Dialog works""" import sys from Qt import QtWidgets, QtCompat msgs = 'QLayout: Attempting to add QLayout "" to QDialog ' \ '"Dialog", which already has a layout' with ignoreQtMessageHandler([msgs]): app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QDialog() QtWidgets.QComboBox(win) QtWidgets.QHBoxLayout(win) QtCompat.loadUi(self.ui_qdialog, win) app.exit() def test_load_ui_existingLayoutOnMainWindow(): """Tests to see if loading a ui onto a layout in a MainWindow works""" import sys from Qt import QtWidgets, QtCompat msgs = 'QLayout: Attempting to add QLayout "" to QMainWindow ' \ '"", which already has a layout' with ignoreQtMessageHandler([msgs]): app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QMainWindow() QtWidgets.QComboBox(win) QtWidgets.QHBoxLayout(win) QtCompat.loadUi(self.ui_qmainwindow, win) app.exit() def test_load_ui_existingLayoutOnDockWidget(): """Tests to see if loading a ui onto a layout in a DockWidget works""" import sys from Qt import QtWidgets, QtCompat msgs = 'QLayout: Attempting to add QLayout "" to QDockWidget ' \ '"", which already has a layout' with ignoreQtMessageHandler([msgs]): app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QDockWidget() QtWidgets.QComboBox(win) QtWidgets.QHBoxLayout(win) QtCompat.loadUi(self.ui_qdockwidget, win) app.exit() def test_load_ui_existingLayoutOnWidget(): """Tests to see if loading a ui onto a layout in a Widget works""" import sys from Qt import QtWidgets, QtCompat msgs = 'QLayout: Attempting to add QLayout "" to QWidget ' \ '"Form", which already has a layout' with ignoreQtMessageHandler([msgs]): app = QtWidgets.QApplication(sys.argv) win = QtWidgets.QWidget() QtWidgets.QComboBox(win) QtWidgets.QHBoxLayout(win) QtCompat.loadUi(self.ui_qwidget, win) app.exit() def test_preferred_none(): """Preferring None shouldn't import anything""" os.environ["QT_PREFERRED_BINDING"] = "None" import Qt assert Qt.__name__ == "Qt", Qt def test_vendoring(): """Qt.py may be bundled along with another library/project Create toy project from project.vendor import Qt # Absolute from .vendor import Qt # Relative project/ vendor/ __init__.py __init__.py """ project = os.path.join(self.tempdir, "myproject") vendor = os.path.join(project, "vendor") os.makedirs(vendor) # Make packages out of folders with open(os.path.join(project, "__init__.py"), "w") as f: f.write("from .vendor.Qt import QtWidgets") with open(os.path.join(vendor, "__init__.py"), "w") as f: f.write("\n") # Copy real Qt.py into myproject shutil.copy(os.path.join(os.path.dirname(__file__), "Qt.py"), os.path.join(vendor, "Qt.py")) # Copy real Qt.py into the root folder shutil.copy(os.path.join(os.path.dirname(__file__), "Qt.py"), os.path.join(self.tempdir, "Qt.py")) print("Testing relative import..") assert subprocess.call( [sys.executable, "-c", "import myproject"], cwd=self.tempdir, stdout=subprocess.PIPE, # With nose process isolation, buffer can stderr=subprocess.STDOUT, # easily get full and throw an error. ) == 0 print("Testing absolute import..") assert subprocess.call( [sys.executable, "-c", "from myproject.vendor.Qt import QtWidgets"], cwd=self.tempdir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) == 0 print("Testing direct import..") assert subprocess.call( [sys.executable, "-c", "import myproject.vendor.Qt"], cwd=self.tempdir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) == 0 # # Test invalid json data # env = os.environ.copy() env["QT_PREFERRED_BINDING_JSON"] = '{"Qt":["PyQt5","PyQt4"],}' cmd = "import myproject.vendor.Qt;" cmd += "import Qt;" cmd += "assert myproject.vendor.Qt.__binding__ != 'None', 'vendor';" cmd += "assert Qt.__binding__ != 'None', 'Qt';" popen = subprocess.Popen( [sys.executable, "-c", cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.tempdir, env=env ) out, err = popen.communicate() if popen.returncode != 0: print(out) msg = "An exception was raised" assert popen.returncode == 0, msg error_check = b"Qt.py [warning]:" assert err.startswith(error_check), err print('out------------------') print(out) print('err ------------------') print(err) # Check QT_PREFERRED_BINDING_JSON works as expected print("Testing QT_PREFERRED_BINDING_JSON is respected..") cmd = "import myproject.vendor.Qt;" # Check that the "None" binding was set for `import myproject.vendor.Qt` cmd += "assert myproject.vendor.Qt.__binding__ == 'None', 'vendor';" cmd += "import Qt;" # Check that the "None" binding was not set for `import Qt`. # This should be PyQt5 or PyQt4 depending on the test environment. cmd += "assert Qt.__binding__ != 'None', 'Qt'" # If the module name is "Qt" use PyQt5 or PyQt4, otherwise use None binding env = os.environ.copy() env["QT_PREFERRED_BINDING_JSON"] = json.dumps( { "Qt": ["PyQt5", "PyQt4"], "default": ["None"] } ) assert subprocess.call( [sys.executable, "-c", cmd], stdout=subprocess.PIPE, cwd=self.tempdir, env=env ) == 0 print("Testing QT_PREFERRED_BINDING_JSON and QT_PREFERRED_BINDING work..") env["QT_PREFERRED_BINDING_JSON"] = '{"Qt":["PyQt5","PyQt4"]}' env["QT_PREFERRED_BINDING"] = "None" assert subprocess.call( [sys.executable, "-c", cmd], stdout=subprocess.PIPE, cwd=self.tempdir, env=env ) == 0 def test_convert_simple(): """python -m Qt --convert works in general""" before = """\ from PySide2 import QtCore, QtGui, QtWidgets class Ui_uic(object): def setupUi(self, uic): self.retranslateUi(uic) def retranslateUi(self, uic): self.pushButton_2.setText( QtWidgets.QApplication.translate("uic", "NOT Ok", None, -1)) """.split("\n") after = """\ from Qt import QtCompat, QtCore, QtGui, QtWidgets class Ui_uic(object): def setupUi(self, uic): self.retranslateUi(uic) def retranslateUi(self, uic): self.pushButton_2.setText( QtCompat.translate("uic", "NOT Ok", None, -1)) """.split("\n") from Qt import QtCompat assert QtCompat._convert(before) == after, after def test_convert_idempotency(): """Converting a converted file produces an identical file""" before = """\ from PySide2 import QtCore, QtGui, QtWidgets class Ui_uic(object): def setupUi(self, uic): self.retranslateUi(uic) def retranslateUi(self, uic): self.pushButton_2.setText( QtWidgets.QApplication.translate("uic", "NOT Ok", None, -1)) """ after = """\ from Qt import QtCompat, QtCore, QtGui, QtWidgets class Ui_uic(object): def setupUi(self, uic): self.retranslateUi(uic) def retranslateUi(self, uic): self.pushButton_2.setText( QtCompat.translate("uic", "NOT Ok", None, -1)) """ fname = os.path.join(self.tempdir, "idempotency.py") with open(fname, "w") as f: f.write(before) from Qt import QtCompat os.chdir(self.tempdir) QtCompat._cli(args=["--convert", "idempotency.py"]) with open(fname) as f: assert f.read() == after QtCompat._cli(args=["--convert", "idempotency.py"]) with open(fname) as f: assert f.read() == after def test_convert_backup(): """Converting produces a backup""" fname = os.path.join(self.tempdir, "idempotency.py") with open(fname, "w") as f: f.write("") from Qt import QtCompat os.chdir(self.tempdir) QtCompat._cli(args=["--convert", "idempotency.py"]) assert os.path.exists( os.path.join(self.tempdir, "%s_backup%s" % os.path.splitext(fname)) ) def test_import_from_qtwidgets(): """Fix #133, `from Qt.QtWidgets import XXX` works""" from Qt.QtWidgets import QPushButton assert QPushButton.__name__ == "QPushButton", QPushButton def test_import_from_qtcompat(): """ `from Qt.QtCompat import XXX` works """ from Qt.QtCompat import loadUi assert loadUi.__name__ == "_loadUi", loadUi def test_i158_qtcore_direct_import(): """import Qt.QtCore works on all bindings This addresses issue #158 """ import Qt.QtCore assert hasattr(Qt.QtCore, "Signal") def test_translate_arguments(): """Arguments of QtCompat.translate are correct QtCompat.translate is a shim over the PySide, PyQt4 and PyQt5 equivalent with an interface like the one found in PySide2. Reference: https://doc.qt.io/qt-5/qcoreapplication.html#translate """ import Qt # This will run on each binding result = Qt.QtCompat.translate("CustomDialog", # context "Status", # sourceText None, # disambiguation -1) # n assert result == u'Status', result def test_binding_and_qt_version(): """Qt's __binding_version__ and __qt_version__ populated""" import Qt assert Qt.__binding_version__ != "0.0.0", ("Binding version was not " "populated") assert Qt.__qt_version__ != "0.0.0", ("Qt version was not populated") def test_binding_states(): """Tests to see if the Qt binding enum states are set properly""" import Qt assert Qt.IsPySide == binding("PySide") assert Qt.IsPySide2 == binding("PySide2") assert Qt.IsPyQt5 == binding("PyQt5") assert Qt.IsPyQt4 == binding("PyQt4") def test_qtcompat_base_class(): """Tests to ensure the QtCompat namespace object works as expected""" import sys import Qt from Qt import QtWidgets from Qt import QtCompat app = QtWidgets.QApplication(sys.argv) # suppress `local variable 'app' is assigned to but never used` app header = QtWidgets.QHeaderView(Qt.QtCore.Qt.Horizontal) # Spot check compatibility functions QtCompat.QHeaderView.setSectionsMovable(header, False) assert QtCompat.QHeaderView.sectionsMovable(header) is False QtCompat.QHeaderView.setSectionsMovable(header, True) assert QtCompat.QHeaderView.sectionsMovable(header) is True # Verify that the grab function actually generates a non-null image button = QtWidgets.QPushButton('TestImage') pixmap = QtCompat.QWidget.grab(button) assert not pixmap.isNull() def test_cli(): """Qt.py is available from the command-line""" env = os.environ.copy() env.pop("QT_VERBOSE") # Do not include debug messages popen = subprocess.Popen( [sys.executable, "Qt.py", "--help"], stdout=subprocess.PIPE, env=env ) out, err = popen.communicate() assert out.startswith(b"usage: Qt.py"), "\n%s" % out def test_membership(): """Common members of Qt.py exist in all bindings, excl exceptions""" import Qt common_members = Qt._common_members.copy() if os.environ.get('VFXPLATFORM') == '2017': # For CY2017, skip the following common_members['QtGui'].remove('QDesktopServices') common_members.pop('QtOpenGL', None) common_members.pop('QtMultimedia', None) missing = list() for module, members in common_members.items(): missing.extend( member for member in members if not hasattr(getattr(Qt, module), member) ) binding = Qt.__binding__ assert not missing, ( "Some members did not exist in {binding}\n{missing}".format( **locals()) ) def test_missing(): """Missing members of Qt.py have been defined with placeholders""" import Qt missing_members = Qt._missing_members.copy() missing = list() for module, members in missing_members.items(): mod = getattr(Qt, module) missing.extend( member for member in members if not hasattr(mod, member) or not isinstance(getattr(mod, member), Qt.MissingMember) ) binding = Qt.__binding__ assert not missing, ( "Some members did not exist in {binding} as " "a Qt.MissingMember type\n{missing}".format(**locals()) ) if sys.version_info <= (3, 4): # PySide is not available for Python > 3.4 # Shiboken(1) doesn't support Python 3.5 # https://github.com/PySide/shiboken-setup/issues/3 def test_wrapInstance(): """.wrapInstance and .getCppPointer is identical across all bindings""" from Qt import QtCompat, QtWidgets app = QtWidgets.QApplication(sys.argv) try: button = QtWidgets.QPushButton("Hello world") button.setObjectName("MySpecialButton") pointer = QtCompat.getCppPointer(button) widget = QtCompat.wrapInstance(long(pointer), QtWidgets.QWidget) assert isinstance(widget, QtWidgets.QWidget), widget assert widget.objectName() == button.objectName() # IMPORTANT: this differs across sip and shiboken. if binding("PySide") or binding("PySide2"): assert widget != button else: assert widget == button finally: app.exit() def test_implicit_wrapInstance(): """.wrapInstance doesn't need the `base` argument""" from Qt import QtCompat, QtWidgets app = QtWidgets.QApplication(sys.argv) try: button = QtWidgets.QPushButton("Hello world") button.setObjectName("MySpecialButton") pointer = QtCompat.getCppPointer(button) widget = QtCompat.wrapInstance(long(pointer)) assert isinstance(widget, QtWidgets.QWidget), widget assert widget.objectName() == button.objectName() if binding("PySide"): assert widget != button elif binding("PySide2") and _pyside2_commit_date() is None: assert widget != button elif binding("PySide2") and \ _pyside2_commit_date() <= datetime.datetime( 2017, 8, 25): assert widget == button else: assert widget == button finally: app.exit() def test_isValid(): """.isValid and .delete work in all bindings""" from Qt import QtCompat, QtCore obj = QtCore.QObject() assert QtCompat.isValid(obj) QtCompat.delete(obj) assert not QtCompat.isValid(obj) if binding("PyQt4"): def test_preferred_pyqt4(): """QT_PREFERRED_BINDING = PyQt4 properly forces the binding""" import Qt assert Qt.__binding__ == "PyQt4", ( "PyQt4 should have been picked, " "instead got %s" % Qt.__binding__) def test_sip_api_qtpy(): """Preferred binding PyQt4 should have sip version 2""" __import__("Qt") # Bypass linter warning import sip assert sip.getapi("QString") == 2, ( "PyQt4 API version should be 2, " "instead is %s" % sip.getapi("QString")) if PYTHON == 2: def test_sip_api_already_set(): """Raise ImportError with sip was set to 1 with no hint, default""" __import__("PyQt4.QtCore") # Bypass linter warning import sip sip.setapi("QString", 1) assert_raises(ImportError, __import__, "Qt") # A sip API hint of any kind bypasses ImportError # on account of it being merely a hint. def test_sip_api_1_1(): """sip=1, hint=1 == OK""" import sip sip.setapi("QString", 1) os.environ["QT_SIP_API_HINT"] = "1" __import__("Qt") # Bypass linter warning def test_sip_api_2_1(): """sip=2, hint=1 == WARNING""" import sip sip.setapi("QString", 2) os.environ["QT_SIP_API_HINT"] = "1" with captured_output() as out: __import__("Qt") # Bypass linter warning stdout, stderr = out assert stderr.getvalue().startswith("Warning:") def test_sip_api_1_2(): """sip=1, hint=2 == WARNING""" import sip sip.setapi("QString", 1) os.environ["QT_SIP_API_HINT"] = "2" with captured_output() as out: __import__("Qt") # Bypass linter warning stdout, stderr = out assert stderr.getvalue().startswith("Warning:") def test_sip_api_2_2(): """sip=2, hint=2 == OK""" import sip sip.setapi("QString", 2) os.environ["QT_SIP_API_HINT"] = "2" __import__("Qt") # Bypass linter warning if binding("PyQt5"): def test_preferred_pyqt5(): """QT_PREFERRED_BINDING = PyQt5 properly forces the binding""" import Qt assert Qt.__binding__ == "PyQt5", ( "PyQt5 should have been picked, " "instead got %s" % Qt.__binding__) if binding("PySide"): def test_preferred_pyside(): """QT_PREFERRED_BINDING = PySide properly forces the binding""" import Qt assert Qt.__binding__ == "PySide", ( "PySide should have been picked, " "instead got %s" % Qt.__binding__) if binding("PySide2"): def test_preferred_pyside2(): """QT_PREFERRED_BINDING = PySide2 properly forces the binding""" import Qt assert Qt.__binding__ == "PySide2", ( "PySide2 should have been picked, " "instead got %s" % Qt.__binding__) def test_coexistence(): """Qt.py may be use alongside the actual binding""" from Qt import QtCore import PySide2.QtGui # Qt remaps QStringListModel assert QtCore.QStringListModel # But does not delete the original assert PySide2.QtGui.QStringListModel if binding("PyQt4") or binding("PyQt5"): def test_multiple_preferred(): """QT_PREFERRED_BINDING = more than one binding excludes others""" # PySide is the more desirable binding os.environ["QT_PREFERRED_BINDING"] = os.pathsep.join( ["PyQt4", "PyQt5"]) import Qt assert Qt.__binding__ == "PyQt4", ( "PyQt4 should have been picked, " "instead got %s" % Qt.__binding__)