0

I'm testing a trivial installable package with Python setuptools. I have read a handful of tutorials and official documentation, so I thought I had a good background. It seems I was wrong.

The minimal package I'm playing with has the following structure:

myapp
├── config
│   └── text.txt
├── MANIFEST.in
├── myapp
│   ├── __init__.py
│   ├── lib.py
│   └── __main__.py
└── setup.py

With the following contents:

__init__.py (this is just to make the package usable as a library when imported):

import myapp.lib

__main__.py (I want to make this application basically runnable from the terminal with -m or better, with an entry point):

import sys
import myapp
import myapp.lib

def main():
    print("main function called")
    print(f"current path: {sys.path}")
    myapp.lib.funct()

if __name__ == "__main__":
    main()

myapp/lib.py (this is the module with the logic, and it relies on external data):

from pathlib import Path

textfile = Path(__file__).parent.parent / 'config/text.txt'

def funct():
    print("hello from function in lib")
    with open(textfile, 'r') as f:
        print(f.read())

config/text.txt (this is the data):

some text constant as part of the module

To package this module, I have crafted the following setup.py

setup(
    name="myapp",
    version="1.0.0",
    description="A simple test",
    author="me",
    url='www.me.com',
    author_email="me@email.com",
    classifiers=[
        "Programming Language :: Python :: 3",
    ],
    packages=["myapp"],
    include_package_data=True,
    entry_points={"console_scripts": ["myapp=myapp.__main__:main"]},
)

As well as a MANIFEST.in file:

include config/*

OK, these are the ingredients. Now, I create a distribution with python setup.py sdist. This creates a file dist/myapp-1.0.0.tar.gz, which does include the data file (along with plenty of metadata I don't fully understand):

> tar tf myapp-1.0.0.tar.gz
myapp-1.0.0/
myapp-1.0.0/MANIFEST.in
myapp-1.0.0/PKG-INFO
myapp-1.0.0/config/
myapp-1.0.0/config/text.txt
myapp-1.0.0/myapp/
myapp-1.0.0/myapp/__init__.py
myapp-1.0.0/myapp/__main__.py
myapp-1.0.0/myapp/lib.py
myapp-1.0.0/myapp.egg-info/
myapp-1.0.0/myapp.egg-info/PKG-INFO
myapp-1.0.0/myapp.egg-info/SOURCES.txt
myapp-1.0.0/myapp.egg-info/dependency_links.txt
myapp-1.0.0/myapp.egg-info/entry_points.txt
myapp-1.0.0/myapp.egg-info/top_level.txt
myapp-1.0.0/setup.cfg
myapp-1.0.0/setup.py

Now, the problem comes when I try to install the package with pip install myapp-1.0.0.tar.gz. It does not copy the config/text.txt anywhere. In my site-packages folder, I see the directories myapp and myapp-1.0.0.dist-info, but there is no text file inside any of them.

There are various things I don't understand:

  • is the definition of textfile in myapp/lib.py correct/sensible? I mean, I find it cumbersome and almost like I'm hardcoding this, having to use a module on purpose to build a simple path to this data file. I feel I'm overdoing this, but I have not found this part of the packing step very well documented. It's surprising, as this must be a very common problem.
  • given the the data file IS in the tar file. Why did pip not copy it to site-packages?
  • what is this myapp-1.0.0.dist-info folder in my site-packages?
Pythonist
  • 1,937
  • 1
  • 14
  • 25
  • 2
    https://stackoverflow.com/a/20885799/7976758 Found in https://stackoverflow.com/search?q=%5Bsetuptools%5D+MANIFEST.in+pkg_resources – phd Jan 25 '22 at 00:39
  • 1
    I would recommend moving `config` as a subdirectory of `myapp`, it will make everything much easier. As the name says "package data" should be in a Python importable package. And as it is configured now, your project has only 1 importable package, and it is `myapp`. -- https://sinoroc.gitlab.io/kb/python/package_data.html – sinoroc Jan 25 '22 at 09:47
  • 1
    Thanks for both comments, they helped me to solve the issue. Still, it was by no means trivial! I had to navigate between a plethora of partial solutions and comments pointing out how each proposed solution had caveats, or is deprecated, or depends on third party libraries to just find a simple file in my package WTF!? I guess this huge complexity is the price to pay for having great flexibility... but we should acknowledge that it is not a small price! – Pythonist Jan 25 '22 at 16:24

0 Answers0