diff --git a/appy/__init__.py b/appy/__init__.py
index 010ddae..7e5a5d2 100644
--- a/appy/__init__.py
+++ b/appy/__init__.py
@@ -88,7 +88,7 @@ class Hack:
         '''Injects any method or attribute from p_patchClass into klass.'''
         patched = []
         added = []
-        for name, attr in patchClass.__dict__.iteritems():
+        for name, attr in patchClass.__dict__.items():
             if name.startswith('__'): continue # Ignore special methods
             # Unwrap functions from static methods
             if attr.__class__.__name__ == 'staticmethod':
diff --git a/appy/pod/__init__.py b/appy/pod/__init__.py
index 6759b0c..233c965 100644
--- a/appy/pod/__init__.py
+++ b/appy/pod/__init__.py
@@ -69,7 +69,7 @@ class PodError(Exception):
                      '<%s:date>%s%s:date><%s:p>' % \
                      (officeNs, dcNs, dcNs, dcNs,
                       time.strftime('%Y-%m-%dT%H:%M:%S'), dcNs, textNs))
-        buffer.dumpContent(message)
+        buffer.dumpContent(str(message))
         buffer.write('%s:p>' % textNs)
         if dumpTb:
             # We don't dump the traceback if it is an expression error (it is
diff --git a/appy/pod/buffers.py b/appy/pod/buffers.py
index 96ceab7..857fcaf 100644
--- a/appy/pod/buffers.py
+++ b/appy/pod/buffers.py
@@ -222,7 +222,7 @@ class FileBuffer(Buffer):
     def __init__(self, env, result):
         Buffer.__init__(self, env, None)
         self.result = result
-        self.content = file(result, 'w')
+        self.content = open(result, 'w', encoding='utf-8')
         self.content.write(xmlPrologue)
 
     # getLength is used to manage insertions into sub-buffers. But in the case
@@ -231,10 +231,7 @@ class FileBuffer(Buffer):
     def getLength(self): return 0
 
     def write(self, something):
-        try:
-            self.content.write(something.encode('utf-8'))
-        except UnicodeDecodeError:
-            self.content.write(something)
+        self.content.write(something)
 
     def addExpression(self, expression, tiedHook=None):
         # At 2013-02-06, this method was not called within the whole test suite.
@@ -674,7 +671,7 @@ class MemoryBuffer(Buffer):
         # Find the start position of the deepest element to remove
         deepestElem = self.action.elem.DEEPEST_TO_REMOVE
         pos = self.content.find('<%s' % deepestElem.elem)
-        for index in self.elements.keys():
+        for index in list(self.elements.keys()):
             if index < pos: del self.elements[index]
 
     reTagContent = re.compile('<(?P
[\w-]+):(?P[\w-]+)(.*?)>.*(?P=p):' \
diff --git a/appy/pod/elements.py b/appy/pod/elements.py
index c4daf7b..1685dfd 100644
--- a/appy/pod/elements.py
+++ b/appy/pod/elements.py
@@ -150,7 +150,7 @@ class Expression(PodElement):
         if resultType == 'NoneType':
             res = ''
         elif resultType == 'str':
-            res = res.decode('utf-8')
+            pass
         elif resultType == 'unicode':
             pass # Don't perform any conversion, unicode is the target type.
         elif resultType == 'Px':
