appy.bin: updated publish.py, that is now able to generate a DistUtils tar.gz for Appy; publish.py can now be called with option '-s' (silent): in this mode no question is asked to the user, default values are used; updated new.py that generates a better Plone4-ready simple Zope instance; appy: moved FileWrapper from appy.gen.utils to appy.shared.utils to avoid circular package dependencies; appy.gen: use of .pyt extensions for template Python classes in appy.gen.templates in order to avoid byte-compilation errors when distutils installs the package; appy.pod: when using function 'document' in 'from' statements, first arg can now be a appy.shared.utils.FileWrapper instance.
This commit is contained in:
		
							parent
							
								
									e78cf62694
								
							
						
					
					
						commit
						6ece750d9a
					
				
					 15 changed files with 237 additions and 210 deletions
				
			
		
							
								
								
									
										56
									
								
								bin/new.py
									
										
									
									
									
								
							
							
						
						
									
										56
									
								
								bin/new.py
									
										
									
									
									
								
							| 
						 | 
					@ -109,8 +109,9 @@ class NewScript:
 | 
				
			||||||
            f.write(self.patchRex.sub('<!--Del. includePlugins-->',fileContent))
 | 
					            f.write(self.patchRex.sub('<!--Del. includePlugins-->',fileContent))
 | 
				
			||||||
            f.close()
 | 
					            f.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    filesToPatch2 = ('profiles/default/skins.xml')
 | 
					    missingIncludes = ('plone.app.upgrade', 'plonetheme.sunburst',
 | 
				
			||||||
    def patchPlone4(self):
 | 
					                       'plonetheme.classic')
 | 
				
			||||||
 | 
					    def patchPlone4(self, versions):
 | 
				
			||||||
        '''Patches Plone 4 that can't live without buildout as-is.'''
 | 
					        '''Patches Plone 4 that can't live without buildout as-is.'''
 | 
				
			||||||
        self.patchPlone3x() # We still need this for Plone 4 as well.
 | 
					        self.patchPlone3x() # We still need this for Plone 4 as well.
 | 
				
			||||||
        # bin/zopectl
 | 
					        # bin/zopectl
 | 
				
			||||||
| 
						 | 
					@ -124,42 +125,44 @@ class NewScript:
 | 
				
			||||||
        f.write(content)
 | 
					        f.write(content)
 | 
				
			||||||
        f.close()
 | 
					        f.close()
 | 
				
			||||||
        j = os.path.join
 | 
					        j = os.path.join
 | 
				
			||||||
        themeFolder = '%s/plonetheme' % self.libFolder
 | 
					        # As eggs have been deleted, versions of components are lost. Reify
 | 
				
			||||||
        for theme in os.listdir(themeFolder):
 | 
					        # them from p_versions.
 | 
				
			||||||
            # Create a simlink to every theme in self.productsFolder
 | 
					        dVersions = ['"%s":"%s"' % (n, v) for n, v in versions.iteritems()]
 | 
				
			||||||
            tFolder = j(themeFolder, theme)
 | 
					        sVersions = 'appyVersions = {' + ','.join(dVersions) + '}'
 | 
				
			||||||
            if not os.path.isdir(tFolder): continue
 | 
					 | 
				
			||||||
            os.system('ln -s %s %s/%s' % (tFolder, self.productsFolder, theme))
 | 
					 | 
				
			||||||
            # Patch skins.xml
 | 
					 | 
				
			||||||
            fileName = '%s/profiles/default/skins.xml' % tFolder
 | 
					 | 
				
			||||||
            f = file(fileName)
 | 
					 | 
				
			||||||
            content = f.read()
 | 
					 | 
				
			||||||
            f.close()
 | 
					 | 
				
			||||||
            f = file(fileName, 'w')
 | 
					 | 
				
			||||||
            f.write(content.replace('plonetheme.%s:' % theme, '%s/' % theme))
 | 
					 | 
				
			||||||
            f.close()
 | 
					 | 
				
			||||||
        # As eggs have been deleted, Plone can't tell which version of Zope and
 | 
					 | 
				
			||||||
        # Plone are there. So we patch the code that tries to get Plone and Zope
 | 
					 | 
				
			||||||
        # versions.
 | 
					 | 
				
			||||||
        codeFile = "%s/pkg_resources.py" % self.libFolder
 | 
					        codeFile = "%s/pkg_resources.py" % self.libFolder
 | 
				
			||||||
        f = file(codeFile)
 | 
					        f = file(codeFile)
 | 
				
			||||||
        content = f.read()
 | 
					        content = sVersions + '\n' + f.read()
 | 
				
			||||||
        f.close()
 | 
					        f.close()
 | 
				
			||||||
        content = content.replace("raise DistributionNotFound(req)",
 | 
					        content = content.replace("raise DistributionNotFound(req)",
 | 
				
			||||||
            "dist = Distribution(project_name=req.project_name, " \
 | 
					            "dist = Distribution(project_name=req.project_name, " \
 | 
				
			||||||
            "version='1.1.1', platform='linux2', location='%s')" % \
 | 
					            "version=appyVersions[req.project_name], platform='linux2', " \
 | 
				
			||||||
            self.instancePath)
 | 
					            "location='%s')" % self.instancePath)
 | 
				
			||||||
        f = file(codeFile, 'w')
 | 
					        f = file(codeFile, 'w')
 | 
				
			||||||
        f.write(content)
 | 
					        f.write(content)
 | 
				
			||||||
        f.close()
 | 
					        f.close()
 | 
				
			||||||
 | 
					        # Some 'include' directives must be added with our install.
 | 
				
			||||||
 | 
					        configPlone = j(self.productsFolder, 'CMFPlone', 'configure.zcml')
 | 
				
			||||||
 | 
					        f = file(configPlone)
 | 
				
			||||||
 | 
					        content = f.read()
 | 
				
			||||||
 | 
					        f.close()
 | 
				
			||||||
 | 
					        missing = ''
 | 
				
			||||||
 | 
					        for missingInclude in self.missingIncludes:
 | 
				
			||||||
 | 
					            missing += '  <include package="%s"/>\n' % missingInclude
 | 
				
			||||||
 | 
					        content = content.replace('</configure>', '%s\n</configure>' % missing)
 | 
				
			||||||
 | 
					        f = file(configPlone, 'w')
 | 
				
			||||||
 | 
					        f.write(content)
 | 
				
			||||||
 | 
					        f.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def copyEggs(self):
 | 
					    def copyEggs(self):
 | 
				
			||||||
        '''Copy content of eggs into the Zope instance.'''
 | 
					        '''Copy content of eggs into the Zope instance. This method also
 | 
				
			||||||
 | 
					           retrieves every egg version and returns a dict {s_egg:s_version}.'''
 | 
				
			||||||
        j = os.path.join
 | 
					        j = os.path.join
 | 
				
			||||||
        eggsFolder = j(self.plonePath, 'buildout-cache/eggs')
 | 
					        eggsFolder = j(self.plonePath, 'buildout-cache/eggs')
 | 
				
			||||||
        self.ploneThemes = []
 | 
					        res = {}
 | 
				
			||||||
        for name in os.listdir(eggsFolder):
 | 
					        for name in os.listdir(eggsFolder):
 | 
				
			||||||
            if name == 'EGG-INFO': continue
 | 
					            if name == 'EGG-INFO': continue
 | 
				
			||||||
 | 
					            splittedName = name.split('-')
 | 
				
			||||||
 | 
					            res[splittedName[0]] = splittedName[1]
 | 
				
			||||||
            absName = j(eggsFolder, name)
 | 
					            absName = j(eggsFolder, name)
 | 
				
			||||||
            # Copy every file or sub-folder into self.libFolder or
 | 
					            # Copy every file or sub-folder into self.libFolder or
 | 
				
			||||||
            # self.productsFolder.
 | 
					            # self.productsFolder.
 | 
				
			||||||
