Skip to content

Commit d57b2fa

Browse files
committedFeb 9, 2016
add 2to3 script to migrate PyQt4 to PyQt wrappers
1 parent 964ae1f commit d57b2fa

File tree

2 files changed

+508
-0
lines changed

2 files changed

+508
-0
lines changed
 

‎scripts/2to3

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/python
2+
import sys, os
3+
4+
from lib2to3.main import main
5+
6+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__))))
7+
sys.exit(main('qgis_fixes'))

‎scripts/qgis_fixes/fix_pyqt.py

Lines changed: 501 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,501 @@
1+
"""Migrate imports of PyQt4 to PyQt wrapper
2+
"""
3+
# Author: Juergen E. Fischer
4+
# Adapted from fix_urllib
5+
6+
# Local imports
7+
from lib2to3.fixes.fix_imports import alternates, FixImports
8+
from lib2to3 import fixer_base
9+
from lib2to3.fixer_util import (Name, Comma, FromImport, Newline,
10+
find_indentation, Node, syms)
11+
12+
MAPPING = {
13+
"PyQt4.QtGui": [
14+
("PyQt.QtGui", [
15+
"QIcon",
16+
"QCursor",
17+
"QColor",
18+
"QDesktopServices",
19+
"QFont",
20+
"QFontMetrics",
21+
"QKeySequence",
22+
"QStandardItemModel",
23+
"QStandardItem",
24+
"QClipboard",
25+
"QPixmap",
26+
"QDoubleValidator",
27+
"QPainter",
28+
"QPen",
29+
"QBrush",
30+
"QPalette",
31+
"QPainterPath",
32+
"QImage",
33+
"QPolygonF",
34+
"QFontMetricsF",
35+
"QGradient",
36+
]),
37+
("PyQt.QtWidgets", [
38+
"QAbstractButton",
39+
"QAbstractGraphicsShapeItem",
40+
"QAbstractItemDelegate",
41+
"QAbstractItemView",
42+
"QAbstractScrollArea",
43+
"QAbstractSlider",
44+
"QAbstractSpinBox",
45+
"QAbstractTableModel",
46+
"QAction",
47+
"QActionGroup",
48+
"QApplication",
49+
"QBoxLayout",
50+
"QButtonGroup",
51+
"QCalendarWidget",
52+
"QCheckBox",
53+
"QColorDialog",
54+
"QColumnView",
55+
"QComboBox",
56+
"QCommandLinkButton",
57+
"QCommonStyle",
58+
"QCompleter",
59+
"QDataWidgetMapper",
60+
"QDateEdit",
61+
"QDateTimeEdit",
62+
"QDesktopWidget",
63+
"QDial",
64+
"QDialog",
65+
"QDialogButtonBox",
66+
"QDirModel",
67+
"QDockWidget",
68+
"QDoubleSpinBox",
69+
"QErrorMessage",
70+
"QFileDialog",
71+
"QFileIconProvider",
72+
"QFileSystemModel",
73+
"QFocusFrame",
74+
"QFontComboBox",
75+
"QFontDialog",
76+
"QFormLayout",
77+
"QFrame",
78+
"QGesture",
79+
"QGestureEvent",
80+
"QGestureRecognizer",
81+
"QGraphicsAnchor",
82+
"QGraphicsAnchorLayout",
83+
"QGraphicsBlurEffect",
84+
"QGraphicsColorizeEffect",
85+
"QGraphicsDropShadowEffect",
86+
"QGraphicsEffect",
87+
"QGraphicsEllipseItem",
88+
"QGraphicsGridLayout",
89+
"QGraphicsItem",
90+
"QGraphicsItemGroup",
91+
"QGraphicsLayout",
92+
"QGraphicsLayoutItem",
93+
"QGraphicsLineItem",
94+
"QGraphicsLinearLayout",
95+
"QGraphicsObject",
96+
"QGraphicsOpacityEffect",
97+
"QGraphicsPathItem",
98+
"QGraphicsPixmapItem",
99+
"QGraphicsPolygonItem",
100+
"QGraphicsProxyWidget",
101+
"QGraphicsRectItem",
102+
"QGraphicsRotation",
103+
"QGraphicsScale",
104+
"QGraphicsScene",
105+
"QGraphicsSceneContextMenuEvent",
106+
"QGraphicsSceneDragDropEvent",
107+
"QGraphicsSceneEvent",
108+
"QGraphicsSceneHelpEvent",
109+
"QGraphicsSceneHoverEvent",
110+
"QGraphicsSceneMouseEvent",
111+
"QGraphicsSceneMoveEvent",
112+
"QGraphicsSceneResizeEvent",
113+
"QGraphicsSceneWheelEvent",
114+
"QGraphicsSimpleTextItem",
115+
"QGraphicsTextItem",
116+
"QGraphicsTransform",
117+
"QGraphicsView",
118+
"QGraphicsWidget",
119+
"QGridLayout",
120+
"QGroupBox",
121+
"QHBoxLayout",
122+
"QHeaderView",
123+
"QInputDialog",
124+
"QItemDelegate",
125+
"QItemEditorCreatorBase",
126+
"QItemEditorFactory",
127+
"QKeyEventTransition",
128+
"QLCDNumber",
129+
"QLabel",
130+
"QLayout",
131+
"QLayoutItem",
132+
"QLineEdit",
133+
"QListView",
134+
"QListWidget",
135+
"QListWidgetItem",
136+
"QMainWindow",
137+
"QMdiArea",
138+
"QMdiSubWindow",
139+
"QMenu",
140+
"QMenuBar",
141+
"QMessageBox",
142+
"QMouseEventTransition",
143+
"QPanGesture",
144+
"QPinchGesture",
145+
"QPlainTextDocumentLayout",
146+
"QPlainTextEdit",
147+
"QProgressBar",
148+
"QProgressDialog",
149+
"QPushButton",
150+
"QRadioButton",
151+
"QRubberBand",
152+
"QScrollArea",
153+
"QScrollBar",
154+
"QShortcut",
155+
"QSizeGrip",
156+
"QSizePolicy",
157+
"QSlider",
158+
"QSpacerItem",
159+
"QSpinBox",
160+
"QSplashScreen",
161+
"QSplitter",
162+
"QSplitterHandle",
163+
"QStackedLayout",
164+
"QStackedWidget",
165+
"QStatusBar",
166+
"QStyle",
167+
"QStyleFactory",
168+
"QStyleHintReturn",
169+
"QStyleHintReturnMask",
170+
"QStyleHintReturnVariant",
171+
"QStyleOption",
172+
"QStyleOptionButton",
173+
"QStyleOptionComboBox",
174+
"QStyleOptionComplex",
175+
"QStyleOptionDockWidget",
176+
"QStyleOptionFocusRect",
177+
"QStyleOptionFrame",
178+
"QStyleOptionGraphicsItem",
179+
"QStyleOptionGroupBox",
180+
"QStyleOptionHeader",
181+
"QStyleOptionMenuItem",
182+
"QStyleOptionProgressBar",
183+
"QStyleOptionRubberBand",
184+
"QStyleOptionSizeGrip",
185+
"QStyleOptionSlider",
186+
"QStyleOptionSpinBox",
187+
"QStyleOptionTab",
188+
"QStyleOptionTabBarBase",
189+
"QStyleOptionTabWidgetFrame",
190+
"QStyleOptionTitleBar",
191+
"QStyleOptionToolBar",
192+
"QStyleOptionToolBox",
193+
"QStyleOptionToolButton",
194+
"QStyleOptionViewItem",
195+
"QStylePainter",
196+
"QStyledItemDelegate",
197+
"QSwipeGesture",
198+
"QSystemTrayIcon",
199+
"QTabBar",
200+
"QTabWidget",
201+
"QTableView",
202+
"QTableWidget",
203+
"QTableWidgetItem",
204+
"QTableWidgetSelectionRange",
205+
"QTapAndHoldGesture",
206+
"QTapGesture",
207+
"QTextBrowser",
208+
"QTextEdit",
209+
"QTimeEdit",
210+
"QToolBar",
211+
"QToolBox",
212+
"QToolButton",
213+
"QToolTip",
214+
"QTreeView",
215+
"QTreeWidget",
216+
"QTreeWidgetItem",
217+
"QTreeWidgetItemIterator",
218+
"QUndoCommand",
219+
"QUndoGroup",
220+
"QUndoStack",
221+
"QUndoView",
222+
"QVBoxLayout",
223+
"QWhatsThis",
224+
"QWidget",
225+
"QWidgetAction",
226+
"QWidgetItem",
227+
"QWizard",
228+
"QWizardPage",
229+
"qApp",
230+
"qDrawBorderPixmap",
231+
"qDrawPlainRect",
232+
"qDrawShadeLine",
233+
"qDrawShadePanel",
234+
"qDrawShadeRect",
235+
"qDrawWinButton",
236+
"qDrawWinPanel",
237+
]),
238+
("PyQt.QtPrintSupport", [
239+
"QPrinter",
240+
"QAbstractPrintDialog",
241+
"QPageSetupDialog",
242+
"QPrintDialog",
243+
"QPrintEngine",
244+
"QPrintPreviewDialog",
245+
"QPrintPreviewWidget",
246+
"QPrinterInfo",
247+
]),
248+
("PyQt.QtCore", [
249+
"QItemSelectionModel",
250+
"QSortFilterProxyModel",
251+
]),
252+
],
253+
"PyQt4.QtCore": [
254+
("PyQt.QtCore", [
255+
"QAbstractItemModel",
256+
"QAbstractTableModel",
257+
"QByteArray",
258+
"QCoreApplication",
259+
"QDataStream",
260+
"QDir",
261+
"QEvent",
262+
"QFile",
263+
"QFileInfo",
264+
"QIODevice",
265+
"QLocale",
266+
"QMimeData",
267+
"QModelIndex",
268+
"QMutex",
269+
"QObject",
270+
"QProcess",
271+
"QSettings",
272+
"QSize",
273+
"QSizeF",
274+
"QTextCodec",
275+
"QThread",
276+
"QThreadPool",
277+
"QTimer",
278+
"QTranslator",
279+
"QUrl",
280+
"Qt",
281+
"pyqtProperty",
282+
"pyqtWrapperType",
283+
"pyqtSignal",
284+
"qDebug",
285+
"qWarning",
286+
"QDate",
287+
"QTime",
288+
"QDateTime",
289+
"QRegExp",
290+
"QTemporaryFile",
291+
"QTextStream",
292+
"QVariant",
293+
"QPyNullVariant",
294+
"QRect",
295+
"QRectF",
296+
"QMetaObject",
297+
"QPoint",
298+
"QPointF",
299+
"QDirIterator",
300+
"SIGNAL",
301+
"SLOT",
302+
]),
303+
],
304+
"PyQt4.QtNetwork": [
305+
( "PyQt.QtNetwork", ["QNetworkReply","QNetworkRequest"] )
306+
],
307+
"PyQt4.QtXml": [
308+
( "PyQt.QtXml", [
309+
"QDomDocument"
310+
]),
311+
],
312+
"PyQt4.QtSci": [
313+
( "PyQt.QtSci", [
314+
"QsciAPIs",
315+
"QsciLexerCustom",
316+
"QsciLexerPython",
317+
"QsciScintilla",
318+
"QsciLexerSQL",
319+
]),
320+
],
321+
"PyQt4.QtWebkit": [
322+
( "PyQt.QtWebkitWidgets", [
323+
"QGraphicsWebView",
324+
"QWebFrame",
325+
"QWebHitTestResult",
326+
"QWebInspector",
327+
"QWebPage",
328+
"QWebView",
329+
]),
330+
],
331+
}
332+
333+
334+
def build_pattern():
335+
bare = set()
336+
for old_module, changes in MAPPING.items():
337+
for change in changes:
338+
new_module, members = change
339+
members = alternates(members)
340+
yield """import_name< 'import' (module=%r
341+
| dotted_as_names< any* module=%r any* >) >
342+
""" % (old_module, old_module)
343+
if '.' not in old_module:
344+
yield """import_from< 'from' mod_member=%r 'import'
345+
( member=%s | import_as_name< member=%s 'as' any > |
346+
import_as_names< members=any* >) >
347+
""" % (old_module, members, members)
348+
else:
349+
dotted = old_module.split('.')
350+
assert len(dotted)==2
351+
yield """import_from< 'from' mod_member=dotted_name<%r '.' %r> 'import'
352+
( member=%s | import_as_name< member=%s 'as' any > |
353+
import_as_names< members=any* >) >
354+
""" % ( dotted[0], dotted[1], members, members)
355+
yield """import_from< 'from' module_star=%r 'import' star='*' >
356+
""" % old_module
357+
yield """import_name< 'import'
358+
dotted_as_name< module_as=%r 'as' any > >
359+
""" % old_module
360+
# bare_with_attr has a special significance for FixImports.match().
361+
yield """power< bare_with_attr=%r trailer< '.' member=%s > any* >
362+
""" % (old_module, members)
363+
364+
365+
class FixPyqt(FixImports):
366+
367+
def build_pattern(self):
368+
return "|".join(build_pattern())
369+
370+
def transform_import(self, node, results):
371+
"""Transform for the basic import case. Replaces the old
372+
import name with a comma separated list of its
373+
replacements.
374+
"""
375+
import_mod = results.get("module")
376+
pref = import_mod.prefix
377+
378+
names = []
379+
380+
# create a Node list of the replacement modules
381+
for name in MAPPING[import_mod.value][:-1]:
382+
names.extend([Name(name[0], prefix=pref), Comma()])
383+
names.append(Name(MAPPING[import_mod.value][-1][0], prefix=pref))
384+
import_mod.replace(names)
385+
386+
def transform_member(self, node, results):
387+
"""Transform for imports of specific module elements. Replaces
388+
the module to be imported from with the appropriate new
389+
module.
390+
"""
391+
mod_member = results.get("mod_member")
392+
if isinstance(mod_member,Node):
393+
module = ""
394+
for l in mod_member.leaves():
395+
module += l.value
396+
mod_member.value = module
397+
pref = mod_member.prefix
398+
member = results.get("member")
399+
400+
# Simple case with only a single member being imported
401+
if member:
402+
# this may be a list of length one, or just a node
403+
if isinstance(member, list):
404+
member = member[0]
405+
new_name = None
406+
for change in MAPPING[mod_member.value]:
407+
if member.value in change[1]:
408+
new_name = change[0]
409+
break
410+
if new_name:
411+
mod_member.replace(Name(new_name, prefix=pref))
412+
else:
413+
self.cannot_convert(node, "This is an invalid module element")
414+
415+
# Multiple members being imported
416+
else:
417+
# a dictionary for replacements, order matters
418+
modules = []
419+
mod_dict = {}
420+
members = results["members"]
421+
for member in members:
422+
# we only care about the actual members
423+
if member.type == syms.import_as_name:
424+
as_name = member.children[2].value
425+
member_name = member.children[0].value
426+
else:
427+
member_name = member.value
428+
as_name = None
429+
if member_name != u",":
430+
found = False
431+
for change in MAPPING[mod_member.value]:
432+
if member_name in change[1]:
433+
if change[0] not in mod_dict:
434+
modules.append(change[0])
435+
mod_dict.setdefault(change[0], []).append(member)
436+
found = True
437+
if not found:
438+
f = open( "/tmp/missing", "a+" )
439+
f.write ( "member %s of %s not found\n" % (member_name, mod_member.value) )
440+
f.close()
441+
442+
new_nodes = []
443+
indentation = find_indentation(node)
444+
first = True
445+
def handle_name(name, prefix):
446+
if name.type == syms.import_as_name:
447+
kids = [Name(name.children[0].value, prefix=prefix),
448+
name.children[1].clone(),
449+
name.children[2].clone()]
450+
return [Node(syms.import_as_name, kids)]
451+
return [Name(name.value, prefix=prefix)]
452+
for module in modules:
453+
elts = mod_dict[module]
454+
names = []
455+
for elt in elts[:-1]:
456+
names.extend(handle_name(elt, pref))
457+
names.append(Comma())
458+
names.extend(handle_name(elts[-1], pref))
459+
new = FromImport(module, names)
460+
if not first or node.parent.prefix.endswith(indentation):
461+
new.prefix = indentation
462+
new_nodes.append(new)
463+
first = False
464+
if new_nodes:
465+
nodes = []
466+
for new_node in new_nodes[:-1]:
467+
nodes.extend([new_node, Newline()])
468+
nodes.append(new_nodes[-1])
469+
node.replace(nodes)
470+
else:
471+
self.cannot_convert(node, "All module elements are invalid")
472+
473+
def transform_dot(self, node, results):
474+
"""Transform for calls to module members in code."""
475+
module_dot = results.get("bare_with_attr")
476+
member = results.get("member")
477+
new_name = None
478+
if isinstance(member, list):
479+
member = member[0]
480+
for change in MAPPING[module_dot.value]:
481+
if member.value in change[1]:
482+
new_name = change[0]
483+
break
484+
if new_name:
485+
module_dot.replace(Name(new_name,
486+
prefix=module_dot.prefix))
487+
else:
488+
self.cannot_convert(node, "This is an invalid module element")
489+
490+
def transform(self, node, results):
491+
if results.get("module"):
492+
self.transform_import(node, results)
493+
elif results.get("mod_member"):
494+
self.transform_member(node, results)
495+
elif results.get("bare_with_attr"):
496+
self.transform_dot(node, results)
497+
# Renaming and star imports are not supported for these modules.
498+
elif results.get("module_star"):
499+
self.cannot_convert(node, "Cannot handle star imports.")
500+
elif results.get("module_as"):
501+
self.cannot_convert(node, "This module is now multiple modules")

0 commit comments

Comments
 (0)
Please sign in to comment.