diff --git a/appy/pod/pod_parser.py b/appy/pod/pod_parser.py
index 720474f..9a77734 100644
--- a/appy/pod/pod_parser.py
+++ b/appy/pod/pod_parser.py
@@ -38,7 +38,7 @@ class OdInsert:
        styles). OdInsert instances define such 'inserts' (what to insert and
        when).'''
     def __init__(self, odtChunk, elem, nsUris={}):
-        self.odtChunk = odtChunk.decode('utf-8') # The odt chunk to insert
+        self.odtChunk = odtChunk # The odt chunk to insert
         self.elem = elem # The p_odtChunk will be inserted just after the p_elem
         # start, which must be an XmlElement instance. If more than one p_elem
         # is present in the odt file, the p_odtChunk will be inserted only at
diff --git a/appy/pod/renderer.py b/appy/pod/renderer.py
index cb05aac..8f54a8c 100644
--- a/appy/pod/renderer.py
+++ b/appy/pod/renderer.py
@@ -177,8 +177,8 @@ class Renderer:
         self.unzipFolder = os.path.join(self.tempFolder, 'unzip')
         os.mkdir(self.unzipFolder)
         info = unzip(template, self.unzipFolder, odf=True)
-        self.contentXml = info['content.xml']
-        self.stylesXml = info['styles.xml']
+        self.contentXml = info['content.xml'].decode('utf-8')
+        self.stylesXml = info['styles.xml'].decode('utf-8')
         self.stylesManager = StylesManager(self.stylesXml)
         # From LibreOffice 3.5, it is not possible anymore to dump errors into
         # the resulting ods as annotations. Indeed, annotations can't reside
@@ -524,11 +524,11 @@ class Renderer:
                         os.path.join(self.unzipFolder, innerFile))
         # Insert dynamic styles
         contentXml = os.path.join(self.unzipFolder, 'content.xml')
-        f = file(contentXml)
+        f = open(contentXml, 'r+', encoding='utf-8')
         dynamicStyles = ''.join(self.dynamicStyles)
         content = f.read().replace('', dynamicStyles)
-        f.close()
-        f = file(contentXml, 'w')
+        f.seek(0)
+        f.truncate(0)
         f.write(content)
         f.close()
         # Call the user-defined "finalize" function when present
diff --git a/appy/pod/test/Tester.py b/appy/pod/test/Tester.py
index 9e0748d..fbc7c4c 100644
--- a/appy/pod/test/Tester.py
+++ b/appy/pod/test/Tester.py
@@ -94,7 +94,7 @@ class Test(appy.shared.test.Test):
             raise TesterError(CONTEXT_NOT_FOUND % contextPy)
         contextPkg = 'appy.pod.test.contexts.%s' % contextName
         exec('import %s' % contextPkg)
-        exec('context = dir(%s)' % contextPkg)
+        context = eval('dir(%s)' % contextPkg)
         res = {}
         for elem in context:
             if not elem.startswith('__'):
@@ -149,7 +149,7 @@ class Test(appy.shared.test.Test):
         zipFile = zipfile.ZipFile(odtFile)
         for zippedFile in zipFile.namelist():
             if zippedFile in self.interestingOdtContent:
-                f = file(os.path.join(self.tempFolder,
+                f = open(os.path.join(self.tempFolder,
                                       '%s.%s' % (filePrefix, zippedFile)), 'wb')
                 fileContent = zipFile.read(zippedFile)
                 if zippedFile == 'content.xml':
@@ -159,12 +159,10 @@ class Test(appy.shared.test.Test):
                     # to the other. So we remove those paths.
                     annotationsRemover = AnnotationsRemover(
                        OdfEnvironment(), self)
-                    annotationsRemover.parse(fileContent)
-                    fileContent = annotationsRemover.getResult()
-                try:
-                    f.write(fileContent.encode('utf-8'))
-                except UnicodeDecodeError:
-                    f.write(fileContent)
+                    annotationsRemover.parse(fileContent.decode('utf-8'))
+                    fileContent = annotationsRemover.getResult().encode('utf-8')
+               
+                f.write(fileContent)
                 f.close()
         zipFile.close()
 
diff --git a/appy/pod/xhtml2odt.py b/appy/pod/xhtml2odt.py
index 9519998..8aa6885 100644
--- a/appy/pod/xhtml2odt.py
+++ b/appy/pod/xhtml2odt.py
@@ -282,7 +282,7 @@ class HtmlTable:
             decl = '<%s:style %s:name="%s.%d" %s:family="table-column">' \
                    '<%s:table-column-properties %s:rel-column-width="%d*"' \
                    '/>%s:style>' % (s, s, self.name, i, s, s, s, width, s)
-            renderer.dynamicStyles.append(decl.encode('utf-8'))
+            renderer.dynamicStyles.append(decl)
 
 # ------------------------------------------------------------------------------
 class XhtmlEnvironment(XmlEnvironment):
@@ -512,7 +512,7 @@ class XhtmlEnvironment(XmlEnvironment):
                 sizes = table.columnContentSizes
                 # Insert None values if the list is too small
                 while (len(sizes)-1) < table.cellIndex: sizes.append(None)
-                highest = max(sizes[table.cellIndex], table.cellContentSize, 5)
+                highest = max(sizes[table.cellIndex] or 0, table.cellContentSize, 5)
                 # Put a maximum
                 highest = min(highest, 100)
                 sizes[table.cellIndex] = highest
diff --git a/appy/shared/css.py b/appy/shared/css.py
index 57f92ab..3edf8e4 100644
--- a/appy/shared/css.py
+++ b/appy/shared/css.py
@@ -10,7 +10,7 @@ def parseStyleAttribute(value, asDict=False):
     else:      res = []
     for attr in value.split(';'):
         if not attr.strip(): continue
-        name, value = attr.split(':')
+        name, value = attr.split(':', 1)
         if asDict: res[name.strip()] = value.strip()
         else:      res.append( (name.strip(), value.strip()) )
     return res
@@ -42,9 +42,9 @@ class CssStyles:
         '''Analyses styles as found in p_attrs and sets, for every found style,
            an attribute on self.'''
         # First, parse the "style" attr if present
-        if attrs.has_key('style'):
+        if 'style' in attrs.keys():
             styles = parseStyleAttribute(attrs['style'], asDict=True)
-            for name, value in styles.iteritems():
+            for name, value in styles.items():
                 if name in CssStyles.withUnit:
                     value = CssValue(value)
                 setattr(self, name.replace('-', ''), value)
@@ -52,12 +52,12 @@ class CssStyles:
         # override corresponding attributes from the "styles" attributes if
         # found.
         for name in ('width', 'height'):
-            if not hasattr(self, name) and attrs.has_key(name):
+            if not hasattr(self, name) and name in attrs.keys():
                 setattr(self, name, CssValue(attrs[name]))
 
     def __repr__(self):
         res = ''
 # ------------------------------------------------------------------------------
diff --git a/appy/shared/csv_parser.py b/appy/shared/csv_parser.py
index 1347cc7..1a10d5f 100644
--- a/appy/shared/csv_parser.py
+++ b/appy/shared/csv_parser.py
@@ -111,15 +111,15 @@ class CsvParser:
            Python type specified in p_basicType (int, float, ...).'''
         if (basicType != str) and (basicType != str):
             try:
-                exec('res = %s' % str(value))
+                res = eval('%s' % str(value))
             except SyntaxError as se:
                 res = None
         else:   
             try:
-                exec('res = """%s"""' % str(value))
+                res = eval('"""%s"""' % str(value))
             except SyntaxError as se:
                 try:
-                    exec("res = '''%s'''" % str(value))
+                    res = eval("res = '''%s'''" % str(value))
                 except SyntaxError as se:
                     res = None
         return res
diff --git a/appy/shared/dav.py b/appy/shared/dav.py
index 295fc02..4d7949f 100644
--- a/appy/shared/dav.py
+++ b/appy/shared/dav.py
@@ -34,7 +34,7 @@ class FormDataEncoder:
 
     def encode(self):
         res = []
-        for name, value in self.data.iteritems():
+        for name, value in self.data.items():
             res.append(self.marshalValue(name, value))
         return '&'.join(res)
 
diff --git a/appy/shared/diff.py b/appy/shared/diff.py
index a836f7a..8167a8b 100644
--- a/appy/shared/diff.py
+++ b/appy/shared/diff.py
@@ -306,16 +306,16 @@ class HtmlDiff:
         else: tag = 'span'
         # What message will it show in its 'title' attribute?
         if not msg:
-            exec('msg = self.%sMsg' % type)
+            msg = eval('self.%sMsg' % type)
         # What CSS class (or, if none, tag-specific style) will be used ?
-        exec('cssClass = self.%sCss' % type)
+        cssClass = eval('self.%sCss' % type)
         if cssClass:
             style = 'class="%s"' % cssClass
         else:
-            exec('style = self.%sStyle' % type)
+            style = eval('self.%sStyle' % type)
             style = 'style="%s"' % style
         # The 'name' attribute of the tag indicates the type of the update.