| 
						 | 
					@ -175,6 +178,7 @@ class NewScript:
 | 
				
			||||||
                    copyFolder(absFileName, j(self.libFolder, fileName))
 | 
					                    copyFolder(absFileName, j(self.libFolder, fileName))
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    shutil.copy(absFileName, self.libFolder)
 | 
					                    shutil.copy(absFileName, self.libFolder)
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createInstance(self, linksForProducts):
 | 
					    def createInstance(self, linksForProducts):
 | 
				
			||||||
        '''Calls the Zope script that allows to create a Zope instance and copy
 | 
					        '''Calls the Zope script that allows to create a Zope instance and copy
 | 
				
			||||||
| 
						 | 
					@ -224,7 +228,7 @@ class NewScript:
 | 
				
			||||||
        if self.ploneVersion in ('plone25', 'plone30'):
 | 
					        if self.ploneVersion in ('plone25', 'plone30'):
 | 
				
			||||||
            self.installPlone25or30Stuff(linksForProducts)
 | 
					            self.installPlone25or30Stuff(linksForProducts)
 | 
				
			||||||
        elif self.ploneVersion in ('plone3x', 'plone4'):
 | 
					        elif self.ploneVersion in ('plone3x', 'plone4'):
 | 
				
			||||||
            self.copyEggs()
 | 
					            versions = self.copyEggs()
 | 
				
			||||||
            if self.ploneVersion == 'plone3x':
 | 
					            if self.ploneVersion == 'plone3x':
 | 
				
			||||||
                self.patchPlone3x()
 | 
					                self.patchPlone3x()
 | 
				
			||||||
            elif self.ploneVersion == 'plone4':
 | 
					            elif self.ploneVersion == 'plone4':
 | 
				
			||||||
| 
						 | 
					@ -234,7 +238,7 @@ class NewScript:
 | 
				
			||||||
                     j(self.instancePath, 'lib/python'))
 | 
					                     j(self.instancePath, 'lib/python'))
 | 
				
			||||||
                print cmd
 | 
					                print cmd
 | 
				
			||||||
                os.system(cmd)
 | 
					                os.system(cmd)
 | 
				
			||||||
                self.patchPlone4()
 | 
					                self.patchPlone4(versions)
 | 
				
			||||||
        # Remove .bat files under Linux
 | 
					        # Remove .bat files under Linux
 | 
				
			||||||
        if os.name == 'posix':
 | 
					        if os.name == 'posix':
 | 
				
			||||||
            cleanFolder(j(self.instancePath, 'bin'), exts=('.bat',))
 | 
					            cleanFolder(j(self.instancePath, 'bin'), exts=('.bat',))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										181
									
								
								bin/publish.py
									
										
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										181
									
								
								bin/publish.py
									
										
									
									
									
										
										
										Executable file → Normal file
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
#!/usr/bin/python2.4.4
 | 
					#!/usr/bin/python
 | 
				
			||||||
# Imports ----------------------------------------------------------------------
 | 
					# Imports ----------------------------------------------------------------------
 | 
				
			||||||
import os, os.path, shutil, re, zipfile, sys, ftplib, time
 | 
					import os, os.path, sys, shutil, re, zipfile, sys, ftplib, time
 | 
				
			||||||
import appy
 | 
					import appy
 | 
				
			||||||
from appy.shared import appyPath
 | 
					from appy.shared import appyPath
 | 
				
			||||||
from appy.shared.utils import FolderDeleter, LinesCounter
 | 
					from appy.shared.utils import FolderDeleter, LinesCounter
 | 
				
			||||||
| 
						 | 
					@ -9,17 +9,23 @@ from appy.gen.utils import produceNiceMessage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
versionRex = re.compile('(\d+\.\d+\.\d+)')
 | 
					versionRex = re.compile('(\d+\.\d+\.\d+)')
 | 
				
			||||||
eggInfo = '''import os, setuptools
 | 
					distInfo = '''from distutils.core import setup
 | 
				
			||||||
