How to Package and Distribute Python Barcode SDK to PyPi

Previously, I wrote an article sharing how to build Python barcode extension using C/C++ with Dynamsoft Barcode Reader SDK. If Python developers want to use the extension, they have to make it by themselves. It is a little bit inconvenient. A better way is to package the extension and distribute it to PyPi. Then developers could directly install the extension using pip commands.

Tools for Packaging and Distribution

Install setuptools, wheel, and twine:

python -m pip install setuptools wheel twine

How to Package Python Barcode Extension Using Wheel

Create a folder named dbr, which contains a __init__.py file which can be empty, dbr.pyd which is the Python extension built from the C/C++ source code, and DynamsoftBarcodeReaderx64.dll which is the external DLL provided by Dynamsoft.

Here is my __init__.py file:

from .dbr import *

Create a MANIFEST.in file:

# Include the README

include *.md

# Include the license file

include LICENSE.txt

Create a setup.cfg file:

[metadata]
license_file = LICENSE.txt

[bdist_wheel]
universal = 0
python-tag = cp27

It is not a universal wheel, so I set universal to 0.
Create a setup.py file:

from setuptools import setup, find_packages, Distribution
from codecs import open
from os import path

here = path.abspath(path.dirname(__file__))

with open(path.join(here, 'README.md'), encoding='utf-8') as f:
    long_description = f.read()

class BinaryDistribution(Distribution):
    """Distribution which always forces a binary package with platform name"""
    def has_ext_modules(foo):
        return True

setup(
    name='dbr',  # Required
    version='6.0.0',  # Required
    description='Dynamsoft Barcode Reader Python project',  # Required
    long_description=long_description,  # Optional
    long_description_content_type='text/markdown',  # Optional (see note above)
    url='https://www.dynamsoft.com/Products/Dynamic-Barcode-Reader.aspx',  # Optional
    license = 'https://www.dynamsoft.com/Products/barcode-reader-license-agreement.aspx',
    author='Dynamsoft',  # Optional
    author_email='support@dynamsoft.com',  # Optional
    classifiers=[  # Optional
        'Development Status :: 5 - Production/Stable',
        'Intended Audience :: Developers',
        'Topic :: Software Development :: Libraries',
        'License :: Other/Proprietary License',
        'Programming Language :: Python :: 2',
        'Programming Language :: Python :: 2.7',
    ],
    keywords='barcode DataMatrix QRCode 1D PDF417',  # Optional
    packages=find_packages(exclude=['contrib', 'docs', 'tests']),  # Required
    install_requires=['numpy', 'opencv-python'],  # Optional
    package_data={  # Optional
        'dbr': ['DynamsoftBarcodeReaderx64.dll', 'dbr.pyd'],
    },
    distclass=BinaryDistribution
)

Use following command to generate the distribution dbr-6.0.0-cp27-cp27m-win_amd64.whl:

python setup.py bdist_wheel --plat-name win_amd64

Note: if you do not override the function has_ext_modules(), the generated file name will be dbr-6.0.0-cp27-none-win_amd64.whl which means the ABI tag is none. I found the workaround from StackOverflow to add the ABI tag to the package name. If you are interested in the code logic, you can open Python27\Lib\site-packages\wheel\bdist_wheel.py and find the code snippet:

self.root_is_pure = not (self.distribution.has_ext_modules()
                                 or self.distribution.has_c_libraries())
if self.root_is_pure:
            if self.universal:
                impl = 'py2.py3'
            else:
                impl = self.python_tag
            tag = (impl, 'none', plat_name)
        else:
            impl_name = get_abbr_impl()
            impl_ver = get_impl_ver()
            impl = impl_name + impl_ver
            # We don't work on CPython 3.1, 3.0.
            if self.py_limited_api and (impl_name + impl_ver).startswith('cp3'):
                impl = self.py_limited_api
                abi_tag = 'abi3'
            else:
                abi_tag = str(get_abi_tag()).lower()
            tag = (impl, abi_tag, plat_name)

Install the package:

pip install dbr-6.0.0-cp27-cp27m-win_amd64.whl

The Python barcode extension will be installed into Python27\Lib\site-packages\dbr.

Use the module in Python project:

import dbr

How to Publish Python Package to PyPi

Register a PyPI account.

Upload distributions:

twine upload dist/*

Login your account to view your package:

python barcode pypi package

Now we can install the package using:

pip install dbr

References

Source Code

https://github.com/dynamsoft-dbr/pypi-wheel