-        exec('tagName = self.%sName' % type)
+        tagName = eval('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.
diff --git a/appy/shared/ldap_connector.py b/appy/shared/ldap_connector.py
index 772853f..e4f4f4f 100644
--- a/appy/shared/ldap_connector.py
+++ b/appy/shared/ldap_connector.py
@@ -83,7 +83,7 @@ class LdapConfig:
            dict of params usable for creating or updating the corresponding
            Appy user.'''
         res = {}
-        for name, appyName in self.ldapAttributes.iteritems():
+        for name, appyName in self.ldapAttributes.items():
             if not appyName: continue
             # Get the name of the attribute as known in the LDAP
             ldapName = getattr(self, name)
@@ -111,7 +111,7 @@ class LdapConfig:
         user = tool.search1('User', noSecurity=True, login=login)
         if user:
             # Yes. Update it with info about him from the LDAP
-            for name, value in attrs.iteritems():
+            for name, value in attrs.items():
                 currentValue = getattr(user, name)
                 if value != currentValue:
                     setattr(user, name, value)
diff --git a/appy/shared/test.py b/appy/shared/test.py
index d5c9863..b16f95c 100644
--- a/appy/shared/test.py
+++ b/appy/shared/test.py
@@ -69,11 +69,9 @@ class TestReport:
             TestReport.instance = self
         else:
             raise InternalError(TEST_REPORT_SINGLETON_ERROR)
-    def say(self, msg, force=False, encoding=None):
+    def say(self, msg, force=False):
         if self.verbose or force:
             print(msg)
-        if encoding:
-            self.report.write(msg.encode(encoding))
         else:
             self.report.write(msg)
         self.report.write('\n')
diff --git a/appy/shared/utils.py b/appy/shared/utils.py
index 6a9cbf5..42e53fb 100644
--- a/appy/shared/utils.py
+++ b/appy/shared/utils.py
@@ -194,7 +194,7 @@ def flipDict(d):
     '''Flips dict p_d: keys become values, values become keys. p_d is left
        untouched: a new, flipped, dict is returned.'''
     res = {}
-    for k, v in d.iteritems(): res[v] = k
+    for k, v in d.items(): res[v] = k
     return 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)
+        rex = eval('%sRex' % usage)
         res = ''
         for char in s:
             if rex.match(char): res += char
diff --git a/appy/shared/xml_parser.py b/appy/shared/xml_parser.py
index d572668..2da2bc9 100644
--- a/appy/shared/xml_parser.py
+++ b/appy/shared/xml_parser.py
@@ -222,7 +222,7 @@ class XmlParser(ContentHandler, ErrorHandler):
         '''This method is called every time expat does not recognize an entity.
            We provide here support for HTML entities.'''
         if name in HTML_ENTITIES:
-            self.characters(HTML_ENTITIES[name].decode('utf-8'))
+            self.characters(HTML_ENTITIES[name])
         else:
             # Put a question mark instead of raising an exception.
             self.characters('?')
@@ -245,23 +245,20 @@ class XmlParser(ContentHandler, ErrorHandler):
              - a file instance opened for reading. Note that in this case, this
                method will close it.
         '''
-        try:
-            from io import StringIO
-        except ImportError:
-            from io import StringIO
+        from io import BytesIO
         self._xml = xml
         self.parser.setContentHandler(self)
         self.parser.setErrorHandler(self)
         self.parser.setFeature(feature_external_ges, False)
         inputSource = InputSource()
         if source == 'string':
-            inputSource.setByteStream(StringIO(xml))
+            inputSource.setByteStream(BytesIO(xml.encode('utf-8')))
         else:
-            if not isinstance(xml, file):
-                xml = file(xml)
+            if isinstance(xml, str):
+                xml = open(xml,'rb')
             inputSource.setByteStream(xml)
         self.parser.parse(inputSource)
-        if isinstance(xml, file): xml.close()
+        if hasattr(xml, 'close'): xml.close()
         return self.res
 
 # ------------------------------------------------------------------------------
@@ -464,7 +461,7 @@ class XmlUnmarshaller(XmlParser):
                 # If not, try a standard conversion
                 elif e.currentBasicType in self.numericTypes:
                     try:
-                        exec('value = %s' % value)
+                        value = eval('%s' % value)
                     except SyntaxError:
                         raise AppyError(CONVERSION_ERROR % (
                             e.currentBasicType, value))
@@ -945,10 +942,10 @@ class XmlComparator:
         # Perform the comparison
         differ = difflib.Differ()
         if self.areXml:
-            f = file(self.fileNameA)
+            f = open(self.fileNameA, 'rb')
             contentA = f.read()
             f.close()
-            f = file(self.fileNameB)
+            f = open(self.fileNameB, 'rb')
             contentB = f.read()
             f.close()
             xmlHandler = XmlHandler(self.xmlTagsToIgnore, self.xmlAttrsToIgnore)
@@ -958,10 +955,10 @@ class XmlComparator:
             xml.sax.parseString(contentB, xmlHandler)
             contentB = xmlHandler.res.split('\n')
         else:
-            f = file(self.fileNameA)
+            f = open(self.fileNameA, 'r', encoding=encoding)
             contentA = f.readlines()
             f.close()
-            f = file(self.fileNameB)
+            f = open(self.fileNameB,  'r', encoding=encoding)
             contentB = f.readlines()
             f.close()
         diffResult = list(differ.compare(contentA, contentB))
@@ -975,17 +972,17 @@ class XmlComparator:
                 if not atLeastOneDiff:
                     msg = 'Difference(s) detected between files %s and %s:' % \
                           (self.fileNameA, self.fileNameB)
-                    if report: report.say(msg, encoding='utf-8')
+                    if report: report.say(msg)
                     else: print(msg)
                     atLeastOneDiff = True
                 if not lastLinePrinted:
                     if report: report.say('...')
                     else: print('...')
                 if self.areXml:
-                    if report: report.say(line, encoding=encoding)
+                    if report: report.say(line)
                     else: print(line)
                 else:
-                    if report: report.say(line[:-1], encoding=encoding)
+                    if report: report.say(line[:-1])
                     else: print((line[:-1]))
                 lastLinePrinted = True
             else: