Using Dummy Python Packages to Distribute Course Code Requirements

One of the approaches I started exploring some time ago for distributing required Python packages to support particular courses (modules) was to create a dummy Python package containing just a list of required packages and push it to PyPi. Installing the single dummy package then installs all the actually required packages. (The dummy package could also include environmental checks, perhaps even some simple module documentation etc. But for now, I’ve been keeping it simple.

The setup I’m using bundles the requirements into a separate file, rather than expicitly listing them. The generic package source tree looks something like this:

.
├── LICENSE
├── MANIFEST.in
├── README.md
├── requirements.txt
├── ou-MODULECODE-py
│   ├── __init__.py
├── setup.py

The MANIFEST.in file is required and ensures that the requirements.py file is bundled into the package that is uploaded to PyPi. If the MANIFEST.in file is omitted, the package install will work from Github (because the requirements.txt will form part of the cloned download, but not from PyPi (becuase the requirements file won’t be uploaded to PyPi). (A safer route to install requirements would be to list them explicitly within the setup.py file. And perhaps not trap for the missing requirements file… Or at least, raise a warning if an anctipated requirements.txt file is not available.)

The __init__.py file can be empty, but I’ve started exploring simple test scripts within it:

def about():
    """Provide a simple description of the package."""
    msg = f"""
# ===== ou_tm351_py, version: {__version__} =====
The `ou_tm351_py` package is an "empty" package that installs Python package requirements 
for the Open University module "Data management and analysis (TM351)" [http://www.open.ac.uk/courses/modules/tm351].
You can test that key required packages are installed by running the command: ou_tm351_py.test_install()
    """
    print(msg)


def test_install(key_packages=None):
    """Test the install of key packages."""
    import importlib

    if key_packages is None:
        key_packages = [
            "pandas",
            "schemadisplay_magic"
        ]
    for p in key_packages:
        try:
            importlib.import_module(p.strip())
            print(f"{p} loaded correctly")
        except:
            print(f"{p} appears to be missing")

A more elaborate test file could attempt to check that all the requirements are installed. We could also bundle simple command line commands, or even a simple webserver serving module help files / documentation.

The setup.py file is largely boilerplate:

from setuptools import setup
from os import path

def get_requirements(fn='requirements.txt'):
   """Get requirements."""
   if path.exists(fn):
      with open(fn, 'r') as f:
        requirements = [r.split()[0].strip() for r in f.read().splitlines() if r and not r.startswith('#')]
   else:
     requirements = []

   return requirements
   
requirements = get_requirements(nogit=False)

print(f'Requirements: {requirements}')

# Any extra requirements will need bundling via MANIFEST.in
extras = {
    'production': get_requirements('requirements_production.txt'),
    'AL': get_requirements('requirements_AL.txt')
    }
    
setup(
    # Meta
    author='Tony Hirst',
    author_email='tony.hirst@open.ac.uk',
    description='Python package installation for OU module MODULECODE',
    name='ou-MODULECODE-py',
    license='MIT',
    url='https://github.com/innovationOUtside/innovationOUtside/ou-MODULECODE-py',
    version='0.0.1',
    packages=['ou_MODUECODE_py'],

    # Dependencies
    install_requires=requirements,
    #setup_requires=[],
    extras_require=extras,

    # Packaging
    #entry_points="",
    include_package_data=True,
    zip_safe=False,

    # Classifiers
    classifiers=[
        'Development Status :: 3 - Alpha',
        'Environment :: Web Environment',
        'Intended Audience :: Education',
        'License :: Free For Educational Use',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Programming Language :: Python :: 3.8',
        'Topic :: Education',
        'Topic :: Scientific/Engineering :: Visualization'
    ],
)

Installing the package is then a command along the lines of pip install ou-MODULECODE-py.

Author: Tony Hirst

I'm a Senior Lecturer at The Open University, with an interest in #opendata policy and practice, as well as general web tinkering...

%d bloggers like this: