Is there a nice way to switch between 2 different packages providing the same APIs?

Mark m.n.summerfield at googlemail.com
Fri Jul 6 08:33:38 EDT 2018


On Friday, July 6, 2018 at 1:22:46 PM UTC+1, Bev in TX wrote:
> > On Jul 6, 2018, at 3:14 AM, Mark via Python-list <python-list at python.org> wrote:
> > 
> > In the end I changed to a completely different approach.
> > 
> > I now have two parallel directories, one with PySide-based code and the other with auto-generated PyQt-based code. And I created a tiny script to copy the PySide code to the PyQt directory & do the necessary changes. (I can post the script if anyone's interested.)
> 
> I am interested.
> > 
> > This means that there are no import hacks and no (manual) duplication, while still being easy to test using either binding library.
> > -- 
> > https://mail.python.org/mailman/listinfo/python-list
> 
> Bev in TX

OK, here're the details. Note that the paths are hard-coded (but could easily be configurable using argparse).

I assume: (1) the source dir is 'app'; (2) the dest dir is 'app-pyqt'; (3) there is a file called app/Uniform.py with this content:

#!/usr/bin/env python3
# Uniform.py
import PySide2.QtCore
from PySide2.shiboken2 import isValid as isObjectAlive
VERSION_STR = (f'PySide2 {PySide2.__version__} / '
               f'Qt {PySide2.QtCore.__version__}')


I then run pyside2pyqt.py in the dir *above* app and app-pyqt:

#!/usr/bin/env python3

import contextlib
import os
import shutil

SRC = 'app'
DST = 'app-pyqt'
UNIFORM_FILE = 'Uniform.py'

def main():
    with contextlib.suppress(FileNotFoundError):
        shutil.rmtree(DST)
    shutil.copytree(SRC, DST, ignore=shutil.ignore_patterns(
                    '__pycache__', UNIFORM_FILE))
    for root, _, files in os.walk(DST):
        for name in files:
            if name.endswith(('.py', '.pyw')):
                filename = os.path.join(root, name)
                with open(filename, 'rt', encoding='utf-8') as file:
                    text = file.read().replace('PySide2', 'PyQt5')
                with open(filename, 'wt', encoding='utf-8') as file:
                    for line in text.splitlines():
                        if 'pyside2pyqt: DELETE' not in line:
                            print(line, file=file)
    with open(os.path.join(DST, UNIFORM_FILE), 'wt',
              encoding='utf-8') as file:
        file.write(UNIFORM)

UNIFORM = '''#!/usr/bin/env python3
import PyQt5.QtCore
import PyQt5.sip
VERSION_STR = (f'PyQt5 {PyQt5.QtCore.PYQT_VERSION_STR} / '
               f'Qt {PyQt5.QtCore.QT_VERSION_STR}')
def isObjectAlive(obj):
    return not PyQt5.sip.isdeleted(obj)
'''

if __name__ == '__main__':
    main()

The reaons for being able to delete lines is that the APIs aren't identical. For example, PySide2's QStyleHints has a setCursorFlashTime() method; but PyQt5's doesn't. So for any lines you don't want copied just add the comment, e.g.:

                    style_hints = self.styleHints()   # pyside2pyqt: DELETE
                    style_hints.setCursorFlashTime(0) # pyside2pyqt: DELETE

I'm sure someone could come up with a more generalized script, but this is sufficient for my needs.


More information about the Python-list mailing list