8

I am working on blowdrycss. The repository is here.

I want the settings file for blowdrycss_settings.py to be excluded from the final package on pypi. The intention is to dynamically build a custom settings file that will be placed in the users virtualenv / project folder.

In setup.py, I have the following:

packages=find_packages(exclude=['blowdrycss_settings.py', ]),

I also tried exclude_package_data:

exclude_package_data={
    '': ['blowdrycss_settings.py'],
    '': ['blowdrycss/blowdrycss_settings.py'],
    'blowdrycss': ['blowdrycss_settings.py'],
},

I then run python setup.py sdist bdist.

However, when I look in the build folder I still see blowdrycss_settings.py:

- build 
    - lib
        - blowdrycss_settings.py

It seems like it should be simple to just exclude a file.

How do I exclude blowdrycss_settings.py from the distributed package?

pjs
  • 18,696
  • 4
  • 27
  • 56
nu everest
  • 9,589
  • 12
  • 71
  • 90

3 Answers3

5

Imagine you have a project

root
├── setup.py
└── spam
    ├── __init__.py
    ├── bacon.py
    └── eggs.py

and you want to exclude spam/eggs.py from packaging:

import fnmatch
from setuptools import find_packages, setup
from setuptools.command.build_py import build_py as build_py_orig


excluded = ['spam/eggs.py']


class build_py(build_py_orig):
    def find_package_modules(self, package, package_dir):
        modules = super().find_package_modules(package, package_dir)
        return [
            (pkg, mod, file)
            for (pkg, mod, file) in modules
            if not any(fnmatch.fnmatchcase(file, pat=pattern) for pattern in excluded)
        ]


setup(
    packages=find_packages(),
    cmdclass={'build_py': build_py}
)

Globs and multiple entries in excluded list will work too because it is consumed by fnmatch, so you can e.g. declare

excluded = [
    'modules_in_directory/*.py',
    'modules_in_subtree/**/*.py',
    'path/to/other/module.py'
]

etc.

This recipe is based on my other answer to the question setup.py exclude some python files from bdist . The difference is that this recipe excludes modules based on file globs, while the other one excludes modules based on qualnames, for example

excluded = ['spam.*', '*.settings']

will exclude all submodules of spam package and all modules named settings in every package and subpackage etc.

hoefling
  • 59,418
  • 12
  • 147
  • 194
1

Here is my solution.

Underneath of blowdrycss, I created a new module called settings so the directory structure now looks like this:

blowdrycss
    blowdrycss
        settings
            blowdrycss_settings.py

Based on this reference, inside of setup.py I have the following:

packages=find_packages(exclude=['*.settings', ]),   

To build the distribution:

  1. Delete the build, dist, and .egg-info folders.
  2. Run python setup.py sdist bdist

In retrospect, it is good that I was unable to do what I was originally attempting. The new structure feels cleaner and is more modular.

yunque
  • 625
  • 1
  • 8
  • 18
nu everest
  • 9,589
  • 12
  • 71
  • 90
  • Step one shoul'nt be mentioned in a subclause. It's a very important step and a very annoying circumstance about setuptools or distutils respectively which introduces history artefacts. – Wör Du Schnaffzig Jan 19 '21 at 14:59
  • @nu everest, can we exclude some files/packages at root level dynamically by passing some arguments to setup.py file while running pip? – Piyush S. Wanare Jun 27 '22 at 05:31
1

The easiest way to remove a single, or at least a few specific files, from a package with setuptools is just to use the MANIFEST.in. For example, in a package you can exclude all files name foo.py by simply specifying global-exclude foo.py. There's no need for setuptools hacking or changing the structure of your package if you just use the MANIFEST.in method.

See the dedicated PyPA article on using MANIFEST.in for more commands you can use.

phetdam
  • 96
  • 1
  • 8