I have written a utility library in Python that works with the Qt framework. My code is pure Python and is compatible with both PyQt5 and PySide2. My main module could either be run on its own from the command line with python -m or it could be imported into another project. Is there a clean way to specify that the project needs either PyQt5 or PySide2 in its a wheel distribution?
Here is what I have found in my research but I am asking in case there is a better way to package the project than these options:
I could add logic to setup.py in a source distribution of the project to check for PyQt5 and PySide2. However, wheels are recommended way to distribute Python projects, and from what I can tell this kind of install-time logic is not possible with wheels. Alternatively, I could not specify either PySide2 or PyQt5 as dependencies and recommend in the install instructions that one of them be installed together with my project.
Use extras_require:
setup(
…
extras_require={
'pyqt5': ['PyQt5'],
'pyside2': ['PySide2'],
},
)
and teach your users to run either
pip install 'yourpackage[pyqt5]'
or
pip install 'yourpackage[pyside2]'
If you don't want to make either a strict requirement (which makes sense), I'd just throw a runtime error if neither is available.
For example
try:
import PyQt5 as some_common_name
except ImportError:
try:
import PySide2 as some_common_name
except ImportError:
raise ImportError('Please install either PyQt5 or PySide2') from None
My particular case is somewhat niche (so I am not accepting this as the answer). I realized the package was really doing two things: acting as a library and as a command line tool. I decided to split it into two packages: package and package-cli. package does not explicitly depend on PyQt5 or PySide2 but specifies that one of them must be installed in the documentation. Since package is a library, it is intended to be integrated into another project where it is easy to list package and PyQt5 together in the requirements.txt. For package-cli, I just choose one of PyQt5 or PySide2 to be the explicit dependency. package-cli depends on package and PyQt5 and just adds a console_script to call the main module in package.
Related
My code requires Python version of 3.6 or higher.
To ensure this, I use the following:
import sys
version = sys.version_info
assert version.major > 3 or (version.major == 3 and version.minor >= 6)
But this doesn't seem like the best way to do this (from a good coding practices viewpoint). Is there a better way?
What is the appropriate way to make sure your script is being run on an appropriate version of Python?
This information should be specified in your packaging information, so users are informed of the python version requirement before they ever install your python package.
Your project should have a setup.py script that is used to install your package. You can specify the python dependency in that setup script
setup(
name='my_package',
version='1.0.0',
python_requires='>=3.6.0'
...
)
This way, it won't allow anyone to build, download, or install your package to an incompatible python version.
I have several different qt packages installed for my development environment on Ubuntu 15.04 (3.19.0-51-generic), and I can't seem to get my imports correct. The error I am running into in PyCharm is:
from PyQt5.QtWebEngineWidgets import QWebEnginePage
ImportError: No module named 'PyQt5.QtWebEngineWidgets'
The following packages I have installed already, which I believe to be relevant are as follows:
python3-pyqt5
python3-pyqt5-dbg
python-pyqt5
python-pyqt5-dbg
pyqt5-dev-tools
pyqt5-dev
python-pyqt5.qtwebkit
python3-pyqt5.qtwebkit
python-pyqt5.qtwebkit-dbg
python3-pyqt5.qtwebkit-dbg
What am I missing here? Note that the project explicitly requires the following in its README:
Qt 5.5+
PyQt5.6+
Furthermore, I understand that webengine and webkit are two different things (I was giving it a stab). I can't find any Webengine for PyQT5 anywhere in the official Vivid repository. I tried installing libqt5webengine5-dev and libqt5webengine5 from https://launchpad.net/~ethereum/+archive/ubuntu/ethereum-qt packages with the same result, perhaps I need to add something to Python path. Can anyone lend a hand (Im normally a .NET developer :/)?
I started writing python codes two weeks ago and until now I have manipulated some excel data after converting it to txt file. Now, I want to manipulate excel data directly, so I need to install openpyxl package. However, my script will be used in many different places and computers (Note that: all of them use either OS X or a Linux distrubution) which might (probably do not) contain openpyxl package installed. I want my script to check whether this package exist, and if it does not exit, then download and install it.
For that purpose, as I searched and found that I could check the existence of the package by first importing the pip module and then pip.get_installed_distributions() method. However, I am not sure whether I am in a wrong way or not. Besides, I have no idea how to download and install openpyxl package "without leaving the script".
Would you like to help me ?
You are getting ahead of yourself by trying to solve a deployment problem when you don't have the software to deploy.
Having a script trying to manage its own deployment is generally a bad idea because it takes responsibility away from package managers.
Here is my answer to your question:
Build your software under the assumption that all packages are installed/deployed correctly. I recommend creating a virtualenv (virtual environment) on your development computer, using pip to install openpyxl and any other packages you need.
Once your software is built and functional, read up on how to deploy your software package to PyPi: https://pypi.python.org/pypi. Create your package by defining openpyxl as a dependency, ensure your package can be installed/run properly (I recommend adding in tests), then deploy to PyPi so anyone can use your software.
If you want to add a nice layer of validation in your script just in case, add the following to your main method:
#!/usr/bin/env python3
import sys
try:
import openpyxl
except ImportError as iE:
print("Python Excel module 'openpyxl' not found")
sys.exit(1)
... rest of script ...
I want to publish the documentation of my project https://bitbucket.org/oaltun/opn in readthedocs.org.
The build fails. There are different errors shown in the log https://readthedocs.org/builds/opn/2247789/ , but the first is "no module named sip".
sip is needed by pyqt, which is needed by the project.
Normally in this kind of situation, as far as I understand, you would add missing package to your setup.py, and check the readthedocs.org option to create a virtualenv. I do check the box to create a virtualenv. But I can not add sip or pyqt to setup.py.
The problem is pyqt & sip does not use setuptools, so can not be installed by pip. So you can not add them to setup.py (This fails even in my local machine).
In my local environment I install pyqt with (ana)conda. But I think readthedocs.org uses pip for calling the dependencies.
So, how can I have my virtualenv include sip?
The trick is to mock these interfaces:
import mock
MOCK_MODULES = ['sip', 'PyQt4', 'PyQt4.QtGui']
sys.modules.update((mod_name, mock.MagicMock()) for mod_name in MOCK_MODULES)
Note that you must also mock the root package 'PyQt4' or will get an ImportError.
Assuming that you already have pip or easy_install installed on your python distribution, I would like to know how can I installed a required package in the user directory from within the script itself.
From what I know pip is also a python module so the solution should look like:
try:
import zumba
except ImportError:
import pip
# ... do "pip install --user zumba" or throw exception <-- how?
import zumba
What I am missing is doing "pip install --user zumba" from inside python, I don't want to do it using os.system() as this may create other problems.
I assume it is possible...
Updated for newer pip version (>= 10.0):
try:
import zumba
except ImportError:
from pip._internal import main as pip
pip(['install', '--user', 'zumba'])
import zumba
Thanks to #Joop I was able to come-up with the proper answer.
try:
import zumba
except ImportError:
import pip
pip.main(['install', '--user', 'zumba'])
import zumba
One important remark is that this will work without requiring root access as it will install the module in user directory.
Not sure if it will work for binary modules or ones that would require compilation, but it clearly works well for pure-python modules.
Now you can write self contained scripts and not worry about dependencies.
As of pip version >= 10.0.0, the above solutions will not work because of internal package restructuring. The new way to use pip inside a script is now as follows:
try: import abc
except ImportError:
from pip._internal import main as pip
pip(['install', '--user', 'abc'])
import abc
I wanted to note that the current accepted answer could result in a possible app name collision. Importing from the app namespace doesn't give you the full picture of what's installed on the system.
A better way would be:
import pip
packages = [package.project_name for package in pip.get_installed_distributions()]
if 'package' not in packages:
pip.main(['install', 'package'])
Do not use pip.main or pip._internal.main.
Quoting directly from the official documentation (boldface emphasis and editing comments mine, italics theirs):
As noted previously, pip is a command line program. While it is... available from your Python code via import pip, you must not use pip’s internal APIs in this way. There are a number of reasons for this:
The pip code assumes that [it] is in sole control of the global state of the program. pip manages things like... without considering the possibility that user code might be affected.
pip’s code is not thread safe. If you were to run pip in a thread, there is no guarantee that either your code or pip’s would work as you expect.
pip assumes that once it has finished its work, the process will terminate... calling pip twice in the same process is likely to have issues.
This does not mean that the pip developers are opposed in principle to the idea that pip could be used as a library - it’s just that this isn’t how it was written, and it would be a lot of work to redesign the internals for use as a library [with a] stable API... And we simply don’t currently have the resources....
...[E]verything inside of pip is considered an implementation detail. Even the fact that the import name is pip is subject to change without notice. While we do try not to break things as much as possible, all the internal APIs can change at any time, for any reason....
...[I]nstalling packages into sys.path in a running Python process is something that should only be done with care. The import system caches certain data, and installing new packages while a program is running may not always behave as expected....
Having said all of the above[:] The most reliable approach, and the one that is fully supported, is to run pip in a subprocess. This is easily done using the standard subprocess module:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'my_package'])
It goes on to describe other more appropriate tools and approaches for related problems.