181 lines
8.3 KiB
Python
181 lines
8.3 KiB
Python
'''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()
|
|
# ------------------------------------------------------------------------------
|