# Command line interface for the `py2deb' program.
#
# Author: Peter Odding <peter.odding@paylogic.com>
# Last Change: May 22, 2017
# URL: https://py2deb.readthedocs.io
"""
Usage: py2deb [OPTIONS] ...
Convert Python packages to Debian packages according to the given
command line options (see below). The command line arguments are the
same as accepted by the `pip install' command because py2deb invokes
pip during the conversion process. This means you can name the
package(s) to convert on the command line but you can also use
`requirement files' if you prefer.
If you want to pass command line options to pip (e.g. because you want
to use a custom index URL or a requirements file) then you will need
to tell py2deb where the options for py2deb stop and the options for
pip begin. In such cases you can use the following syntax:
$ py2deb -r /tmp -- -r requirements.txt
So the `--' marker separates the py2deb options from the pip options.
Supported options:
-c, --config=FILENAME
Load a configuration file. Because the command line arguments are processed
in the given order, you have the choice and responsibility to decide if
command line options override configuration file options or vice versa.
Refer to the documentation for details on the configuration file format.
The default configuration files /etc/py2deb.ini and ~/.py2deb.ini are
automatically loaded if they exist. This happens before environment
variables and command line options are processed.
Can also be set using the environment variable $PY2DEB_CONFIG.
-r, --repository=DIRECTORY
Change the directory where *.deb archives are stored. Defaults to
the system wide temporary directory (which is usually /tmp). If
this directory doesn't exist py2deb refuses to run.
Can also be set using the environment variable $PY2DEB_REPOSITORY.
--use-system-package=PYTHON_PACKAGE_NAME,DEBIAN_PACKAGE_NAME
Exclude a Python package (the name before the comma) from conversion and
replace references to the Python package with a specific Debian package
name. This allows you to use system packages for specific Python
requirements.
--name-prefix=PREFIX
Set the name prefix used during the name conversion from Python to
Debian packages. Defaults to `python'. The name prefix and package
names are always delimited by a dash.
Can also be set using the environment variable $PY2DEB_NAME_PREFIX.
--no-name-prefix=PYTHON_PACKAGE_NAME
Exclude a Python package from having the name prefix applied
during the package name conversion. This is useful to avoid
awkward repetitions.
--rename=PYTHON_PACKAGE_NAME,DEBIAN_PACKAGE_NAME
Override the package name conversion algorithm for the given pair
of package names. Useful if you don't agree with the algorithm :-)
--install-prefix=DIRECTORY
Override the default system wide installation prefix. By setting
this to anything other than `/usr' or `/usr/local' you change the
way py2deb works. It will build packages with a file system layout
similar to a Python virtual environment, except there will not be
a Python executable: The packages are meant to be loaded by
modifying Python's module search path. Refer to the documentation
for details.
Can also be set using the environment variable $PY2DEB_INSTALL_PREFIX.
--install-alternative=LINK,PATH
Use Debian's `update-alternatives' system to add an executable
that's installed in a custom installation prefix (see above) to
the system wide executable search path. Refer to the documentation
for details.
--python-callback=EXPRESSION
Set a Python callback to be called during the conversion process. Refer to
the documentation for details about the use of this feature and the syntax
of EXPRESSION.
Can also be set using the environment variable $PY2DEB_CALLBACK.
--report-dependencies=FILENAME
Add the Debian relationships needed to depend on the converted
package(s) to the given control file. If the control file already
contains relationships the additional relationships will be added
to the control file; they won't overwrite existing relationships.
-y, --yes
Instruct pip-accel to automatically install build time dependencies
where possible. Refer to the pip-accel documentation for details.
Can also be set using the environment variable $PY2DEB_AUTO_INSTALL.
-v, --verbose
Make more noise :-).
-h, --help
Show this message and exit.
"""
# Standard library modules.
import getopt
import logging
import os
import sys
# External dependencies.
import coloredlogs
from deb_pkg_tools.control import patch_control_file
from humanfriendly.terminal import usage, warning
# Modules included in our package.
from py2deb.converter import PackageConverter
# Initialize a logger.
logger = logging.getLogger(__name__)
[docs]def main():
"""Command line interface for the ``py2deb`` program."""
# Configure terminal output.
coloredlogs.install()
try:
# Initialize a package converter.
converter = PackageConverter()
# Parse and validate the command line options.
options, arguments = getopt.getopt(sys.argv[1:], 'c:r:yvh', [
'config=', 'repository=', 'use-system-package=', 'name-prefix=',
'no-name-prefix=', 'rename=', 'install-prefix=',
'install-alternative=', 'python-callback=', 'report-dependencies=',
'yes', 'verbose', 'help',
])
control_file_to_update = None
for option, value in options:
if option in ('-c', '--config'):
converter.load_configuration_file(value)
elif option in ('-r', '--repository'):
converter.set_repository(value)
elif option == '--use-system-package':
python_package_name, _, debian_package_name = value.partition(',')
converter.use_system_package(python_package_name, debian_package_name)
elif option == '--name-prefix':
converter.set_name_prefix(value)
elif option == '--no-name-prefix':
converter.rename_package(value, value)
elif option == '--rename':
python_package_name, _, debian_package_name = value.partition(',')
converter.rename_package(python_package_name, debian_package_name)
elif option == '--install-prefix':
converter.set_install_prefix(value)
elif option == '--install-alternative':
link, _, path = value.partition(',')
converter.install_alternative(link, path)
elif option == '--python-callback':
converter.set_python_callback(value)
elif option == '--report-dependencies':
control_file_to_update = value
if not os.path.isfile(control_file_to_update):
msg = "The given control file doesn't exist! (%s)"
raise Exception(msg % control_file_to_update)
elif option in ('-y', '--yes'):
converter.set_auto_install(True)
elif option in ('-v', '--verbose'):
coloredlogs.increase_verbosity()
elif option in ('-h', '--help'):
usage(__doc__)
return
else:
assert False, "Unhandled option!"
except Exception as e:
warning("Failed to parse command line arguments: %s", e)
sys.exit(1)
# Convert the requested package(s).
try:
if arguments:
archives, relationships = converter.convert(arguments)
if relationships and control_file_to_update:
patch_control_file(control_file_to_update, dict(depends=relationships))
else:
usage(__doc__)
except Exception:
logger.exception("Caught an unhandled exception!")
sys.exit(1)