Search code examples
setuptoolspython-wheelegg

Exclude single source file from python bdist_egg or bdist_wheel


Background: I have one source file which is responsible for security. In there are magic keys and specific algorithms.

Is it possible to remove a this single source file from a python egg or wheel package?

I already accomplished to ship only binarys with the the egg command.

python setup.py bdist_egg --exclude-source-files

Edit Project structure:

├── setup.py
├── src
|   ├── __init__.py
|   ├── file1.py
|   ├── file2.py
|   ├── file_to_exclude.py

Thanks for your help!


Solution

  • Unfortunately, neither distutils nor setuptools provide a possibility to exclude single modules, so you have to work around it.

    Update:

    I have described a better solution here that mimics the package exclusion setuptools does in find_packages(). You will have to override the build_py command in the setup script that can accept a list of exclude patterns, same as exclude list in find_packages. In your case, it would be:

    import fnmatch
    from setuptools import find_packages, setup
    from setuptools.command.build_py import build_py as build_py_orig
    
    
    exclude = ['src.file_to_exclude']
    
    
    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(pkg + '.' + mod, pat=pattern)
                    for pattern in exclude)]
    
    
    setup(
        ...,
        packages=find_packages(),
        cmdclass={'build_py': build_py},
    )
    

    I find this to be a much more powerful and distutils-conform solution than the ones below. It also enables one to exclude multiple modules via wildcard matching, for example

    exclude = ['src.file*']
    

    will exclude all modules starting with file in src package, or

    exclude = ['*.file1']
    

    will exclude file1.py in all packages.

    Original answer

    put modules to be excluded in a separate package

    You can use the fact that setuptools can exclude packages (dirs containing the __init__.py files), but it will require some refactoring. Create a package_to_exclude, put file_to_exclude.py in there and fix all the eventual import errors:

    project
    ├── setup.py
    └── src
         ├── __init__.py
         ├── file1.py
         ├── file2.py
         └── package_to_exclude
              ├── __init__.py
              └── file_to_exclude.py
    

    Now you can exclude package_to_exclude in the setup script:

    from setuptools import find_packages, setup
    
    setup(
        ...,
        packages=find_packages(exclude=['src.package_to_exclude'])
    )
    

    exclude package, add modules to be included via py_modules

    If you can't or don't want to move the module in a separate package, you can exclude the src package and add all the modules in src except file_to_exclude in py_modules. Example:

    import os
    from setuptools import find_packages, setup
    
    excluded_files = ['file_to_exclude.py']
    included_modules = ['src.' + os.path.splitext(f)[0]
                        for f in os.listdir('src')
                        if f not in excluded_files]
    
    setup(
        ...,
        packages=find_packages(exclude=['src']),
        py_modules=included_modules,
    )