setuptools.setup(
 | 
					setup(name = "appy", version = "%s",
 | 
				
			||||||
    name = "appy", version = "%s", description = "The Appy framework",
 | 
					      description = "The Appy framework",
 | 
				
			||||||
    long_description = "See http://appyframework.org",
 | 
					      long_description = "Appy builds simple but complex web Python apps.",
 | 
				
			||||||
    author = "Gaetan Delannay", author_email = "gaetan.delannay AT gmail.com",
 | 
					      author = "Gaetan Delannay",
 | 
				
			||||||
    license = "GPL", keywords = "plone, pod, pdf, odt, document",
 | 
					      author_email = "gaetan.delannay AT geezteem.com",
 | 
				
			||||||
    url = 'http://appyframework.org',
 | 
					      license = "GPL", platforms="all",
 | 
				
			||||||
    classifiers = ['Development Status :: 4 - Beta', "License :: OSI Approved"],
 | 
					      url = 'http://appyframework.org',
 | 
				
			||||||
    packages = setuptools.find_packages('src'), include_package_data = True,
 | 
					      packages = [%s],
 | 
				
			||||||
    package_dir = {'':'src'}, data_files = [('.', [])],
 | 
					      package_data = {'':["*.*"]})
 | 
				
			||||||
    namespace_packages = ['appy'], zip_safe = False)'''
 | 
					'''
 | 
				
			||||||
 | 
					manifestInfo = '''
 | 
				
			||||||
 | 
					recursive-include appy/bin *
 | 
				
			||||||
 | 
					recursive-include appy/gen *
 | 
				
			||||||
 | 
					recursive-include appy/pod *
 | 
				
			||||||
 | 
					recursive-include appy/shared *
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def askLogin():
 | 
					def askLogin():
 | 
				
			||||||
    print 'Login: ',
 | 
					    print 'Login: ',
 | 
				
			||||||
| 
						 | 
					@ -28,29 +34,6 @@ def askLogin():
 | 
				
			||||||
    passwd = sys.stdin.readline().strip()
 | 
					    passwd = sys.stdin.readline().strip()
 | 
				
			||||||
    return (login, passwd)
 | 
					    return (login, passwd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def askQuestion(question, default='yes'):
 | 
					 | 
				
			||||||
    '''Asks a question to the user (yes/no) and returns True if the user
 | 
					 | 
				
			||||||
        answered "yes".'''
 | 
					 | 
				
			||||||
    defaultIsYes = (default.lower() in ('y', 'yes'))
 | 
					 | 
				
			||||||
    if defaultIsYes:
 | 
					 | 
				
			||||||
        yesNo = '[Y/n]'
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
        yesNo = '[y/N]'
 | 
					 | 
				
			||||||
    print question + ' ' + yesNo + ' ',
 | 
					 | 
				
			||||||
    response = sys.stdin.readline().strip().lower()
 | 
					 | 
				
			||||||
    res = False
 | 
					 | 
				
			||||||
    if response in ('y', 'yes'):
 | 
					 | 
				
			||||||
        res = True
 | 
					 | 
				
			||||||
    elif response in ('n', 'no'):
 | 
					 | 
				
			||||||
        res = False
 | 
					 | 
				
			||||||
    elif not response:
 | 
					 | 
				
			||||||
        # It depends on default value
 | 
					 | 
				
			||||||
        if defaultIsYes:
 | 
					 | 
				
			||||||
            res = True
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            res = False
 | 
					 | 
				
			||||||
    return res
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FtpFolder:
 | 
					class FtpFolder:
 | 
				
			||||||
    '''Represents a folder on a FTP site.'''
 | 
					    '''Represents a folder on a FTP site.'''
 | 
				
			||||||
    def __init__(self, name):
 | 
					    def __init__(self, name):
 | 
				
			||||||
| 
						 | 
					@ -247,52 +230,95 @@ class Publisher:
 | 
				
			||||||
        self.versionLong = '%s (%s)' % (self.versionShort,
 | 
					        self.versionLong = '%s (%s)' % (self.versionShort,
 | 
				
			||||||
                                        time.strftime('%Y/%m/%d %H:%M'))
 | 
					                                        time.strftime('%Y/%m/%d %H:%M'))
 | 
				
			||||||
        f.close()
 | 
					        f.close()
 | 
				
			||||||
 | 
					        # In silent mode (option -s), no question is asked, default answers are
 | 
				
			||||||
 | 
					        # automatically given.
 | 
				
			||||||
 | 
					        if (len(sys.argv) > 1) and (sys.argv[1] == '-s'):
 | 
				
			||||||
 | 
					            self.silent = True
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.silent = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def askQuestion(self, question, default='yes'):
 | 
				
			||||||
 | 
					        '''Asks a question to the user (yes/no) and returns True if the user
 | 
				
			||||||
 | 
					            answered "yes".'''
 | 
				
			||||||
 | 
					        if self.silent: return (default == 'yes')
 | 
				
			||||||
 | 
					        defaultIsYes = (default.lower() in ('y', 'yes'))
 | 
				
			||||||
 | 
					        if defaultIsYes:
 | 
				
			||||||
 | 
					            yesNo = '[Y/n]'
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            yesNo = '[y/N]'
 | 
				
			||||||
 | 
					        print question + ' ' + yesNo + ' ',
 | 
				
			||||||
 | 
					        response = sys.stdin.readline().strip().lower()
 | 
				
			||||||
 | 
					        res = False
 | 
				
			||||||
 | 
					        if response in ('y', 'yes'):
 | 
				
			||||||
 | 
					            res = True
 | 
				
			||||||
 | 
					        elif response in ('n', 'no'):
 | 
				
			||||||
 | 
					            res = False
 | 
				
			||||||
 | 
					        elif not response:
 | 
				
			||||||
 | 
					            # It depends on default value
 | 
				
			||||||
 | 
					            if defaultIsYes:
 | 
				
			||||||
 | 
					                res = True
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                res = False
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def executeCommand(self, cmd):
 | 
					    def executeCommand(self, cmd):
 | 
				
			||||||
        '''Executes the system command p_cmd.'''
 | 
					        '''Executes the system command p_cmd.'''
 | 
				
			||||||
        print 'Executing %s...' % cmd
 | 
					        print 'Executing %s...' % cmd
 | 
				
			||||||
        os.system(cmd)
 | 
					        os.system(cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createCodeAndEggReleases(self):
 | 
					    distExcluded = ('appy/doc', 'appy/temp', 'appy/versions', 'appy/gen/test')
 | 
				
			||||||
        '''Publishes the egg on pypi.python.org.'''
 | 
					    def isDistExcluded(self, name):
 | 
				
			||||||
 | 
					        '''Returns True if folder named p_name must be included in the
 | 
				
			||||||
 | 
					           distribution.'''
 | 
				
			||||||
 | 
					        if '.bzr' in name: return True
 | 
				
			||||||
 | 
					        for prefix in self.distExcluded:
 | 
				
			||||||
 | 
					            if name.startswith(prefix): return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def createDistRelease(self):
 | 
				
			||||||
 | 
					        '''Create the distutils package.'''
 | 
				
			||||||
        curdir = os.getcwd()
 | 
					        curdir = os.getcwd()
 | 
				
			||||||
        if askQuestion('Upload eggs on PyPI?', default='no'):
 | 
					        distFolder = '%s/dist' % self.genFolder
 | 
				
			||||||
            # Create egg structure
 | 
					        # Create setup.py
 | 
				
			||||||
            eggFolder = '%s/egg' % self.genFolder
 | 
					        os.mkdir(distFolder)
 | 
				
			||||||
            os.mkdir(eggFolder)
 | 
					        f = file('%s/setup.py' % distFolder, 'w')
 | 
				
			||||||
            f = file('%s/setup.py' % eggFolder, 'w')
 | 
					        # List all packages to include
 | 
				
			||||||
            f.write(eggInfo % self.versionShort)
 | 
					        packages = []
 | 
				
			||||||
            f.close()
 | 
					        os.chdir(os.path.dirname(appyPath))
 | 
				
			||||||
            os.mkdir('%s/docs' % eggFolder)
 | 
					        for dir, dirnames, filenames in os.walk('appy'):
 | 
				
			||||||
            os.mkdir('%s/src' % eggFolder)
 | 
					            if self.isDistExcluded(dir): continue
 | 
				
			||||||
            os.mkdir('%s/src/appy' % eggFolder)
 | 
					            packageName = dir.replace('/', '.')
 | 
				
			||||||
            shutil.copy('%s/doc/version.txt' % appyPath,
 | 
					            packages.append('"%s"' % packageName)
 | 
				
			||||||
                        '%s/docs/HISTORY.txt' % eggFolder)
 | 
					        f.write(distInfo % (self.versionShort, ','.join(packages)))
 | 
				
			||||||
            shutil.copy('%s/doc/license.txt' % appyPath,
 | 
					        f.close()
 | 
				
			||||||
                        '%s/docs/LICENSE.txt' % eggFolder)
 | 
					        # Create MANIFEST.in
 | 
				
			||||||
            # Move appy sources within the egg
 | 
					        f = file('%s/MANIFEST.in' % distFolder, 'w')
 | 
				
			||||||
            os.rename('%s/appy' % self.genFolder, '%s/src/appy' % eggFolder)
 | 
					        f.write(manifestInfo)
 | 
				
			||||||
            # Create eggs and publish them on pypi
 | 
					        f.close()
 | 
				
			||||||
            os.chdir(eggFolder)
 | 
					        # Move appy sources within the dist folder
 | 
				
			||||||
            print 'Uploading appy%s source egg on PyPI...' % self.versionShort
 | 
					        os.rename('%s/appy' % self.genFolder, '%s/appy' % distFolder)
 | 
				
			||||||
            #self.executeCommand('python setup.py sdist upload')
 | 
					        # Create the source distribution
 | 
				
			||||||
            self.executeCommand('python setup.py sdist')
 | 
					        os.chdir(distFolder)
 | 
				
			||||||
            for pythonTarget in self.pythonTargets:
 | 
					        self.executeCommand('python setup.py sdist')
 | 
				
			||||||
                print 'Uploading appy%s binary egg for python%s...' % \
 | 
					        # DistUtils has created the .tar.gz file. Copy it into folder "versions"
 | 
				
			||||||
                      (self.versionShort, pythonTarget)
 | 
					        name = 'appy-%s.tar.gz' % self.versionShort
 | 
				
			||||||
                #self.executeCommand('python%s setup.py bdist_egg upload' % \
 | 
					        os.rename('%s/dist/%s' % (distFolder, name),
 | 
				
			||||||
                #    pythonTarget)
 | 
					                  '%s/versions/%s' % (appyPath, name))
 | 
				
			||||||
                self.executeCommand('python%s setup.py bdist_egg' % \
 | 
					        # Clean temp files
 | 
				
			||||||
                    pythonTarget)
 | 
					 | 
				
			||||||
        os.chdir(curdir)
 | 
					        os.chdir(curdir)
 | 
				
			||||||
 | 
					        FolderDeleter.delete(os.path.join(self.genFolder, 'dist'))
 | 
				
			||||||
 | 
					        return name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def uploadOnPypi(self, name):
 | 
				
			||||||
 | 
					        print 'Uploading %s on PyPI...' % name
 | 
				
			||||||
 | 
					        #self.executeCommand('python setup.py sdist upload')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createZipRelease(self):
 | 
					    def createZipRelease(self):
 | 
				
			||||||
        '''Creates a zip file with the appy sources.'''
 | 
					        '''Creates a zip file with the appy sources.'''
 | 
				
			||||||
        newZipRelease = '%s/versions/appy%s.zip' % (appyPath, self.versionShort)
 | 
					        newZipRelease = '%s/versions/appy%s.zip' % (appyPath, self.versionShort)
 | 
				
			||||||
        if os.path.exists(newZipRelease):
 | 
					        if os.path.exists(newZipRelease):
 | 
				
			||||||
            if not askQuestion('"%s" already exists. Replace it?' % \
 | 
					            if not self.askQuestion('"%s" already exists. Replace it?' % \
 | 
				
			||||||
                               newZipRelease, default='yes'):
 | 
					                                    newZipRelease, default='yes'):
 | 
				
			||||||
                print 'Publication cancelled.'
 | 
					                print 'Publication canceled.'
 | 
				
			||||||
                sys.exit(1)
 | 
					                sys.exit(1)
 | 
				
			||||||
            print 'Removing obsolete %s...' % newZipRelease
 | 
					            print 'Removing obsolete %s...' % newZipRelease
 | 
				
			||||||
            os.remove(newZipRelease)
 | 
					            os.remove(newZipRelease)
 | 
				
			||||||
| 
						 | 
					@ -306,8 +332,6 @@ class Publisher:
 | 
				
			||||||
                # [2:] is there to avoid havin './' in the path in the zip file.
 | 
					                # [2:] is there to avoid havin './' in the path in the zip file.
 | 
				
			||||||
        zipFile.close()
 | 
					        zipFile.close()
 | 
				
			||||||
        os.chdir(curdir)
 | 
					        os.chdir(curdir)
 | 
				
			||||||
        # Remove the "appy" folder within the gen folder.
 | 
					 | 
				
			||||||
        FolderDeleter.delete(os.path.join(self.genFolder, 'appy'))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def applyTemplate(self):
 | 
					    def applyTemplate(self):
 | 
				
			||||||
        '''Decorates each page with the template.'''
 | 
					        '''Decorates each page with the template.'''
 | 
				
			||||||
| 
						 | 
					@ -405,7 +429,7 @@ class Publisher:
 | 
				
			||||||
        # Create a temp clean copy of appy sources (without .svn folders, etc)
 | 
					        # Create a temp clean copy of appy sources (without .svn folders, etc)
 | 
				
			||||||
        genSrcFolder = '%s/appy' % self.genFolder
 | 
					        genSrcFolder = '%s/appy' % self.genFolder
 | 
				
			||||||
        os.mkdir(genSrcFolder)
 | 
					        os.mkdir(genSrcFolder)
 | 
				
			||||||
        for aFile in ('__init__.py', 'install.txt'):
 | 
					        for aFile in ('__init__.py',):
 | 
				
			||||||
            shutil.copy('%s/%s' % (appyPath, aFile), genSrcFolder)
 | 
					            shutil.copy('%s/%s' % (appyPath, aFile), genSrcFolder)
 | 
				
			||||||
        for aFolder in ('gen', 'pod', 'shared', 'bin'):
 | 
					        for aFolder in ('gen', 'pod', 'shared', 'bin'):
 | 
				
			||||||
            shutil.copytree('%s/%s' % (appyPath, aFolder),
 | 
					            shutil.copytree('%s/%s' % (appyPath, aFolder),
 | 
				
			||||||
| 
						 | 
					@ -438,17 +462,18 @@ class Publisher:
 | 
				
			||||||
        # Perform a small analysis on the Appy code
 | 
					        # Perform a small analysis on the Appy code
 | 
				
			||||||
        LinesCounter(appy).run()
 | 
					        LinesCounter(appy).run()
 | 
				
			||||||
        print 'Generating site in %s...' % self.genFolder
 | 
					        print 'Generating site in %s...' % self.genFolder
 | 
				
			||||||
        minimalist = askQuestion('Minimalist (shipped without tests)?',
 | 
					        minimalist = self.askQuestion('Minimalist (shipped without tests)?',
 | 
				
			||||||
                                 default='no')
 | 
					                                      default='no')
 | 
				
			||||||
        self.prepareGenFolder(minimalist)
 | 
					        self.prepareGenFolder(minimalist)
 | 
				
			||||||
        self.createDocToc()
 | 
					        self.createDocToc()
 | 
				
			||||||
        self.applyTemplate()
 | 
					        self.applyTemplate()
 | 
				
			||||||
        self.createZipRelease()
 | 
					        self.createZipRelease()
 | 
				
			||||||
        #self.createCodeAndEggReleases()
 | 
					        tarball = self.createDistRelease()
 | 
				
			||||||
        if askQuestion('Do you want to publish the site on ' \
 | 
					        if self.askQuestion('Upload %s on PyPI?' % tarball, default='no'):
 | 
				
			||||||
                       'appyframework.org?', default='no'):
 | 
					            self.uploadOnPypi(tarball)
 | 
				
			||||||
 | 
					        if self.askQuestion('Publish on appyframework.org?', default='no'):
 | 
				
			||||||
            AppySite().publish()
 | 
					            AppySite().publish()
 | 
				
			||||||
        if askQuestion('Delete locally generated site ?', default='no'):
 | 
					        if self.askQuestion('Delete locally generated site ?', default='yes'):
 | 
				
			||||||
            FolderDeleter.delete(self.genFolder)
 | 
					            FolderDeleter.delete(self.genFolder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,12 +5,13 @@ from appy import Object
 | 
				
			||||||
from appy.gen.layout import Table
 | 
					from appy.gen.layout import Table
 | 
				
			||||||
from appy.gen.layout import defaultFieldLayouts
 | 
					from appy.gen.layout import defaultFieldLayouts
 | 
				
			||||||
from appy.gen.po import PoMessage
 | 
					from appy.gen.po import PoMessage
 | 
				
			||||||
from appy.gen.utils import sequenceTypes, GroupDescr, Keywords, FileWrapper, \
 | 
					from appy.gen.utils import sequenceTypes, GroupDescr, Keywords, getClassName, \
 | 
				
			||||||
                           getClassName, SomeObjects
 | 
					                           SomeObjects
 | 
				
			||||||
import appy.pod
 | 
					import appy.pod
 | 
				
			||||||
from appy.pod.renderer import Renderer
 | 
					from appy.pod.renderer import Renderer
 | 
				
			||||||
from appy.shared.data import countries
 | 
					from appy.shared.data import countries
 | 
				
			||||||
from appy.shared.utils import Traceback, getOsTempFolder, formatNumber
 | 
					from appy.shared.utils import Traceback, getOsTempFolder, formatNumber, \
 | 
				
			||||||
 | 
					                              FileWrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Default Appy permissions -----------------------------------------------------
 | 
					# Default Appy permissions -----------------------------------------------------
 | 
				
			||||||
r, w, d = ('read', 'write', 'delete')
 | 
					r, w, d = ('read', 'write', 'delete')
 | 
				
			||||||
| 
						 | 
					@ -1505,7 +1506,7 @@ class File(Type):
 | 
				
			||||||
    def getRequestValue(self, request):
 | 
					    def getRequestValue(self, request):
 | 
				
			||||||
        return request.get('%s_file' % self.name)
 | 
					        return request.get('%s_file' % self.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getDefaultLayouts(self): return {'view':'lf','edit':'lrv-f'}
 | 
					    def getDefaultLayouts(self): return {'view':'l-f','edit':'lrv-f'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def isEmptyValue(self, value, obj=None):
 | 
					    def isEmptyValue(self, value, obj=None):
 | 
				
			||||||
        '''Must p_value be considered as empty?'''
 | 
					        '''Must p_value be considered as empty?'''
 | 
				
			||||||
| 
						 | 
					@ -1536,8 +1537,9 @@ class File(Type):
 | 
				
			||||||
           * an instance of Zope class ZPublisher.HTTPRequest.FileUpload. In
 | 
					           * an instance of Zope class ZPublisher.HTTPRequest.FileUpload. In
 | 
				
			||||||
             this case, it is file content coming from a HTTP POST;
 | 
					             this case, it is file content coming from a HTTP POST;
 | 
				
			||||||
           * an instance of Zope class OFS.Image.File;
 | 
					           * an instance of Zope class OFS.Image.File;
 | 
				
			||||||
           * an instance of appy.gen.utils.FileWrapper, which wraps an instance
 | 
					           * an instance of appy.shared.utils.FileWrapper, which wraps an
 | 
				
			||||||
             of OFS.Image.File and adds useful methods for manipulating it;
 | 
					             instance of OFS.Image.File and adds useful methods for manipulating
 | 
				
			||||||
 | 
					             it;
 | 
				
			||||||
           * a string. In this case, the string represents the path of a file
 | 
					           * a string. In this case, the string represents the path of a file
 | 
				
			||||||
             on disk;
 | 
					             on disk;
 | 
				
			||||||
           * a 2-tuple (fileName, fileContent) where:
 | 
					           * a 2-tuple (fileName, fileContent) where:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -675,7 +675,7 @@ class ZopeGenerator(Generator):
 | 
				
			||||||
        repls['languages'] = ','.join('"%s"' % l for l in self.config.languages)
 | 
					        repls['languages'] = ','.join('"%s"' % l for l in self.config.languages)
 | 
				
			||||||
        repls['languageSelector'] = self.config.languageSelector
 | 
					        repls['languageSelector'] = self.config.languageSelector
 | 
				
			||||||
        repls['sourceLanguage'] = self.config.sourceLanguage
 | 
					        repls['sourceLanguage'] = self.config.sourceLanguage
 | 
				
			||||||
        self.copyFile('config.py', repls)
 | 
					        self.copyFile('config.pyt', repls, destName='config.py')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def generateInit(self):
 | 
					    def generateInit(self):
 | 
				
			||||||
        # Compute imports
 | 
					        # Compute imports
 | 
				
			||||||
| 
						 | 
					@ -690,7 +690,7 @@ class ZopeGenerator(Generator):
 | 
				
			||||||
        repls['imports'] = '\n'.join(imports)
 | 
					        repls['imports'] = '\n'.join(imports)
 | 
				
			||||||
        repls['classes'] = ','.join(classNames)
 | 
					        repls['classes'] = ','.join(classNames)
 | 
				
			||||||
        repls['totalNumberOfTests'] = self.totalNumberOfTests
 | 
					        repls['totalNumberOfTests'] = self.totalNumberOfTests
 | 
				
			||||||
        self.copyFile('__init__.py', repls)
 | 
					        self.copyFile('__init__.pyt', repls, destName='__init__.py')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getClassesInOrder(self, allClasses):
 | 
					    def getClassesInOrder(self, allClasses):
 | 
				
			||||||
        '''When generating wrappers, classes mut be dumped in order (else, it
 | 
					        '''When generating wrappers, classes mut be dumped in order (else, it
 | 
				
			||||||
| 
						 | 
					@ -757,7 +757,7 @@ class ZopeGenerator(Generator):
 | 
				
			||||||
        for klass in self.getClasses(include='predefined'):
 | 
					        for klass in self.getClasses(include='predefined'):
 | 
				
			||||||
            modelClass = klass.modelClass
 | 
					            modelClass = klass.modelClass
 | 
				
			||||||
            repls['%s' % modelClass.__name__] = modelClass._appy_getBody()
 | 
					            repls['%s' % modelClass.__name__] = modelClass._appy_getBody()
 | 
				
			||||||
        self.copyFile('wrappers.py', repls)
 | 
					        self.copyFile('wrappers.pyt', repls, destName='wrappers.py')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def generateTests(self):
 | 
					    def generateTests(self):
 | 
				
			||||||
        '''Generates the file needed for executing tests.'''
 | 
					        '''Generates the file needed for executing tests.'''
 | 
				
			||||||
| 
						 | 
					@ -765,7 +765,8 @@ class ZopeGenerator(Generator):
 | 
				
			||||||
        modules = self.modulesWithTests
 | 
					        modules = self.modulesWithTests
 | 
				
			||||||
        repls['imports'] = '\n'.join(['import %s' % m for m in modules])
 | 
					        repls['imports'] = '\n'.join(['import %s' % m for m in modules])
 | 
				
			||||||
        repls['modulesWithTests'] = ','.join(modules)
 | 
					        repls['modulesWithTests'] = ','.join(modules)
 | 
				
			||||||
        self.copyFile('testAll.py', repls, destFolder='tests')
 | 
					        self.copyFile('testAll.pyt', repls, destName='testAll.py',
 | 
				
			||||||
 | 
					                      destFolder='tests')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def generateTool(self):
 | 
					    def generateTool(self):
 | 
				
			||||||
        '''Generates the tool that corresponds to this application.'''
 | 
					        '''Generates the tool that corresponds to this application.'''
 | 
				
			||||||
| 
						 | 
					@ -790,7 +791,7 @@ class ZopeGenerator(Generator):
 | 
				
			||||||
            repls.update({'methods': klass.methods, 'genClassName': klass.name,
 | 
					            repls.update({'methods': klass.methods, 'genClassName': klass.name,
 | 
				
			||||||
              'baseMixin':'BaseMixin', 'parents': 'BaseMixin, SimpleItem',
 | 
					              'baseMixin':'BaseMixin', 'parents': 'BaseMixin, SimpleItem',
 | 
				
			||||||
              'classDoc': 'Standard Appy class', 'icon':'object.gif'})
 | 
					              'classDoc': 'Standard Appy class', 'icon':'object.gif'})
 | 
				
			||||||
            self.copyFile('Class.py', repls, destName='%s.py' % klass.name)
 | 
					            self.copyFile('Class.pyt', repls, destName='%s.py' % klass.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Before generating the Tool class, finalize it with query result
 | 
					        # Before generating the Tool class, finalize it with query result
 | 
				
			||||||
        # columns, with fields to propagate, workflow-related fields.
 | 
					        # columns, with fields to propagate, workflow-related fields.
 | 
				
			||||||
| 
						 | 
					@ -817,7 +818,7 @@ class ZopeGenerator(Generator):
 | 
				
			||||||
          'genClassName': self.tool.name, 'baseMixin':'ToolMixin',
 | 
					          'genClassName': self.tool.name, 'baseMixin':'ToolMixin',
 | 
				
			||||||
          'parents': 'ToolMixin, Folder', 'icon': 'folder.gif',
 | 
					          'parents': 'ToolMixin, Folder', 'icon': 'folder.gif',
 | 
				
			||||||
          'classDoc': 'Tool class for %s' % self.applicationName})
 | 
					          'classDoc': 'Tool class for %s' % self.applicationName})
 | 
				
			||||||
        self.copyFile('Class.py', repls, destName='%s.py' % self.tool.name)
 | 
					        self.copyFile('Class.pyt', repls, destName='%s.py' % self.tool.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def generateClass(self, classDescr):
 | 
					    def generateClass(self, classDescr):
 | 
				
			||||||
        '''Is called each time an Appy class is found in the application, for
 | 
					        '''Is called each time an Appy class is found in the application, for
 | 
				
			||||||
| 
						 | 
					@ -863,7 +864,7 @@ class ZopeGenerator(Generator):
 | 
				
			||||||
                if poMsg not in self.labels:
 | 
					                if poMsg not in self.labels:
 | 
				
			||||||
                    self.labels.append(poMsg)
 | 
					                    self.labels.append(poMsg)
 | 
				
			||||||
        # Generate the resulting Zope class.
 | 
					        # Generate the resulting Zope class.
 | 
				
			||||||
        self.copyFile('Class.py', repls, destName=fileName)
 | 
					        self.copyFile('Class.pyt', repls, destName=fileName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def generateWorkflow(self, wfDescr):
 | 
					    def generateWorkflow(self, wfDescr):
 | 
				
			||||||
        '''This method creates the i18n labels related to the workflow described
 | 
					        '''This method creates the i18n labels related to the workflow described
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -781,7 +781,7 @@ class ToolMixin(BaseMixin):
 | 
				
			||||||
                    if brain:
 | 
					                    if brain:
 | 
				
			||||||
                        sibling = brain.getObject()
 | 
					                        sibling = brain.getObject()
 | 
				
			||||||
                        res[urlKey] = sibling.getUrl(nav=newNav % (index + 1),
 | 
					                        res[urlKey] = sibling.getUrl(nav=newNav % (index + 1),
 | 
				
			||||||
                                                     page='main')
 | 
					                                          page=self.REQUEST.get('page', 'main'))
 | 
				
			||||||
        return res
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tabularize(self, data, numberOfRows):
 | 
					    def tabularize(self, data, numberOfRows):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										81
									
								
								gen/utils.py
									
										
									
									
									
								
							
							
						
						
									
										81
									
								
								gen/utils.py
									
										
									
									
									
								
							| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
import re, os, os.path, time
 | 
					import re, os, os.path
 | 
				
			||||||
import appy.pod
 | 
					import appy.pod
 | 
				
			||||||
from appy.shared.utils import getOsTempFolder, normalizeString, executeCommand
 | 
					 | 
				
			||||||
sequenceTypes = (list, tuple)
 | 
					sequenceTypes = (list, tuple)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Function for creating a Zope object ------------------------------------------
 | 
					# Function for creating a Zope object ------------------------------------------
 | 
				
			||||||
| 
						 | 
					@ -242,84 +241,6 @@ class Keywords:
 | 
				
			||||||
            return op.join(self.keywords)+'*'
 | 
					            return op.join(self.keywords)+'*'
 | 
				
			||||||
        return ''
 | 
					        return ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
CONVERSION_ERROR = 'An error occurred while executing command "%s". %s'
 | 
					 | 
				
			||||||
class FileWrapper:
 | 
					 | 
				
			||||||
    '''When you get, from an appy object, the value of a File attribute, you
 | 
					 | 
				
			||||||
       get an instance of this class.'''
 | 
					 | 
				
			||||||
    def __init__(self, zopeFile):
 | 
					 | 
				
			||||||
        '''This constructor is only used by Appy to create a nice File instance
 | 
					 | 
				
			||||||
           from a Zope corresponding instance (p_zopeFile). If you need to
 | 
					 | 
				
			||||||
           create a new file and assign it to a File attribute, use the
 | 
					 | 
				
			||||||
           attribute setter, do not create yourself an instance of this
 | 
					 | 
				
			||||||
           class.'''
 | 
					 | 
				
			||||||
        d = self.__dict__
 | 
					 | 
				
			||||||
        d['_zopeFile'] = zopeFile # Not for you!
 | 
					 | 
				
			||||||
        d['name'] = zopeFile.filename
 | 
					 | 
				
			||||||
        d['content'] = zopeFile.data
 | 
					 | 
				
			||||||
        d['mimeType'] = zopeFile.content_type
 | 
					 | 
				
			||||||
        d['size'] = zopeFile.size # In bytes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __setattr__(self, name, v):
 | 
					 | 
				
			||||||
        d = self.__dict__
 | 
					 | 
				
			||||||
        if name == 'name':
 | 
					 | 
				
			||||||
            self._zopeFile.filename = v
 | 
					 | 
				
			||||||
            d['name'] = v
 | 
					 | 
				
			||||||
        elif name == 'content':
 | 
					 | 
				
			||||||
            self._zopeFile.update_data(v, self.mimeType, len(v))
 | 
					 | 
				
			||||||
            d['content'] = v
 | 
					 | 
				
			||||||
            d['size'] = len(v)
 | 
					 | 
				
			||||||
        elif name == 'mimeType':
 | 
					 | 
				
			||||||
            self._zopeFile.content_type = self.mimeType = v
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            raise 'Impossible to set attribute %s. "Settable" attributes ' \
 | 
					 | 
				
			||||||
                  'are "name", "content" and "mimeType".' % name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def dump(self, filePath=None, format=None, tool=None):
 | 
					 | 
				
			||||||
        '''Writes the file on disk. If p_filePath is specified, it is the
 | 
					 | 
				
			||||||
           path name where the file will be dumped; folders mentioned in it
 | 
					 | 
				
			||||||
           must exist. If not, the file will be dumped in the OS temp folder.
 | 
					 | 
				
			||||||
           The absolute path name of the dumped file is returned.
 | 
					 | 
				
			||||||
           If an error occurs, the method returns None. If p_format is
 | 
					 | 
				
			||||||
           specified, OpenOffice will be called for converting the dumped file
 | 
					 | 
				
			||||||
           to the desired format. In this case, p_tool, a Appy tool, must be
 | 
					 | 
				
			||||||
           provided. Indeed, any Appy tool contains parameters for contacting
 | 
					 | 
				
			||||||
           OpenOffice in server mode.'''
 | 
					 | 
				
			||||||
        if not filePath:
 | 
					 | 
				
			||||||
            filePath = '%s/file%f.%s' % (getOsTempFolder(), time.time(),
 | 
					 | 
				
			||||||
                normalizeString(self.name))
 | 
					 | 
				
			||||||
        f = file(filePath, 'w')
 | 
					 | 
				
			||||||
        if self.content.__class__.__name__ == 'Pdata':
 | 
					 | 
				
			||||||
            # The file content is splitted in several chunks.
 | 
					 | 
				
			||||||
            f.write(self.content.data)
 | 
					 | 
				
			||||||
            nextPart = self.content.next
 | 
					 | 
				
			||||||
            while nextPart:
 | 
					 | 
				
			||||||
                f.write(nextPart.data)
 | 
					 | 
				
			||||||
                nextPart = nextPart.next
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            # Only one chunk
 | 
					 | 
				
			||||||
            f.write(self.content)
 | 
					 | 
				
			||||||
        f.close()
 | 
					 | 
				
			||||||
        if format:
 | 
					 | 
				
			||||||
            if not tool: return
 | 
					 | 
				
			||||||
            # Convert the dumped file using OpenOffice
 | 
					 | 
				
			||||||
            errorMessage = tool.convert(filePath, format)
 | 
					 | 
				
			||||||
            # Even if we have an "error" message, it could be a simple warning.
 | 
					 | 
				
			||||||
            # So we will continue here and, as a subsequent check for knowing if
 | 
					 | 
				
			||||||
            # an error occurred or not, we will test the existence of the
 | 
					 | 
				
			||||||
            # converted file (see below).
 | 
					 | 
				
			||||||
            os.remove(filePath)
 | 
					 | 
				
			||||||
            # Return the name of the converted file.
 | 
					 | 
				
			||||||
            baseName, ext = os.path.splitext(filePath)
 | 
					 | 
				
			||||||
            if (ext == '.%s' % format):
 | 
					 | 
				
			||||||
                filePath = '%s.res.%s' % (baseName, format)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                filePath = '%s.%s' % (baseName, format)
 | 
					 | 
				
			||||||
            if not os.path.exists(filePath):
 | 
					 | 
				
			||||||
                tool.log(CONVERSION_ERROR % (cmd, errorMessage), type='error')
 | 
					 | 
				
			||||||
                return
 | 
					 | 
				
			||||||
        return filePath
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
def getClassName(klass, appName=None):
 | 
					def getClassName(klass, appName=None):
 | 
				
			||||||
    '''Generates, from appy-class p_klass, the name of the corresponding
 | 
					    '''Generates, from appy-class p_klass, the name of the corresponding
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								install.txt
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								install.txt
									
										
									
									
									
								
							| 
						 | 
					@ -1,11 +0,0 @@
 | 
				
			||||||
Installation under Windows or MacOS
 | 
					 | 
				
			||||||
-----------------------------------
 | 
					 | 
				
			||||||
Copy the content of this folder to
 | 
					 | 
				
			||||||
    <where_you_installed_python>\Lib\site-packages\appy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Installation under Linux
 | 
					 | 
				
			||||||
------------------------
 | 
					 | 
				
			||||||
Copy the content of this folder wherever you want (in /opt/appy for example)
 | 
					 | 
				
			||||||
and make a symbolic link in your Python lib folder (for example:
 | 
					 | 
				
			||||||
"ln -s /opt/appy /usr/lib/python2.5/site-packages/appy").
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@
 | 
				
			||||||
import os, os.path, time, shutil, struct, random
 | 
					import os, os.path, time, shutil, struct, random
 | 
				
			||||||
from appy.pod import PodError
 | 
					from appy.pod import PodError
 | 
				
			||||||
from appy.pod.odf_parser import OdfEnvironment
 | 
					from appy.pod.odf_parser import OdfEnvironment
 | 
				
			||||||
 | 
					from appy.shared.utils import FileWrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
FILE_NOT_FOUND = "'%s' does not exist or is not a file."
 | 
					FILE_NOT_FOUND = "'%s' does not exist or is not a file."
 | 
				
			||||||
| 
						 | 
					@ -59,9 +60,12 @@ class DocImporter:
 | 
				
			||||||
            self.importPath = self.moveFile(at, self.importPath)
 | 
					            self.importPath = self.moveFile(at, self.importPath)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            # We need to dump the file content (in self.content) in a temp file
 | 
					            # We need to dump the file content (in self.content) in a temp file
 | 
				
			||||||
            # first. self.content may be binary or a file handler.
 | 
					            # first. self.content may be binary, a file handler or a
 | 
				
			||||||
 | 
					            # FileWrapper.
 | 
				
			||||||
            if isinstance(self.content, file):
 | 
					            if isinstance(self.content, file):
 | 
				
			||||||
                fileContent = self.content.read()
 | 
					                fileContent = self.content.read()
 | 
				
			||||||
 | 
					            elif isinstance(self.content, FileWrapper):
 | 
				
			||||||
 | 
					                fileContent = content.content
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                fileContent = self.content
 | 
					                fileContent = self.content
 | 
				
			||||||
            f = file(self.importPath, 'wb')
 | 
					            f = file(self.importPath, 'wb')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@ from appy.pod import PodError
 | 
				
			||||||
from appy.shared import mimeTypesExts
 | 
					from appy.shared import mimeTypesExts
 | 
				
			||||||
from appy.shared.xml_parser import XmlElement
 | 
					from appy.shared.xml_parser import XmlElement
 | 
				
			||||||
from appy.shared.utils import FolderDeleter, executeCommand
 | 
					from appy.shared.utils import FolderDeleter, executeCommand
 | 
				
			||||||
 | 
					from appy.shared.utils import FileWrapper
 | 
				
			||||||
from appy.pod.pod_parser import PodParser, PodEnvironment, OdInsert
 | 
					from appy.pod.pod_parser import PodParser, PodEnvironment, OdInsert
 | 
				
			||||||
from appy.pod.converter import FILE_TYPES
 | 
					from appy.pod.converter import FILE_TYPES
 | 
				
			||||||
from appy.pod.buffers import FileBuffer
 | 
					from appy.pod.buffers import FileBuffer
 | 
				
			||||||
| 
						 | 
					@ -281,6 +282,8 @@ class Renderer:
 | 
				
			||||||
        if not content and not at:
 | 
					        if not content and not at:
 | 
				
			||||||
            raise PodError(DOC_NOT_SPECIFIED)
 | 
					            raise PodError(DOC_NOT_SPECIFIED)
 | 
				
			||||||
        # Guess document format
 | 
					        # Guess document format
 | 
				
			||||||
 | 
					        if isinstance(content, FileWrapper):
 | 
				
			||||||
 | 
					            format = content.mimeType
 | 
				
			||||||
        if not format:
 | 
					        if not format:
 | 
				
			||||||
            # It should be deduced from p_at
 | 
					            # It should be deduced from p_at
 | 
				
			||||||
            if not at:
 | 
					            if not at:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -410,4 +410,82 @@ class LinesCounter:
 | 
				
			||||||
                elif ext in exts['pt']:
 | 
					                elif ext in exts['pt']:
 | 
				
			||||||
                    self.zpt[self.inTest].analyseFile(j(root, fileName))
 | 
					                    self.zpt[self.inTest].analyseFile(j(root, fileName))
 | 
				
			||||||
        self.printReport()
 | 
					        self.printReport()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					CONVERSION_ERROR = 'An error occurred while executing command "%s". %s'
 | 
				
			||||||
 | 
					class FileWrapper:
 | 
				
			||||||
 | 
					    '''When you get, from an appy object, the value of a File attribute, you
 | 
				
			||||||
 | 
					       get an instance of this class.'''
 | 
				
			||||||
 | 
					    def __init__(self, zopeFile):
 | 
				
			||||||
 | 
					        '''This constructor is only used by Appy to create a nice File instance
 | 
				
			||||||
 | 
					           from a Zope corresponding instance (p_zopeFile). If you need to
 | 
				
			||||||
 | 
					           create a new file and assign it to a File attribute, use the
 | 
				
			||||||
 | 
					           attribute setter, do not create yourself an instance of this
 | 
				
			||||||
 | 
					           class.'''
 | 
				
			||||||
 | 
					        d = self.__dict__
 | 
				
			||||||
 | 
					        d['_zopeFile'] = zopeFile # Not for you!
 | 
				
			||||||
 | 
					        d['name'] = zopeFile.filename
 | 
				
			||||||
 | 
					        d['content'] = zopeFile.data
 | 
				
			||||||
 | 
					        d['mimeType'] = zopeFile.content_type
 | 
				
			||||||
 | 
					        d['size'] = zopeFile.size # In bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __setattr__(self, name, v):
 | 
				
			||||||
 | 
					        d = self.__dict__
 | 
				
			||||||
 | 
					        if name == 'name':
 | 
				
			||||||
 | 
					            self._zopeFile.filename = v
 | 
				
			||||||
 | 
					            d['name'] = v
 | 
				
			||||||
 | 
					        elif name == 'content':
 | 
				
			||||||
 | 
					            self._zopeFile.update_data(v, self.mimeType, len(v))
 | 
				
			||||||
 | 
					            d['content'] = v
 | 
				
			||||||
 | 
					            d['size'] = len(v)
 | 
				
			||||||
 | 
					        elif name == 'mimeType':
 | 
				
			||||||
 | 
					            self._zopeFile.content_type = self.mimeType = v
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise 'Impossible to set attribute %s. "Settable" attributes ' \
 | 
				
			||||||
 | 
					                  'are "name", "content" and "mimeType".' % name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dump(self, filePath=None, format=None, tool=None):
 | 
				
			||||||
 | 
					        '''Writes the file on disk. If p_filePath is specified, it is the
 | 
				
			||||||
 | 
					           path name where the file will be dumped; folders mentioned in it
 | 
				
			||||||
 | 
					           must exist. If not, the file will be dumped in the OS temp folder.
 | 
				
			||||||
 | 
					           The absolute path name of the dumped file is returned.
 | 
				
			||||||
 | 
					           If an error occurs, the method returns None. If p_format is
 | 
				
			||||||
 | 
					           specified, OpenOffice will be called for converting the dumped file
 | 
				
			||||||
 | 
					           to the desired format. In this case, p_tool, a Appy tool, must be
 | 
				
			||||||
 | 
					           provided. Indeed, any Appy tool contains parameters for contacting
 | 
				
			||||||
 | 
					           OpenOffice in server mode.'''
 | 
				
			||||||
 | 
					        if not filePath:
 | 
				
			||||||
 | 
					            filePath = '%s/file%f.%s' % (getOsTempFolder(), time.time(),
 | 
				
			||||||
 | 
					                normalizeString(self.name))
 | 
				
			||||||
 | 
					        f = file(filePath, 'w')
 | 
				
			||||||
 | 
					        if self.content.__class__.__name__ == 'Pdata':
 | 
				
			||||||
 | 
					            # The file content is splitted in several chunks.
 | 
				
			||||||
 | 
					            f.write(self.content.data)
 | 
				
			||||||
 | 
					            nextPart = self.content.next
 | 
				
			||||||
 | 
					            while nextPart:
 | 
				
			||||||
 | 
					                f.write(nextPart.data)
 | 
				
			||||||
 | 
					                nextPart = nextPart.next
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # Only one chunk
 | 
				
			||||||
 | 
					            f.write(self.content)
 | 
				
			||||||
 | 
					        f.close()
 | 
				
			||||||
 | 
					        if format:
 | 
				
			||||||
 | 
					            if not tool: return
 | 
				
			||||||
 | 
					            # Convert the dumped file using OpenOffice
 | 
				
			||||||
 | 
					            errorMessage = tool.convert(filePath, format)
 | 
				
			||||||
 | 
					            # Even if we have an "error" message, it could be a simple warning.
 | 
				
			||||||
 | 
					            # So we will continue here and, as a subsequent check for knowing if
 | 
				
			||||||
 | 
					            # an error occurred or not, we will test the existence of the
 | 
				
			||||||
 | 
					            # converted file (see below).
 | 
				
			||||||
 | 
					            os.remove(filePath)
 | 
				
			||||||
 | 
					            # Return the name of the converted file.
 | 
				
			||||||
 | 
					            baseName, ext = os.path.splitext(filePath)
 | 
				
			||||||
 | 
					            if (ext == '.%s' % format):
 | 
				
			||||||
 | 
					                filePath = '%s.res.%s' % (baseName, format)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                filePath = '%s.%s' % (baseName, format)
 | 
				
			||||||
 | 
					            if not os.path.exists(filePath):
 | 
				
			||||||
 | 
					                tool.log(CONVERSION_ERROR % (cmd, errorMessage), type='error')
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					        return filePath
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue