How should I set this up for a working package?

I'm barely out of infancy in python and trying to figure out how to package a few of my tools for distributing, and having a hard time understanding the documentation.

After reading the docs, I have a package structure that looks like this:

/UTM (git repo)
    /utmizer
        /raster
            raster.py
        __init__.py
        convert.py
        LICENSE
        README.md
        setup.py
        utmize.py

The __init__.py looks like this because I'm not sure what I'm doing:

import utmizer.utmize as utmize
from utmizer.raster import raster
from .convert import Converter

My import statements throughout look like this:

convert.py

import fiona
import geopandas as gpd
import json
import os
import re

from raster import raster

utmize.py

from utmizer.convert import Converter
import getopt
import os
from pathlib import Path
import sys

And finally, the setup.py looks like this:

from pathlib import Path
import setuptools
from setuptools import find_packages


VERSION = '0.0.1'
HERE = Path(__file__).parent.resolve()


long_description = (HERE / 'README.md').read_text(encoding='utf-8')


setuptools.setup(
    name='utmizer',
    version=VERSION,
    author='username',
    author_email='[email protected]',
    description='Automatic projection of data to appropriate UTM zone',
    long_description=long_description,
    long_description_content_type='text/markdown',
    url='https://github.com/username/UTM',
    install_requires=find_packages(),
    classifiers=[
        "Programming Language :: Python :: 3"
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>=3.8, <4',
)

When I run python setup.py bdist_wheel, it completes; but when I install it in a new conda instance and attempt to import utmizer it throws a ModuleNotFoundError.


Solution 1:

First things:

  • Move setup.py LICENSE and README.md to the root directory of your project (git repository), outside of the actual top-level importable packages.
  • Add a package initializer __init__.py in utmizer/raster, and any other importable package and sub-package.

So you should get a directory tree like this:

/UTM (git repo)
    setup.py
    LICENSE
    README.md
    /utmizer
        /raster
            __init__.py
            raster.py
        __init__.py
        convert.py
        utmize.py

Seems like you mixed up packages and install_requires in your setup.py. Not sure exactly what you need in install_requires, but I guess at least geopandas and fiona.

Also no need to restrict to Python <4, because no one knows if it will be indeed incompatible. As a rule, you should only add restrictions for things you know for sure are not compatible at the time of writing (no one can guess the future).

from pathlib import Path
import setuptools

VERSION = '0.0.1'
HERE = Path(__file__).parent.resolve()

long_description = (HERE / 'README.md').read_text(encoding='utf-8')

setuptools.setup(
    name='utmizer',
    version=VERSION,
    author='username',
    author_email='[email protected]',
    description='Automatic projection of data to appropriate UTM zone',
    long_description=long_description,
    long_description_content_type='text/markdown',
    url='https://github.com/username/UTM',
    packages=setuptools.find_packages(),
    install_requires=[
        'fiona',
        'geopandas',
    ],
    classifiers=[
        "Programming Language :: Python :: 3"
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>=3.8',
)

You probably can leave the __init__.py files empty.

In general you should always use absolute imports starting from the top-level importable packages (or modules). In your case there is is only one top-level importable package utmizer (and no top-level importable module).

So in utmizer/convert.py:

import fiona
import geopandas as gpd
import json
import os
import re

from utmizer.raster import raster

And utmizer/utmize.py seems already correct.