Library versioning

Phil Thompson phil at riverbankcomputing.com
Tue Mar 7 11:44:35 GMT 2023


On 06/03/2023 17:35, Phil Thompson wrote:
> On 04/03/2023 09:52, Jean Abou Samra wrote:
>> Hi,
>> 
>> 
>> I'm a bit confused regarding how wheels of libraries linking to Qt5
>> should be built. Specifically, I am interested in
>> [python-poppler-qt5](https://github.com/frescobaldi/python-poppler-qt5).
>> The binding is built (with sip) on top of PyQt5, therefore I suppose
>> it must link into Qt5 and PyQt5 libraries. This means that if I advise
>> people to do "pip install python-poppler-qt5", it will download
>> python-poppler-qt5 wheels built for one exact version of PyQt5, and
>> other versions will not work due to ABI incompatibility. Right?
>> 
>> So, how should this be done? Should the project specify a requirement
>> "PyQt5 == specific-version"? Also, what ABI compatibility guarantees
>> are provided on PyQt5? (I know Qt5 is guaranteed ABI-compatible within
>> a minor release series.)
>> 
>> As advised on
>> [https://discuss.python.org/t/packaging-a-c-extension-with-a-dependency-on-another-c-extension/24462/2](https://discuss.python.org/t/packaging-a-c-extension-with-a-dependency-on-another-c-extension/24462/2),
>> I looked briefly at how QScintilla does this, but I'm a bit confused.
>> The project page says it's statically linked, yet on my system:
>> 
>> ```
>> $ ldd Qsci.abi3.so
>> ldd: attention : vous n'avez pas la permission d'exécution pour 
>> `./Qsci.abi3.so'
>> 	linux-vdso.so.1 (0x00007fff737e7000)
>> 	libQt5PrintSupport.so.5 => /lib64/libQt5PrintSupport.so.5 
>> (0x00007f941df7e000)
>> 	libQt5Widgets.so.5 => /lib64/libQt5Widgets.so.5 (0x00007f941d200000)
>> 	libQt5Gui.so.5 => /lib64/libQt5Gui.so.5 (0x00007f941ca00000)
>> 	libQt5Core.so.5 => /lib64/libQt5Core.so.5 (0x00007f941c400000)
>> 	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f941c000000)
>> 	libm.so.6 => /lib64/libm.so.6 (0x00007f941d920000)
>> 	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f941df5c000)
>> 	libc.so.6 => /lib64/libc.so.6 (0x00007f941be23000)
>> 	libGL.so.1 => /lib64/libGL.so.1 (0x00007f941d179000)
>> 	libpng16.so.16 => /lib64/libpng16.so.16 (0x00007f941df22000)
>> 	libz.so.1 => /lib64/libz.so.1 (0x00007f941df08000)
>> 	libharfbuzz.so.0 => /lib64/libharfbuzz.so.0 (0x00007f941d08d000)
>> 	libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007f941c323000)
>> 	libdouble-conversion.so.3 => /lib64/libdouble-conversion.so.3
>> (0x00007f941d078000)
>> 	libicui18n.so.71 => /lib64/libicui18n.so.71 (0x00007f941ba00000)
>> 	libicuuc.so.71 => /lib64/libicuuc.so.71 (0x00007f941b803000)
>> 	libpcre2-16.so.0 => /lib64/libpcre2-16.so.0 (0x00007f941c972000)
>> 	libzstd.so.1 => /lib64/libzstd.so.1 (0x00007f941c26e000)
>> 	libglib-2.0.so.0 => /lib64/libglib-2.0.so.0 (0x00007f941b6c2000)
>> 	/lib64/ld-linux-x86-64.so.2 (0x00007f941e01a000)
>> 	libGLX.so.0 => /lib64/libGLX.so.0 (0x00007f941d046000)
>> 	libX11.so.6 => /lib64/libX11.so.6 (0x00007f941b57b000)
>> 	libXext.so.6 => /lib64/libXext.so.6 (0x00007f941c259000)
>> 	libGLdispatch.so.0 => /lib64/libGLdispatch.so.0 (0x00007f941bd6a000)
>> 	libfreetype.so.6 => /lib64/libfreetype.so.6 (0x00007f941b4ad000)
>> 	libgraphite2.so.3 => /lib64/libgraphite2.so.3 (0x00007f941c237000)
>> 	libcap.so.2 => /lib64/libcap.so.2 (0x00007f941d916000)
>> 	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007f941bd36000)
>> 	liblz4.so.1 => /lib64/liblz4.so.1 (0x00007f941b48a000)
>> 	libicudata.so.71 => /lib64/libicudata.so.71 (0x00007f9419600000)
>> 	libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f941b3ed000)
>> 	libxcb.so.1 => /lib64/libxcb.so.1 (0x00007f941b3c2000)
>> 	libbz2.so.1 => /lib64/libbz2.so.1 (0x00007f941b3af000)
>> 	libbrotlidec.so.1 => /lib64/libbrotlidec.so.1 (0x00007f941b3a2000)
>> 	libXau.so.6 => /lib64/libXau.so.6 (0x00007f941d910000)
>> 	libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 
>> (0x00007f941b37f000)
>> ```
>> 
>> which looks like it links dynamically to several Qt5 libraries, though
>> not to PyQt5's `*.abi3.so` libraries (is that what is meant by
>> "statically linked"?).
>> 
>> If I try to import it, I get
>> 
>> ```
>> $ python
>> Python 3.11.2 (main, Feb  8 2023, 00:00:00) [GCC 12.2.1 20221121 (Red
>> Hat 12.2.1-4)] on linux
>> Type "help", "copyright", "credits" or "license" for more information.
>>>>> import PyQt5.Qsci
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>> ImportError:
>> /home/jean/.local/lib/python3.11/site-packages/PyQt5/Qsci.abi3.so:
>> undefined symbol: _ZdlPvm, version Qt_5
>> ```
>> 
>> because it's linking to Qt5 libs provided by my system. It works if I
>> install qscintilla in a fresh venv, because pip then installs PyQt5 in
>> the venv, with the latest PyPI version instead of my distro version.
>> But if I downgrade qscintilla, it fails again, which I interpret as
>> qscintilla really being built for one PyQt5 version and not being
>> compatible with the next one. Yet, it seems to use "PyQt5 >= x.y"
>> requirements, not "PyQt5 == x.y". Why?
>> 
>> As you can see, I'm a beginner to this sort of stuff. If the questions
>> seem dumb to you, I appreciate pointers to relevant resources.
>> 
>> Thanks,
>> 
>> Jean
> 
> With the current SIP implementation PyQt will guarantee ABI
> compatibility for the same minor version number so in your case the
> correct 'Requires-Dist' is "PyQt5 (>=5.15, <5.16)" to avoid potential
> crashes.
> 
> The current PyQt sub-projects (Charts etc) don't do this and it is a
> bug. However the pattern of releases and the way users upgrade means
> this is rarely a problem (but I will fix it).
> 
> I am considering changing the SIP implementation so that it is the API
> rather than the ABI that matters.

Sorry, I was having a mad moment. I fixed this issue a long time ago. 
What matters is the PyQt API, ie. the parts of the Qt API that a 
particular version of PyQt wraps and the C++ API PyQt provides for other 
bindings...

https://www.riverbankcomputing.com/static/Docs/PyQt5/extension_api.html#c-api
https://www.riverbankcomputing.com/static/Docs/PyQt6/extension_api.html#c-api

So it depends on what Qt classes your library uses. If it only uses 
classes that have always been present in Qt5 (or Qt6) then you don't 
need to specify any version number.

Phil


More information about the PyQt mailing list