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
|
@ -5,12 +5,13 @@ from appy import Object
|
|||
from appy.gen.layout import Table
|
||||
from appy.gen.layout import defaultFieldLayouts
|
||||
from appy.gen.po import PoMessage
|
||||
from appy.gen.utils import sequenceTypes, GroupDescr, Keywords, FileWrapper, \
|
||||
getClassName, SomeObjects
|
||||
from appy.gen.utils import sequenceTypes, GroupDescr, Keywords, getClassName, \
|
||||
SomeObjects
|
||||
import appy.pod
|
||||
from appy.pod.renderer import Renderer
|
||||
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 -----------------------------------------------------
|
||||
r, w, d = ('read', 'write', 'delete')
|
||||
|
@ -1505,7 +1506,7 @@ class File(Type):
|
|||
def getRequestValue(self, request):
|
||||
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):
|
||||
'''Must p_value be considered as empty?'''
|
||||
|
@ -1536,8 +1537,9 @@ class File(Type):
|
|||
* an instance of Zope class ZPublisher.HTTPRequest.FileUpload. In
|
||||
this case, it is file content coming from a HTTP POST;
|
||||
* an instance of Zope class OFS.Image.File;
|
||||
* an instance of appy.gen.utils.FileWrapper, which wraps an instance
|
||||
of OFS.Image.File and adds useful methods for manipulating it;
|
||||
* an instance of appy.shared.utils.FileWrapper, which wraps an
|
||||
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
|
||||
on disk;
|
||||
* 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['languageSelector'] = self.config.languageSelector
|
||||
repls['sourceLanguage'] = self.config.sourceLanguage
|
||||
self.copyFile('config.py', repls)
|
||||
self.copyFile('config.pyt', repls, destName='config.py')
|
||||
|
||||
def generateInit(self):
|
||||
# Compute imports
|
||||
|
@ -690,7 +690,7 @@ class ZopeGenerator(Generator):
|
|||
repls['imports'] = '\n'.join(imports)
|
||||
repls['classes'] = ','.join(classNames)
|
||||
repls['totalNumberOfTests'] = self.totalNumberOfTests
|
||||
self.copyFile('__init__.py', repls)
|
||||
self.copyFile('__init__.pyt', repls, destName='__init__.py')
|
||||
|
||||
def getClassesInOrder(self, allClasses):
|
||||
'''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'):
|
||||
modelClass = klass.modelClass
|
||||
repls['%s' % modelClass.__name__] = modelClass._appy_getBody()
|
||||
self.copyFile('wrappers.py', repls)
|
||||
self.copyFile('wrappers.pyt', repls, destName='wrappers.py')
|
||||
|
||||
def generateTests(self):
|
||||
'''Generates the file needed for executing tests.'''
|
||||
|
@ -765,7 +765,8 @@ class ZopeGenerator(Generator):
|
|||
modules = self.modulesWithTests
|
||||
repls['imports'] = '\n'.join(['import %s' % m for m in 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):
|
||||
'''Generates the tool that corresponds to this application.'''
|
||||
|
@ -790,7 +791,7 @@ class ZopeGenerator(Generator):
|
|||
repls.update({'methods': klass.methods, 'genClassName': klass.name,
|
||||
'baseMixin':'BaseMixin', 'parents': 'BaseMixin, SimpleItem',
|
||||
'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
|
||||
# columns, with fields to propagate, workflow-related fields.
|
||||
|
@ -817,7 +818,7 @@ class ZopeGenerator(Generator):
|
|||
'genClassName': self.tool.name, 'baseMixin':'ToolMixin',
|
||||
'parents': 'ToolMixin, Folder', 'icon': 'folder.gif',
|
||||
'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):
|
||||
'''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:
|
||||
self.labels.append(poMsg)
|
||||
# Generate the resulting Zope class.
|
||||
self.copyFile('Class.py', repls, destName=fileName)
|
||||
self.copyFile('Class.pyt', repls, destName=fileName)
|
||||
|
||||
def generateWorkflow(self, wfDescr):
|
||||
'''This method creates the i18n labels related to the workflow described
|
||||
|
|
|
@ -781,7 +781,7 @@ class ToolMixin(BaseMixin):
|
|||
if brain:
|
||||
sibling = brain.getObject()
|
||||
res[urlKey] = sibling.getUrl(nav=newNav % (index + 1),
|
||||
page='main')
|
||||
page=self.REQUEST.get('page', 'main'))
|
||||
return res
|
||||
|
||||
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
|
||||
from appy.shared.utils import getOsTempFolder, normalizeString, executeCommand
|
||||
sequenceTypes = (list, tuple)
|
||||
|
||||
# Function for creating a Zope object ------------------------------------------
|
||||
|
@ -242,84 +241,6 @@ class Keywords:
|
|||
return op.join(self.keywords)+'*'
|
||||
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):
|
||||
'''Generates, from appy-class p_klass, the name of the corresponding
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue