diff --git a/__init__.py b/__init__.py index 36e44a1..60f7d63 100644 --- a/__init__.py +++ b/__init__.py @@ -22,26 +22,27 @@ class Object: '''At every place we need an object, but without any requirement on its class (methods, attributes,...) we will use this minimalist class.''' def __init__(self, **fields): - for k, v in fields.iteritems(): + for k, v in fields.items(): setattr(self, k, v) def __repr__(self): - res = u' ' % attrName + res += '%s= ' % attrName res = res.strip() + '>' return res.encode('utf-8') - def __nonzero__(self): return bool(self.__dict__) + def __bool__(self): + return bool(self.__dict__) def get(self, name, default=None): return getattr(self, name, default) def __getitem__(self, k): return getattr(self, k) def update(self, other): - '''Includes information from p_other into p_self''' - for k, v in other.__dict__.iteritems(): + '''Includes information from p_other into p_self.''' + for k, v in other.__dict__.items(): setattr(self, k, v) def clone(self): res = Object() @@ -59,11 +60,11 @@ class Hack: "_base__". In the patched method, one may use Hack.base to call the base method. If p_method is static, you must specify its class in p_klass.''' - # Get the class on which the surgery will take place + # Get the class on which the surgery will take place. isStatic = klass - klass = klass or method.im_class - # On this class, store m_method under its "base" name - name = isStatic and method.func_name or method.im_func.__name__ + klass = klass or method.__self__.__class__ + # On this class, store m_method under its "base" name. + name = isStatic and method.__name__ or method.__func__.__name__ baseName = '_base_%s_' % name if isStatic: # If "staticmethod" isn't called hereafter, the static functions @@ -78,8 +79,8 @@ class Hack: '''Allows to call the base (replaced) method. If p_method is static, you must specify its p_klass.''' isStatic = klass - klass = klass or method.im_class - name = isStatic and method.func_name or method.im_func.__name__ + klass = klass or method.__self__.__class__ + name = isStatic and method.__name__ or method.__func__.__name__ return getattr(klass, '_base_%s_' % name) @staticmethod @@ -87,7 +88,7 @@ class Hack: '''Injects any method or attribute from p_patchClass into klass.''' patched = [] added = [] - for name, attr in patchClass.__dict__.items(): + for name, attr in patchClass.__dict__.iteritems(): if name.startswith('__'): continue # Ignore special methods # Unwrap functions from static methods if attr.__class__.__name__ == 'staticmethod': diff --git a/bin/asksap.py b/bin/asksap.py index 3752a5f..193309b 100644 --- a/bin/asksap.py +++ b/bin/asksap.py @@ -73,10 +73,10 @@ class AskSap: # Return info about a given function. info = sap.getFunctionInfo(sapElement) prefix = 'Function' - print('%s: %s' % (prefix, sapElement)) + print(('%s: %s' % (prefix, sapElement))) print(info) sap.disconnect() - except SapError, se: + except SapError as se: sys.stderr.write(str(se)) sys.stderr.write('\n') sys.exit(ERROR_CODE) diff --git a/bin/backup.py b/bin/backup.py index 68388a5..33ae555 100644 --- a/bin/backup.py +++ b/bin/backup.py @@ -4,7 +4,7 @@ from optparse import OptionParser import ZODB.FileStorage import ZODB.serialize from DateTime import DateTime -from StringIO import StringIO +from io import StringIO folderName = os.path.dirname(__file__) # ------------------------------------------------------------------------------ @@ -87,9 +87,9 @@ class ZodbBackuper: w('Try to create backup folder for logs "%s"...' % \ self.logsBackupFolder) os.mkdir(self.logsBackupFolder) - except IOError, ioe: + except IOError as ioe: w(folderCreateError % str(ioe)) - except OSError, oe: + except OSError as oe: w(folderCreateError % str(oe)) if os.path.exists(self.logsBackupFolder): # Ok, we can make the backup of the log files. @@ -175,7 +175,7 @@ class ZodbBackuper: except smtplib.SMTPException, sme: w('Error while contacting SMTP server %s (%s).' % \ (self.options.smtpServer, str(se))) - except socket.error, se: + except socket.error as se: w('Could not connect to SMTP server %s (%s).' % \ (self.options.smtpServer, str(se))) @@ -191,12 +191,12 @@ class ZodbBackuper: for fileName in os.listdir(self.tempFolder): ext = os.path.splitext(fileName)[1] if ext in self.toRemoveExts: - exec '%sCount += 1' % ext[1:] + exec('%sCount += 1' % ext[1:]) fullFileName = os.path.join(self.tempFolder, fileName) #w('Removing "%s"...' % fullFileName) try: os.remove(fullFileName) - except OSError, oe: + except OSError as oe: w('Could not remove "%s" (%s).' % (fullFileName, str(oe))) w('%d .pdf, %d .doc, %d .rtf and %d .odt file(s) removed.' % \ (pdfCount, docCount, rtfCount, odtCount)) @@ -268,7 +268,7 @@ class ZodbBackuper: if self.emails: self.sendEmails() self.logFile.close() - print(self.logMem.getvalue()) + print((self.logMem.getvalue())) self.logMem.close() # ------------------------------------------------------------------------------ @@ -302,7 +302,7 @@ class ZodbBackupScript: f.write('Hello.') f.close() os.remove(fileName) - except OSError, oe: + except OSError as oe: raise BackupError('I do not have the right to write in ' \ 'folder "%s".' % args[1]) # Check temp folder @@ -401,7 +401,7 @@ class ZodbBackupScript: self.checkArgs(options, args) backuper = ZodbBackuper(args[0], args[1], options) backuper.run() - except BackupError, be: + except BackupError as be: sys.stderr.write(str(be) + '\nRun the script without any ' \ 'argument for getting help.\n') sys.exit(ERROR_CODE) diff --git a/bin/checkldap.py b/bin/checkldap.py index a517a38..a1c55d3 100644 --- a/bin/checkldap.py +++ b/bin/checkldap.py @@ -21,7 +21,7 @@ class LdapTester: def __init__(self): # Get params from shell args. if len(sys.argv) != 8: - print(LdapTester.__doc__) + print((LdapTester.__doc__)) sys.exit(0) s = self s.uri,s.login,s.password,s.base,s.attrs,s.filter,s.scope = sys.argv[1:] @@ -33,15 +33,15 @@ class LdapTester: def test(self): # Connect the the LDAP - print('Connecting to... %s' % self.uri) + print(('Connecting to... %s' % self.uri)) connector = LdapConnector(self.uri) success, msg = connector.connect(self.login, self.password) if not success: return # Perform the query. - print ('Querying %s...' % self.base) + print(('Querying %s...' % self.base)) res = connector.search(self.base, self.scope, self.filter, self.attributes) - print('Got %d results' % len(res)) + print(('Got %d results' % len(res))) # ------------------------------------------------------------------------------ if __name__ == '__main__': LdapTester().test() diff --git a/bin/eggify.py b/bin/eggify.py index a99e425..1fcc16e 100644 --- a/bin/eggify.py +++ b/bin/eggify.py @@ -110,12 +110,12 @@ class EggifyScript: eggFullName = j(self.eggFolder, self.eggName) if os.path.exists(eggFullName): os.remove(eggFullName) - print('Existing "%s" was removed.' % 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) + 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 @@ -170,7 +170,7 @@ class EggifyScript: try: self.checkArgs(options, args) self.eggify() - except EggifierError, ee: + except EggifierError as ee: sys.stderr.write(str(ee) + '\nRun eggify.py -h for getting help.\n') sys.exit(ERROR_CODE) diff --git a/bin/generate.py b/bin/generate.py index 0e5a28a..a08737f 100644 --- a/bin/generate.py +++ b/bin/generate.py @@ -41,7 +41,7 @@ class GeneratorScript: sys.exit(ERROR_CODE) # Check existence of application if not os.path.exists(args[0]): - print(APP_NOT_FOUND % args[0]) + print((APP_NOT_FOUND % args[0])) sys.exit(ERROR_CODE) # Convert app path to an absolute path args[0] = os.path.abspath(args[0]) @@ -55,8 +55,8 @@ class GeneratorScript: (options, args) = optParser.parse_args() try: self.manageArgs(optParser, options, args) - print('Appy version: %s' % appy.version.verbose) - print('Generating Zope product in %s/zope...' % args[0]) + print(('Appy version: %s' % appy.version.verbose)) + print(('Generating Zope product in %s/zope...' % args[0])) ZopeGenerator(args[0], options).run() # Give the user some statistics about its code LinesCounter(args[0], excludes=['%szope' % os.sep]).run() @@ -71,7 +71,7 @@ class GeneratorScript: f.close() version = version[:version.find('build')-1] Debianizer(app, appDir, appVersion=version).run() - except GeneratorError, ge: + except GeneratorError as ge: sys.stderr.write(str(ge)) sys.stderr.write('\n') optParser.print_help() diff --git a/bin/job.py b/bin/job.py index f308925..4dca931 100644 --- a/bin/job.py +++ b/bin/job.py @@ -82,6 +82,6 @@ else: targetObject = getattr(targetObject, elem) # Execute the method on the target object if args: args = args.split('*') - exec 'targetObject.%s(*args)' % toolMethod + exec('targetObject.%s(*args)' % toolMethod) transaction.commit() # ------------------------------------------------------------------------------ diff --git a/bin/new.py b/bin/new.py index 7700f15..f74767c 100644 --- a/bin/new.py +++ b/bin/new.py @@ -106,17 +106,17 @@ class ZopeInstanceCreator: f = file('bin/zopectl', 'w') f.write(zopeCtl % self.instancePath) f.close() - os.chmod('bin/zopectl', 0744) # Make it executable by owner. + os.chmod('bin/zopectl', 0o744) # Make it executable by owner. # Create bin/runzope f = file('bin/runzope', 'w') f.write(runZope % self.instancePath) f.close() - os.chmod('bin/runzope', 0744) # Make it executable by owner. + os.chmod('bin/runzope', 0o744) # Make it executable by owner. # Create bin/startoo f = file('bin/startoo', 'w') f.write(ooStart) f.close() - os.chmod('bin/startoo', 0744) # Make it executable by owner. + os.chmod('bin/startoo', 0o744) # Make it executable by owner. # Create etc/zope.conf os.mkdir('etc') f = file('etc/zope.conf', 'w') @@ -138,10 +138,10 @@ class ZopeInstanceCreator: password = binascii.b2a_base64(sha('admin').digest())[:-1] f.write('admin:{SHA}%s\n' % password) f.close() - os.chmod('inituser', 0644) + os.chmod('inituser', 0o644) # User "zope" must own this instance os.system('chown -R zope %s' % self.instancePath) - print('Zope instance created in %s.' % self.instancePath) + print(('Zope instance created in %s.' % self.instancePath)) os.chdir(curdir) # ------------------------------------------------------------------------------ @@ -229,7 +229,7 @@ class NewScript: j = os.path.join # As eggs have been deleted, versions of components are lost. Reify # them from p_versions. - dVersions = ['"%s":"%s"' % (n, v) for n, v in versions.iteritems()] + dVersions = ['"%s":"%s"' % (n, v) for n, v in versions.items()] sVersions = 'appyVersions = {' + ','.join(dVersions) + '}' codeFile = "%s/pkg_resources.py" % self.libFolder f = file(codeFile) @@ -326,7 +326,7 @@ class NewScript: action = 'Copying' if linksForProducts: action = 'Symlinking' - print('%s Plone stuff in the Zope instance...' % action) + print(('%s Plone stuff in the Zope instance...' % action)) if self.ploneVersion in ('plone25', 'plone30'): self.installPlone25or30Stuff(linksForProducts) elif self.ploneVersion in ('plone3x', 'plone4'): @@ -379,11 +379,11 @@ class NewScript: try: self.manageArgs(args) if self.ploneVersion != 'zope': - print('Creating new %s instance...' % self.ploneVersion) + print(('Creating new %s instance...' % self.ploneVersion)) self.createInstance(linksForProducts) else: ZopeInstanceCreator(self.instancePath).run() - except NewError, ne: + except NewError as ne: optParser.print_help() sys.stderr.write(str(ne)) sys.stderr.write('\n') diff --git a/bin/odfgrep.py b/bin/odfgrep.py index 4320acf..af00819 100644 --- a/bin/odfgrep.py +++ b/bin/odfgrep.py @@ -52,7 +52,7 @@ class OdfGrep: # Run "grep" in this folder match = self.callGrep(tempFolder) if match: - print('Found in %s' % fileName) + print(('Found in %s' % fileName)) FolderDeleter.delete(tempFolder) def run(self): @@ -65,7 +65,7 @@ class OdfGrep: if os.path.splitext(name)[1] in self.toUnzip: self.grepFile(os.path.join(dir, name)) else: - print('%s does not exist.' % self.fileOrFolder) + print(('%s does not exist.' % self.fileOrFolder)) # ------------------------------------------------------------------------------ if __name__ == '__main__': diff --git a/bin/publish.py b/bin/publish.py index a86bd66..ba9ecad 100644 --- a/bin/publish.py +++ b/bin/publish.py @@ -85,8 +85,8 @@ class FtpFolder: def clean(self, site): '''Cleans this folder''' # First, clean subFolders if they exist - print('Cleaning %s %d subFolders' % \ - (self.getFullName(), len(self.subFolders))) + print(('Cleaning %s %d subFolders' % \ + (self.getFullName(), len(self.subFolders)))) for subFolder in self.subFolders: subFolder.clean(site) # Remove the subFolder @@ -95,7 +95,7 @@ class FtpFolder: for f in self.files: fileName = '%s/%s' % (self.getFullName(), f) site.delete(fileName) - print('%s removed.' % fileName) + print(('%s removed.' % fileName)) # ------------------------------------------------------------------------------ class AppySite: @@ -148,11 +148,11 @@ class AppySite: fileExt = os.path.splitext(fileName)[1] if fileExt in self.textExtensions: # Make a transfer in text mode - print('Transfer file %s (text mode)' % fileName) + print(('Transfer file %s (text mode)' % fileName)) self.site.storlines(cmd, localFile) else: # Make a transfer in binary mode - print('Transfer file %s (binary mode)' % fileName) + print(('Transfer file %s (binary mode)' % fileName)) self.site.storbinary(cmd, localFile) def publish(self): @@ -257,7 +257,7 @@ class Publisher: yesNo = '[Y/n]' else: yesNo = '[y/N]' - print(question + ' ' + yesNo + ' ') + print((question + ' ' + yesNo + ' ')) response = sys.stdin.readline().strip().lower() res = False if response in ('y', 'yes'): @@ -274,7 +274,7 @@ class Publisher: def executeCommand(self, cmd): '''Executes the system command p_cmd.''' - print('Executing %s...' % cmd) + print(('Executing %s...' % cmd)) os.system(cmd) distExcluded = ('appy/doc', 'appy/temp', 'appy/versions', 'appy/gen/test') @@ -340,7 +340,7 @@ class Publisher: newZipRelease, default='yes'): print('Publication canceled.') sys.exit(1) - print('Removing obsolete %s...' % newZipRelease) + print(('Removing obsolete %s...' % newZipRelease)) os.remove(newZipRelease) zipFile = zipfile.ZipFile(newZipRelease, 'w', zipfile.ZIP_DEFLATED) curdir = os.getcwd() @@ -474,7 +474,7 @@ class Publisher: if minimalist: FolderDeleter.delete('%s/pod/test' % genSrcFolder) # Write the appy version into the code itself (in appy/version.py)''' - print('Publishing version %s...' % self.versionShort) + print(('Publishing version %s...' % self.versionShort)) # Dump version info in appy/version.py f = file('%s/version.py' % genSrcFolder, 'w') f.write('short = "%s"\n' % self.versionShort) @@ -495,7 +495,7 @@ class Publisher: Cleaner().run(verbose=False) # Perform a small analysis on the Appy code LinesCounter(appy).run() - print('Generating site in %s...' % self.genFolder) + print(('Generating site in %s...' % self.genFolder)) minimalist = self.askQuestion('Minimalist (shipped without tests)?', default='no') self.prepareGenFolder(minimalist) diff --git a/bin/restore.py b/bin/restore.py index f77fcad..b8fb343 100644 --- a/bin/restore.py +++ b/bin/restore.py @@ -21,10 +21,10 @@ class ZodbRestorer: datePart = '-D %s' % self.restoreDate repozoCmd = '%s %s -Rv -r %s %s -o %s' % (self.python, self.repozo, self.backupFolder, datePart, self.storageLocation) - print('Executing %s...' % repozoCmd) + print(('Executing %s...' % repozoCmd)) os.system(repozoCmd) stopTime = time.time() - print('Done in %d minute(s).' % ((stopTime-startTime)/60)) + print(('Done in %d minute(s).' % ((stopTime-startTime)/60))) # ------------------------------------------------------------------------------ class ZodbRestoreScript: @@ -56,7 +56,7 @@ class ZodbRestoreScript: f.write('Hello.') f.close() os.remove(args[0]) - except OSError, oe: + except OSError as oe: raise RestoreError('I do not have the right to write file ' \ '"%s".' % args[0]) @@ -81,7 +81,7 @@ class ZodbRestoreScript: self.checkArgs(options, args) backuper = ZodbRestorer(args[0], args[1], options) backuper.run() - except RestoreError, be: + except RestoreError as be: sys.stderr.write(str(be)) sys.stderr.write('\n') optParser.print_help() diff --git a/bin/zip.py b/bin/zip.py index 49cb810..5feb399 100644 --- a/bin/zip.py +++ b/bin/zip.py @@ -8,29 +8,29 @@ class Zipper: def __init__(self): self.zipFileName = '%s/Desktop/appy.zip' % os.environ['HOME'] def createZipFile(self): - print('Creating %s...' % self.zipFileName) + print(('Creating %s...' % self.zipFileName)) zipFile = zipfile.ZipFile(self.zipFileName, 'w', zipfile.ZIP_DEFLATED) for dir, dirnames, filenames in os.walk(appyPath): for f in filenames: fileName = os.path.join(dir, f) arcName = fileName[fileName.find('appy/'):] - print('Adding %s' % fileName) + print(('Adding %s' % fileName)) zipFile.write(fileName, arcName) zipFile.close() def run(self): # Where to put the zip file ? - print("Where do you want to put appy.zip ? [Default is %s] " % \ - os.path.dirname(self.zipFileName)) + print(("Where do you want to put appy.zip ? [Default is %s] " % \ + os.path.dirname(self.zipFileName))) response = sys.stdin.readline().strip() if response: if os.path.exists(response) and os.path.isdir(response): self.zipFileName = '%s/appy.zip' % response else: - print('%s is not a folder.' % response) + print(('%s is not a folder.' % response)) sys.exit(1) if os.path.exists(self.zipFileName): - print('Removing existing %s...' % self.zipFileName) + print(('Removing existing %s...' % self.zipFileName)) os.remove(self.zipFileName) Cleaner().run(verbose=False) self.createZipFile() diff --git a/fields/__init__.py b/fields/__init__.py index e72f422..b254f2a 100644 --- a/fields/__init__.py +++ b/fields/__init__.py @@ -21,9 +21,9 @@ from appy.gen.layout import Table, defaultFieldLayouts from appy.gen import utils as gutils from appy.px import Px from appy.shared import utils as sutils -from group import Group -from search import Search -from page import Page +from .group import Group +from .page import Page +import collections # In this file, names "list" and "dict" refer to sub-modules. To use Python # builtin types, use __builtins__['list'] and __builtins__['dict'] @@ -370,7 +370,7 @@ class Field: labelName = name trPrefix = None if self.label: - if isinstance(self.label, basestring): trPrefix = self.label + if isinstance(self.label, str): trPrefix = self.label else: # It is a tuple (trPrefix, name) if self.label[1]: labelName = self.label[1] if self.label[0]: trPrefix = self.label[0] @@ -382,16 +382,16 @@ class Field: self.helpId = self.labelId + '_help' # Determine read and write permissions for this field rp = self.specificReadPermission - if rp and not isinstance(rp, basestring): + if rp and not isinstance(rp, str): self.readPermission = '%s: Read %s %s' % (appName, prefix, name) - elif rp and isinstance(rp, basestring): + elif rp and isinstance(rp, str): self.readPermission = rp else: self.readPermission = 'read' wp = self.specificWritePermission - if wp and not isinstance(wp, basestring): + if wp and not isinstance(wp, str): self.writePermission = '%s: Write %s %s' % (appName, prefix, name) - elif wp and isinstance(wp, basestring): + elif wp and isinstance(wp, str): self.writePermission = wp else: self.writePermission = 'write' @@ -442,7 +442,7 @@ class Field: self.readPermission if not obj.allows(perm): return # Evaluate self.show - if callable(self.show): + if isinstance(self.show, collections.Callable): res = self.callMethod(obj, self.show) else: res = self.show @@ -480,7 +480,7 @@ class Field: if not masterData: return True else: master, masterValue = masterData - if masterValue and callable(masterValue): return True + if masterValue and isinstance(masterValue, collections.Callable): return True reqValue = master.getRequestValue(obj) # reqValue can be a list or not if type(reqValue) not in sutils.sequenceTypes: @@ -496,8 +496,8 @@ class Field: if isinstance(mapping, __builtins__['dict']): # Is it a dict like {'label':..., 'descr':...}, or is it directly a # dict with a mapping? - for k, v in mapping.iteritems(): - if (k not in self.labelTypes) or isinstance(v, basestring): + for k, v in mapping.items(): + if (k not in self.labelTypes) or isinstance(v, str): # It is already a mapping return {'label':mapping, 'descr':mapping, 'help':mapping} # If we are here, we have {'label':..., 'descr':...}. Complete @@ -520,7 +520,7 @@ class Field: areDefault = True layouts = self.computeDefaultLayouts() else: - if isinstance(layouts, basestring): + if isinstance(layouts, str): # The user specified a single layoutString (the "edit" one) layouts = {'edit': layouts} elif isinstance(layouts, Table): @@ -541,8 +541,8 @@ class Field: # We have now a dict of layouts in p_layouts. Ensure now that a Table # instance is created for every layout (=value from the dict). Indeed, # a layout could have been expressed as a simple layout string. - for layoutType in layouts.iterkeys(): - if isinstance(layouts[layoutType], basestring): + for layoutType in layouts.keys(): + if isinstance(layouts[layoutType], str): layouts[layoutType] = Table(layouts[layoutType]) # Derive "view", "search" and "cell" layouts from the "edit" layout # when relevant. @@ -566,11 +566,11 @@ class Field: if areDefault and not self.group and \ not ((self.type == 'String') and (self.format == self.XHTML)) and \ not (self.type == 'Ref'): - for layoutType in layouts.iterkeys(): + for layoutType in layouts.keys(): layouts[layoutType].width = '' # Remove letters "r" from the layouts if the field is not required. if not self.required: - for layoutType in layouts.iterkeys(): + for layoutType in layouts.keys(): layouts[layoutType].removeElement('r') # Derive some boolean values from the layouts. self.hasLabel = self.hasLayoutElement('l', layouts) @@ -597,7 +597,7 @@ class Field: def hasLayoutElement(self, element, layouts): '''This method returns True if the given layout p_element can be found at least once among the various p_layouts defined for this field.''' - for layout in layouts.itervalues(): + for layout in layouts.values(): if element in layout.layoutString: return True return False @@ -610,7 +610,7 @@ class Field: '''Gets, as a string, the layouts as could have been specified as input value for the Field constructor.''' res = '{' - for k, v in self.layouts.iteritems(): + for k, v in self.layouts.items(): res += '"%s":"%s",' % (k, v.layoutString) res += '}' return res @@ -650,7 +650,7 @@ class Field: if self.isEmptyValue(obj, value): # If there is no value, get the default value if any: return # self.default, of self.default() if it is a method. - if callable(self.default): + if isinstance(self.default, collections.Callable): try: # Caching a default value can lead to problems. For example, # the process of creating an object from another one, or @@ -660,7 +660,7 @@ class Field: # but it they depend on values set at (b), and are cached # and indexed, (c) will get the wrong, cached value. return self.callMethod(obj, self.default, cache=False) - except Exception, e: + except Exception as e: # Already logged. Here I do not raise the exception, # because it can be raised as the result of reindexing # the object in situations that are not foreseen by @@ -723,12 +723,13 @@ class Field: # Start by getting the field value on p_obj res = self.getValue(obj) # Zope catalog does not like unicode strings - if isinstance(res, unicode): res = res.encode('utf-8') + if isinstance(value, str): + res = value.encode('utf-8') if forSearch and (res != None): if type(res) in sutils.sequenceTypes: vals = [] for v in res: - if isinstance(v, unicode): vals.append(v.encode('utf-8')) + if isinstance(v, str): vals.append(v.encode('utf-8')) else: vals.append(str(v)) res = ' '.join(vals) else: @@ -824,7 +825,7 @@ class Field: this field is the slave of another field.''' if not self.master: return '' res = 'slave*%s*' % self.masterName - if not callable(self.masterValue): + if not isinstance(self.masterValue, collections.Callable): res += '*'.join(self.masterValue) else: res += '+' @@ -866,7 +867,7 @@ class Field: def securityCheck(self, obj, value): '''This method performs some security checks on the p_value that represents user input.''' - if not isinstance(value, basestring): return + if not isinstance(value, str): return # Search Javascript code in the value (prevent XSS attacks). if ' maxYear): continue months = years[year] # Browse this year's months - for month in months.keys(): + for month in list(months.keys()): # Don't take this month into account if outside interval thisMonth = (year, month) if minDate and (thisMonth < minMonth): continue if maxDate and (thisMonth > maxMonth): continue days = months[month] # Browse this month's days - for day in days.keys(): + for day in list(days.keys()): # Don't take this day into account if outside interval thisDay = (year, month, day) if minDate and (thisDay < minDay): continue diff --git a/fields/computed.py b/fields/computed.py index 3a7d48e..376e411 100644 --- a/fields/computed.py +++ b/fields/computed.py @@ -47,7 +47,7 @@ class Computed(Field): # the user interface while m_method computes the value stored in the # catalog. self.formatMethod = formatMethod - if isinstance(self.method, basestring): + if isinstance(self.method, str): # A legacy macro identifier. Raise an exception raise Exception(self.WRONG_METHOD % self.method) # Does field computation produce plain text or XHTML? @@ -99,6 +99,6 @@ class Computed(Field): res = self.formatMethod(obj, value) else: res = value - if not isinstance(res, basestring): res = str(res) + if not isinstance(res, str): res = str(res) return res # ------------------------------------------------------------------------------ diff --git a/fields/date.py b/fields/date.py index 75001c4..4393c2d 100644 --- a/fields/date.py +++ b/fields/date.py @@ -219,7 +219,7 @@ class Date(Field): def getSelectableYears(self): '''Gets the list of years one may select for this field.''' - res = range(self.startYear, self.endYear + 1) + res = list(range(self.startYear, self.endYear + 1)) if self.reverseYears: res.reverse() return res @@ -227,7 +227,7 @@ class Date(Field): DateTime = obj.getProductConfig().DateTime try: value = DateTime(value) - except DateTime.DateError, ValueError: + except DateTime.DateError as ValueError: return obj.translate('bad_date') def getFormattedValue(self, obj, value, layoutType='view', @@ -278,7 +278,7 @@ class Date(Field): # Get the value we must compare (from request or from database) rq = obj.REQUEST partName = '%s_%s' % (self.name, fieldPart) - if rq.has_key(partName): + if partName in rq: compValue = rq.get(partName) if compValue.isdigit(): compValue = int(compValue) diff --git a/fields/file.py b/fields/file.py index a9a09a3..f255647 100644 --- a/fields/file.py +++ b/fields/file.py @@ -108,7 +108,7 @@ class FileInfo: '''Removes the file from the filesystem.''' try: os.remove(osPathJoin(dbFolder, self.fsPath, self.fsName)) - except Exception, e: + except Exception as e: # If the current ZODB transaction is re-triggered, the file may # already have been deleted. pass @@ -207,10 +207,10 @@ class FileInfo: if fileObj.data.__class__.__name__ == 'Pdata': # The file content is splitted in several chunks f.write(fileObj.data.data) - nextPart = fileObj.data.next + nextPart = fileObj.data.__next__ while nextPart: f.write(nextPart.data) - nextPart = nextPart.next + nextPart = nextPart.__next__ else: # Only one chunk f.write(fileObj.data) @@ -393,7 +393,7 @@ class File(Field): def validateValue(self, obj, value): form = obj.REQUEST.form action = '%s_delete' % self.name - if (not value or not value.filename) and form.has_key(action) and \ + if (not value or not value.filename) and action in form and \ not form[action]: # If this key is present but empty, it means that the user selected # "replace the file with a new one". So in this case he must provide @@ -450,7 +450,7 @@ class File(Field): # Case c fileInfo = (value.name, value.content, value.mimeType) info.writeFile(self.name, fileInfo, dbFolder) - elif isinstance(value, basestring): + elif isinstance(value, str): # Case d info.copyFile(self.name, value, dbFolder) elif isinstance(value, FileInfo): diff --git a/fields/group.py b/fields/group.py index 540f2b2..bedf94e 100644 --- a/fields/group.py +++ b/fields/group.py @@ -40,7 +40,7 @@ class Group: # which will be used for HTML param "width". if wide == True: self.wide = '100%' - elif isinstance(wide, basestring): + elif isinstance(wide, str): self.wide = wide else: self.wide = '' @@ -106,7 +106,7 @@ class Group: can be a string or a Group instance; this method returns always a Group instance.''' res = groupData - if res and isinstance(res, basestring): + if res and isinstance(res, str): # Group data is given as a string. 2 more possibilities: # (a) groupData is simply the name of the group; # (b) groupData is of the form _. @@ -336,7 +336,7 @@ class UiGroup: # All p_group attributes become self attributes. This is required # because a UiGroup, in some PXs, must behave like a Field (ie, have # the same attributes, like "master". - for name, value in group.__dict__.iteritems(): + for name, value in group.__dict__.items(): if not name.startswith('_'): setattr(self, name, value) self.group = group @@ -346,7 +346,7 @@ class UiGroup: labelName = self.name prefix = className if group.label: - if isinstance(group.label, basestring): prefix = group.label + if isinstance(group.label, str): prefix = group.label else: # It is a tuple (className, name) if group.label[1]: labelName = group.label[1] if group.label[0]: prefix = group.label[0] diff --git a/fields/integer.py b/fields/integer.py index 7369b89..bb8110f 100644 --- a/fields/integer.py +++ b/fields/integer.py @@ -60,7 +60,7 @@ class Integer(Field): height, maxChars, colspan, master, masterValue, focus, historized, mapping, label, sdefault, scolspan, swidth, sheight, persist, view, xml) - self.pythonType = long + self.pythonType = int def validateValue(self, obj, value): try: diff --git a/fields/list.py b/fields/list.py index 48dda1d..494cc82 100644 --- a/fields/list.py +++ b/fields/list.py @@ -132,7 +132,7 @@ class List(Field): name = requestName or self.name # A List may be into another List (?) prefix = name + '*' + self.fields[0][0] + '*' res = {} - for key in request.keys(): + for key in list(request.keys()): if not key.startswith(prefix): continue # I have found a row. Gets its index. row = Object() @@ -145,7 +145,7 @@ class List(Field): setattr(row, subName, v) res[rowIndex] = row # Produce a sorted list - keys = res.keys() + keys = list(res.keys()) keys.sort() res = [res[key] for key in keys] # I store in the request this computed value. This way, when individual diff --git a/fields/ogone.py b/fields/ogone.py index af589cb..28b1a5b 100644 --- a/fields/ogone.py +++ b/fields/ogone.py @@ -83,13 +83,13 @@ class Ogone(Field): # Create a new dict by removing p_keysToIgnore from p_values, and by # upperizing all keys. shaRes = {} - for k, v in values.iteritems(): + for k, v in values.items(): if k in keysToIgnore: continue # Ogone: we must not include empty values. if (v == None) or (v == ''): continue shaRes[k.upper()] = v # Create a sorted list of keys - keys = shaRes.keys() + keys = list(shaRes.keys()) keys.sort() shaList = [] for k in keys: @@ -127,7 +127,7 @@ class Ogone(Field): # Ogone's response. res['paramplus'] = 'name=%s' % self.name # Ensure every value is a str - for k in res.iterkeys(): + for k in res.keys(): if not isinstance(res[k], str): res[k] = str(res[k]) # Compute a SHA-1 key as required by Ogone and add it to the res @@ -154,7 +154,7 @@ class Ogone(Field): 'administrator has been contacted.') # Create a nice object from the form. response = Object() - for k, v in obj.REQUEST.form.iteritems(): + for k, v in obj.REQUEST.form.items(): setattr(response, k, v) # Call the field method that handles the response received from Ogone. url = self.responseMethod(obj.appy(), response) diff --git a/fields/page.py b/fields/page.py index aac6feb..ba53d02 100644 --- a/fields/page.py +++ b/fields/page.py @@ -16,6 +16,7 @@ # ------------------------------------------------------------------------------ from appy import Object +import collections # ------------------------------------------------------------------------------ class Page: @@ -53,7 +54,7 @@ class Page: (c) a Page instance. This method returns always a Page instance.''' res = pageData - if res and isinstance(res, basestring): + if res and isinstance(res, str): # Page data is given as a string. pageElems = pageData.rsplit('_', 1) if len(pageElems) == 1: # We have case (a) diff --git a/fields/pod.py b/fields/pod.py index 8112de3..4e8106e 100644 --- a/fields/pod.py +++ b/fields/pod.py @@ -16,7 +16,7 @@ # ------------------------------------------------------------------------------ import time, os, os.path -from file import FileInfo +from .file import FileInfo from appy import Object from appy.fields import Field from appy.px import Px @@ -25,6 +25,7 @@ from appy.gen import utils as gutils from appy.pod import PodError from appy.pod.renderer import Renderer from appy.shared import utils as sutils +import collections # ------------------------------------------------------------------------------ class Mailing: @@ -170,7 +171,7 @@ class Pod(Field): # of your app will be referred as "Test.odt" in self.template. If it is # stored within sub-folder "pod", it will be referred as "pod/Test.odt". if not template: raise Exception(Pod.NO_TEMPLATE) - if isinstance(template, basestring): + if isinstance(template, str): self.template = [template] elif isinstance(template, tuple): self.template = list(template) @@ -396,7 +397,7 @@ class Pod(Field): formats = self.showTemplate(obj, template) if not formats: continue elif isinstance(formats, bool): formats = self.formats - elif isinstance(formats, basestring): formats = (formats,) + elif isinstance(formats, str): formats = (formats,) res.append(Object(template=template, formats=formats, freezeFormats=self.getFreezeFormats(obj, template))) return res @@ -525,7 +526,7 @@ class Pod(Field): templatePath = self.getTemplatePath(diskFolder, template) # Get or compute the specific POD context specificContext = None - if callable(self.context): + if isinstance(self.context, collections.Callable): specificContext = self.callMethod(obj, self.context) else: specificContext = self.context @@ -544,7 +545,7 @@ class Pod(Field): # Retrieve query params cmd = ', '.join(Pod.queryParams) cmd += " = queryData.split(';')" - exec cmd + exec(cmd) # (re-)execute the query, but without any limit on the number of # results; return Appy objects. objs = tool.o.executeQuery(obj.o.portal_type, searchName=search, @@ -559,7 +560,7 @@ class Pod(Field): # when generating frozen documents). if '_checked' not in podContext: podContext['_checked'] = Object() # Define a potential global styles mapping - if callable(self.stylesMapping): + if isinstance(self.stylesMapping, collections.Callable): stylesMapping = self.callMethod(obj, self.stylesMapping) else: stylesMapping = self.stylesMapping @@ -575,7 +576,7 @@ class Pod(Field): try: renderer = Renderer(**rendererParams) renderer.run() - except PodError, pe: + except PodError as pe: if not os.path.exists(result): # In some (most?) cases, when OO returns an error, the result is # nevertheless generated. @@ -643,7 +644,7 @@ class Pod(Field): # Generate the document doc = self.getValue(obj, template=template, format=format, result=result) - if isinstance(doc, basestring): + if isinstance(doc, str): # An error occurred, the document was not generated. obj.log(self.FREEZE_ERROR % (format, self.name, doc), type='error') @@ -658,7 +659,7 @@ class Pod(Field): obj.log('freeze: overwriting %s...' % result) doc = self.getValue(obj, template=template, format='odt', result=result) - if isinstance(doc, basestring): + if isinstance(doc, str): self.log(self.FREEZE_ERROR % ('odt', self.name, doc), type='error') raise Exception(self.FREEZE_FATAL_ERROR) @@ -762,7 +763,7 @@ class Pod(Field): res = self.getValue(obj, template=template, format=format, queryData=rq.get('queryData'), customContext=self.getCustomContext(obj, rq)) - if isinstance(res, basestring): + if isinstance(res, str): # An error has occurred, and p_res contains the error message obj.say(res) return tool.goto(rq.get('HTTP_REFERER')) diff --git a/fields/ref.py b/fields/ref.py index 7735200..480575b 100644 --- a/fields/ref.py +++ b/fields/ref.py @@ -23,6 +23,7 @@ from appy.px import Px from appy.gen.layout import Table from appy.gen import utils as gutils from appy.shared import utils as sutils +import collections # ------------------------------------------------------------------------------ class Ref(Field): @@ -826,7 +827,7 @@ class Ref(Field): # If this field is an ajax-updatable slave, no need to compute # possible values: it will be overridden by method self.masterValue # by a subsequent ajax request (=the "if" statement above). - if self.masterValue and callable(self.masterValue): + if self.masterValue and isinstance(self.masterValue, collections.Callable): objects = [] else: if not self.select: @@ -921,7 +922,7 @@ class Ref(Field): res = self.getAttribute(obj, 'numbered') if not res: return res # Returns the column width. - if not isinstance(res, basestring): return '15px' + if not isinstance(res, str): return '15px' return res def getMenuUrl(self, zobj, tied): @@ -996,14 +997,14 @@ class Ref(Field): # Also ensure that multiplicities are enforced. if not value: nbOfRefs = 0 - elif isinstance(value, basestring): + elif isinstance(value, str): nbOfRefs = 1 else: nbOfRefs = len(value) minRef = self.multiplicity[0] maxRef = self.multiplicity[1] if maxRef == None: - maxRef = sys.maxint + maxRef = sys.maxsize if nbOfRefs < minRef: return obj.translate('min_ref_violated') elif nbOfRefs > maxRef: @@ -1119,7 +1120,7 @@ class Ref(Field): if type(objects) not in sutils.sequenceTypes: objects = [objects] tool = obj.getTool() for i in range(len(objects)): - if isinstance(objects[i], basestring): + if isinstance(objects[i], str): # We have an UID here objects[i] = tool.getObject(objects[i], appy=True) else: diff --git a/fields/search.py b/fields/search.py index 91c1be4..4260fb3 100644 --- a/fields/search.py +++ b/fields/search.py @@ -19,7 +19,7 @@ from appy.px import Px from appy.gen import utils as gutils from appy.gen.indexer import defaultIndexes from appy.shared import utils as sutils -from group import Group +from .group import Group # ------------------------------------------------------------------------------ class Search: @@ -85,12 +85,12 @@ class Search: (fieldName == 'SearchableText'): # For TextIndex indexes. We must split p_fieldValue into keywords. res = gutils.Keywords(fieldValue).get() - elif isinstance(fieldValue, basestring) and fieldValue.endswith('*'): + elif isinstance(fieldValue, str) and fieldValue.endswith('*'): v = fieldValue[:-1] # Warning: 'z' is higher than 'Z'! res = {'query':(v,v+'z'), 'range':'min:max'} elif type(fieldValue) in sutils.sequenceTypes: - if fieldValue and isinstance(fieldValue[0], basestring): + if fieldValue and isinstance(fieldValue[0], str): # We have a list of string values (ie: we need to # search v1 or v2 or...) res = fieldValue @@ -118,7 +118,7 @@ class Search: sortBy and sortOrder (and not "resolve" them to Zope's sort_on and sort_order).''' # Put search criteria in p_criteria - for name, value in self.fields.iteritems(): + for name, value in self.fields.items(): # Management of searches restricted to objects linked through a # Ref field: not implemented yet. if name == '_ref': continue diff --git a/fields/string.py b/fields/string.py index 1bbc56c..685274c 100644 --- a/fields/string.py +++ b/fields/string.py @@ -25,6 +25,7 @@ from appy.shared.data import countries from appy.shared.xml_parser import XhtmlCleaner from appy.shared.diff import HtmlDiff from appy.shared import utils as sutils +import collections # ------------------------------------------------------------------------------ digit = re.compile('[0-9]') @@ -471,7 +472,7 @@ class String(Field): res = True if type(self.validator) in (list, tuple): for elem in self.validator: - if not isinstance(elem, basestring): + if not isinstance(elem, str): res = False break else: @@ -533,7 +534,7 @@ class String(Field): if not value: if self.isMultiValued(): return emptyTuple else: return value - if isinstance(value, basestring) and self.isMultiValued(): + if isinstance(value, str) and self.isMultiValued(): value = [value] elif isinstance(value, tuple): value = list(value) @@ -602,8 +603,7 @@ class String(Field): identifies the language-specific part we will work on.''' res = None lastEvent = None - name = language and ('%s-%s' % (self.name, language)) or self.name - for event in obj.workflow_history['appy']: + for event in obj.workflow_history.values()[0]: if event['action'] != '_datachange_': continue if name not in event['changes']: continue if res == None: @@ -660,7 +660,7 @@ class String(Field): res = obj.formatText(res, format='html') # If value starts with a carriage return, add a space; else, it will # be ignored. - if isinstance(res, basestring) and \ + if isinstance(res, str) and \ (res.startswith('\n') or res.startswith('\r\n')): res = ' ' + res return res @@ -774,7 +774,7 @@ class String(Field): # If this field is an ajax-updatable slave, no need to compute # possible values: it will be overridden by method self.masterValue # by a subsequent ajax request (=the "if" statement above). - if self.masterValue and callable(self.masterValue) and \ + if self.masterValue and isinstance(self.masterValue, collections.Callable) and \ not ignoreMasterValues: return [] if isinstance(self.validator, Selection): # We need to call self.methodName for getting the (dynamic) @@ -808,9 +808,9 @@ class String(Field): obj = brains[0].getObject() # Do we need to call the method on the object or on the wrapper? if methodName.startswith('_appy_'): - exec 'res = obj.%s(*args)' % methodName + exec('res = obj.%s(*args)' % methodName) else: - exec 'res = obj.appy().%s(*args)' % methodName + exec('res = obj.appy().%s(*args)' % methodName) if not withTranslations: res = [v[0] for v in res] elif isinstance(res, list): res = res[:] else: @@ -847,7 +847,7 @@ class String(Field): elif self.isSelect: # Check that the value is among possible values possibleValues = self.getPossibleValues(obj,ignoreMasterValues=True) - if isinstance(value, basestring): + if isinstance(value, str): error = value not in possibleValues else: error = False @@ -872,7 +872,7 @@ class String(Field): return value def getUnilingualStorableValue(self, obj, value): - isString = isinstance(value, basestring) + isString = isinstance(value, str) isEmpty = Field.isEmptyValue(self, obj, value) # Apply transform if required if isString and not isEmpty and (self.transform != 'none'): @@ -1021,7 +1021,7 @@ class String(Field): if self.allowImageUpload: ckAttrs['filebrowserUploadUrl'] = '%s/upload' % obj.absolute_url() ck = [] - for k, v in ckAttrs.iteritems(): + for k, v in ckAttrs.items(): if isinstance(v, int): sv = str(v) if isinstance(v, bool): sv = str(v).lower() else: sv = '"%s"' % v @@ -1059,7 +1059,7 @@ class String(Field): name containing a row number from a field within a list field.''' rq = obj.REQUEST # Get the value we must compare (from request or from database) - if rq.has_key(fieldName): + if fieldName in rq: compValue = rq.get(fieldName) else: compValue = dbValue diff --git a/fields/workflow.py b/fields/workflow.py index bd539e9..258f629 100644 --- a/fields/workflow.py +++ b/fields/workflow.py @@ -14,10 +14,12 @@ # You should have received a copy of the GNU General Public License along with # Appy. If not, see . # ------------------------------------------------------------------------------ -import types, string -from group import Group +import types +import .string +from .group import Group from appy.px import Px from appy.gen.utils import User +import collections # Default Appy permissions ----------------------------------------------------- r, w, d = ('read', 'write', 'delete') @@ -73,7 +75,7 @@ class State: exists, or creates a Role instance, puts it in self.usedRoles and returns it else. If it is a Role instance, the method stores it in self.usedRoles if it is not in it yet and returns it.''' - if isinstance(role, basestring): + if isinstance(role, str): if role in self.usedRoles: return self.usedRoles[role] else: @@ -88,24 +90,24 @@ class State: def standardizeRoles(self): '''This method converts, within self.permissions, every role to a Role instance. Every used role is stored in self.usedRoles.''' - for permission, roles in self.permissions.iteritems(): + for permission, roles in self.permissions.items(): if not roles: continue # Nobody may have this permission - if isinstance(roles, basestring) or isinstance(roles, Role): + if isinstance(roles, str) or isinstance(roles, Role): self.permissions[permission] = [self.getRole(roles)] elif isinstance(roles, list): for i in range(len(roles)): roles[i] = self.getRole(roles[i]) else: # A tuple self.permissions[permission] = [self.getRole(r) for r in roles] - def getUsedRoles(self): return self.usedRoles.values() + def getUsedRoles(self): return list(self.usedRoles.values()) def addRoles(self, roleNames, permissions=()): '''Adds p_roleNames in self.permissions. If p_permissions is specified, roles are added to those permissions only. Else, roles are added for every permission within self.permissions.''' - if isinstance(roleNames, basestring): roleNames = (roleNames,) - if isinstance(permissions, basestring): permissions = (permissions,) - for perm, roles in self.permissions.iteritems(): + if isinstance(roleNames, str): roleNames = (roleNames,) + if isinstance(permissions, str): permissions = (permissions,) + for perm, roles in self.permissions.items(): if permissions and (perm not in permissions): continue for roleName in roleNames: # Do nothing if p_roleName is already almong roles. @@ -124,9 +126,9 @@ class State: '''Removes p_roleNames within dict self.permissions. If p_permissions is specified, removal is restricted to those permissions. Else, removal occurs throughout the whole dict self.permissions.''' - if isinstance(roleNames, basestring): roleNames = (roleNames,) - if isinstance(permissions, basestring): permissions = (permissions,) - for perm, roles in self.permissions.iteritems(): + if isinstance(roleNames, str): roleNames = (roleNames,) + if isinstance(permissions, str): permissions = (permissions,) + for perm, roles in self.permissions.items(): if permissions and (perm not in permissions): continue for roleName in roleNames: # Remove this role if present in roles for this permission. @@ -138,9 +140,9 @@ class State: def setRoles(self, roleNames, permissions=()): '''Sets p_rolesNames for p_permissions if not empty, for every permission in self.permissions else.''' - if isinstance(roleNames, basestring): roleNames = (roleNames,) - if isinstance(permissions, basestring): permissions = (permissions,) - for perm in self.permissions.iterkeys(): + if isinstance(roleNames, str): roleNames = (roleNames,) + if isinstance(permissions, str): permissions = (permissions,) + for perm in self.permissions.keys(): if permissions and (perm not in permissions): continue roles = self.permissions[perm] = [] for roleName in roleNames: @@ -150,8 +152,8 @@ class State: '''Replaces p_oldRoleName by p_newRoleName. If p_permissions is specified, the replacement is restricted to those permissions. Else, replacements apply to the whole dict self.permissions.''' - if isinstance(permissions, basestring): permissions = (permissions,) - for perm, roles in self.permissions.iteritems(): + if isinstance(permissions, str): permissions = (permissions,) + for perm, roles in self.permissions.items(): if permissions and (perm not in permissions): continue # Find and delete p_oldRoleName for role in roles: @@ -170,7 +172,7 @@ class State: worklflow, this method will always return True (I mean: in this case, having an isolated state does not mean the state has been deactivated).''' - for tr in wf.__dict__.itervalues(): + for tr in wf.__dict__.values(): if not isinstance(tr, Transition): continue if not tr.hasState(self, True): continue # Transition "tr" has this state as start state. If the end state is @@ -200,7 +202,7 @@ class Transition: # be useful for "undo" transitions, for example. self.states = self.standardiseStates(states) self.condition = condition - if isinstance(condition, basestring): + if isinstance(condition, str): # The condition specifies the name of a role. self.condition = Role(condition) self.action = action @@ -276,7 +278,7 @@ class Transition: def isShowable(self, workflow, obj): '''Is this transition showable?''' - if callable(self.show): + if isinstance(self.show, collections.Callable): return self.show(workflow, obj.appy()) else: return self.show @@ -330,7 +332,7 @@ class Transition: for condition in self.condition: # "Unwrap" role names from Role instances if isinstance(condition, Role): condition = condition.name - if isinstance(condition, basestring): # It is a role + if isinstance(condition, str): # It is a role if hasRole == None: hasRole = False if user.has_role(condition, obj): @@ -450,7 +452,7 @@ class Transition: transition = getattr(workflow, transition) # Browse all transitions and find the one starting at p_transition's end # state and coming back to p_transition's start state. - for trName, tr in workflow.__dict__.iteritems(): + for trName, tr in workflow.__dict__.items(): if not isinstance(tr, Transition) or (tr == transition): continue if transition.isSingle(): if tr.hasState(transition.states[1], True) and \ diff --git a/gen/descriptors.py b/gen/descriptors.py index a1a73d1..f289a13 100644 --- a/gen/descriptors.py +++ b/gen/descriptors.py @@ -5,9 +5,9 @@ # ------------------------------------------------------------------------------ import types, copy import appy.gen as gen -import po -from model import ModelClass -from utils import produceNiceMessage, getClassName +from . import po +from .model import ModelClass +from .utils import produceNiceMessage, getClassName TABS = 4 # Number of blanks in a Python indentation. # ------------------------------------------------------------------------------ @@ -130,7 +130,7 @@ class ClassDescriptor(Descriptor): def isAbstract(self): '''Is self.klass abstract?''' res = False - if self.klass.__dict__.has_key('abstract'): + if 'abstract' in self.klass.__dict__: res = self.klass.__dict__['abstract'] return res @@ -139,7 +139,7 @@ class ClassDescriptor(Descriptor): concept into the application. For example, creating instances of such classes will be easy from the user interface.''' res = False - if self.klass.__dict__.has_key('root'): + if 'root' in self.klass.__dict__: res = self.klass.__dict__['root'] return res @@ -150,7 +150,7 @@ class ClassDescriptor(Descriptor): theClass = self.klass if klass: theClass = klass - if theClass.__dict__.has_key('folder'): + if 'folder' in theClass.__dict__: res = theClass.__dict__['folder'] else: if theClass.__bases__: @@ -176,14 +176,14 @@ class ClassDescriptor(Descriptor): def getCreateMean(self, type='Import'): '''Returns the mean for this class that corresponds to p_type, or None if the class does not support this create mean.''' - if not self.klass.__dict__.has_key('create'): return + if 'create' not in self.klass.__dict__: return else: means = self.klass.create if not means: return if not isinstance(means, tuple) and not isinstance(means, list): means = [means] for mean in means: - exec 'found = isinstance(mean, %s)' % type + exec('found = isinstance(mean, %s)' % type) if found: return mean @staticmethod @@ -192,7 +192,7 @@ class ClassDescriptor(Descriptor): p_tool is given, we are at execution time (not a generation time), and we may potentially execute search.show methods that allow to conditionnaly include a search or not.''' - if klass.__dict__.has_key('search'): + if 'search' in klass.__dict__: searches = klass.__dict__['search'] if not tool: return searches # Evaluate attributes "show" for every search. @@ -229,10 +229,10 @@ class ClassDescriptor(Descriptor): def addField(self, fieldName, fieldType): '''Adds a new field to the Tool.''' - exec "self.modelClass.%s = fieldType" % fieldName + exec("self.modelClass.%s = fieldType" % fieldName) if fieldName in self.modelClass._appy_attributes: - print('Warning, field "%s" is already existing on class "%s"' % \ - (fieldName, self.modelClass.__name__)) + print(('Warning, field "%s" is already existing on class "%s"' % \ + (fieldName, self.modelClass.__name__))) return self.modelClass._appy_attributes.append(fieldName) self.orderedAttributes.append(fieldName) @@ -488,9 +488,9 @@ class TranslationClassDescriptor(ClassDescriptor): maxLine = 100 # We suppose a line is 100 characters long. width = 0 height = 0 - for fileName, poFile in i18nFiles.iteritems(): + for fileName, poFile in i18nFiles.items(): if not fileName.startswith('%s-' % appName) or \ - not i18nFiles[fileName].messagesDict.has_key(messageId): + messageId not in i18nFiles[fileName].messagesDict: # In this case this is not one of our Appy-managed translation # files. continue diff --git a/gen/generator.py b/gen/generator.py index 0923c9e..0b4c09c 100644 --- a/gen/generator.py +++ b/gen/generator.py @@ -3,10 +3,11 @@ import os, os.path, re, sys, parser, symbol, token, types import appy, appy.pod.renderer from appy.shared.utils import FolderDeleter import appy.gen as gen -import po -from descriptors import * -from utils import getClassName -from model import ModelClass, User, Group, Tool, Translation, Page +from . import po +from .descriptors import * +from .utils import getClassName +from .model import ModelClass, User, Group, Tool, Translation, Page +import collections # ------------------------------------------------------------------------------ class GeneratorError(Exception): pass @@ -160,7 +161,7 @@ class Generator: appy.fields.Field, it will be considered a gen-class. If p_klass declares at least one static attribute that is a appy.gen.State, it will be considered a gen-workflow.''' - for attr in klass.__dict__.itervalues(): + for attr in klass.__dict__.values(): if isinstance(attr, gen.Field): return 'class' elif isinstance(attr, gen.State): return 'workflow' @@ -173,11 +174,11 @@ class Generator: self.totalNumberOfTests += 1 res = True # Count also docstring in methods - if type(moduleOrClass) == types.ClassType: - for name, elem in moduleOrClass.__dict__.iteritems(): + if type(moduleOrClass) == type: + for name, elem in moduleOrClass.__dict__.items(): if type(elem) in (staticmethod, classmethod): elem = elem.__get__(name) - if callable(elem) and (type(elem) != types.ClassType) and \ + if isinstance(elem, collections.Callable) and (type(elem) != type) and \ hasattr(elem, '__doc__') and elem.__doc__ and \ (elem.__doc__.find('>>>') != -1): res = True @@ -198,8 +199,8 @@ class Generator: self.modulesWithTests.add(module.__name__) classType = type(Generator) # Find all classes in this module - for name in module.__dict__.keys(): - exec 'moduleElem = module.%s' % name + for name in list(module.__dict__.keys()): + exec('moduleElem = module.%s' % name) # Ignore non-classes module elements or classes that were imported # from other modules. if (type(moduleElem) != classType) or \ @@ -213,7 +214,7 @@ class Generator: # Collect non-parsable attrs = back references added # programmatically moreAttrs = [] - for eName, eValue in moduleElem.__dict__.iteritems(): + for eName, eValue in moduleElem.__dict__.items(): if isinstance(eValue, gen.Field) and (eName not in attrs): moreAttrs.append(eName) # Sort them in alphabetical order: else, order would be random @@ -257,7 +258,7 @@ class Generator: # What is the name of the application ? appName = os.path.basename(self.application) # Get the app-specific config if any - exec 'import %s as appModule' % appName + exec('import %s as appModule' % appName) if hasattr (appModule, 'Config'): self.config = appModule.Config if not issubclass(self.config, gen.Config): @@ -273,7 +274,7 @@ class Generator: # Ignore non Python files if not fileName.endswith('.py'): continue moduleName = '%s.%s' % (appName, os.path.splitext(fileName)[0]) - exec 'import %s' % moduleName + exec('import %s' % moduleName) modules.append(eval(moduleName)) # Parse imported modules for module in modules: @@ -321,7 +322,7 @@ class Generator: fileContent = f.read() f.close() if not fileName.endswith('.png'): - for rKey, rValue in replacements.iteritems(): + for rKey, rValue in replacements.items(): fileContent = fileContent.replace( '' % rKey, str(rValue)) f = file(resultPath, 'w') @@ -343,7 +344,7 @@ class Generator: msg = '' if self.totalNumberOfTests: msg = ' (number of tests found: %d)' % self.totalNumberOfTests - print('Done%s.' % msg) + print(('Done%s.' % msg)) # ------------------------------------------------------------------------------ class ZopeGenerator(Generator): @@ -413,7 +414,7 @@ class ZopeGenerator(Generator): f.close() # Generate i18n pot file potFileName = '%s.pot' % self.applicationName - if self.i18nFiles.has_key(potFileName): + if potFileName in self.i18nFiles: potFile = self.i18nFiles[potFileName] else: fullName = os.path.join(self.application, 'tr', potFileName) @@ -427,14 +428,14 @@ class ZopeGenerator(Generator): self.options.i18nClean, keepExistingOrder=False) potFile.generate() if removedLabels: - print('Warning: %d messages were removed from translation ' \ - 'files: %s' % (len(removedLabels), str(removedLabels))) + print(('Warning: %d messages were removed from translation ' \ + 'files: %s' % (len(removedLabels), str(removedLabels)))) # Generate i18n po files for language in self.config.languages: # I must generate (or update) a po file for the language(s) # specified in the configuration. poFileName = potFile.getPoFileName(language) - if self.i18nFiles.has_key(poFileName): + if poFileName in self.i18nFiles: poFile = self.i18nFiles[poFileName] else: fullName = os.path.join(self.application, 'tr', poFileName) @@ -501,7 +502,7 @@ class ZopeGenerator(Generator): for role in creators: if role.name not in allRoles: allRoles[role.name] = role - res = allRoles.values() + res = list(allRoles.values()) # Filter the result according to parameters for p in ('appy', 'local', 'grantable'): if eval(p) != None: @@ -621,12 +622,12 @@ class ZopeGenerator(Generator): else: # If a child of this class is already present, we must insert # this klass before it. - lowestChildIndex = sys.maxint + lowestChildIndex = sys.maxsize for resClass in resClasses: if klass in resClass.__bases__: lowestChildIndex = min(lowestChildIndex, resClasses.index(resClass)) - if lowestChildIndex != sys.maxint: + if lowestChildIndex != sys.maxsize: res.insert(lowestChildIndex, classDescr) resClasses.insert(lowestChildIndex, klass) else: @@ -745,7 +746,7 @@ class ZopeGenerator(Generator): '''Is called each time an Appy class is found in the application, for generating the corresponding Archetype class.''' k = classDescr.klass - print('Generating %s.%s (gen-class)...' % (k.__module__, k.__name__)) + print(('Generating %s.%s (gen-class)...' % (k.__module__, k.__name__))) # Determine base Zope class isFolder = classDescr.isFolder() baseClass = isFolder and 'Folder' or 'SimpleItem' @@ -772,7 +773,7 @@ class ZopeGenerator(Generator): '''This method creates the i18n labels related to the workflow described by p_wfDescr.''' k = wfDescr.klass - print('Generating %s.%s (gen-workflow)...' % (k.__module__, k.__name__)) + print(('Generating %s.%s (gen-workflow)...' % (k.__module__, k.__name__))) # Identify workflow name wfName = WorkflowDescriptor.getWorkflowName(wfDescr.klass) # Add i18n messages for states diff --git a/gen/indexer.py b/gen/indexer.py index 05436a2..d54317d 100644 --- a/gen/indexer.py +++ b/gen/indexer.py @@ -33,7 +33,7 @@ def updateIndexes(installer, indexInfo): '''This function updates the indexes defined in the catalog.''' catalog = installer.app.catalog logger = installer.logger - for indexName, indexType in indexInfo.iteritems(): + for indexName, indexType in indexInfo.items(): indexRealType = indexType if indexType in ('XhtmlIndex', 'TextIndex', 'ListIndex'): indexRealType = 'ZCTextIndex' diff --git a/gen/installer.py b/gen/installer.py index 52a672e..13f25c3 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -43,7 +43,7 @@ class FakeZCatalog: def onDelSession(sessionObject, container): '''This function is called when a session expires.''' rq = container.REQUEST - if rq.cookies.has_key('_appy_') and rq.cookies.has_key('_ZopeId') and \ + if '_appy_' in rq.cookies and '_ZopeId' in rq.cookies and \ (rq['_ZopeId'] == sessionObject.token): # The request comes from a guy whose session has expired. resp = rq.RESPONSE @@ -155,7 +155,7 @@ class ZopeInstaller: # Create or update Appy-wide indexes and field-related indexes indexInfo = defaultIndexes.copy() tool = self.app.config - for className in self.config.attributes.iterkeys(): + for className in self.config.attributes.keys(): wrapperClass = tool.getAppyClass(className, wrapper=True) indexInfo.update(wrapperClass.getIndexes(includeDefaults=False)) updateIndexes(self, indexInfo) @@ -196,7 +196,7 @@ class ZopeInstaller: if hasattr(appyTool, 'beforeInstall'): appyTool.beforeInstall() # Create the default users if they do not exist. - for login, roles in self.defaultUsers.iteritems(): + for login, roles in self.defaultUsers.items(): if not appyTool.count('User', noSecurity=True, login=login): appyTool.create('users', noSecurity=True, id=login, login=login, password3=login, password4=login, @@ -277,7 +277,7 @@ class ZopeInstaller: name = klass.__name__ module = klass.__module__ wrapper = klass.wrapperClass - exec 'from %s import manage_add%s as ctor' % (module, name) + exec('from %s import manage_add%s as ctor' % (module, name)) self.zopeContext.registerClass(meta_type=name, constructors = (ctor,), permission = None) # Create workflow prototypical instances in __instance__ attributes @@ -316,7 +316,7 @@ class ZopeInstaller: # Post-initialise every Appy type for baseClass in klass.wrapperClass.__bases__: if baseClass.__name__ == 'AbstractWrapper': continue - for name, appyType in baseClass.__dict__.iteritems(): + for name, appyType in baseClass.__dict__.items(): if not isinstance(appyType, gen.Field) or \ (isinstance(appyType, gen.Ref) and appyType.isBack): continue # Back refs are initialised within fw refs diff --git a/gen/layout.py b/gen/layout.py index 46263f5..e2caa51 100644 --- a/gen/layout.py +++ b/gen/layout.py @@ -39,9 +39,9 @@ # ------------------------------------------------------------------------------ rowDelimiters = {'-':'middle', '=':'top', '_':'bottom'} -rowDelms = ''.join(rowDelimiters.keys()) +rowDelms = ''.join(list(rowDelimiters.keys())) cellDelimiters = {'|': 'center', ';': 'left', '!': 'right'} -cellDelms = ''.join(cellDelimiters.keys()) +cellDelms = ''.join(list(cellDelimiters.keys())) pxDict = { # Page-related elements @@ -138,7 +138,7 @@ class Table: # Initialise simple params, either from the true params, either from # the p_other Table instance. for param in Table.simpleParams: - exec 'self.%s = %s%s' % (param, source, param) + exec('self.%s = %s%s' % (param, source, param)) # The following attribute will store a special Row instance used for # defining column properties. self.headerRow = None diff --git a/gen/mail.py b/gen/mail.py index 08f3ac9..c223d60 100644 --- a/gen/mail.py +++ b/gen/mail.py @@ -120,10 +120,10 @@ def sendMail(config, to, subject, body, attachments=None, log=None): if res and log: log('could not send mail to some recipients. %s' % str(res), type='warning') - except smtplib.SMTPException, e: + except smtplib.SMTPException as e: if log: log('%s: mail sending failed (%s)' % (config, str(e)), type='error') - except socket.error, se: + except socket.error as se: if log: log('%s: mail sending failed (%s)' % (config, str(se)),type='error') # ------------------------------------------------------------------------------ diff --git a/gen/migrator.py b/gen/migrator.py index 967e701..0fc3829 100644 --- a/gen/migrator.py +++ b/gen/migrator.py @@ -38,10 +38,10 @@ class Migrator: if frozen.data.__class__.__name__ == 'Pdata': # The file content is splitted in several chunks. f.write(frozen.data.data) - nextPart = frozen.data.next + nextPart = frozen.data.__next__ while nextPart: f.write(nextPart.data) - nextPart = nextPart.next + nextPart = nextPart.__next__ else: # Only one chunk f.write(frozen.data) diff --git a/gen/mixins/TestMixin.py b/gen/mixins/TestMixin.py index aefe1f2..7ad0ba0 100644 --- a/gen/mixins/TestMixin.py +++ b/gen/mixins/TestMixin.py @@ -13,18 +13,18 @@ class TestMixin: '''Returns the list of sub-modules of p_app that are non-empty.''' res = [] try: - exec 'import %s' % moduleName - exec 'moduleObj = %s' % moduleName + exec('import %s' % moduleName) + exec('moduleObj = %s' % moduleName) moduleFile = moduleObj.__file__ if moduleFile.endswith('.pyc'): moduleFile = moduleFile[:-1] - except ImportError, ie: + except ImportError as ie: return res - except SyntaxError, se: + except SyntaxError as se: return res # Include the module if not empty. "Emptyness" is determined by the # absence of names beginning with other chars than "__". - for elem in moduleObj.__dict__.iterkeys(): + for elem in moduleObj.__dict__.keys(): if not elem.startswith('__'): res.append(moduleObj) break @@ -66,10 +66,10 @@ def afterTest(test): '''Is executed after every test.''' g = test.globs appName = g['tool'].o.getAppName() - exec 'from Products.%s import cov, covFolder, totalNumberOfTests, ' \ - 'countTest' % appName + exec('from Products.%s import cov, covFolder, totalNumberOfTests, ' \ + 'countTest' % appName) countTest() - exec 'from Products.%s import numberOfExecutedTests' % appName + exec('from Products.%s import numberOfExecutedTests' % appName) if cov and (numberOfExecutedTests == totalNumberOfTests): cov.stop() appModules = test.getNonEmptySubModules(appName) diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 5b46e12..11bc99c 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -13,6 +13,7 @@ from appy.shared import mimeTypes from appy.shared import utils as sutils from appy.shared.data import languages from appy.shared.ldap_connector import LdapConnector +import collections try: from AccessControl.ZopeSecurityPolicy import _noroles except ImportError: @@ -36,7 +37,7 @@ class ToolMixin(BaseMixin): p_metaTypeOrAppyType.''' appName = self.getProductConfig().PROJECTNAME res = metaTypeOrAppyClass - if not isinstance(metaTypeOrAppyClass, basestring): + if not isinstance(metaTypeOrAppyClass, str): res = gutils.getClassName(metaTypeOrAppyClass, appName) if res.find('_wrappers') != -1: elems = res.split('_') @@ -439,7 +440,7 @@ class ToolMixin(BaseMixin): def quote(self, s, escapeWithEntity=True): '''Returns the quoted version of p_s.''' - if not isinstance(s, basestring): s = str(s) + if not isinstance(s, str): s = str(s) repl = escapeWithEntity and ''' or "\\'" s = s.replace('\r\n', '').replace('\n', '').replace("'", repl) return "'%s'" % s @@ -452,7 +453,7 @@ class ToolMixin(BaseMixin): def getZopeClass(self, name): '''Returns the Zope class whose name is p_name.''' - exec 'from Products.%s.%s import %s as C'% (self.getAppName(),name,name) + exec('from Products.%s.%s import %s as C'% (self.getAppName(),name,name)) return C def getAppyClass(self, zopeName, wrapper=False): @@ -476,12 +477,12 @@ class ToolMixin(BaseMixin): '''Gets the different ways objects of p_klass can be created (currently: via a web form or programmatically only). Result is a list.''' res = [] - if not klass.__dict__.has_key('create'): + if 'create' not in klass.__dict__: return ['form'] else: means = klass.create if means: - if isinstance(means, basestring): res = [means] + if isinstance(means, str): res = [means] else: res = means return res @@ -511,7 +512,7 @@ class ToolMixin(BaseMixin): else: creators = self.getProductConfig().appConfig.defaultCreators # Resolve case (3): if "creators" is a method, execute it. - if callable(creators): creators = creators(self.appy()) + if isinstance(creators, collections.Callable): creators = creators(self.appy()) # Resolve case (2) if isinstance(creators, bool) or not creators: return creators # Resolve case (1): checks whether the user has at least one of the @@ -595,7 +596,7 @@ class ToolMixin(BaseMixin): rq = self.REQUEST # Store the search criteria in the session criteria = self._getDefaultSearchCriteria() - for name in rq.form.keys(): + for name in list(rq.form.keys()): if name.startswith('w_') and not self._searchValueIsEmpty(name): hasStar = name.find('*') != -1 fieldName = not hasStar and name[2:] or name[2:name.find('*')] @@ -609,17 +610,17 @@ class ToolMixin(BaseMixin): # The type of the value is encoded after char "*". name, type = name.split('*') if type == 'bool': - exec 'value = %s' % value + exec('value = %s' % value) elif type in ('int', 'float'): # Get the "from" value if not value: value = None else: - exec 'value = %s(value)' % type + exec('value = %s(value)' % type) # Get the "to" value toValue = rq.form['%s_to' % name[2:]].strip() if not toValue: toValue = None else: - exec 'toValue = %s(toValue)' % type + exec('toValue = %s(toValue)' % type) value = (value, toValue) elif type == 'date': prefix = name[2:] @@ -640,8 +641,8 @@ class ToolMixin(BaseMixin): if len(type) > 6: transform = type.split('-')[1] if (transform != 'none') and value: - exec 'value = value.%s()' % \ - self.transformMethods[transform] + exec('value = value.%s()' % \ + self.transformMethods[transform]) if isinstance(value, list): # It is a list of values. Check if we have an operator for # the field, to see if we make an "and" or "or" for all @@ -697,7 +698,7 @@ class ToolMixin(BaseMixin): it among search criteria in the session.''' if not refInfo and (self.REQUEST.get('search', None) == 'customSearch'): criteria = self.REQUEST.SESSION.get('searchCriteria', None) - if criteria and criteria.has_key('_ref'): refInfo = criteria['_ref'] + if criteria and '_ref' in criteria: refInfo = criteria['_ref'] if not refInfo: return None, None objectUid, fieldName = refInfo.split(':') obj = self.getObject(objectUid) @@ -856,7 +857,7 @@ class ToolMixin(BaseMixin): try: creds = creds.split(' ')[-1] login, password = base64.decodestring(creds).split(':', 1) - except Exception, e: + except Exception as e: pass # b. Identify the user from the authentication cookie. if not login: @@ -968,7 +969,7 @@ class ToolMixin(BaseMixin): # Invalidate the user session. try: sdm = self.session_data_manager - except AttributeError, ae: + except AttributeError as ae: # When ran in test mode, session_data_manager is not there. sdm = None if sdm: @@ -977,7 +978,7 @@ class ToolMixin(BaseMixin): session.invalidate() self.log('logged out.') # Remove user from variable "loggedUsers" - if self.loggedUsers.has_key(userId): del self.loggedUsers[userId] + if userId in self.loggedUsers: del self.loggedUsers[userId] return self.goto(self.getApp().absolute_url()) # This dict stores, for every logged user, the date/time of its last access @@ -1247,7 +1248,7 @@ class ToolMixin(BaseMixin): if hasattr(klass, 'popup'): res.target = 'appyIFrame' d = klass.popup - if isinstance(d, basestring): + if isinstance(d, str): # Width only params = d[:-2] else: diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py index 1a6e92b..9d87457 100644 --- a/gen/mixins/__init__.py +++ b/gen/mixins/__init__.py @@ -3,7 +3,7 @@ - mixins/ToolMixin is mixed in with the generated application Tool class.''' # ------------------------------------------------------------------------------ -import os, os.path, re, sys, types, urllib, cgi +import os, os.path, re, sys, types, urllib.request, urllib.parse, urllib.error, cgi from appy import Object from appy.px import Px from appy.fields.workflow import UiTransition @@ -15,6 +15,7 @@ from appy.shared import utils as sutils from appy.shared.data import rtlLanguages from appy.shared.xml_parser import XmlMarshaller, XmlUnmarshaller from appy.shared.diff import HtmlDiff +import collections # ------------------------------------------------------------------------------ NUMBERED_ID = re.compile('.+\d{4}$') @@ -371,9 +372,9 @@ class BaseMixin: # p_errors object. Within this object, for every error message that is # not a string, we replace it with the standard validation error for the # corresponding field. - for key, value in errors.__dict__.iteritems(): + for key, value in errors.__dict__.items(): resValue = value - if not isinstance(resValue, basestring): + if not isinstance(resValue, str): resValue = self.translate('field_invalid') setattr(errors, key, resValue) return msg @@ -419,7 +420,7 @@ class BaseMixin: # Trigger field-specific validation self.intraFieldValidation(errors, values) if errors.__dict__: - for k,v in errors.__dict__.iteritems(): rq.set('%s_error' % k, v) + for k,v in errors.__dict__.items(): rq.set('%s_error' % k, v) self.say(errorMessage) return self.gotoEdit() @@ -427,7 +428,7 @@ class BaseMixin: msg = self.interFieldValidation(errors, values) if not msg: msg = errorMessage if errors.__dict__: - for k,v in errors.__dict__.iteritems(): rq.set('%s_error' % k, v) + for k,v in errors.__dict__.items(): rq.set('%s_error' % k, v) self.say(msg) return self.gotoEdit() @@ -506,7 +507,7 @@ class BaseMixin: # Get the list of indexes that apply on this object. Else, Zope # will reindex all indexes defined in the catalog, and through # acquisition, wrong methods can be called on wrong objects. - iNames = self.wrapperClass.getIndexes().keys() + iNames = list(self.wrapperClass.getIndexes().keys()) catalog.catalog_object(self, path, idxs=iNames) def xml(self, action=None): @@ -529,14 +530,14 @@ class BaseMixin: elif isinstance(methodRes, file): res = methodRes.read() methodRes.close() - elif isinstance(methodRes, basestring) and \ + elif isinstance(methodRes, str) and \ methodRes.startswith('-.''' # Add to the p_changes dict the field labels - for name in changes.keys(): + for name in list(changes.keys()): # "name" can contain the language for multilingual fields. if '-' in name: fieldName, lg = name.split('-') @@ -646,7 +647,7 @@ class BaseMixin: historized fields, while p_self already contains the (potentially) modified values.''' # Remove from previousData all values that were not changed - for name in previousData.keys(): + for name in list(previousData.keys()): field = self.getAppyType(name) prev = previousData[name] curr = field.getValue(self) @@ -655,7 +656,7 @@ class BaseMixin: ((prev == '') and (curr == None)): del previousData[name] continue - except UnicodeDecodeError, ude: + except UnicodeDecodeError as ude: # The string comparisons above may imply silent encoding-related # conversions that may produce this exception. continue @@ -743,15 +744,15 @@ class BaseMixin: else: klass = self.appy().klass moduleName = klass.__module__ - exec 'import %s' % moduleName - exec 'reload(%s)' % moduleName - exec 'res = %s.%s' % (moduleName, klass.__name__) + exec('import %s' % moduleName) + exec('reload(%s)' % moduleName) + exec('res = %s.%s' % (moduleName, klass.__name__)) # More manipulations may have occurred in m_update if hasattr(res, 'update'): parentName= res.__bases__[-1].__name__ moduleName= 'Products.%s.wrappers' % self.getTool().getAppName() - exec 'import %s' % moduleName - exec 'parent = %s.%s' % (moduleName, parentName) + exec('import %s' % moduleName) + exec('parent = %s.%s' % (moduleName, parentName)) res.update(parent) return res @@ -839,15 +840,15 @@ class BaseMixin: req = self.REQUEST for field in self.getAllAppyTypes(): if field.page.name != pageName: continue - if field.masterValue and callable(field.masterValue): + if field.masterValue and isinstance(field.masterValue, collections.Callable): # We have a slave field that is updated via ajax requests. name = field.name # Remember the request value for this field if present. - if req.has_key(name) and req[name]: + if name in req and req[name]: requestValues[name] = req[name] # Remember the validation error for this field if present. errorKey = '%s_error' % name - if req.has_key(errorKey): + if errorKey in req: errors[name] = req[errorKey] return sutils.getStringDict(requestValues), sutils.getStringDict(errors) @@ -899,7 +900,7 @@ class BaseMixin: del phases[res[i].name] del res[i] # Compute next/previous phases of every phase - for ph in phases.itervalues(): + for ph in phases.values(): ph.computeNextPrevious(res) ph.totalNbOfPhases = len(res) # Restrict the result to the current phase if required @@ -1208,7 +1209,7 @@ class BaseMixin: # fields' old values by their formatted counterparts. event = history[i].copy() event['changes'] = {} - for name, oldValue in history[i]['changes'].iteritems(): + for name, oldValue in history[i]['changes'].items(): # "name" can specify a language-specific part in a # multilingual field. "oldValue" is a tuple # (value, fieldName). @@ -1455,7 +1456,7 @@ class BaseMixin: # Add users or groups having, locally, this role on this object. localRoles = getattr(self.aq_base, '__ac_local_roles__', None) if not localRoles: return res - for id, roles in localRoles.iteritems(): + for id, roles in localRoles.items(): for role in roles: if role in res: usr = 'user:%s' % id @@ -1465,7 +1466,7 @@ class BaseMixin: def showState(self): '''Must I show self's current state ?''' stateShow = self.State(name=False).show - if callable(stateShow): + if isinstance(stateShow, collections.Callable): return stateShow(self.getWorkflow(), self.appy()) return stateShow @@ -1479,7 +1480,7 @@ class BaseMixin: if not hasattr(klass, 'showTransitions'): return (layoutType=='view') showValue = klass.showTransitions # This value can be a single value or a tuple/list of values. - if isinstance(showValue, basestring): return layoutType == showValue + if isinstance(showValue, str): return layoutType == showValue return layoutType in showValue getUrlDefaults = {'page':True, 'nav':True} @@ -1524,8 +1525,8 @@ class BaseMixin: if 'nav' not in kwargs: kwargs['nav'] = True # Create URL parameters from kwargs params = [] - for name, value in kwargs.iteritems(): - if isinstance(value, basestring): + for name, value in kwargs.items(): + if isinstance(value, str): params.append('%s=%s' % (name, value)) elif self.REQUEST.get(name, ''): params.append('%s=%s' % (name, self.REQUEST[name])) @@ -1601,7 +1602,7 @@ class BaseMixin: # Find the name of the method to call. methodName = rq.QUERY_STRING.split('=')[1] return self.xml(action=methodName) - elif rq.has_key('do'): + elif 'do' in rq: # The user wants to call a method on this object and get its result # as XML. return self.xml(action=rq['do']) @@ -1672,7 +1673,7 @@ class BaseMixin: if field.type != 'group': fieldMapping = field.mapping[label] if fieldMapping: - if callable(fieldMapping): + if isinstance(fieldMapping, collections.Callable): fieldMapping = field.callMethod(self, fieldMapping) mapping.update(fieldMapping) label = getattr(field, '%sId' % label) @@ -1697,15 +1698,15 @@ class BaseMixin: # Perform replacements, according to p_format. res = self.formatText(res, format) # Perform variable replacements - for name, repl in mapping.iteritems(): - if not isinstance(repl, basestring): repl = str(repl) + for name, repl in mapping.items(): + if not isinstance(repl, str): repl = str(repl) res = res.replace('${%s}' % name, repl) return res def getPageLayout(self, layoutType): '''Returns the layout corresponding to p_layoutType for p_self.''' res = self.wrapperClass.getPageLayouts()[layoutType] - if isinstance(res, basestring): res = Table(res) + if isinstance(res, str): res = Table(res) return res def download(self, name=None): diff --git a/gen/model.py b/gen/model.py index 35082c9..814afeb 100644 --- a/gen/model.py +++ b/gen/model.py @@ -5,6 +5,7 @@ # ------------------------------------------------------------------------------ import types import appy.gen as gen +import collections # Prototypical instances of every type ----------------------------------------- class Protos: @@ -49,7 +50,7 @@ class ModelClass: '''This method returns the code declaration for p_appyType.''' typeArgs = '' proto = Protos.get(appyType) - for name, value in appyType.__dict__.iteritems(): + for name, value in appyType.__dict__.items(): # Some attrs can't be given to the constructor if name in Protos.notInit: continue # If the given value corresponds to the default value, don't give it @@ -74,7 +75,7 @@ class ModelClass: # defined. So we initialize it to None. The post-init of the # field must be done manually in wrappers.py. value = 'None' - elif isinstance(value, basestring): + elif isinstance(value, str): value = '"%s"' % value elif isinstance(value, gen.Ref): if not value.isBack: continue @@ -91,10 +92,10 @@ class ModelClass: value = 'Grp("%s")' % value.name elif isinstance(value, gen.Page): value = 'pges["%s"]' % value.name - elif callable(value): + elif isinstance(value, collections.Callable): className = wrapperName if (appyType.type == 'Ref') and appyType.isBack: - className = value.im_class.__name__ + className = value.__self__.__class__.__name__ value = '%s.%s' % (className, value.__name__) typeArgs += '%s=%s,' % (name, value) return '%s(%s)' % (appyType.__class__.__name__, typeArgs) @@ -118,17 +119,17 @@ class ModelClass: pages = {} layouts = [] for name in klass._appy_attributes: - exec 'appyType = klass.%s' % name + exec('appyType = klass.%s' % name) if appyType.page.name not in pages: pages[appyType.page.name] = appyType.page res += ' pges = {' - for page in pages.itervalues(): + for page in pages.values(): # Determine page "show" attributes pShow = '' for attr in ('',) + page.subElements: attrName = 'show%s' % attr.capitalize() pageShow = getattr(page, attrName) - if isinstance(pageShow, basestring): pageShow='"%s"' % pageShow + if isinstance(pageShow, str): pageShow='"%s"' % pageShow elif callable(pageShow): pageShow = '%s.%s' % (wrapperName, pageShow.__name__) if pageShow != True: @@ -142,7 +143,7 @@ class ModelClass: res += '}\n' # Secondly, dump every (not Ref.isBack) attribute for name in klass._appy_attributes: - exec 'appyType = klass.%s' % name + exec('appyType = klass.%s' % name) if (appyType.type == 'Ref') and appyType.isBack: continue typeBody = klass._appy_getTypeBody(appyType, wrapperName) res += ' %s=%s\n' % (name, typeBody) @@ -305,12 +306,12 @@ class Tool(ModelClass): @classmethod def _appy_clean(klass): toClean = [] - for k, v in klass.__dict__.iteritems(): + for k, v in klass.__dict__.items(): if not k.startswith('__') and (not k.startswith('_appy_')): if k not in defaultToolFields: toClean.append(k) for k in toClean: - exec 'del klass.%s' % k + exec('del klass.%s' % k) klass._appy_attributes = list(defaultToolFields) klass.folder = True # ------------------------------------------------------------------------------ diff --git a/gen/po.py b/gen/po.py index 78ba3e3..54c73c5 100644 --- a/gen/po.py +++ b/gen/po.py @@ -1,6 +1,6 @@ # ------------------------------------------------------------------------------ import os, re, time, copy -from utils import produceNiceMessage +from .utils import produceNiceMessage # ------------------------------------------------------------------------------ poHeader = '''msgid "" @@ -213,7 +213,7 @@ class PoFile: if keepExistingOrder: # Update existing messages and add inexistent messages to the end. for newMsg in newMessages: - if self.messagesDict.has_key(newMsg.id): + if newMsg.id in self.messagesDict: msg = self.messagesDict[newMsg.id] else: msg = self.addMessage(newMsg) @@ -224,7 +224,7 @@ class PoFile: notNewMessages = [m for m in self.messages if m.id not in newIds] del self.messages[:] for newMsg in newMessages: - if self.messagesDict.has_key(newMsg.id): + if newMsg.id in self.messagesDict: msg = self.messagesDict[newMsg.id] self.messages.append(msg) else: @@ -240,7 +240,7 @@ class PoFile: fb = '' if not self.isPot: # I must add fallbacks - if fallbacks.has_key(self.language): + if self.language in fallbacks: fb = '"X-is-fallback-for: %s\\n"' % fallbacks[self.language] f.write(poHeader % (self.applicationName, creationTime, self.language, self.language, self.domain, fb)) diff --git a/gen/utils.py b/gen/utils.py index fdaf8a1..ac18be5 100644 --- a/gen/utils.py +++ b/gen/utils.py @@ -1,7 +1,8 @@ # ------------------------------------------------------------------------------ -import re, os, os.path, base64, urllib +import re, os, os.path, base64, urllib.request, urllib.parse, urllib.error from appy.px import Px from appy.shared import utils as sutils +import collections # Function for creating a Zope object ------------------------------------------ def createObject(folder, id, className, appName, wf=True, noSecurity=False): @@ -10,8 +11,8 @@ def createObject(folder, id, className, appName, wf=True, noSecurity=False): creation of the config object), computing workflow-related info is not possible at this time. This is why this function can be called with p_wf=False.''' - exec 'from Products.%s.%s import %s as ZopeClass' % \ - (appName, className, className) + exec('from Products.%s.%s import %s as ZopeClass' % \ + (appName, className, className)) # Get the tool. It may not be present yet, maybe are we creating it now. if folder.meta_type.endswith('Folder'): # p_folder is a standard Zope (temp) folder. @@ -178,7 +179,7 @@ def callMethod(obj, method, klass=None, cache=True): if methodType == 'staticmethod': method = method.__get__(klass) elif methodType == 'instancemethod': - method = method.im_func + method = method.__func__ # Call the method if cache is not needed if not cache: return method(obj) # If first arg of method is named "tool" instead of the traditional "self", @@ -187,7 +188,7 @@ def callMethod(obj, method, klass=None, cache=True): # Every method call, even on different instances, will be cached in a unique # key. cheat = False - if not klass and (method.func_code.co_varnames[0] == 'tool'): + if not klass and (method.__code__.co_varnames[0] == 'tool'): prefix = obj.klass.__name__ obj = obj.tool cheat = True @@ -200,7 +201,7 @@ def callMethod(obj, method, klass=None, cache=True): else: prefix = obj.uid # Second part of the key: p_method name - key = '%s:%s' % (prefix, method.func_name) + key = '%s:%s' % (prefix, method.__name__) # Return the cached value if present in the method cache. if key in rq.methodCache: return rq.methodCache[key] @@ -216,20 +217,20 @@ def readCookie(request): (None, None).''' cookie = request.get('_appy_', None) if not cookie: return None, None - cookieValue = base64.decodestring(urllib.unquote(cookie)) + cookieValue = base64.decodestring(urllib.parse.unquote(cookie)) if ':' in cookieValue: return cookieValue.split(':') return None, None def writeCookie(login, password, request): '''Encode p_login and p_password into the cookie set in the p_request.''' cookieValue = base64.encodestring('%s:%s' % (login, password)).rstrip() - cookieValue = urllib.quote(cookieValue) + cookieValue = urllib.parse.quote(cookieValue) request.RESPONSE.setCookie('_appy_', cookieValue, path='/') # ------------------------------------------------------------------------------ def initMasterValue(v): '''Standardizes p_v as a list of strings, excepted if p_v is a method.''' - if callable(v): return v + if isinstance(v, collections.Callable): return v if not isinstance(v, bool) and not v: res = [] elif type(v) not in sutils.sequenceTypes: res = [v] else: res = v @@ -243,7 +244,7 @@ class No: instead. When creating such an instance, you can specify an error message.''' def __init__(self, msg): self.msg = msg - def __nonzero__(self): return False + def __bool__(self): return False def __repr__(self): return '' % self.msg # ------------------------------------------------------------------------------ diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py index 87d7646..e3e00c6 100644 --- a/gen/wrappers/ToolWrapper.py +++ b/gen/wrappers/ToolWrapper.py @@ -424,7 +424,7 @@ class ToolWrapper(AbstractWrapper): '%s' % \ self.translate('last_user_access') rows = [] - for userId, lastAccess in self.o.loggedUsers.items(): + for userId, lastAccess in list(self.o.loggedUsers.items()): user = self.search1('User', noSecurity=True, login=userId) if not user: continue # Could have been deleted in the meanwhile fmt = '%s (%s)' % (self.dateFormat, self.hourFormat) @@ -515,7 +515,7 @@ class ToolWrapper(AbstractWrapper): failed += subFailed try: startObject.reindex() - except Exception, e: + except Exception as e: failed.append(startObject) return nb, failed diff --git a/gen/wrappers/UserWrapper.py b/gen/wrappers/UserWrapper.py index 3844d1d..df6619f 100644 --- a/gen/wrappers/UserWrapper.py +++ b/gen/wrappers/UserWrapper.py @@ -343,7 +343,7 @@ class UserWrapper(AbstractWrapper): if not localRoles: return res # Gets the logins of this user and all its groups. logins = self.getLogins() - for login, roles in localRoles.iteritems(): + for login, roles in localRoles.items(): # Ignore logins not corresponding to this user. if login not in logins: continue for role in roles: @@ -388,7 +388,7 @@ class UserWrapper(AbstractWrapper): if not localRoles: return # Gets the logins of this user and all its groups. userLogins = self.getLogins() - for login, roles in localRoles.iteritems(): + for login, roles in localRoles.items(): # Ignore logins not corresponding to this user. if login not in userLogins: continue for role in roles: diff --git a/gen/wrappers/__init__.py b/gen/wrappers/__init__.py index 79dec60..a2858fc 100644 --- a/gen/wrappers/__init__.py +++ b/gen/wrappers/__init__.py @@ -861,7 +861,7 @@ class AbstractWrapper(object): if len(self.__class__.__bases__) > 1: # There is a custom user class custom = self.__class__.__bases__[-1] - if custom.__dict__.has_key(methodName): + if methodName in custom.__dict__: return custom.__dict__[methodName] def _callCustom(self, methodName, *args, **kwargs): @@ -973,7 +973,7 @@ class AbstractWrapper(object): present, will not be called; any other defined method will not be called neither (ie, Ref.insert, Ref.beforeLink, Ref.afterLink...). ''' - isField = isinstance(fieldNameOrClass, basestring) + isField = isinstance(fieldNameOrClass, str) tool = self.tool.o # Determine the class of the object to create if isField: @@ -984,7 +984,7 @@ class AbstractWrapper(object): klass = fieldNameOrClass portalType = tool.getPortalType(klass) # Determine object id - if kwargs.has_key('id'): + if 'id' in kwargs: objId = kwargs['id'] del kwargs['id'] else: @@ -1002,7 +1002,7 @@ class AbstractWrapper(object): noSecurity=noSecurity) appyObj = zopeObj.appy() # Set object attributes - for attrName, attrValue in kwargs.iteritems(): + for attrName, attrValue in kwargs.items(): try: setattr(appyObj, attrName, attrValue) except AttributeError, ae: @@ -1183,8 +1183,8 @@ class AbstractWrapper(object): # Get the Appy object from the brain if noSecurity: method = '_unrestrictedGetObject' else: method = 'getObject' - exec 'obj = brain.%s().appy()' % method - exec expression + exec('obj = brain.%s().appy()' % method) + exec(expression) return ctx def reindex(self, fields=None, unindex=False): @@ -1249,7 +1249,7 @@ class AbstractWrapper(object): else: return xml elif format == 'csv': - if isinstance(at, basestring): + if isinstance(at, str): marshaller = CsvMarshaller(include=include, exclude=exclude) return marshaller.marshall(self) else: diff --git a/pod/actions.py b/pod/actions.py index 1e78d3b..095de5f 100644 --- a/pod/actions.py +++ b/pod/actions.py @@ -101,7 +101,7 @@ class BufferAction: try: res = self._evalExpr(expr, context) error = False - except Exception, e: + except Exception as e: res = None errorMessage = EVAL_ERROR % (expr, self.getExceptionLine(e)) self.manageError(result, context, errorMessage) @@ -134,7 +134,7 @@ class BufferAction: error = False try: feRes = eval(self.fromExpr, context) - except Exception, e: + except Exception as e: msg = FROM_EVAL_ERROR% (self.fromExpr, self.getExceptionLine(e)) self.manageError(result, context, msg) error = True @@ -240,7 +240,7 @@ class ForAction(BufferAction): return # Remember variable hidden by iter if any hasHiddenVariable = False - if context.has_key(self.iter): + if self.iter in context: hiddenVariable = context[self.iter] hasHiddenVariable = True # In the case of cells, initialize some values diff --git a/pod/buffers.py b/pod/buffers.py index 01397f1..3bff041 100644 --- a/pod/buffers.py +++ b/pod/buffers.py @@ -80,15 +80,15 @@ NULL_ACTION_ERROR = 'There was a problem with this action. Possible causes: ' \ class BufferIterator: def __init__(self, buffer): self.buffer = buffer - self.remainingSubBufferIndexes = self.buffer.subBuffers.keys() - self.remainingElemIndexes = self.buffer.elements.keys() + self.remainingSubBufferIndexes = list(self.buffer.subBuffers.keys()) + self.remainingElemIndexes = list(self.buffer.elements.keys()) self.remainingSubBufferIndexes.sort() self.remainingElemIndexes.sort() def hasNext(self): return self.remainingSubBufferIndexes or self.remainingElemIndexes - def next(self): + def __next__(self): nextSubBufferIndex = None if self.remainingSubBufferIndexes: nextSubBufferIndex = self.remainingSubBufferIndexes[0] @@ -131,7 +131,7 @@ class Buffer: return subBuffer def removeLastSubBuffer(self): - subBufferIndexes = self.subBuffers.keys() + subBufferIndexes = list(self.subBuffers.keys()) subBufferIndexes.sort() lastIndex = subBufferIndexes.pop() del self.subBuffers[lastIndex] @@ -176,7 +176,7 @@ class Buffer: self.write('<%s' % elem) # Some table elements must be patched (pod only) if self.pod: self.patchTableElement(elem, attrs) - for name, value in attrs.items(): + for name, value in list(attrs.items()): if ignoreAttrs and (name in ignoreAttrs): continue if renamedAttrs and (name in renamedAttrs): name=renamedAttrs[name] # If the value begins with ':', it is a Python expression. Else, @@ -244,7 +244,7 @@ class FileBuffer(Buffer): res, escape = expr.evaluate(self.env.context) if escape: self.dumpContent(res) else: self.write(res) - except Exception, e: + except Exception as e: if not self.env.raiseOnError: PodError.dump(self, EVAL_EXPR_ERROR % (expression, e), dumpTb=False) @@ -271,7 +271,7 @@ class MemoryBuffer(Buffer): def __init__(self, env, parent): Buffer.__init__(self, env, parent) - self.content = u'' + self.content = '' self.elements = {} self.action = None @@ -297,7 +297,7 @@ class MemoryBuffer(Buffer): def getIndex(self, podElemName): res = -1 - for index, podElem in self.elements.iteritems(): + for index, podElem in self.elements.items(): if podElem.__class__.__name__.lower() == podElemName: if index > res: res = index @@ -305,7 +305,7 @@ class MemoryBuffer(Buffer): def getMainElement(self): res = None - if self.elements.has_key(0): + if 0 in self.elements: res = self.elements[0] return res @@ -317,7 +317,7 @@ class MemoryBuffer(Buffer): if elem != mainElem: return # elem is the same as the main elem. But is it really the main elem, or # the same elem, found deeper in the buffer? - for index, iElem in self.elements.iteritems(): + for index, iElem in self.elements.items(): foundElem = None if hasattr(iElem, 'OD'): if iElem.OD: @@ -331,7 +331,7 @@ class MemoryBuffer(Buffer): def unreferenceElement(self, elem): # Find last occurrence of this element elemIndex = -1 - for index, iElem in self.elements.iteritems(): + for index, iElem in self.elements.items(): foundElem = None if hasattr(iElem, 'OD'): # A POD element @@ -347,7 +347,7 @@ class MemoryBuffer(Buffer): def pushSubBuffer(self, subBuffer): '''Sets p_subBuffer at the very end of the buffer.''' subIndex = None - for index, aSubBuffer in self.subBuffers.iteritems(): + for index, aSubBuffer in self.subBuffers.items(): if aSubBuffer == subBuffer: subIndex = index break @@ -356,7 +356,7 @@ class MemoryBuffer(Buffer): # in the parent (if it is a temp buffer generated from a cut) del self.subBuffers[subIndex] self.subBuffers[self.getLength()] = subBuffer - self.content += u' ' + self.content += ' ' def transferAllContent(self): '''Transfer all content to parent.''' @@ -370,10 +370,10 @@ class MemoryBuffer(Buffer): oldParentLength = self.parent.getLength() self.parent.write(self.content) # Transfer elements - for index, podElem in self.elements.iteritems(): + for index, podElem in self.elements.items(): self.parent.elements[oldParentLength + index] = podElem # Transfer sub-buffers - for index, buf in self.subBuffers.iteritems(): + for index, buf in self.subBuffers.items(): self.parent.subBuffers[oldParentLength + index] = buf # Empty the buffer MemoryBuffer.__init__(self, self.env, self.parent) @@ -391,7 +391,7 @@ class MemoryBuffer(Buffer): elem.colIndex = elem.tableInfo.curColIndex if elem == 'x': # See comment on similar statement in the method below. - self.content += u' ' + self.content += ' ' def addExpression(self, expression, tiedHook=None): # Create the POD expression @@ -400,20 +400,20 @@ class MemoryBuffer(Buffer): self.elements[self.getLength()] = expr # To be sure that an expr and an elem can't be found at the same index # in the buffer. - self.content += u' ' + self.content += ' ' def addAttributes(self): '''pod-only: adds an Attributes instance into this buffer.''' attrs = Attributes(self.env) self.elements[self.getLength()] = attrs - self.content += u' ' + self.content += ' ' return attrs def addAttribute(self, name, expr): '''px-only: adds an Attribute instance into this buffer.''' attr = Attribute(name, expr) self.elements[self.getLength()] = attr - self.content += u' ' + self.content += ' ' return attr def _getVariables(self, expr): @@ -453,7 +453,7 @@ class MemoryBuffer(Buffer): raise ParsingError( ELEMENT_NOT_FOUND % (podElem, str([ e.__class__.__name__.lower() \ - for e in self.elements.values()]))) + for e in list(self.elements.values())]))) podElem = self.elements[indexPodElem] # Check the 'from' clause fromClause = None @@ -471,7 +471,7 @@ class MemoryBuffer(Buffer): self.env.ifActions.append(self.action) if self.action.name: # We must register this action as a named action - if self.env.namedIfActions.has_key(self.action.name): + if self.action.name in self.env.namedIfActions: raise ParsingError(DUPLICATE_NAMED_IF) self.env.namedIfActions[self.action.name] = self.action elif actionType == 'else': @@ -480,7 +480,7 @@ class MemoryBuffer(Buffer): # Does the "else" action reference a named "if" action? ifReference = subExpr.strip() if ifReference: - if not self.env.namedIfActions.has_key(ifReference): + if ifReference not in self.env.namedIfActions: raise ParsingError(ELSE_WITHOUT_NAMED_IF % ifReference) linkedIfAction = self.env.namedIfActions[ifReference] # This "else" action "consumes" the "if" action: this way, @@ -510,7 +510,7 @@ class MemoryBuffer(Buffer): self.action = NullAction(statementName, self, None, podElem, None, source, fromClause) res = indexPodElem - except ParsingError, ppe: + except ParsingError as ppe: PodError.dump(self, ppe, removeFirstLine=True) return res @@ -552,7 +552,7 @@ class MemoryBuffer(Buffer): elementsToDelete = [] mustShift = False while iter.hasNext(): - itemIndex, item = iter.next() + itemIndex, item = next(iter) if keepFirstPart: if itemIndex >= index: newIndex = itemIndex-index @@ -580,11 +580,11 @@ class MemoryBuffer(Buffer): del self.subBuffers[subIndex] if mustShift: elements = {} - for elemIndex, elem in self.elements.iteritems(): + for elemIndex, elem in self.elements.items(): elements[elemIndex-index] = elem self.elements = elements subBuffers = {} - for subIndex, buf in self.subBuffers.iteritems(): + for subIndex, buf in self.subBuffers.items(): subBuffers[subIndex-index] = buf self.subBuffers = subBuffers # Manage content @@ -598,7 +598,7 @@ class MemoryBuffer(Buffer): def getElementIndexes(self, expressions=True): res = [] - for index, elem in self.elements.iteritems(): + for index, elem in self.elements.items(): condition = isinstance(elem, Expression) or \ isinstance(elem, Attributes) if not expressions: @@ -696,7 +696,7 @@ class MemoryBuffer(Buffer): iter = BufferIterator(self) currentIndex = self.getStartIndex(removeMainElems) while iter.hasNext(): - index, evalEntry = iter.next() + index, evalEntry = next(iter) result.write(self.content[currentIndex:index]) currentIndex = index + 1 if isinstance(evalEntry, Expression): @@ -708,7 +708,7 @@ class MemoryBuffer(Buffer): # This exception has already been treated (see the # "except" block below). Simply re-raise it when needed. if self.env.raiseOnError: raise e - except Exception, e: + except Exception as e: if not self.env.raiseOnError: PodError.dump(result, EVAL_EXPR_ERROR % ( evalEntry.expr, e)) @@ -729,5 +729,5 @@ class MemoryBuffer(Buffer): def clean(self): '''Cleans the buffer content.''' - self.content = u'' + self.content = '' # ------------------------------------------------------------------------------ diff --git a/pod/converter.py b/pod/converter.py index c00592e..f3cf8c9 100644 --- a/pod/converter.py +++ b/pod/converter.py @@ -109,7 +109,7 @@ class Converter: res = res[self.inputType] else: raise ConverterError(BAD_RESULT_TYPE % (self.resultType, - FILE_TYPES.keys())) + list(FILE_TYPES.keys()))) return res def getResultUrl(self): @@ -275,7 +275,7 @@ class ConverterScript: ' %s.\n' \ ' "python" should be a UNO-enabled Python interpreter (ie the ' \ ' one which is included in the LibreOffice distribution).' % \ - str(FILE_TYPES.keys()) + str(list(FILE_TYPES.keys())) def run(self): optParser = OptionParser(usage=ConverterScript.usage) optParser.add_option("-p", "--port", dest="port", diff --git a/pod/doc_importers.py b/pod/doc_importers.py index 41be73f..4f1f366 100644 --- a/pod/doc_importers.py +++ b/pod/doc_importers.py @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA. # ------------------------------------------------------------------------------ -import os, os.path, time, shutil, struct, random, urlparse +import os, os.path, time, shutil, struct, random, urllib.parse from appy.pod import PodError from appy.pod.odf_parser import OdfEnvironment from appy.shared import mimeTypesExts @@ -53,7 +53,7 @@ class DocImporter: if at and not at.startswith('http') and not os.path.isfile(at): raise PodError(FILE_NOT_FOUND % at) self.format = format - self.res = u'' + self.res = '' self.renderer = renderer self.ns = renderer.currentParser.env.namespaces # Unpack some useful namespaces @@ -285,7 +285,7 @@ class ImageImporter(DocImporter): def moveFile(self, at, importPath): '''Copies file at p_at into the ODT file at p_importPath.''' # Has this image already been imported ? - for imagePath, imageAt in self.fileNames.iteritems(): + for imagePath, imageAt in self.fileNames.items(): if imageAt == at: # Yes! i = importPath.rfind(self.pictFolder) + 1 @@ -327,7 +327,7 @@ class ImageImporter(DocImporter): # The imageResolver is a Zope application. From it, we will # retrieve the object on which the image is stored and get # the file to download. - urlParts = urlparse.urlsplit(at) + urlParts = urllib.parse.urlsplit(at) path = urlParts[2][1:].split('/')[:-1] try: obj = imageResolver.unrestrictedTraverse(path) diff --git a/pod/elements.py b/pod/elements.py index f7679ab..c4daf7b 100644 --- a/pod/elements.py +++ b/pod/elements.py @@ -148,7 +148,7 @@ class Expression(PodElement): # pod/px result. resultType = res.__class__.__name__ if resultType == 'NoneType': - res = u'' + res = '' elif resultType == 'str': res = res.decode('utf-8') elif resultType == 'unicode': @@ -160,7 +160,7 @@ class Expression(PodElement): # Force escapeXml to False. escapeXml = False else: - res = unicode(res) + res = str(res) return res, escapeXml class Attributes(PodElement): @@ -199,7 +199,7 @@ class Attributes(PodElement): try: self.tiedExpression.evaluate(context) self.tiedExpression.evaluated = True - except Exception, e: + except Exception as e: # Don't set "evaluated" to True. This way, when the buffer will # evaluate the expression directly, we will really evaluate it, so # the error will be dumped into the pod result. @@ -208,7 +208,7 @@ class Attributes(PodElement): self.computeAttributes(self.tiedExpression) # Now, self.attrs has been populated. Transform it into a string. res = '' - for name, value in self.attrs.iteritems(): + for name, value in self.attrs.items(): res += ' %s=%s' % (name, quoteattr(value)) return res diff --git a/pod/pod_parser.py b/pod/pod_parser.py index dafd5b6..720474f 100644 --- a/pod/pod_parser.py +++ b/pod/pod_parser.py @@ -50,7 +50,7 @@ class OdInsert: def resolve(self, namespaces): '''Replaces all unresolved namespaces in p_odtChunk, thanks to the dict of p_namespaces.''' - for nsName, nsUri in self.nsUris.iteritems(): + for nsName, nsUri in self.nsUris.items(): self.odtChunk = re.sub('@%s@' % nsName, namespaces[nsUri], self.odtChunk) return self.odtChunk @@ -126,14 +126,14 @@ class PodEnvironment(OdfEnvironment): res = {} for insert in self.inserts: elemName = insert.elem.getFullName(self.namespaces) - if not res.has_key(elemName): + if elemName not in res: res[elemName] = insert return res def manageInserts(self): '''We just dumped the start of an elem. Here we will insert any odt chunk if needed.''' - if self.inserts.has_key(self.currentElem.elem): + if self.currentElem.elem in self.inserts: insert = self.inserts[self.currentElem.elem] self.currentBuffer.write(insert.resolve(self.namespaces)) # The insert is destroyed after single use @@ -160,12 +160,12 @@ class PodEnvironment(OdfEnvironment): elif elem == Cell.OD.elem: colspan = 1 attrSpan = self.tags['number-columns-spanned'] - if self.currentElem.attrs.has_key(attrSpan): + if attrSpan in self.currentElem.attrs: colspan = int(self.currentElem.attrs[attrSpan]) self.getTable().curColIndex += colspan elif elem == self.tags['table-column']: attrs = self.currentElem.attrs - if attrs.has_key(self.tags['number-columns-repeated']): + if self.tags['number-columns-repeated'] in attrs: self.getTable().nbOfColumns += int( attrs[self.tags['number-columns-repeated']]) else: @@ -254,8 +254,8 @@ class PodParser(OdfParser): e.state = e.READING_EXPRESSION e.exprHasStyle = False elif (elem == e.tags['table-cell']) and \ - attrs.has_key(e.tags['formula']) and \ - attrs.has_key(e.tags['value-type']) and \ + e.tags['formula'] in attrs and \ + e.tags['value-type'] in attrs and \ (attrs[e.tags['value-type']] == 'string') and \ attrs[e.tags['formula']].startswith('of:="'): # In an ODS template, any cell containing a formula of type "string" diff --git a/pod/renderer.py b/pod/renderer.py index 32a1df9..354b77d 100644 --- a/pod/renderer.py +++ b/pod/renderer.py @@ -18,7 +18,11 @@ # ------------------------------------------------------------------------------ import zipfile, shutil, xml.sax, os, os.path, re, mimetypes, time -from UserDict import UserDict +#python3 compat +try: + from UserDict import UserDict +except ImportError: + from collections import UserDict import appy.pod from appy.pod import PodError from appy.shared import mimeTypes, mimeTypesExts @@ -80,7 +84,7 @@ CONTENT_POD_FONTS = '<@style@:font-face @style@:name="PodStarSymbol" ' \ '@svg@:font-family="StarSymbol"/>' # Default text styles added by pod in styles.xml -f = file('%s/styles.in.styles.xml' % os.path.dirname(appy.pod.__file__)) +f = open('%s/styles.in.styles.xml' % os.path.dirname(appy.pod.__file__)) STYLES_POD_STYLES = f.read() f.close() @@ -263,7 +267,7 @@ class Renderer: imageFormats = ('png', 'jpeg', 'jpg', 'gif', 'svg') ooFormats = ('odt',) - convertibleFormats = FILE_TYPES.keys() + convertibleFormats = list(FILE_TYPES.keys()) def importDocument(self, content=None, at=None, format=None, anchor='as-char', wrapInPara=True, size=None, sizeUnit='cm', style=None, @@ -309,7 +313,7 @@ class Renderer: format = os.path.splitext(at)[1][1:] else: # If format is a mimeType, convert it to an extension - if mimeTypesExts.has_key(format): + if format in mimeTypesExts: format = mimeTypesExts[format] isImage = False isOdt = False @@ -370,9 +374,9 @@ class Renderer: f = open(self.result, 'w') f.write('Hello') f.close() - except OSError, oe: + except OSError as oe: raise PodError(CANT_WRITE_RESULT % (self.result, oe)) - except IOError, ie: + except IOError as ie: raise PodError(CANT_WRITE_RESULT % (self.result, ie)) self.result = os.path.abspath(self.result) os.remove(self.result) @@ -381,7 +385,7 @@ class Renderer: self.tempFolder = '%s.%f' % (absResult, time.time()) try: os.mkdir(self.tempFolder) - except OSError, oe: + except OSError as oe: raise PodError(CANT_WRITE_TEMP_FOLDER % (self.result, oe)) def patchManifest(self): @@ -390,7 +394,7 @@ class Renderer: if self.fileNames: j = os.path.join toInsert = '' - for fileName in self.fileNames.iterkeys(): + for fileName in self.fileNames.keys(): if fileName.endswith('.svg'): fileName = os.path.splitext(fileName)[0] + '.png' mimeType = mimetypes.guess_type(fileName)[0] @@ -442,7 +446,7 @@ class Renderer: if 'span[font-style=italic]' not in stylesMapping: stylesMapping['span[font-style=italic]'] = 'podItalic' self.stylesManager.stylesMapping = stylesMapping - except PodError, po: + except PodError as po: self.contentParser.env.currentBuffer.content.close() self.stylesParser.env.currentBuffer.content.close() if os.path.exists(self.tempFolder): @@ -454,14 +458,14 @@ class Renderer: loOutput = '' try: if (not isinstance(self.ooPort, int)) and \ - (not isinstance(self.ooPort, long)): + (not isinstance(self.ooPort, int)): raise PodError(BAD_OO_PORT % str(self.ooPort)) try: from appy.pod.converter import Converter, ConverterError try: Converter(resultName, resultType, self.ooPort, self.stylesTemplate).run() - except ConverterError, ce: + except ConverterError as ce: raise PodError(CONVERT_ERROR % str(ce)) except ImportError: # I do not have UNO. So try to launch a UNO-enabled Python @@ -485,13 +489,13 @@ class Renderer: self.ooPort) if self.stylesTemplate: cmd += ' -t%s' % self.stylesTemplate loOutput = executeCommand(cmd) - except PodError, pe: + except PodError as pe: # When trying to call LO in server mode for producing ODT or ODS # (=forceOoCall=True), if an error occurs we have nevertheless # an ODT or ODS to return to the user. So we produce a warning # instead of raising an error. if (resultType in self.templateTypes) and self.forceOoCall: - print(WARNING_INCOMPLETE_OD % str(pe)) + print((WARNING_INCOMPLETE_OD % str(pe))) else: raise pe return loOutput @@ -501,7 +505,7 @@ class Renderer: (ods or odt). If self.template is a string, it is a file name and we simply get its extension. Else, it is a binary file in a StringIO instance, and we seek the mime type from the first bytes.''' - if isinstance(self.template, basestring): + if isinstance(self.template, str): res = os.path.splitext(self.template)[1][1:] else: # A StringIO instance @@ -534,8 +538,8 @@ class Renderer: if self.finalizeFunction: try: self.finalizeFunction(self.unzipFolder) - except Exception, e: - print(WARNING_FINALIZE_ERROR % str(e)) + except Exception as e: + print((WARNING_FINALIZE_ERROR % str(e))) # Re-zip the result, first as an OpenDocument file of the same type as # the POD template (odt, ods...) resultExt = self.getTemplateType() diff --git a/pod/styles_manager.py b/pod/styles_manager.py index 50f67f3..bfe026e 100644 --- a/pod/styles_manager.py +++ b/pod/styles_manager.py @@ -18,7 +18,12 @@ # ------------------------------------------------------------------------------ import re, os.path -from UserDict import UserDict +#python3 compat +try: + from UserDict import UserDict +except ImportError: + from collections import UserDict + import appy.pod from appy.pod import * from appy.pod.odf_parser import OdfEnvironment, OdfParser @@ -93,7 +98,7 @@ class Styles(UserDict): '''Tries to find a style which has level p_level. Returns None if no such style exists.''' res = None - for style in self.itervalues(): + for style in self.values(): if (style.family == 'paragraph') and (style.outlineLevel == level): res = style break @@ -102,7 +107,7 @@ class Styles(UserDict): '''Gets the style that has this p_displayName. Returns None if not found.''' res = None - for style in self.itervalues(): + for style in self.values(): if style.displayName == displayName: res = style break @@ -111,9 +116,9 @@ class Styles(UserDict): '''Returns a list of all the styles of the given p_stylesType.''' res = [] if stylesType == 'all': - res = self.values() + res = list(self.values()) else: - for style in self.itervalues(): + for style in self.values(): if (style.family == stylesType) and style.displayName: res.append(style) return res @@ -145,22 +150,22 @@ class StylesParser(OdfParser): displayNameAttr = '%s:display-name' % e.ns(e.NS_STYLE) # Create the style style = Style(name=attrs[nameAttr], family=attrs[familyAttr]) - if attrs.has_key(classAttr): + if classAttr in attrs: style.styleClass = attrs[classAttr] - if attrs.has_key(displayNameAttr): + if displayNameAttr in attrs: style.displayName = attrs[displayNameAttr] # Record this style in the environment e.styles[style.name] = style e.currentStyle = style levelKey = '%s:default-outline-level' % e.ns(e.NS_STYLE) - if attrs.has_key(levelKey) and attrs[levelKey].strip(): + if levelKey in attrs and attrs[levelKey].strip(): style.outlineLevel = int(attrs[levelKey]) else: if e.state == PARSING_STYLE: # I am parsing tags within the style. if elem == ('%s:text-properties' % e.ns(e.NS_STYLE)): fontSizeKey = '%s:font-size' % e.ns(e.NS_FO) - if attrs.has_key(fontSizeKey): + if fontSizeKey in attrs: e.currentStyle.setFontSize(attrs[fontSizeKey]) def endElement(self, elem): e = OdfParser.endElement(self, elem) @@ -250,14 +255,14 @@ class StylesManager: if not isinstance(stylesMapping, dict) and \ not isinstance(stylesMapping, UserDict): raise PodError(MAPPING_NOT_DICT) - for xhtmlStyleName, odtStyleName in stylesMapping.iteritems(): - if not isinstance(xhtmlStyleName, basestring): + for xhtmlStyleName, odtStyleName in stylesMapping.items(): + if not isinstance(xhtmlStyleName, str): raise PodError(MAPPING_ELEM_NOT_STRING) if (xhtmlStyleName == 'h*') and \ not isinstance(odtStyleName, int): raise PodError(MAPPING_OUTLINE_DELTA_NOT_INT) if (xhtmlStyleName != 'h*') and \ - not isinstance(odtStyleName, basestring): + not isinstance(odtStyleName, str): raise PodError(MAPPING_ELEM_NOT_STRING) if (xhtmlStyleName != 'h*') and \ ((not xhtmlStyleName) or (not odtStyleName)): @@ -278,7 +283,7 @@ class StylesManager: if xhtmlStyleName != 'h*': odtStyle = self.styles.getStyle(odtStyleName) if not odtStyle: - if self.podSpecificStyles.has_key(odtStyleName): + if odtStyleName in self.podSpecificStyles: odtStyle = self.podSpecificStyles[odtStyleName] else: raise PodError(STYLE_NOT_FOUND % odtStyleName) @@ -311,7 +316,7 @@ class StylesManager: This method returns True if p_attrs contains the winning (name,value) pairs that match those in p_matchingAttrs. Note that ALL attrs in p_matchingAttrs must be present in p_attrs.''' - for name, value in matchingAttrs.iteritems(): + for name, value in matchingAttrs.items(): if name not in attrs: return if value != attrs[name]: return return True @@ -356,29 +361,29 @@ class StylesManager: ''' res = None cssStyleName = None - if attrs and attrs.has_key('class'): + if attrs and 'class' in attrs: cssStyleName = attrs['class'] if classValue: cssStyleName = classValue # (1) - if localStylesMapping.has_key(cssStyleName): + if cssStyleName in localStylesMapping: res = localStylesMapping[cssStyleName] # (2) - if (not res) and localStylesMapping.has_key(elem): + if (not res) and elem in localStylesMapping: styles = localStylesMapping[elem] res = self.getStyleFromMapping(elem, attrs, styles) # (3) - if (not res) and self.stylesMapping.has_key(cssStyleName): + if (not res) and cssStyleName in self.stylesMapping: res = self.stylesMapping[cssStyleName] # (4) - if (not res) and self.stylesMapping.has_key(elem): + if (not res) and elem in self.stylesMapping: styles = self.stylesMapping[elem] res = self.getStyleFromMapping(elem, attrs, styles) # (5) - if (not res) and self.styles.has_key(cssStyleName): + if (not res) and cssStyleName in self.styles: res = self.styles[cssStyleName] # (6) - if (not res) and self.podSpecificStyles.has_key(cssStyleName): + if (not res) and cssStyleName in self.podSpecificStyles: res = self.podSpecificStyles[cssStyleName] # (7) if not res: @@ -386,9 +391,9 @@ class StylesManager: if elem in XHTML_HEADINGS: # Is there a delta that must be taken into account ? outlineDelta = 0 - if localStylesMapping.has_key('h*'): + if 'h*' in localStylesMapping: outlineDelta += localStylesMapping['h*'] - elif self.stylesMapping.has_key('h*'): + elif 'h*' in self.stylesMapping: outlineDelta += self.stylesMapping['h*'] outlineLevel = int(elem[1]) + outlineDelta # Normalize the outline level diff --git a/pod/test/Tester.py b/pod/test/Tester.py index b2ca636..9e0748d 100644 --- a/pod/test/Tester.py +++ b/pod/test/Tester.py @@ -38,7 +38,7 @@ class AnnotationsRemover(OdfParser): machine-specific info, like absolute paths to the python files, etc.''' def __init__(self, env, caller): OdfParser.__init__(self, env, caller) - self.res = u'' + self.res = '' self.inAnnotation = False # Are we parsing an annotation ? self.textEncountered = False # Within an annotation, have we already # met a text ? @@ -58,7 +58,7 @@ class AnnotationsRemover(OdfParser): self.ignore = True if not self.ignore: self.res += '<%s' % elem - for attrName, attrValue in attrs.items(): + for attrName, attrValue in list(attrs.items()): self.res += ' %s="%s"' % (attrName, attrValue) self.res += '>' def endElement(self, elem): @@ -93,12 +93,12 @@ class Test(appy.shared.test.Test): if not os.path.exists(contextPy): raise TesterError(CONTEXT_NOT_FOUND % contextPy) contextPkg = 'appy.pod.test.contexts.%s' % contextName - exec 'import %s' % contextPkg - exec 'context = dir(%s)' % contextPkg + exec('import %s' % contextPkg) + exec('context = dir(%s)' % contextPkg) res = {} for elem in context: if not elem.startswith('__'): - exec 'res[elem] = %s.%s' % (contextPkg, elem) + exec('res[elem] = %s.%s' % (contextPkg, elem)) return res def do(self): diff --git a/pod/test/contexts/ForCell6.py b/pod/test/contexts/ForCell6.py index 7a11142..f08e973 100644 --- a/pod/test/contexts/ForCell6.py +++ b/pod/test/contexts/ForCell6.py @@ -1,6 +1,6 @@ class Student: def __init__(self, **kwargs): - for k, v in kwargs.iteritems(): + for k, v in kwargs.items(): setattr(self, k, v) students = [ diff --git a/pod/xhtml2odt.py b/pod/xhtml2odt.py index b33f09e..9519998 100644 --- a/pod/xhtml2odt.py +++ b/pod/xhtml2odt.py @@ -47,7 +47,7 @@ class HtmlElement: # but for a strange reason those attrs are back to None (probably for # performance reasons they become inaccessible after a while). self.classAttr = None - if attrs.has_key('class'): + if 'class' in attrs: self.classAttr = attrs['class'] self.tagsToReopen = [] # When the HTML element corresponding to self # is completely dumped, if there was a problem related to tags @@ -58,7 +58,7 @@ class HtmlElement: # to self, we may need to close other tags (ie closing a paragraph # before closing a cell). This list contains HtmlElement instances. self.elemType = self.elem - if self.elemTypes.has_key(self.elem): + if self.elem in self.elemTypes: self.elemType = self.elemTypes[self.elem] # If a conflict occurs on this element, we will note it. self.isConflictual = False @@ -71,7 +71,7 @@ class HtmlElement: def getOdfTag(self, env): '''Gets the raw ODF tag that corresponds to me''' res = '' - if HTML_2_ODT.has_key(self.elem): + if self.elem in HTML_2_ODT: res += '%s:%s' % (env.textNs, HTML_2_ODT[self.elem]) elif self.elem == 'a': res += '%s:a' % env.textNs @@ -216,8 +216,8 @@ class HtmlTable: elems = str(time.time()).split('.') self.name= 'AppyTable%s%s%d' % (elems[0],elems[1],random.randint(1,100)) self.styleNs = env.ns[OdfEnvironment.NS_STYLE] - self.res = u'' # The sub-buffer - self.tempRes = u'' # The temporary sub-buffer, into which we will + self.res = '' # The sub-buffer + self.tempRes = '' # The temporary sub-buffer, into which we will # dump all table sub-elements, until we encounter the end of the first # row. Then, we will know how much columns are defined in the table; # we will dump columns declarations into self.res and dump self.tempRes @@ -294,8 +294,8 @@ class XhtmlEnvironment(XmlEnvironment): XmlEnvironment.__init__(self) self.renderer = renderer self.ns = renderer.currentParser.env.namespaces - self.res = u'' - self.currentContent = u'' + self.res = '' + self.currentContent = '' self.currentElements = [] # Stack of currently walked elements self.currentLists = [] # Stack of currently walked lists (ul or ol) self.currentTables = [] # Stack of currently walked tables @@ -349,7 +349,7 @@ class XhtmlEnvironment(XmlEnvironment): # Dump and reinitialize the current content contentSize = len(self.currentContent) self.dumpString(escapeXml(self.currentContent)) - self.currentContent = u'' + self.currentContent = '' # If we are within a table cell, update the total size of cell content if not contentSize: return if self.currentTables and self.currentTables[-1].inCell: @@ -363,7 +363,7 @@ class XhtmlEnvironment(XmlEnvironment): styleName = None if odtStyle: styleName = odtStyle.name - elif DEFAULT_ODT_STYLES.has_key(htmlElem.elem): + elif htmlElem.elem in DEFAULT_ODT_STYLES: styleName = DEFAULT_ODT_STYLES[htmlElem.elem] res = '' if styleName: @@ -458,7 +458,7 @@ class XhtmlEnvironment(XmlEnvironment): elif elem in TABLE_CELL_TAGS: # Determine colspan colspan = 1 - if attrs.has_key('colspan'): colspan = int(attrs['colspan']) + if 'colspan' in attrs: colspan = int(attrs['colspan']) table = self.currentTables[-1] table.inCell = colspan table.cellIndex += colspan @@ -503,7 +503,7 @@ class XhtmlEnvironment(XmlEnvironment): table.res+= '<%s:table-column %s:style-name="%s.%d"/>' % \ (self.tableNs, self.tableNs, table.name, i) table.res += table.tempRes - table.tempRes = u'' + table.tempRes = '' elif elem in TABLE_CELL_TAGS: # Update attr "columnContentSizes" of the currently parsed table, # excepted if the cell spans several columns. @@ -535,7 +535,7 @@ class XhtmlParser(XmlParser): resAttrs = attrs if attrs: resAttrs = {} - for attrName in attrs.keys(): + for attrName in list(attrs.keys()): resAttrs[attrName.lower()] = attrs[attrName] if attrs == None: return resElem @@ -548,11 +548,11 @@ class XhtmlParser(XmlParser): currentElem = e.onElementStart(elem, attrs) odfTag = currentElem.getOdfTag(e) - if HTML_2_ODT.has_key(elem): + if elem in HTML_2_ODT: e.dumpStyledElement(currentElem, odfTag, attrs) elif elem == 'a': e.dumpString('<%s %s:type="simple"' % (odfTag, e.linkNs)) - if attrs.has_key('href'): + if 'href' in attrs: e.dumpString(' %s:href="%s"' % (e.linkNs, escapeXml(attrs['href']))) e.dumpString('>') @@ -577,13 +577,13 @@ class XhtmlParser(XmlParser): elif elem in TABLE_CELL_TAGS: e.dumpString('<%s %s:style-name="%s"' % \ (odfTag, e.tableNs, DEFAULT_ODT_STYLES[elem])) - if attrs.has_key('colspan'): + if 'colspan' in attrs: e.dumpString(' %s:number-columns-spanned="%s"' % \ (e.tableNs, attrs['colspan'])) e.dumpString('>') elif elem == 'img': style = None - if attrs.has_key('style'): style = attrs['style'] + if 'style' in attrs: style = attrs['style'] imgCode = e.renderer.importDocument(at=attrs['src'], wrapInPara=False, style=style) e.dumpString(imgCode) diff --git a/px/__init__.py b/px/__init__.py index 96bab94..c449334 100644 --- a/px/__init__.py +++ b/px/__init__.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ import xml.sax -from px_parser import PxParser, PxEnvironment +from .px_parser import PxParser, PxEnvironment from appy.pod.buffers import MemoryBuffer from appy.shared.xml_parser import xmlPrologue, xhtmlPrologue @@ -18,7 +18,7 @@ class Px: xhtmlPrologue = xhtmlPrologue def __init__(self, content, isFileName=False, partial=True, - template=None, hook=None, prologue=None, unicode=True): + template=None, hook=None, prologue=None, str=True): '''p_content is the PX code, as a string, or a file name if p_isFileName is True. If this code represents a complete XML file, p_partial is False. Else, we must surround p_content with a root tag to be able @@ -49,7 +49,7 @@ class Px: # Is there some (XML, XHTML...) prologue to dump? self.prologue = prologue # Will the result be unicode or str? - self.unicode = unicode + self.str = str self.parse() def parse(self): @@ -64,7 +64,7 @@ class Px: # produce a tree of memory buffers. try: self.parser.parse(self.content) - except xml.sax.SAXParseException, spe: + except xml.sax.SAXParseException as spe: self.completeErrorMessage(spe) raise spe @@ -110,7 +110,7 @@ class Px: res = result.content if self.prologue: res = self.prologue + res - if not self.unicode: + if not self.str: res = res.encode('utf-8') return res diff --git a/px/px_parser.py b/px/px_parser.py index 2df697d..2878d48 100644 --- a/px/px_parser.py +++ b/px/px_parser.py @@ -66,7 +66,7 @@ class PxParser(XmlParser): # See if we have a PX attribute among p_attrs. found = False for name in self.pxAttributes: - if attrs.has_key(name): + if name in attrs: if not found: # This is the first PX attr we find. # Create a sub-buffer with an action. @@ -87,7 +87,7 @@ class PxParser(XmlParser): hook = None ignorableAttrs = self.pxAttributes for name in self.noDumpTags: - if attrs.has_key(name) and attrs[name].startswith(':'): + if name in attrs and attrs[name].startswith(':'): hook = (name, attrs[name][1:]) ignorableAttrs += (name,) break diff --git a/shared/__init__.py b/shared/__init__.py index 48b9596..c71c320 100644 --- a/shared/__init__.py +++ b/shared/__init__.py @@ -50,12 +50,12 @@ class UnicodeBuffer: self.buffer = [] def write(self, s): if s == None: return - if isinstance(s, unicode): + if isinstance(s, str): self.buffer.append(s) elif isinstance(s, str): self.buffer.append(s.decode('utf-8')) else: - self.buffer.append(unicode(s)) + self.buffer.append(str(s)) def getValue(self): - return u''.join(self.buffer) + return ''.join(self.buffer) # ------------------------------------------------------------------------------ diff --git a/shared/csv_parser.py b/shared/csv_parser.py index 7726207..1347cc7 100644 --- a/shared/csv_parser.py +++ b/shared/csv_parser.py @@ -109,18 +109,18 @@ class CsvParser: def convertValue(self, value, basicType): '''Converts the atomic p_value which is a string into some other atomic Python type specified in p_basicType (int, float, ...).''' - if (basicType != str) and (basicType != unicode): + if (basicType != str) and (basicType != str): try: - exec 'res = %s' % str(value) - except SyntaxError, se: + exec('res = %s' % str(value)) + except SyntaxError as se: res = None else: try: - exec 'res = """%s"""' % str(value) - except SyntaxError, se: + exec('res = """%s"""' % str(value)) + except SyntaxError as se: try: - exec "res = '''%s'''" % str(value) - except SyntaxError, se: + exec("res = '''%s'''" % str(value)) + except SyntaxError as se: res = None return res @@ -183,7 +183,7 @@ class CsvParser: self.res.append(lineObject) f.close() # The second pass resolves the p_references if any - for attrName, refInfo in self.references.iteritems(): + for attrName, refInfo in self.references.items(): if attrName in self.attributes: # Replace ID with real object from p_references for obj in self.res: @@ -228,7 +228,7 @@ class CsvMarshaller: def marshallValue(self, field, value): '''Produces a version of p_value that can be dumped in a CSV file.''' - if isinstance(value, basestring): + if isinstance(value, str): # Format the string as a one-line CSV-ready value res = self.marshallString(value) elif type(value) in sequenceTypes: diff --git a/shared/dav.py b/shared/dav.py index 42e63f6..41c043a 100644 --- a/shared/dav.py +++ b/shared/dav.py @@ -1,6 +1,6 @@ # ------------------------------------------------------------------------------ -import os, re, httplib, sys, stat, urlparse, time, socket, xml.sax -import urllib +import os, re, http.client, sys, stat, urllib.parse, time, socket, xml.sax +from urllib.parse import quote from StringIO import StringIO from mimetypes import guess_type from base64 import encodestring @@ -19,12 +19,12 @@ class FormDataEncoder: def marshalValue(self, name, value): if isinstance(value, basestring): - return '%s=%s' % (name, urllib.quote(str(value))) + return '%s=%s' % (name, quote(str(value))) elif isinstance(value, float): return '%s:float=%s' % (name, value) elif isinstance(value, int): return '%s:int=%s' % (name, value) - elif isinstance(value, long): + elif isinstance(value, int): res = '%s:long=%s' % (name, value) if res[-1] == 'L': res = res[:-1] @@ -101,8 +101,8 @@ class HttpResponse: redirect the user to if self.code is 302, or will unmarshall XML data into Python objects.''' if self.code == 302: - return urlparse.urlparse(self.headers['location'])[2] - elif self.headers.has_key('content-type'): + return urllib.parse.urlparse(self.headers['location'])[2] + elif 'content-type' in self.headers: contentType = self.extractContentType(self.headers['content-type']) for xmlHeader in self.xmlHeaders: if contentType.startswith(xmlHeader): @@ -116,7 +116,7 @@ class HttpResponse: raise ResourceError('Distant server exception: ' \ '%s' % res) return res - except xml.sax.SAXParseException, se: + except xml.sax.SAXParseException as se: raise ResourceError('Invalid XML response (%s)'%str(se)) # ------------------------------------------------------------------------------ @@ -158,7 +158,7 @@ class Resource: def updateHeaders(self, headers): # Add credentials if present if not (self.username and self.password): return - if headers.has_key('Authorization'): return + if 'Authorization' in headers: return credentials = '%s:%s' % (self.username, self.password) credentials = credentials.replace('\012', '') headers['Authorization'] = "Basic %s" % encodestring(credentials) @@ -170,19 +170,19 @@ class Resource: def send(self, method, uri, body=None, headers={}, bodyType=None): '''Sends a HTTP request with p_method, for p_uri.''' - conn = httplib.HTTPConnection(self.host, self.port) + conn = http.client.HTTPConnection(self.host, self.port) try: conn.connect() - except socket.gaierror, sge: + except socket.gaierror as sge: raise ResourceError('Check your Internet connection (%s)'% str(sge)) - except socket.error, se: + except socket.error as se: raise ResourceError('Connection error (%s)' % str(se)) # Tell what kind of HTTP request it will be. conn.putrequest(method, uri, skip_host=True) # Add HTTP headers self.updateHeaders(headers) if self.headers: headers.update(self.headers) - for n, v in headers.items(): conn.putheader(n, v) + for n, v in list(headers.items()): conn.putheader(n, v) conn.endheaders() # Add HTTP body if body: diff --git a/shared/diff.py b/shared/diff.py index ca2ec2b..a836f7a 100644 --- a/shared/diff.py +++ b/shared/diff.py @@ -250,7 +250,7 @@ class HtmlDiff: deleteStyle = 'color: red; text-decoration: line-through; cursor: help' def __init__(self, old, new, - insertMsg=u'Inserted text', deleteMsg=u'Deleted text', + insertMsg='Inserted text', deleteMsg='Deleted text', insertCss=None, deleteCss=None, insertName='insert', deleteName='delete', diffRatio=0.7): # p_old and p_new are strings containing chunks of HTML. If they are not @@ -306,24 +306,24 @@ class HtmlDiff: else: tag = 'span' # What message will it show in its 'title' attribute? if not msg: - exec 'msg = self.%sMsg' % type + exec('msg = self.%sMsg' % type) # What CSS class (or, if none, tag-specific style) will be used ? - exec 'cssClass = self.%sCss' % type + exec('cssClass = self.%sCss' % type) if cssClass: style = 'class="%s"' % cssClass else: - exec 'style = self.%sStyle' % type + exec('style = self.%sStyle' % type) style = 'style="%s"' % style # The 'name' attribute of the tag indicates the type of the update. - exec 'tagName = self.%sName' % type + exec('tagName = self.%sName' % type) # The idea is: if there are several lines, every line must be surrounded # by a tag. This way, we know that a surrounding tag can't span several # lines, which is a prerequisite for managing cumulative diffs. if sep == ' ': - if not isinstance(seq, basestring): + if not isinstance(seq, str): seq = sep.join(seq) sep = '' - if isinstance(seq, basestring): + if isinstance(seq, str): return '%s<%s name="%s" %s title="%s">%s%s' % \ (sep, tag, tagName, style, msg, seq, tag, sep) else: @@ -592,7 +592,7 @@ class HtmlDiff: try: merger = Merger(lineA, add, previousDiffsA, self) add = merger.merge() - except Merger.MergeError, e: + except Merger.MergeError as e: # The merge algorithm has made a burn out. Simplify and # consider lineA has having been completely deleted and # lineB has completely inserted. diff --git a/shared/ldap_connector.py b/shared/ldap_connector.py index e43f2cd..772853f 100644 --- a/shared/ldap_connector.py +++ b/shared/ldap_connector.py @@ -239,14 +239,14 @@ class LdapConnector: self.server = ldap.initialize(self.serverUri) self.server.simple_bind_s(login, password) return True, None - except AttributeError, ae: + except AttributeError as ae: # When the ldap module is not there, trying to catch ldap.LDAPError # will raise an error. message = str(ae) self.log('Ldap connect error with login %s (%s).' % \ (login, message)) return False, message - except ldap.LDAPError, le: + except ldap.LDAPError as le: message = str(le) self.log('%s: connect error with login %s (%s).' % \ (self.serverUri, login, message)) @@ -275,7 +275,7 @@ class LdapConnector: timeout=self.timeout) except ldap.TIMEOUT: pass - except ldap.LDAPError, le: + except ldap.LDAPError as le: self.log('LDAP query error %s: %s' % \ (le.__class__.__name__, str(le))) # ------------------------------------------------------------------------------ diff --git a/shared/packaging.py b/shared/packaging.py index 5e5d61b..c79cd3c 100644 --- a/shared/packaging.py +++ b/shared/packaging.py @@ -160,20 +160,20 @@ class Debianizer: name = '%s/%sctl' % (binFolder, self.appNameLower) f = file(name, 'w') f.write(appCtl % self.appNameLower) - os.chmod(name, 0744) # Make it executable by owner. + os.chmod(name, 0o744) # Make it executable by owner. f.close() # run name = '%s/%srun' % (binFolder, self.appNameLower) f = file(name, 'w') f.write(appRun % self.appNameLower) - os.chmod(name, 0744) # Make it executable by owner. + os.chmod(name, 0o744) # Make it executable by owner. f.close() # startoo name = '%s/startoo' % binFolder f = file(name, 'w') f.write(ooStart) f.close() - os.chmod(name, 0744) # Make it executable by owner. + os.chmod(name, 0o744) # Make it executable by owner. # /var/lib/ (will store Data.fs, lock files, etc) varLibFolder = j(debFolder, 'var', 'lib', self.appNameLower) os.makedirs(varLibFolder) @@ -208,7 +208,7 @@ class Debianizer: 'application.' % n, '%sctl start' % n, '%sctl restart' % n, '%sctl stop' % n)) f.close() - os.chmod(name, 0744) # Make it executable by owner. + os.chmod(name, 0o744) # Make it executable by owner. # /etc/init.d/oo (start OpenOffice at boot time) name = '%s/oo' % initdFolder f = file(name, 'w') @@ -216,7 +216,7 @@ class Debianizer: 'startoo', 'startoo', "#Can't stop OO.")) f.write('\n') f.close() - os.chmod(name, 0744) # Make it executable by owner. + os.chmod(name, 0o744) # Make it executable by owner. # Get the size of the app, in Kb. os.chdir(tempFolder) cmd = subprocess.Popen(['du', '-b', '-s', 'debian'], diff --git a/shared/rtf.py b/shared/rtf.py index 352287b..be9ddf6 100644 --- a/shared/rtf.py +++ b/shared/rtf.py @@ -25,8 +25,8 @@ be strictly greater than 1.''' # ----------------------------------------------------------------------------- -import re, sys, UserList, UserDict -from StringIO import StringIO +import re, sys, collections, UserDict +from io import StringIO # ----------------------------------------------------------------------------- class ParserError(Exception): pass @@ -69,7 +69,7 @@ LIST_VALUE_ERROR = 'Value "%s" is malformed: within it, %s. You should check ' \ # ----------------------------------------------------------------------------- class Type: - basicTypes = {'f': float, 'i':int, 'g':long, 'b':bool} + basicTypes = {'f': float, 'i':int, 'g':int, 'b':bool} separators = ['-', ';', ',', ':'] def __init__(self, typeDecl): self.basicType = None # The python basic type @@ -85,12 +85,12 @@ class Type: self.listNumber += 1 else: # Get the basic type - if not (char in Type.basicTypes.keys()): + if not (char in list(Type.basicTypes.keys())): raise TypeError(BASIC_TYPE_ERROR % char) self.basicType = Type.basicTypes[char] break if not self.basicType: - self.basicType = unicode + self.basicType = str def convertBasicValue(self, value): try: return self.basicType(value.strip()) @@ -136,7 +136,7 @@ class Type: elif not resIsComplete: try: res = self.convertListItem(value, separators) - except TypeError, te: + except TypeError as te: raise TypeError(LIST_VALUE_ERROR % (value, te, self.name)) return res def convertListItem(self, stringItem, remainingSeps): @@ -161,9 +161,9 @@ class Type: return self.name # ----------------------------------------------------------------------------- -class Table(UserList.UserList): +class Table(collections.UserList): def __init__(self): - UserList.UserList.__init__(self) + collections.UserList.__init__(self) self.name = None self.parent = None self.parentRow = None @@ -213,7 +213,7 @@ class TableRow(UserDict.UserDict): via the parent table self.table.''' keyError = False t = self.table - if self.has_key(key): + if key in self: res = UserDict.UserDict.__getitem__(self, key) else: # Get the parent row @@ -259,9 +259,9 @@ class TableRow(UserDict.UserDict): # ----------------------------------------------------------------------------- class NameResolver: def resolveNames(self, tables): - for tableName, table in tables.iteritems(): + for tableName, table in tables.items(): if table.parent: - if not tables.has_key(table.parent): + if table.parent not in tables: raise ParserError(PARENT_NOT_FOUND % (table.parent, table.name)) table.parent = tables[table.parent] @@ -330,10 +330,10 @@ class TableParser: def manageSpecialChar(self): specialChar = int(self.specialCharBuffer) self.specialCharBuffer = '' - if self.specialChars.has_key(specialChar): + if specialChar in self.specialChars: self.contentBuffer.write(self.specialChars[specialChar]) else: - print('Warning: char %d not known.' % specialChar) + print(('Warning: char %d not known.' % specialChar)) self.state = TableParser.READING_CONTENT def bufferize(self, char): if self.state == TableParser.READING_CONTROL_WORD: @@ -403,7 +403,7 @@ class TableParser: columnNames.append(name.strip()) try: columnTypes.append(Type(typeDecl.strip())) - except TypeError, te: + except TypeError as te: raise ParserError(TYPE_ERROR % (header, self.currentTableName, te)) else: @@ -449,7 +449,7 @@ class TableParser: if columnType: try: columnValue = columnType.convertValue(columnValue) - except TypeError, te: + except TypeError as te: raise ParserError(VALUE_ERROR % (columnName, self.currentTableName, te)) @@ -496,7 +496,7 @@ class RtfTablesParser: # ----------------------------------------------------------------------------- if __name__ =='__main__': tables = RtfTablesParser("Tests.rtf").parse() - for key, item in tables.iteritems(): - print('Table %s' % key) + for key, item in tables.items(): + print(('Table %s' % key)) print(item) # ----------------------------------------------------------------------------- diff --git a/shared/sap.py b/shared/sap.py index b8c747b..5c5859c 100644 --- a/shared/sap.py +++ b/shared/sap.py @@ -74,7 +74,7 @@ class Sap: try: self.sap = pysap.Rfc_connection(conn_string = params) self.sap.open() - except pysap.BaseSapRfcError, se: + except pysap.BaseSapRfcError as se: # Put in the error message the connection string without the # password. connNoPasswd = params[:params.index('PASSWD')] + 'PASSWD=********' @@ -84,7 +84,7 @@ class Sap: '''Create a struct corresponding to SAP/C structure definition p_structDef and fills it with dict p_userData.''' res = structDef() - for name, value in userData.iteritems(): + for name, value in userData.items(): if name not in structDef._sfield_names_: raise SapError(SAP_STRUCT_ELEM_NOT_FOUND % (paramName, name)) sapType = structDef._sfield_sap_types_[name] @@ -93,7 +93,7 @@ class Sap: sType = '%s%d' % (sapType[0], sapType[1]) # "None" value is tolerated. if value == None: value = '' - if not isinstance(value, basestring): + if not isinstance(value, str): raise SapError( SAP_STRING_REQUIRED % (name, paramName, sType)) if len(value) > sapType[1]: @@ -113,7 +113,7 @@ class Sap: functionName = self.functionName function = self.sap.get_interface(functionName) # Specify the parameters - for name, value in params.iteritems(): + for name, value in params.items(): if type(value) == dict: # The param corresponds to a SAP/C "struct" v = self.createStructure( @@ -140,7 +140,7 @@ class Sap: function[name] = v # Call the function function() - except pysap.BaseSapRfcError, se: + except pysap.BaseSapRfcError as se: raise SapError(SAP_FUNCTION_ERROR % (functionName, str(se))) return SapResult(function) @@ -198,10 +198,10 @@ class Sap: self.sap.get_structure(typeName) res += '%s\n%s\n\n' % \ (typeName, self.getTypeInfo(typeName)) - except pysap.BaseSapRfcError, ee: + except pysap.BaseSapRfcError as ee: pass return res - except pysap.BaseSapRfcError, se: + except pysap.BaseSapRfcError as se: if se.value == 'FU_NOT_FOUND': raise SapError(SAP_FUNCTION_NOT_FOUND % (functionName)) else: @@ -229,6 +229,6 @@ class Sap: '''Disconnects from SAP.''' try: self.sap.close() - except pysap.BaseSapRfcError, se: + except pysap.BaseSapRfcError as se: raise SapError(SAP_DISCONNECT_ERROR % str(se)) # ------------------------------------------------------------------------------ diff --git a/shared/test.py b/shared/test.py index dacb93b..d5c9863 100644 --- a/shared/test.py +++ b/shared/test.py @@ -177,7 +177,7 @@ class Tester: (not isinstance(flavours, tuple)): raise TesterError(FLAVOURS_NOT_LIST) for flavour in flavours: - if not isinstance(flavour, basestring): + if not isinstance(flavour, str): raise TesterError(FLAVOUR_NOT_STRING) self.flavours = flavours self.flavour = None @@ -198,7 +198,7 @@ class Tester: if self.flavour: ext = '.%s' % self.flavour configTableName = 'Configuration%s' % ext - if self.tables.has_key(configTableName): + if configTableName in self.tables: self.config = self.tables[configTableName].asDict() self.tempFolder = os.path.join(self.testFolder, 'temp') if os.path.exists(self.tempFolder): @@ -249,11 +249,11 @@ class Tester: self.report.say('Test successful.\n') self.nbOfSuccesses += 1 def run(self): - assert self.tables.has_key('TestSuites'), \ + assert 'TestSuites' in self.tables, \ TesterError(MAIN_TABLE_NOT_FOUND % self.testPlan) for testSuite in self.tables['TestSuites']: - if (not testSuite.has_key('Name')) or \ - (not testSuite.has_key('Description')): + if ('Name' not in testSuite) or \ + ('Description' not in testSuite): raise TesterError(MAIN_TABLE_MALFORMED) if testSuite['Name'].startswith('_'): tsName = testSuite['Name'][1:] @@ -261,8 +261,8 @@ class Tester: else: tsName = testSuite['Name'] tsIgnored = False - assert self.tables.has_key('%s.descriptions' % tsName) \ - and self.tables.has_key('%s.data' % tsName), \ + assert '%s.descriptions' % tsName in self.tables \ + and '%s.data' % tsName in self.tables, \ TesterError(TEST_SUITE_NOT_FOUND % (tsName, tsName)) assert len(self.tables['%s.descriptions' % tsName]) == \ len(self.tables['%s.data' % tsName]), \ diff --git a/shared/utils.py b/shared/utils.py index 9b68680..daef14a 100644 --- a/shared/utils.py +++ b/shared/utils.py @@ -44,7 +44,7 @@ class FolderDeleter: dirName = os.path.dirname(dirName) else: break - except OSError, oe: + except OSError: break # ------------------------------------------------------------------------------ @@ -53,7 +53,7 @@ def cleanFolder(folder, exts=extsToClean, folders=(), verbose=False): '''This function allows to remove, in p_folder and subfolders, any file whose extension is in p_exts, and any folder whose name is in p_folders.''' - if verbose: print('Cleaning folder %s...' % folder) + if verbose: print(('Cleaning folder %s...' % folder)) # Remove files with an extension listed in p_exts if exts: for root, dirs, files in os.walk(folder): @@ -61,7 +61,7 @@ def cleanFolder(folder, exts=extsToClean, folders=(), verbose=False): ext = os.path.splitext(fileName)[1] if (ext in exts) or ext.endswith('~'): fileToRemove = os.path.join(root, fileName) - if verbose: print('Removing file %s...' % fileToRemove) + if verbose: print(('Removing file %s...' % fileToRemove)) os.remove(fileToRemove) # Remove folders whose names are in p_folders. if folders: @@ -69,7 +69,7 @@ def cleanFolder(folder, exts=extsToClean, folders=(), verbose=False): for folderName in dirs: if folderName in folders: toDelete = os.path.join(root, folderName) - if verbose: print('Removing folder %s...' % toDelete) + if verbose: print(('Removing folder %s...' % toDelete)) FolderDeleter.delete(toDelete) # ------------------------------------------------------------------------------ @@ -142,14 +142,14 @@ def copyData(data, target, targetMethod, type='string', encoding=None, dump(encodeData(chunk, encoding)) elif type == 'zope': # A OFS.Image.File instance can be split into several chunks - if isinstance(data.data, basestring): # One chunk + if isinstance(data.data, str): # One chunk dump(encodeData(data.data, encoding)) else: # Several chunks data = data.data while data is not None: dump(encodeData(data.data, encoding)) - data = data.next + data = data.__next__ # ------------------------------------------------------------------------------ def splitList(l, sub): @@ -222,12 +222,12 @@ def getOsTempFolder(): tmp = '/tmp' if os.path.exists(tmp) and os.path.isdir(tmp): res = tmp - elif os.environ.has_key('TMP'): + elif 'TMP' in os.environ: res = os.environ['TMP'] - elif os.environ.has_key('TEMP'): + elif 'TEMP' in os.environ: res = os.environ['TEMP'] else: - raise "Sorry, I can't find a temp folder on your machine." + raise Exception("Sorry, I can't find a temp folder on your machine.") return res def getTempFileName(prefix='', extension=''): @@ -273,10 +273,10 @@ def normalizeString(s, usage='fileName'): except UnicodeDecodeError: # Another encoding may be in use s = s.decode('latin-1') - elif not isinstance(s, unicode): s = unicode(s) + elif not isinstance(s, str): s = str(s) # For extracted text, replace any unwanted char with a blank if usage == 'extractedText': - res = u'' + res = '' for char in s: if char not in extractIgnore: res += char else: res += ' ' @@ -291,7 +291,7 @@ def normalizeString(s, usage='fileName'): for char in s: if char not in fileNameIgnore: res += char elif usage.startswith('alpha'): - exec 'rex = %sRex' % usage + exec('rex = %sRex' % usage) res = '' for char in s: if rex.match(char): res += char @@ -319,7 +319,7 @@ def keepDigits(s): def getStringDict(d): '''Gets the string literal corresponding to dict p_d.''' res = [] - for k, v in d.iteritems(): + for k, v in d.items(): if type(v) not in sequenceTypes: if not isinstance(k, basestring): k = str(k) if not isinstance(v, basestring): v = str(v) @@ -386,7 +386,7 @@ def formatNumber(n, sep=',', precision=2, tsep=' '): # ------------------------------------------------------------------------------ def lower(s): '''French-accents-aware variant of string.lower.''' - isUnicode = isinstance(s, unicode) + isUnicode = isinstance(s, str) if not isUnicode: s = s.decode('utf-8') res = s.lower() if not isUnicode: res = res.encode('utf-8') @@ -394,14 +394,14 @@ def lower(s): def upper(s): '''French-accents-aware variant of string.upper.''' - isUnicode = isinstance(s, unicode) + isUnicode = isinstance(s, str) if not isUnicode: s = s.decode('utf-8') res = s.upper() if not isUnicode: res = res.encode('utf-8') return res # ------------------------------------------------------------------------------ -typeLetters = {'b': bool, 'i': int, 'j': long, 'f':float, 's':str, 'u':unicode, +typeLetters = {'b': bool, 'i': int, 'j': int, 'f':float, 's':str, 'u':str, 'l': list, 'd': dict} caExts = {'py': ('.py', '.vpy', '.cpy'), 'xml': ('.pt', '.cpt', '.xml')} @@ -506,8 +506,8 @@ class CodeAnalysis: if not lines: return commentRate = (self.commentLines / float(lines)) * 100.0 blankRate = (self.emptyLines / float(lines)) * 100.0 - print('%s: %d files, %d lines (%.0f%% comments, %.0f%% blank)' % \ - (self.name, self.numberOfFiles, lines, commentRate, blankRate)) + print(('%s: %d files, %d lines (%.0f%% comments, %.0f%% blank)' % \ + (self.name, self.numberOfFiles, lines, commentRate, blankRate))) # ------------------------------------------------------------------------------ class LinesCounter: @@ -516,7 +516,7 @@ class LinesCounter: '%stemp' % os.sep) def __init__(self, folderOrModule, excludes=None): - if isinstance(folderOrModule, basestring): + if isinstance(folderOrModule, str): # It is the path of some folder self.folder = folderOrModule else: @@ -624,10 +624,10 @@ class FileWrapper: if self.content.__class__.__name__ == 'Pdata': # The file content is splitted in several chunks. f.write(self.content.data) - nextPart = self.content.next + nextPart = self.content.__next__ while nextPart: f.write(nextPart.data) - nextPart = nextPart.next + nextPart = nextPart.__next__ else: # Only one chunk f.write(self.content) diff --git a/shared/xml_parser.py b/shared/xml_parser.py index 02dcc26..d572668 100644 --- a/shared/xml_parser.py +++ b/shared/xml_parser.py @@ -66,9 +66,9 @@ HTML_ENTITIES = { 'euro':'€', 'nbsp':' ', "rsquo":"'", "lsquo":"'", "ldquo":"'", "rdquo":"'", 'ndash': '—', 'mdash': '—', 'oelig':'oe', 'quot': "'", 'mu': 'µ'} -import htmlentitydefs -for k, v in htmlentitydefs.entitydefs.iteritems(): - if not HTML_ENTITIES.has_key(k) and not XML_ENTITIES.has_key(k): +import html.entities +for k, v in html.entities.entitydefs.items(): + if k not in HTML_ENTITIES and k not in XML_ENTITIES: HTML_ENTITIES[k] = '' def escapeXml(s, format='xml', nsText='text'): @@ -77,13 +77,13 @@ def escapeXml(s, format='xml', nsText='text'): their ODF counterparts. In this case, it is needed to give the name of the "text" namespace (p_nsText) as defined in the ODF document where the line breaks and tabs must be inserted.''' - if isinstance(s, unicode): - res = u'' + if isinstance(s, str): + res = '' else: res = '' odf = format == 'odf' for c in s: - if XML_SPECIAL_CHARS_NO_APOS.has_key(c): + if c in XML_SPECIAL_CHARS_NO_APOS: # We do not escape 'apos': there is no particular need for that. res += XML_SPECIAL_CHARS_NO_APOS[c] elif odf and (c == '\n'): @@ -99,12 +99,12 @@ def escapeXml(s, format='xml', nsText='text'): def escapeXhtml(s): '''Return p_s, whose XHTML special chars and carriage return chars have been replaced with corresponding XHTML entities.''' - if isinstance(s, unicode): - res = u'' + if isinstance(s, str): + res = '' else: res = '' for c in s: - if XML_SPECIAL_CHARS_NO_APOS.has_key(c): + if c in XML_SPECIAL_CHARS_NO_APOS: res += XML_SPECIAL_CHARS_NO_APOS[c] elif c == '\n': res += '
' @@ -144,7 +144,7 @@ class XmlElement: res = self.elem if self.attrs: res += '(' - for attrName, attrValue in self.attrs.items(): + for attrName, attrValue in list(self.attrs.items()): res += '%s="%s"' % (attrName, attrValue) res += ')' return res @@ -167,7 +167,7 @@ class XmlEnvironment: self.parser = None def manageNamespaces(self, attrs): '''Manages namespaces definitions encountered in p_attrs.''' - for attrName, attrValue in attrs.items(): + for attrName, attrValue in list(attrs.items()): if attrName.startswith('xmlns:'): self.namespaces[attrValue] = attrName[6:] def ns(self, nsUri): @@ -221,7 +221,7 @@ class XmlParser(ContentHandler, ErrorHandler): def skippedEntity(self, name): '''This method is called every time expat does not recognize an entity. We provide here support for HTML entities.''' - if HTML_ENTITIES.has_key(name): + if name in HTML_ENTITIES: self.characters(HTML_ENTITIES[name].decode('utf-8')) else: # Put a question mark instead of raising an exception. @@ -230,10 +230,10 @@ class XmlParser(ContentHandler, ErrorHandler): # ErrorHandler methods --------------------------------------------------- def error(self, error): if self.raiseOnError: raise error - else: print('SAX error %s' % str(error)) + else: print(('SAX error %s' % str(error))) def fatalError(self, error): if self.raiseOnError: raise error - else: print('SAX fatal error %s' % str(error)) + else: print(('SAX fatal error %s' % str(error))) def warning(self, error): pass def parse(self, xml, source='string'): @@ -246,9 +246,9 @@ class XmlParser(ContentHandler, ErrorHandler): method will close it. ''' try: - from cStringIO import StringIO + from io import StringIO except ImportError: - from StringIO import StringIO + from io import StringIO self._xml = xml self.parser.setContentHandler(self) self.parser.setErrorHandler(self) @@ -333,7 +333,7 @@ class XmlUnmarshaller(XmlParser): def convertAttrs(self, attrs): '''Converts XML attrs to a dict.''' res = {} - for k, v in attrs.items(): + for k, v in list(attrs.items()): if ':' in k: # An attr prefixed with a namespace. Remove this. k = k.split(':')[-1] res[str(k)] = self.encode(v) @@ -362,9 +362,9 @@ class XmlUnmarshaller(XmlParser): e = XmlParser.startElement(self, elem, attrs) # Determine the type of the element. elemType = 'unicode' # Default value - if attrs.has_key('type'): + if 'type' in attrs: elemType = attrs['type'] - elif self.tagTypes.has_key(elem): + elif elem in self.tagTypes: elemType = self.tagTypes[elem] if elemType in self.containerTags: # I must create a new container object. @@ -375,9 +375,9 @@ class XmlUnmarshaller(XmlParser): elif elemType == 'dict': newObject = {} elif elemType == 'file': newObject = UnmarshalledFile() - if attrs.has_key('name'): + if 'name' in attrs: newObject.name = self.encode(attrs['name']) - if attrs.has_key('mimeType'): + if 'mimeType' in attrs: newObject.mimeType = self.encode(attrs['mimeType']) else: newObject = Object(**self.convertAttrs(attrs)) # Store the value on the last container, or on the root object. @@ -454,17 +454,17 @@ class XmlUnmarshaller(XmlParser): if not value: value = None else: # If we have a custom converter for values of this type, use it. - if self.conversionFunctions.has_key(e.currentBasicType): + if e.currentBasicType in self.conversionFunctions: try: value = self.conversionFunctions[e.currentBasicType]( value) - except Exception, err: + except Exception as err: raise AppyError(CUSTOM_CONVERSION_ERROR % ( e.currentBasicType, value, str(err))) # If not, try a standard conversion elif e.currentBasicType in self.numericTypes: try: - exec 'value = %s' % value + exec('value = %s' % value) except SyntaxError: raise AppyError(CONVERSION_ERROR % ( e.currentBasicType, value)) @@ -578,7 +578,7 @@ class XmlMarshaller: tagName = self.getTagName(self.rootElementName) res.write('<'); res.write(tagName) # Dumps namespace definitions if any - for prefix, url in self.namespaces.iteritems(): + for prefix, url in self.namespaces.items(): if not prefix: pre = 'xmlns' # The default namespace else: @@ -597,7 +597,7 @@ class XmlMarshaller: s = s.decode('utf-8') # Replace special chars by XML entities for c in s: - if self.xmlEntities.has_key(c): + if c in self.xmlEntities: res.write(self.xmlEntities[c]) else: res.write(c) @@ -617,13 +617,13 @@ class XmlMarshaller: # There will be several parts. w(v.data.data.encode('base64')) # Write subsequent parts - nextPart = v.data.next + nextPart = v.data.__next__ nextPartNb = 2 while nextPart: w('' % partTag) # Close the previous part w('<%s type="base64" number="%d">' % (partTag, nextPartNb)) w(nextPart.data.encode('base64')) - nextPart = nextPart.next + nextPart = nextPart.__next__ nextPartNb += 1 else: w(v.data.encode('base64')) @@ -654,7 +654,7 @@ class XmlMarshaller: def dumpDict(self, res, v): '''Dumps the XML version of dict p_v.''' - for key, value in v.iteritems(): + for key, value in v.items(): res.write('') self.dumpField(res, 'k', key) self.dumpField(res, 'v', value) @@ -681,7 +681,7 @@ class XmlMarshaller: # The previous condition must be checked before this one because # referred objects may be stored in lists or tuples, too. for elem in value: self.dumpField(res, 'e', elem) - elif isinstance(value, basestring): self.dumpString(res, value) + elif isinstance(value, str): self.dumpString(res, value) elif isinstance(value, bool): res.write(self.trueFalse[value]) elif fieldType == 'object': if hasattr(value, 'absolute_url'): @@ -689,7 +689,7 @@ class XmlMarshaller: res.write(value.absolute_url()) else: # Dump the entire object content - for k, v in value.__dict__.iteritems(): + for k, v in value.__dict__.items(): if not k.startswith('__'): self.dumpField(res, k, v) # Maybe we could add a parameter to the marshaller to know how @@ -771,7 +771,7 @@ class XmlMarshaller: rootTagName = self.dumpRootTag(res, instance) # Dump the fields of this root object if objectType == 'popo': - for fieldName, fieldValue in instance.__dict__.iteritems(): + for fieldName, fieldValue in instance.__dict__.items(): mustDump = False if fieldName in self.fieldsToExclude: mustDump = False @@ -823,11 +823,11 @@ class XmlMarshaller: histTag = self.getTagName('history') eventTag = self.getTagName('event') res.write('<%s type="list">' % histTag) - key = instance.workflow_history.keys()[0] + key = list(instance.workflow_history.keys())[0] history = instance.workflow_history[key] for event in history: res.write('<%s type="object">' % eventTag) - for k, v in event.iteritems(): + for k, v in event.items(): self.dumpField(res, k, v) res.write('' % eventTag) res.write('' % histTag) @@ -856,7 +856,7 @@ class XmlHandler(ContentHandler): (like dates) from a file that need to be compared to another file.''' def __init__(self, xmlTagsToIgnore, xmlAttrsToIgnore): ContentHandler.__init__(self) - self.res = unicode(xmlPrologue) + self.res = str(xmlPrologue) self.namespaces = {} # ~{s_namespaceUri:s_namespaceName}~ self.indentLevel = -1 self.tabWidth = 3 @@ -891,7 +891,7 @@ class XmlHandler(ContentHandler): self.res += '\n' + (' ' * self.indentLevel * self.tabWidth) def manageNamespaces(self, attrs): '''Manage namespaces definitions encountered in attrs''' - for attrName, attrValue in attrs.items(): + for attrName, attrValue in list(attrs.items()): if attrName.startswith('xmlns:'): self.namespaces[attrValue] = attrName[6:] def ns(self, nsUri): @@ -906,7 +906,7 @@ class XmlHandler(ContentHandler): self.indentLevel += 1 self.dumpSpaces() self.res += '<%s' % elem - attrsNames = attrs.keys() + attrsNames = list(attrs.keys()) attrsNames.sort() for attrToIgnore in self.attrsToIgnore: if attrToIgnore in attrsNames: @@ -986,7 +986,7 @@ class XmlComparator: else: print(line) else: if report: report.say(line[:-1], encoding=encoding) - else: print(line[:-1]) + else: print((line[:-1])) lastLinePrinted = True else: lastLinePrinted = False @@ -1061,7 +1061,7 @@ class XhtmlCleaner(XmlParser): self.env.ignoreContent = False try: res = self.parse('%s' % s).encode('utf-8') - except SAXParseException, e: + except SAXParseException as e: raise self.Error(str(e)) return res @@ -1106,7 +1106,7 @@ class XhtmlCleaner(XmlParser): prefix = '' res = '%s<%s' % (prefix, elem) # Include the found attributes, excepted those that must be ignored. - for name, value in attrs.items(): + for name, value in list(attrs.items()): if not self.keepStyles: if name in self.attrsToIgnore: continue elif name == 'style': @@ -1115,7 +1115,7 @@ class XhtmlCleaner(XmlParser): res += ' %s="%s"' % (name, value) # Include additional attributes if required. if elem in self.attrsToAdd: - for name, value in self.attrsToAdd[elem].iteritems(): + for name, value in self.attrsToAdd[elem].items(): res += ' %s="%s"' % (name, value) # Close the tag if it is a no-end tag if elem in self.noEndTags: @@ -1163,7 +1163,7 @@ class XhtmlCleaner(XmlParser): # I give here to lstrip an explicit list of what is to be considered # as blank chars, because I do not want unicode NBSP chars to be in # this list. - toAdd = content.lstrip(u' \n\r\t') + toAdd = content.lstrip(' \n\r\t') else: toAdd = content # Re-transform XML special chars to entities.