'''This sript allows to wrap a Python module into an egg.''' # ------------------------------------------------------------------------------ import os, os.path, sys, zipfile, appy from appy.bin.clean import Cleaner from appy.shared.utils import FolderDeleter, copyFolder, cleanFolder from optparse import OptionParser # ------------------------------------------------------------------------------ class EggifierError(Exception): pass ERROR_CODE = 1 eggInfo = '''from setuptools import setup, find_packages import os setup(name = "%s", version = "%s", description = "%s", long_description = "%s", author = "%s", author_email = "%s", license = "GPL", keywords = "plone, appy", url = '%s', classifiers = ["Framework :: Appy", "Programming Language :: Python",], packages=find_packages(exclude=['ez_setup']), include_package_data = True, namespace_packages=['%s'], zip_safe = False, install_requires=['setuptools'],)''' initInfo = ''' # See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages try: __import__('pkg_resources').declare_namespace(__name__) except ImportError: from pkgutil import extend_path __path__ = extend_path(__path__, __name__) ''' # ------------------------------------------------------------------------------ class EggifyScript: '''usage: python eggify.py pythonModule [options] pythonModule is the path to a Python module or the name of a Python file. Available options are: -a --appy If specified, the Appy module (light version, without test code) will be included in the egg. -r --result The path where to create the egg (defaults to the current working directory) -p --products If specified, the module will be packaged in the "Products" namespace. -v --version Egg version. Defaults to 1.0.0. ''' def createSetupFile(self, eggTempFolder): '''Creates the setup.py file in the egg.''' content = eggInfo % (self.moduleName, self.version, 'Appy module', 'Appy module', 'Gaetan Delannay', 'gaetan.delannay AT gmail.com', 'http://appyframework.org', self.moduleName.split('.')[0]) f = file(os.path.join(eggTempFolder, 'setup.py'), 'w') f.write(content) f.close() def createInitFile(self, eggTempFolder): '''Creates the ez_setup-compliant __init__ files.''' initPath = os.path.join(eggTempFolder,self.moduleName.split('.')[0]) f = file(os.path.join(initPath, '__init__.py'), 'w') f.write(initInfo) f.close() def getEggName(self): '''Creates the egg name.''' return '%s-%s.egg' % (self.moduleName, self.version) zipExclusions = ('.bzr', 'doc', 'test', 'versions') def dirInZip(self, dir): '''Returns True if the p_dir must be included in the zip.''' for exclusion in self.zipExclusions: if dir.endswith(exclusion) or ('/%s/' % exclusion in dir): return False return True def zipResult(self, eggFullName, eggTempFolder): '''Zips the result and removes the egg temp folder.''' zipFile = zipfile.ZipFile(eggFullName, 'w', zipfile.ZIP_DEFLATED) # Put the Python module inside the egg. prefix = os.path.dirname(eggTempFolder) for dir, dirnames, filenames in os.walk(eggTempFolder): for f in filenames: fileName = os.path.join(dir, f) zipFile.write(fileName, fileName[len(prefix):]) # Put the Appy module inside it if required. if self.includeAppy: eggPrefix = '%s/%s' % (eggTempFolder[len(prefix):], self.moduleName.replace('.', '/')) # Where is Appy? appyPath = os.path.dirname(appy.__file__) appyPrefix = os.path.dirname(appyPath) # Clean the Appy folder Cleaner().run(verbose=False) # Insert appy files into the zip for dir, dirnames, filenames in os.walk(appyPath): if not self.dirInZip(dir): continue for f in filenames: fileName = os.path.join(dir, f) zipName = eggPrefix + fileName[len(appyPrefix):] zipFile.write(fileName, zipName) zipFile.close() # Remove the temp egg folder. FolderDeleter.delete(eggTempFolder) def eggify(self): '''Let's wrap a nice Python module into an ugly egg.''' j = os.path.join # First, clean the Python module cleanFolder(self.pythonModule, verbose=False) # Create the egg folder eggFullName = j(self.eggFolder, self.eggName) if os.path.exists(eggFullName): os.remove(eggFullName) print(('Existing "%s" was removed.' % eggFullName)) # Create a temp folder where to store the egg eggTempFolder = os.path.splitext(eggFullName)[0] if os.path.exists(eggTempFolder): FolderDeleter.delete(eggTempFolder) print(('Removed "%s" that was in my way.' % eggTempFolder)) os.mkdir(eggTempFolder) # Create the "Products" sub-folder if we must wrap the package in this # namespace eggModulePath = j(j(eggTempFolder, self.moduleName.replace('.', '/'))) # Copy the Python module into the egg. os.makedirs(eggModulePath) copyFolder(self.pythonModule, eggModulePath) # Create setup files in the root egg folder self.createSetupFile(eggTempFolder) self.createInitFile(eggTempFolder) self.zipResult(eggFullName, eggTempFolder) def checkArgs(self, options, args): # Check that we have the correct number of args. if len(args) != 1: raise EggifierError('Wrong number of arguments.') # Check that the arg corresponds to an existing Python module if not os.path.exists(args[0]): raise EggifierError('Path "%s" does not correspond to an ' \ 'existing Python package.' % args[0]) self.pythonModule = args[0] # At present I only manage Python modules, not 1-file Python packages. if not os.path.isdir(self.pythonModule): raise EggifierError('"%s" is not a folder. One-file Python ' \ 'packages are not supported yet.' % args[0]) self.eggFolder = options.result if not os.path.exists(self.eggFolder): raise EggifierError('"%s" does not exist. Please create this ' \ 'folder first.' % self.eggFolder) self.includeAppy = options.appy self.inProducts = options.products self.version = options.version self.moduleName = os.path.basename(self.pythonModule) if self.inProducts: self.moduleName = 'Products.' + self.moduleName self.eggName = self.getEggName() def run(self): optParser = OptionParser(usage=EggifyScript.__doc__) optParser.add_option("-r", "--result", dest="result", help="The folder where to create the egg", default=os.getcwd(), metavar="RESULT", type='string') optParser.add_option("-a", "--appy", action="store_true", help="Includes the Appy module in the egg") optParser.add_option("-p", "--products", action="store_true", help="Includes the module in the 'Products' " \ "namespace") optParser.add_option("-v", "--version", dest="version", help="The module version", default='1.0.0', metavar="VERSION", type='string') options, args = optParser.parse_args() try: self.checkArgs(options, args) self.eggify() except EggifierError as ee: sys.stderr.write(str(ee) + '\nRun eggify.py -h for getting help.\n') sys.exit(ERROR_CODE) # ------------------------------------------------------------------------------ if __name__ == '__main__': EggifyScript().run() # ------------------------------------------------------------------------------