Search code examples
pythonsetuptoolsegg

How do I create and load an egg-file in Python?


I am researching for a way to distribute a python module as a single egg-file. Supposing I have a python module called my_module and I want to write a python script that generates an egg-file for my module. So I found setuptools.

from setuptools import setup

setup(
    name="my_module",
    packages=[my_package],
    version="1.0",
)

And I've had some disadvantages regarding these issues:

  1. This script should be run as python setup.py install. In other words, I need to specify command line arguments. Instead I want to generate my egg-file automatically during my python code, that has its own control over the command line arguments.
  2. The result files are outputed into the setup's file directory. I would like to control the output directory path in the script.
  3. The script create build and dist folders that I don't actually need. Probably, I could solve it by removing the folders after calling setup.

How should I use setuptools for my purposes covering the issues above?

And also how can I load my module from given egg-file?


Supposing I have a following module:

# my_module.py

class MyClass:
    def __init__(self):
        self._x = None

    def set_x(self, x):
        self._x = x

    def get_x(self):
        return self._x

I wrote this script to create an egg-file:

# create_egg.py

from setuptools import setup

setup(
    name="my_module",
    packages=['my_module'],
    version="1.0",
)

I get such error, when I run creage_egg.py:

$ python3 create_egg.py
usage: create_egg.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: create_egg.py --help [cmd1 cmd2 ...]
   or: create_egg.py --help-commands
   or: create_egg.py cmd --help

error: no commands supplied

Solution

  • First thing I found out is that a package must be a directory. So it's necessary to keep such structure:

    my_project/
        my_module/
            __init__.py
        output/
        create_egg.py
    test_egg.py
    

    To skip the necessity of specifying the command line arguments there's a special option script_args. I found it here: http://peak.telecommunity.com/DevCenter/setuptools

    My __init__.py:

    class MyClass:
        def __init__(self):
            self._x = None
    
        def set_x(self, x):
            self._x = x
    
        def get_x(self):
            return self._x
    

    My create_egg.py:

    import os
    import shutil
    from setuptools import setup
    
    
    OUTPUT_DIR = 'output'
    
    
    if __name__ == "__main__":
        setup(
            name="my_module",
            packages=['my_module'],
            version="1.0",
            script_args=['--quiet', 'bdist_egg'], # to create egg-file only
        )
    
        egg_name = os.listdir('dist')[0]
    
        os.rename(
            os.path.join('dist', egg_name),
            os.path.join(OUTPUT_DIR, egg_name)
        )
    
        shutil.rmtree('build')
        shutil.rmtree('dist')
        shutil.rmtree('my_module.egg-info')
    

    My test_egg.py:

    import sys
    sys.path.append('my_project/output/my_module-1.0-py3.5.egg')
    
    import my_module
    
    
    obj = my_module.MyClass()
    obj.set_x(29)
    print(obj.get_x())
    

    Creating egg-file:

    ~/Stuff/my_project $ python3 create_egg.py 
    zip_safe flag not set; analyzing archive contents...
    

    Testing the module:

    ~/Stuff $ python3 test_egg.py 
    29