koryakinp
koryakinp

Reputation: 4125

pip package with OS specififc dependency

I want to create a pip package which dependent on some OS specific files:

Let's say there are:

I do not want to include all three archives in a package project, but download them dynamically during the pip install my-package based on user's OS. How can I do that ? Where should I put the code responsible for downloading/unzipping those files ?

My setup.py looks like this:

from setuptools import setup 

setup(
  name='my-package',
  version='0.0.1',
  description='Package description',
  py_modules=['my_package'],
  package_dir={'': 'src'},
  classifiers=[
    'Intended Audience :: Developers',
    'License :: OSI Approved :: MIT License',
    'Operating System :: POSIX :: Linux',
    'Operating System :: Microsoft :: Windows',
    'Operating System :: MacOS',
    'Programming Language :: Python :: 3',
    'Programming Language :: Python :: 3.7'
  ],
  python_requires='>=3.7'
)

Upvotes: 3

Views: 4362

Answers (3)

sinoroc
sinoroc

Reputation: 22305

The platform specific dependencies could be kept in separate Python projects (wrappers around data only packages) and then required from the main project like the following:

# setup.cfg
# ...
[options]
install_requires =
    my_package_win_amd64 ; platform_system=="Windows" and platform_machine=="x86_64"
    my_package_linux-x86_64 ; platform_system=="Linux" and platform_machine=="x86_64"

This approach doesn't depend on setuptools and could be used with other build systems.

Upvotes: 6

sinoroc
sinoroc

Reputation: 22305

A solution could be to publish platform specific Python wheels of your project. The platform specific files could be added to the pre-built distributions via a custom setuptools command (probably a sub-command of build, or maybe install).

This is not a full solution, but something like this might be a good start:

#!/usr/bin/env python3

import distutils.command.build
import setuptools

class build_something(setuptools.Command):
    user_options = [
        ('plat-name=', 'p', "platform name to build for"),
    ]
    def initialize_options(self):
        self.plat_name = None
    def finalize_options(self):
        self.set_undefined_options('bdist_wheel', ('plat_name', 'plat_name'))
    def run(self):
        print(" *** plat_name: {} ***".format(self.plat_name))
        print(" *** download the platform specific bits to 'build' ***")

class build(distutils.command.build.build):
    sub_commands = [(
        'build_something',
        None,
    )] + distutils.command.build.build.sub_commands

setuptools.setup(
    cmdclass={
        'build_something': build_something,
        'build': build,
    },
    # ...
)

And then the Python wheels could be built like this:

$ ./setup.py bdist_wheel -p win_amd64

Upvotes: 2

Charles Merriam
Charles Merriam

Reputation: 20510

First answer is give up and use setuptools. Look at Platform specific dependencies for a good write up of what to do.

Second answer is to make separate packages such as 'mylib-mac', 'mylib-win', 'mylib-linux'.

Third answer is to use the "console_script" approach. This will break. While it generates .exe files on Windows, it has odd failure modes. Also, some users will not be able to dynamically download files because they work from an internal clone of a repository. Randomly running code from the Internet on production can scare people.

Hope this helps!

Upvotes: 1

Related Questions