9

I want to create a bytecode-only distribution from distutils (no really, I do; I know what I'm doing). Using setuptools and the bdist_egg command, you can simply provide the --exclude-source parameter. Unfortunately the standard commands don't have such an option.

  • Is there an easy way to strip the source files just before the tar.gz, zip, rpm or deb is created.
  • Is there a relatively clean per-command way to do this (eg just for tar.gz or zip).
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Draemon
  • 33,955
  • 16
  • 77
  • 104
  • 1
    Very similar: http://stackoverflow.com/questions/261638/how-do-i-protect-python-code The compiled python files (pyc/pyo) are fairly trivial to decompile. – Nick T Aug 09 '10 at 13:31
  • 8
    @Nick: Not really. I didn't mention protection at all, and that question doesn't mention distutils. Obviously python bytecode is easy to analyse, now how about addressing the question I actually asked? – Draemon Aug 09 '10 at 13:57
  • If you just want to remove all the *.py files from a zip: `7z d archive.zip *.py -r` – Nick T Aug 09 '10 at 14:24
  • 5
    I realise I can remove them afterwards, but I'd rather not add the .py files in the first place. Exec'ing an external tool isn't nice, and requiring 7z is even worse; hence why I asked how to do it using distutils. – Draemon Aug 09 '10 at 14:42

4 Answers4

11

The distutils "build_py" command is the one that matters, as it's (indirectly) reused by all the commands that create distributions. If you override the byte_compile(files) method, something like:

try:
    from setuptools.command.build_py import build_py
except ImportError:
    from distutils.command.build_py import build_py

class build_py(build_py)
   def byte_compile(self, files):
       super(build_py, self).byte_compile(files)
       for file in files:
           if file.endswith('.py'):
               os.unlink(file)

setup(
    ...
    cmdclass = dict(build_py=build_py),
    ...
)

You should be able to make it so that the source files are deleted from the build tree before they're copied to the "install" directory (which is a temporary directory when bdist commands invoke them).

Note: I have not tested this code; YMMV.

PJ Eby
  • 8,760
  • 5
  • 23
  • 19
  • +1. This is just the sort of thing I was hoping for. I hadn't realised there was a common build_py I could hook into. I'll try this and see if it needs any tweaking. – Draemon Aug 10 '10 at 09:47
  • 1
    +1 but on Python 2.7.6 it doesn't work because build_py is an old-style class and can't be used with super(). I used `build_py.byte_compile(self, files)` instead. (I also renamed the build_py class so it wouldn't clobber the imported build_py.) – Mark E. Haase Jul 28 '14 at 18:08
1

Try this:

from distutils.command.install_lib import install_lib

class install_lib(install_lib, object):

    """ Class to overload install_lib so we remove .py files from the resulting
    RPM """

    def run(self):

        """ Overload the run method and remove all .py files after compilation
        """

        super(install_lib, self).run()
        for filename in self.install():
            if filename.endswith('.py'):
                os.unlink(filename)

    def get_outputs(self):

        """ Overload the get_outputs method and remove any .py entries in the
        file list """

        filenames = super(install_lib, self).get_outputs()
        return [filename for filename in filenames
                if not filename.endswith('.py')]
Alex
  • 11
  • 1
1

Maybe a full working code here :)

try:
        from setuptools.command.build_py import build_py
except ImportError:
        from distutils.command.build_py import build_py

import os
import py_compile

class custom_build_pyc(build_py):
    def byte_compile(self, files):
        for file in files:
            if file.endswith('.py'):
                py_compile.compile(file)
                os.unlink(file)
....
setup(
    name= 'sample project',
    cmdclass = dict(build_py=custom_build_pyc),
....
Community
  • 1
  • 1
cackharot
  • 688
  • 1
  • 6
  • 13
0

"the standard commands don't have such an option"?

Do you have the latest version of setuptools installed? And did you write a setup.py file?

If so, this should work: python setup.py bdist_egg --exclude-source-files.

Aeon
  • 99
  • 1
  • 4
  • I noted in the question that I managed to do this for bdist_egg. It was the other outputs (e.g. zip) that are lacking the option. – Draemon Sep 22 '16 at 09:44