# patch for python 3 support, taken from PR at upstream bitbucket;
# coded 2014 by Martin Panter ("vadmium")
# https://bitbucket.org/dual75/yenc/pull-requests/3/python-3-support/diff
--- a/README
+++ b/README
@@ -42,10 +42,10 @@
 
 Requirements:
 ------------
-A C developement environment, Python>=2.6 and python developement libs (look for 
+A C developement environment, Python>=2.7 or 3.1, and python development libs (look for 
 python-dev or something like that if you're using .rpm or .deb packages, if you 
 installed from sources you already have everything you need).
-The module is known to work with Python2.6, testing with other version is needed.
+The module is known to work with Python 2.7 and 3.4; testing with other version is needed.
 
 
 Installation:
@@ -100,8 +100,8 @@
 output can't be written or calculated crc doesn't match the optional crc_in 
 argument (useful for writing decoding tools).
 
-CRC32 sums are always represented as lowercase strings, whithout any
-preceeding simbol (like '0x').
+CRC32 sums are always represented as lowercase strings, without any
+preceeding symbol (like '0x').
 
 
 Decoder and Encoder:
@@ -152,4 +152,3 @@
 
 
 Greets, Sandro.
-
--- a/setup.py
+++ b/setup.py
@@ -24,32 +24,31 @@
 from distutils.core import setup, Extension
 
 setup(	
-	name		 = "yenc",
-	version		 = "0.4.0",
-	author		 = "Alessandro Duca",
-	author_email	 = "alessandro.duca@gmail.com",
-        url		 = "https://bitbucket.org/dual75/yenc",
-	license		 = "LGPL",
-        platforms        = ["Unix"],
-	package_dir	 = { '': 'lib' },
-	py_modules	 = ["yenc"],
-	ext_modules	 = [Extension("_yenc",["src/_yenc.c"],extra_compile_args=[])],
-        classifiers      = [
-            "Programming Language :: Python",
-            "Programming Language :: Python :: 2.5",
-            "Programming Language :: Python :: 2.6",
-            "Programming Language :: Python :: 2.7",
-            "Programming Language :: C",
-            "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
-            "Operating System :: Unix",
-            "Development Status :: 4 - Beta",
-            "Environment :: Other Environment",
-            "Intended Audience :: Developers",
-            "Topic :: Software Development :: Libraries :: Python Modules",
-            "Topic :: Communications :: Usenet News"
-            ],
-	description	 = "yEnc Module for Python",
-        long_description = """
+    name		 = "yenc",
+    version		 = "0.4.0",
+    author		 = "Alessandro Duca",
+    author_email	 = "alessandro.duca@gmail.com",
+    url		 = "https://bitbucket.org/dual75/yenc",
+    license		 = "LGPL",
+    platforms        = ["Unix"],
+    package_dir	 = { '': 'lib' },
+    py_modules	 = ["yenc"],
+    ext_modules	 = [Extension("_yenc",["src/_yenc.c"],extra_compile_args=[])],
+    classifiers      = [
+        "Programming Language :: Python",
+        "Programming Language :: Python :: 2.7",
+        "Programming Language :: Python :: 3.1",
+        "Programming Language :: C",
+        "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
+        "Operating System :: Unix",
+        "Development Status :: 4 - Beta",
+        "Environment :: Other Environment",
+        "Intended Audience :: Developers",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        "Topic :: Communications :: Usenet News"
+        ],
+    description	 = "yEnc Module for Python",
+    long_description = """
 yEnc Encoding/Decoding for Python
 ---------------------------------
 
@@ -61,5 +60,5 @@
 with helper classes Encoder and Decoder.
 """
 
-	)
+    )
 
--- a/examples/ydecode.py
+++ b/examples/ydecode.py
@@ -26,68 +26,73 @@
 import sys
 import re
 
-NAME_RE 	= re.compile(r"^.*? name=(.+?)\r\n$")
-LINE_RE 	= re.compile(r"^.*? line=(\d{3}) .*$")
-SIZE_RE 	= re.compile(r"^.*? size=(\d+) .*$")
-CRC32_RE	= re.compile(r"^.*? crc32=(\w+)")
+NAME_RE 	= re.compile(br"^.*? name=(.+?)\r\n$")
+LINE_RE 	= re.compile(br"^.*? line=(\d{3}) .*$")
+SIZE_RE 	= re.compile(br"^.*? size=(\d+) .*$")
+CRC32_RE	= re.compile(br"^.*? crc32=(\w+)")
 
 def main():
-	if len(sys.argv) > 1:
-		file_in = open(sys.argv[1],"rb")
-	else:
-		file_in	= sys.stdin
-	while 1:
-		line = file_in.readline()
-		if line.startswith("=ybegin "):
-			try:
-				name, size = NAME_RE.match(line).group(1), int(SIZE_RE.match(line).group(1))
-				m_obj = CRC32_RE.match(line)
-				if m_obj:
-					head_crc = m_obj.group(1)
-			except re.error, e:
-				sys.stderr.write("err-critical: malformed =ybegin header\n")
-				sys.exit(1)
-			break
-		elif not line:
-			sys.stderr.write("err-critical: no valid =ybegin header found\n")
-			sys.exit(1)
-	file_out = open(name,"wb")
-	try:
-		dec, dec_crc = yenc.decode(file_in, file_out, size)
-	except yenc.Error, e:
-		sys.stderr.write(str(e) + '\n')
-		sys.exit(1)
-	head_crc = trail_crc = tmp_crc = ""
-	garbage	= 0
-	for line in file_in.read().split("\r\n"):
-		if line.startswith("=yend "):
-			try:	
-				size = int( SIZE_RE.match(line).group(1) )
-				m_obj = CRC32_RE.match(line)
-				if m_obj:
-					trail_crc = m_obj.group(1)
-			except re.error, e:
-				sys.stderr.write("err: malformed =yend trailer\n")
-			break
-		elif not line:
-			continue
-		else:
-			garbage = 1
-	else:
-		sys.stderr.write("warning: couldn't find =yend trailer\n")
-	if garbage:
-		sys.stderr.write("warning: garbage before =yend trailer\n")
-	if head_crc:
-		tmp_crc = head_crc.lower()
-	elif trail_crc:
-		tmp_crc = trail_crc.lower()
-	else:
-		sys.exit(0)
-	if cmp(tmp_crc,dec_crc):
-		sys.stderr.write("err: header: %s dec: %s CRC32 mismatch\n" % (tmp_crc,dec_crc) )
-		sys.exit(1)
-	else:
-		sys.exit(0)
+    head_crc = trail_crc = None
+    if len(sys.argv) > 1:
+        file_in = open(sys.argv[1],"rb")
+    else:
+        try:  # Python 3
+            file_in	= sys.stdin.detach()
+        except AttributeError:  # Python < 3
+            file_in	= sys.stdin
+        sys.stdin = None
+    with file_in:
+        while True:
+            line = file_in.readline()
+            if line.startswith(b"=ybegin "):
+                try:
+                    name, size = NAME_RE.match(line).group(1), int(SIZE_RE.match(line).group(1))
+                    m_obj = CRC32_RE.match(line)
+                    if m_obj:
+                        head_crc = m_obj.group(1)
+                except re.error:
+                    sys.stderr.write("err-critical: malformed =ybegin header\n")
+                    sys.exit(1)
+                break
+            elif not line:
+                sys.stderr.write("err-critical: no valid =ybegin header found\n")
+                sys.exit(1)
+        with open(name,"wb") as file_out:
+            try:
+                dec, dec_crc = yenc.decode(file_in, file_out, size)
+            except yenc.Error as e:
+                sys.stderr.write(str(e) + '\n')
+                sys.exit(1)
+        garbage	= False
+        for line in file_in.read().split(b"\r\n"):
+            if line.startswith(b"=yend "):
+                try:	
+                    size = int( SIZE_RE.match(line).group(1) )
+                    m_obj = CRC32_RE.match(line)
+                    if m_obj:
+                        trail_crc = m_obj.group(1)
+                except re.error:
+                    sys.stderr.write("err: malformed =yend trailer\n")
+                break
+            elif not line:
+                continue
+            else:
+                garbage = True
+        else:
+            sys.stderr.write("warning: couldn't find =yend trailer\n")
+    if garbage:
+        sys.stderr.write("warning: garbage before =yend trailer\n")
+    if head_crc:
+        tmp_crc = head_crc.decode("ascii").lower()
+    elif trail_crc:
+        tmp_crc = trail_crc.decode("ascii").lower()
+    else:
+        sys.exit(0)
+    if tmp_crc != dec_crc:
+        sys.stderr.write("err: header: %s dec: %s CRC32 mismatch\n" % (tmp_crc,dec_crc) )
+        sys.exit(1)
+    else:
+        sys.exit(0)
 
 if __name__ == "__main__":
-	main()
+    main()
--- a/examples/ydecode_Decoder.py
+++ b/examples/ydecode_Decoder.py
@@ -27,70 +27,75 @@
 
 import yenc
 
-NAME_RE 	= re.compile(r"^.*? name=(.+?)\r\n$")
-LINE_RE 	= re.compile(r"^.*? line=(\d{3}) .*$")
-SIZE_RE 	= re.compile(r"^.*? size=(\d+) .*$")
-CRC32_RE	= re.compile(r"^.*? crc32=(\w+)")
+NAME_RE 	= re.compile(br"^.*? name=(.+?)\r\n$")
+LINE_RE 	= re.compile(br"^.*? line=(\d{3}) .*$")
+SIZE_RE 	= re.compile(br"^.*? size=(\d+) .*$")
+CRC32_RE	= re.compile(br"^.*? crc32=(\w+)")
 
 def main():
-	head_crc = trail_crc = ""
-	if len(sys.argv) > 1:
-		file_in = open(sys.argv[1],"rb")
-	else:
-		file_in	= sys.stdin
-	while 1:
-		line = file_in.readline()
-		if line.startswith("=ybegin "):
-			try:
-				name, size = NAME_RE.match(line).group(1), int(SIZE_RE.match(line).group(1))
-				m_obj = CRC32_RE.match(line)
-				if m_obj:
-					head_crc = m_obj.group(1)
-			except re.error, e:
-				sys.stderr.write("err-critical: malformed =ybegin header\n")
-				sys.exit(1)
-			break
-		elif not line:
-			sys.stderr.write("err-critical: no valid =ybegin header found\n")
-			sys.exit(1)
-	file_out = open(name,"wb")
-	dec = yenc.Decoder(file_out)
-	trailer = ""
-	garbage = 0
-	while True:
-		data = file_in.readline()
-		if data.startswith("=yend"):
-			trailer = data
-			break
-		elif dec.getSize() >= size:
-			garbage = 1
-		else:
-			dec.feed(data)
-			dec.flush()
-	if trailer:
-		try:	
-			size = int(SIZE_RE.match(trailer).group(1))
-			m_obj = CRC32_RE.search(trailer)
-			if m_obj:
-				trail_crc = m_obj.group(1)
-		except re.error, e:
-			sys.stderr.write("err: malformed =yend trailer\n")
-	else:
-		sys.stderr.write("warning: couldn't find =yend trailer\n")
-	if garbage:
-		sys.stderr.write("warning: garbage before =yend trailer\n")
-	if head_crc:
-		tmp_crc = head_crc.lower()
-	elif trail_crc:
-		tmp_crc = trail_crc.lower()
-	else:
-		sys.exit(0)
-#	print "comparing"
-	if cmp(tmp_crc, dec.getCrc32()):
-		sys.stderr.write("err: header: %s dec: %s CRC32 mismatch\n" % (tmp_crc,dec.getCrc32()) )
-		sys.exit(1)
-	else:
-		sys.exit(0)
+    head_crc = trail_crc = None
+    if len(sys.argv) > 1:
+        file_in = open(sys.argv[1],"rb")
+    else:
+        try:  # Python 3
+            file_in	= sys.stdin.detach()
+        except:  # Python < 3
+            file_in	= sys.stdin
+        sys.stdin = None
+    with file_in:
+        while True:
+            line = file_in.readline()
+            if line.startswith(b"=ybegin "):
+                try:
+                    name, size = NAME_RE.match(line).group(1), int(SIZE_RE.match(line).group(1))
+                    m_obj = CRC32_RE.match(line)
+                    if m_obj:
+                        head_crc = m_obj.group(1)
+                except re.error:
+                    sys.stderr.write("err-critical: malformed =ybegin header\n")
+                    sys.exit(1)
+                break
+            elif not line:
+                sys.stderr.write("err-critical: no valid =ybegin header found\n")
+                sys.exit(1)
+        file_out = open(name,"wb")
+        dec = yenc.Decoder(file_out)
+        trailer = None
+        garbage = False
+        while True:
+            data = file_in.readline()
+            if data.startswith(b"=yend"):
+                trailer = data
+                break
+            elif dec.getSize() >= size:
+                garbage = True
+            else:
+                dec.feed(data)
+                dec.flush()
+    if trailer:
+        try:	
+            size = int(SIZE_RE.match(trailer).group(1))
+            m_obj = CRC32_RE.search(trailer)
+            if m_obj:
+                trail_crc = m_obj.group(1)
+        except re.error:
+            sys.stderr.write("err: malformed =yend trailer\n")
+    else:
+        sys.stderr.write("warning: couldn't find =yend trailer\n")
+    if garbage:
+        sys.stderr.write("warning: garbage before =yend trailer\n")
+    if head_crc:
+        tmp_crc = head_crc.decode("ascii").lower()
+    elif trail_crc:
+        tmp_crc = trail_crc.decode("ascii").lower()
+    else:
+        sys.exit(0)
+#    sys.stderr.write("comparing\n")
+    if tmp_crc != dec.getCrc32():
+        sys.stderr.write("err: header: %s dec: %s CRC32 mismatch\n" % (tmp_crc,dec.getCrc32()) )
+        sys.exit(1)
+    else:
+        sys.exit(0)
 
 if __name__ == "__main__":
-	main()
+    main()
--- a/examples/yencode.py
+++ b/examples/yencode.py
@@ -30,38 +30,48 @@
 from binascii import crc32
 
 def main():
-	try:
-		opts, args = getopt.getopt(sys.argv[1:], "o:")
-	except getopt.GetoptError:
-		usage()
-	file_out = sys.stdout
-	for o,a in opts:
-		if o == '-o':
-			file_out = open(a,"wb")
-	if args:
-		filename = args[0]
-		if os.access( filename, os.F_OK | os.R_OK ):
-			file_in = open(filename,"rb")
-		else:
-			print "couldn't access %s" % filename
-			sys.exit(2)
-	else:
-		usage()
-	crc = "%08x"%(0xFFFFFFFF & crc32(open(filename,"rb").read())) 
-	name = os.path.split(filename)[1]
-	size = os.stat(filename)[ST_SIZE]
-	file_out.write("=ybegin line=128 size=%d crc32=%s name=%s\r\n" % (size, crc, name) )
-	try:
-		encoded, crc_out = yenc.encode(file_in, file_out, size)
-	except Exception, e:
-		print e
-		sys.exit(3)
-	file_out.write("=yend size=%d crc32=%s\r\n" % (encoded, crc_out))
-	
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], "o:")
+    except getopt.GetoptError:
+        usage()
+    file_out = None
+    for o,a in opts:
+        if o == '-o':
+            file_out = open(a,"wb")
+    if not file_out:
+        try:  # Python 3
+            file_out = sys.stdout.detach()
+        except AttributeError:  # Python < 3
+            file_out = sys.stdout
+        sys.stdout = None
+    with file_out:
+        if args:
+            filename = args[0]
+            if os.access( filename, os.F_OK | os.R_OK ):
+                file_in = open(filename,"rb")
+            else:
+                sys.stderr.write("couldn't access %s\n" % filename)
+                sys.exit(2)
+        else:
+            usage()
+        with file_in:
+            with open(filename,"rb") as file:
+                crc = "%08x"%(0xFFFFFFFF & crc32(file.read())) 
+            name = os.path.split(filename)[1]
+            size = os.stat(filename)[ST_SIZE]
+            line = "=ybegin line=128 size=%d crc32=%s name=%s\r\n"
+            file_out.write((line % (size, crc, name)).encode("ascii"))
+            try:
+                encoded, crc_out = yenc.encode(file_in, file_out, size)
+            except Exception as e:
+                sys.stderr.write("{}\n".format(e))
+                sys.exit(3)
+        line = "=yend size=%d crc32=%s\r\n" % (encoded, crc_out)
+        file_out.write(line.encode("ascii"))
 
 def usage():
-	print "Usage: yencode.py <-o outfile> filename"
-	sys.exit(1)
-	
+    sys.stderr.write("Usage: yencode.py <-o outfile> filename\n")
+    sys.exit(1)
+
 if __name__ == "__main__":
-	main()
+    main()
--- a/examples/yencode_Encoder.py
+++ b/examples/yencode_Encoder.py
@@ -30,42 +30,45 @@
 from binascii import crc32
 
 def main():
-	try:
-		opts, args = getopt.getopt(sys.argv[1:], "o:")
-	except getopt.GetoptError:
-		usage()
-	file_out = sys.stdout
-	for o,a in opts:
-		if o == '-o':
-			file_out = open(a,"wb")
-	if args:
-		filename = args[0]
-		if os.access( filename, os.F_OK | os.R_OK ):
-			file_in = open(filename,"rb")
-		else:
-			print "couldn't access %s" % filename
-			sys.exit(2)
-	else:
-		usage()
-	crc = "%x"%(0xFFFFFFFF & crc32( open(filename,"rb").read()))
-	name = os.path.split(filename)[1]
-	size = os.stat(filename)[ST_SIZE]
-	file_out.write("=ybegin line=128 size=%d crc32=%s name=%s\r\n" % (size, crc, name) )
-	file_in = open(filename, "rb")
-	encoder = yenc.Encoder(file_out)
-	while True:
-		data_in = file_in.read(1024)
-		encoder.feed(data_in)
-		encoder.flush()
-		if len(data_in) < 1024: break
-	encoder.terminate()
-	encoder.flush()
-	file_out.write("=yend size=%d crc32=%s\r\n" % (size, encoder.getCrc32()) )
-	
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], "o:")
+    except getopt.GetoptError:
+        usage()
+    try:  # Python 3
+        file_out = sys.stdout.buffer
+    except AttributeError:  # Python < 3
+        file_out = sys.stdout
+    for o,a in opts:
+        if o == '-o':
+            file_out = open(a,"wb")
+    if args:
+        filename = args[0]
+        if not os.access( filename, os.F_OK | os.R_OK ):
+            sys.stderr.write("couldn't access %s\n" % filename)
+            sys.exit(2)
+    else:
+        usage()
+    with open(filename,"rb") as file_in:
+        crc = "%x"%(0xFFFFFFFF & crc32(file_in.read()))
+    name = os.path.split(filename)[1]
+    size = os.stat(filename)[ST_SIZE]
+    line = "=ybegin line=128 size=%d crc32=%s name=%s\r\n"
+    file_out.write((line % (size, crc, name)).encode("ascii"))
+    with open(filename, "rb") as file_in:
+        encoder = yenc.Encoder(file_out)
+        while True:
+            data_in = file_in.read(1024)
+            encoder.feed(data_in)
+            encoder.flush()
+            if len(data_in) < 1024: break
+    encoder.terminate()
+    encoder.flush()
+    line = "=yend size=%d crc32=%s\r\n" % (size, encoder.getCrc32())
+    file_out.write(line.encode("ascii"))
 
 def usage():
-	print "Usage: yencode_Encoder.py <-o outfile> filename"
-	sys.exit(1)
-	
+    sys.stderr.write("Usage: yencode_Encoder.py <-o outfile> filename\n")
+    sys.exit(1)
+
 if __name__ == "__main__":
-	main()
+    main()
--- a/lib/yenc.py
+++ b/lib/yenc.py
@@ -21,213 +21,243 @@
 
 
 import sys
-from cStringIO import StringIO
+from io import BytesIO
 import _yenc
+from contextlib import contextmanager
 
 E_ERROR		= 64
 E_CRC32		= 65
 E_PARMS		= 66
 
-BIN_MASK        = 0xffffffffL
+BIN_MASK        = 0xffffffff
 
 class Error(Exception):
-	""" 	Class for specific yenc errors
-	"""
-	def __init__(self, value="", code=E_ERROR):
-		self.value = value
-		
-	def __str__(self):
-		return "yenc.Error: %s\n" % self.value, self.value
+    """ 	Class for specific yenc errors
+    """
+    def __init__(self, value="", code=E_ERROR):
+        self.value = value
+    
+    def __str__(self):
+        return "yenc.Error: %s\n" % self.value
 
 
+@contextmanager
 def _checkArgsType(file_in, file_out, bytez):
-	""" 	Internal checkings, not to be used from outside this module.
-	"""
-	if bytez < 0: 
-            raise Error("No. of bytes can't be negative", E_PARMS)
-	if type(file_in) == str:
-		if file_in == "-":
-			if bytez == 0: raise Error("No. of bytes is 0 or not \
-				specified while reading from stdin", E_PARMS)
-			file_in = sys.stdin
-		else: file_in = open(file_in,"rb")
-	if type(file_out) == str:
-		if file_out == "-": file_out = sys.stdout
-		else: file_out = open(file_out,"wb")
-	return file_in, file_out, bytez
+    """ 	Internal checkings, not to be used from outside this module.
+    """
+    if bytez < 0: 
+        raise Error("No. of bytes can't be negative", E_PARMS)
+    opened_in = None
+    opened_out = None
+    try:
+        if type(file_in) == str:
+            if file_in == "-":
+                if bytez == 0: raise Error("No. of bytes is 0 or not "
+                    "specified while reading from stdin", E_PARMS)
+                try:  # Python 3
+                    file_in = sys.stdin.buffer
+                except AttributeError:  # Python < 3
+                    file_in = sys.stdin
+            else:
+                opened_in = open(file_in,"rb")
+                file_in = opened_in
+        if type(file_out) == str:
+            if file_out == "-":
+                try:  # Python 3
+                    file_out = sys.stdout.buffer
+                except AttributeError:  # Python < 3
+                    file_out = sys.stdout
+            else:
+                opened_out = open(file_out,"wb")
+                file_out = opened_out
+        yield file_in, file_out, bytez
+    finally:
+        if opened_out:
+            opened_out.close()
+        if opened_in:
+            opened_in.close()
 
 
 def encode(file_in, file_out, bytez=0):
-	"""	encode(file_in, file_out, bytez=0): write "bytez" encoded bytes from
-		file_in to file_out, if "bytez" is 0 encodes bytez until EOF.
-	"""
-	file_in, file_out, bytez = _checkArgsType(file_in, file_out, bytez)
-	encoded, crc32 = _yenc.encode(file_in, file_out, bytez)
-	return encoded, "%08x" % (crc32 ^ BIN_MASK)
+    """	encode(file_in, file_out, bytez=0): write "bytez" encoded bytes from
+        file_in to file_out, if "bytez" is 0 encodes bytes until EOF.
+        
+        The input file object must implement a read() method, which will be
+        passed a "size" parameter, and should always return a bytes() object,
+        even at EOF. The output file object must implement write() and
+        flush() methods. The write() method is passed a byte string and
+        should write all the data it is passed; no return value is required.
+        The flush() method need not accept any parameters or return a value.
+        Both io.BufferedIOBase objects and Python 2's file objects are
+        compatible.
+    """
+    with _checkArgsType(file_in, file_out, bytez) as [file_in, file_out, bytez]:
+        encoded, crc32 = _yenc.encode(file_in, file_out, bytez)
+    return encoded, "%08x" % (crc32 ^ BIN_MASK)
 
 
 def decode(file_in, file_out, bytez=0, crc_in=""):
-	""" 	decode(file_in, file_out, bytez=0): write "bytez" decoded bytes from
-		file_in to file_out, if "bytez" is 0 decodes bytes until EOF.
-	"""
-	file_in, file_out, bytez = _checkArgsType(file_in, file_out, bytez)
-	decoded, crc32 = _yenc.decode(file_in, file_out, bytez)
-	crc_hex = "%08x" % (crc32 ^ BIN_MASK)
-	if crc_in and not cmp(crc_hex, crc_in.lower()):
-		raise Error("crc32 error", E_CRC32)
-	else:
-		return decoded, crc_hex
+    """ 	decode(file_in, file_out, bytez=0): write "bytez" decoded bytes from
+        file_in to file_out, if "bytez" is 0 decodes bytes until EOF. Input
+        and output file objects must implement same interfaces as for
+        encode().
+    """
+    with _checkArgsType(file_in, file_out, bytez) as [file_in, file_out, bytez]:
+        decoded, crc32 = _yenc.decode(file_in, file_out, bytez)
+    crc_hex = "%08x" % (crc32 ^ BIN_MASK)
+    if crc_in and crc_hex != crc_in.lower():
+        raise Error("crc32 error", E_CRC32)
+    else:
+        return decoded, crc_hex
 
 
 class Encoder:
-	""" 	class Encoder: facility class for encoding one string at time
-                Constructor accepts an optional "output_file" argument, file must be opened in write mode.
-                When output_file is specified flush() will write the buffer content onto the
-                and close() will flush and close the file. After close() further calls to feed() will fail.
-	"""
-	def __init__(self, output_file = None):
-		self._buffer = StringIO()
-		self._column = 0
-		self._output_file = output_file
-		self._crc = BIN_MASK
-		self._encoded = 0
-		self._feedable = True
-
-	def __del__(self):
-                if self._output_file is not None:
-                    self.flush()
-                    self.close()
-	
-	def feed(self, data):
-		"""	Encode some data and write the encoded data 
-			into the internal buffer.
-		"""
-		if not self._feedable:
-			raise IOError("Encoding already terminated")
-
-		encoded, self._crc, self._column = _yenc.encode_string(data, self._crc, self._column)
-		self._encoded = self._encoded + len(encoded)
-		self._buffer.write(encoded)
-		return len(encoded)
-	
-	def terminate(self):
-		"""	Appends the terminal CRLF sequence to the encoded data.
-                        Further calls to feed() will fail.
-		"""
-		self._buffer.write("\r\n")
-		self._feedable = False
-	
-	def flush(self):
-		"""	Writes the content of the internal buffer on output_file.
-		"""
-		if self._output_file is None:
-			raise ValueError("Output file is 'None'")
-
-                self._output_file.write(self._buffer.getvalue())
-                self._buffer = StringIO()
-
-        def close(self):
-                """     Flushes and closes output_file.
-                        The output buffer IS NOT automatically written to the file.
-                """
-                if self._output_file is None:
-                        raise ValueError("Output file is 'None'")
-
-                self._output_file.flush()
-                self._output_file.close()
-                self._output_file = None
-                self._feedable = False
-	
-	def getEncoded(self):
-		"""	Returns the data in the internal buffer.
-		"""
-		if self._output_file is not None:
-			raise ValueError("Output file is not 'None'")
-
-                return self._buffer.getvalue()
-	
-	def getSize(self):
-		"""	Returns the total number of encoded bytes (not the size of
-			the buffer).
-		"""
-		return self._encoded
-
-	def getCrc32(self):
-		"""	Returns the calculated crc32 string for the clear data.
-		"""
-                return "%08x" % (self._crc ^ BIN_MASK)
+    """ 	class Encoder: facility class for encoding one string at time
+        Constructor accepts an optional "output_file" argument, file must be opened in write mode.
+        When output_file is specified flush() will write the buffer content onto the
+        and close() will flush and close the file. After close() further calls to feed() will fail.
+    """
+    def __init__(self, output_file = None):
+        self._buffer = BytesIO()
+        self._column = 0
+        self._output_file = output_file
+        self._crc = BIN_MASK
+        self._encoded = 0
+        self._feedable = True
+
+    def __del__(self):
+        if self._output_file is not None:
+            self.flush()
+            self.close()
+    
+    def feed(self, data):
+        """	Encode some data and write the encoded data 
+            into the internal buffer.
+        """
+        if not self._feedable:
+            raise IOError("Encoding already terminated")
+
+        encoded, self._crc, self._column = _yenc.encode_string(data, self._crc, self._column)
+        self._encoded = self._encoded + len(encoded)
+        self._buffer.write(encoded)
+        return len(encoded)
+    
+    def terminate(self):
+        """	Appends the terminal CRLF sequence to the encoded data.
+            Further calls to feed() will fail.
+        """
+        self._buffer.write(b"\r\n")
+        self._feedable = False
+    
+    def flush(self):
+        """	Writes the content of the internal buffer on output_file.
+        """
+        if self._output_file is None:
+            raise ValueError("Output file is 'None'")
+
+        self._output_file.write(self._buffer.getvalue())
+        self._buffer = BytesIO()
+
+    def close(self):
+        """     Flushes and closes output_file.
+            The output buffer IS NOT automatically written to the file.
+        """
+        if self._output_file is None:
+            raise ValueError("Output file is 'None'")
+
+        self._output_file.close()
+        self._output_file = None
+        self._feedable = False
+    
+    def getEncoded(self):
+        """	Returns the data in the internal buffer.
+        """
+        if self._output_file is not None:
+            raise ValueError("Output file is not 'None'")
+
+        return self._buffer.getvalue()
+    
+    def getSize(self):
+        """	Returns the total number of encoded bytes (not the size of
+            the buffer).
+        """
+        return self._encoded
+
+    def getCrc32(self):
+        """	Returns the calculated crc32 string for the clear data.
+        """
+        return "%08x" % (self._crc ^ BIN_MASK)
 
 
 class Decoder:
-	""" class Decoder: facility class for decoding one string at time
-            Constructor accepts an optional "output_file" argument, file must be opened in write mode.
-            When output_file is specified flush() will write the buffer content onto the
-            and close() will flush and close the file. After close() further calls to feed() will fail.
-	"""
-	def __init__(self, output_file = None):
-		self._buffer = StringIO()
-		self._escape = 0
-		self._output_file = output_file
-		self._crc = BIN_MASK
-		self._decoded = 0
-                self._feedable = True
-	
-	def __del__(self):
-                if self._output_file is not None:
-                    self.flush()
-                    self.close()
-	
-	def feed(self, data):
-		"""	Decode some data and write the decoded data 
-			into the internal buffer.
-		"""
-		if not self._feedable:
-			raise IOError("Decoding already terminated")
-
-		decoded, self._crc, self._escape = _yenc.decode_string(data, self._crc, self._escape)
-		self._decoded = self._decoded + len(decoded)
-		self._buffer.write(decoded)
-		return len(decoded)
-	
-	def flush(self):
-		"""	Writes the content of the internal buffer on the file
-			passed as argument to the constructor.
-		"""
-		if self._output_file is None:
-			raise ValueError("Output file is 'None'")
-
-                self._output_file.write(self._buffer.getvalue())
-                self._buffer = StringIO()
-
-        def close(self):
-                """     Flushes and closes output_file.
-                        The output file is flushed before closing, further calls to feed() will fail.
-                """
-		if self._output_file is None:
-			raise ValueError("Output file is 'None'")
-
-                self._output_file.flush()
-                self._output_file.close()
-                self._output_file = None
-                self._feedable = False
-	
-	def getDecoded(self):
-		"""	Returns the decoded data from the internal buffer.
-                        If output_file is not None this is going to raise a ValueError.
-		"""
-		if self._output_file is not None:
-			raise ValueError("Output file is not 'None'")
-
-                return self._buffer.getvalue()
-
-	def getSize(self):
-		"""	Returns the total number of decoded bytes (not the size of
-			the buffer).
-		"""
-		return self._decoded
-	
-	def getCrc32(self):
-		"""	Returns the calculated crc32 string for the decoded data.
-		"""
-		return "%08x" % (self._crc ^ BIN_MASK) 
+    """ class Decoder: facility class for decoding one string at time
+        Constructor accepts an optional "output_file" argument, file must be opened in write mode.
+        When output_file is specified flush() will write the buffer content onto the
+        and close() will flush and close the file. After close() further calls to feed() will fail.
+    """
+    def __init__(self, output_file = None):
+        self._buffer = BytesIO()
+        self._escape = 0
+        self._output_file = output_file
+        self._crc = BIN_MASK
+        self._decoded = 0
+        self._feedable = True
+    
+    def __del__(self):
+        if self._output_file is not None:
+            self.flush()
+            self.close()
+    
+    def feed(self, data):
+        """	Decode some data and write the decoded data 
+            into the internal buffer.
+        """
+        if not self._feedable:
+            raise IOError("Decoding already terminated")
+
+        decoded, self._crc, self._escape = _yenc.decode_string(data, self._crc, self._escape)
+        self._decoded = self._decoded + len(decoded)
+        self._buffer.write(decoded)
+        return len(decoded)
+    
+    def flush(self):
+        """	Writes the content of the internal buffer on the file
+            passed as argument to the constructor.
+        """
+        if self._output_file is None:
+            raise ValueError("Output file is 'None'")
+
+        self._output_file.write(self._buffer.getvalue())
+        self._buffer = BytesIO()
+
+    def close(self):
+        """     Flushes and closes output_file.
+            The output file is flushed before closing, further calls to feed() will fail.
+        """
+        if self._output_file is None:
+            raise ValueError("Output file is 'None'")
+
+        self._output_file.close()
+        self._output_file = None
+        self._feedable = False
+    
+    def getDecoded(self):
+        """	Returns the decoded data from the internal buffer.
+            If output_file is not None this is going to raise a ValueError.
+        """
+        if self._output_file is not None:
+            raise ValueError("Output file is not 'None'")
+
+        return self._buffer.getvalue()
+
+    def getSize(self):
+        """	Returns the total number of decoded bytes (not the size of
+            the buffer).
+        """
+        return self._decoded
+    
+    def getCrc32(self):
+        """	Returns the calculated crc32 string for the decoded data.
+        """
+        return "%08x" % (self._crc ^ BIN_MASK) 
 
--- a/src/_yenc.c
+++ b/src/_yenc.c
@@ -20,6 +20,12 @@
 
 #include "_yenc.h"
 
+#if PY_MAJOR_VERSION < 3
+#	define BYTES_ARG "s"
+#else
+#	define BYTES_ARG "y"
+#endif
+
 /* Typedefs */
 typedef struct {
 	uInt crc;
@@ -77,11 +83,8 @@
 /* Function declarations */
 static void crc_init(Crc32 *, uInt);
 static void crc_update(Crc32 *, uInt);
-static Bool readable(FILE *);
-static Bool writable(FILE *);
-void init_yenc(void);
-static int encode_buffer(Byte *, Byte *, uInt, Crc32 *, uInt *);
-static int decode_buffer(Byte *, Byte *, uInt, Crc32 *, Bool *);
+static int encode_buffer(PyObject *, Byte *, Crc32 *, uInt *);
+static int decode_buffer(PyObject *, Byte *, Crc32 *, Bool *);
 PyObject* decode_string(PyObject* ,PyObject* ,PyObject* );
 
 /* Python API requirements */
@@ -109,32 +112,53 @@
 	crc->bytes++;
 }
 
-/*
- * Todo: provide alternatives for this to work on win32
- */
-static Bool writable(FILE *file)
+static PyObject* file_read(PyObject *Py_infile, uLong read_max)
 {
-	int mode = fcntl(fileno(file),F_GETFL) & O_ACCMODE;
-	return (mode == O_WRONLY) || (mode == O_RDWR);
+	PyObject *read_buffer;
+	Py_ssize_t read_bytes;
+	
+	read_buffer = PyObject_CallMethod(Py_infile, "read", "k", read_max);
+	if(!read_buffer) {
+		return NULL;
+	}
+	read_bytes = PyBytes_Size(read_buffer);
+	if(read_bytes < 0) {
+		Py_DECREF(read_buffer);
+        	return NULL;
+	}
+	if(read_bytes > read_max) {
+		Py_DECREF(read_buffer);
+		PyErr_SetString(PyExc_ValueError,
+			"read() returned too much data");
+		return NULL;
+	}
+	return read_buffer;
 }
 
-static Bool readable(FILE *file)
+static Bool file_write(
+		PyObject *Py_outfile,
+		Byte *write_buffer,
+		int encoded_bytes
+		)
 {
-	int mode = fcntl(fileno(file),F_GETFL) & O_ACCMODE;
-	return (mode == O_RDONLY) || (mode == O_RDWR);
+	PyObject *result = PyObject_CallMethod(Py_outfile, "write",
+		BYTES_ARG "#", write_buffer, encoded_bytes);
+	if(!result) {
+		return 0;
+	}
+	Py_DECREF(result);
+	return 1;
 }
-/*
- * 
- */
 
 static int encode_buffer(
-		Byte *input_buffer, 
+		PyObject *Py_input_string, 
 		Byte *output_buffer, 
-		uInt bytes, 
 		Crc32 *crc, 
 		uInt *col
 		)
 {
+	Byte *input_buffer = (Byte *)PyBytes_AS_STRING(Py_input_string);
+	uInt bytes = PyBytes_GET_SIZE(Py_input_string);
 	uInt encoded;
 	uInt in_ind;
 	uInt out_ind;
@@ -185,30 +209,22 @@
 		PyObject* kwds
 		)
 {
-	Byte read_buffer[BLOCK];
+	PyObject *read_buffer;
 	Byte write_buffer[LONGBUFF];
 	uLong encoded = 0;
 	uInt col = 0;
-	uInt read_bytes;
 	uInt in_ind;
-	uInt encoded_bytes;
+	int encoded_bytes;
 	uLong bytes = 0;
 	Crc32 crc;
+	PyObject *result;
 
-	FILE *infile = NULL, *outfile = NULL;
 	PyObject *Py_infile = NULL, *Py_outfile = NULL;
 	
-	if(!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|l", argnames, \
-				&PyFile_Type, &Py_infile, \
-				&PyFile_Type, &Py_outfile, \
+	if(!PyArg_ParseTupleAndKeywords(args, kwds, "OO|l", argnames, \
+				&Py_infile, \
+				&Py_outfile, \
 				&bytes)) return NULL;
-
-	infile = PyFile_AsFile(Py_infile);
-	outfile = PyFile_AsFile(Py_outfile);
-	
-	if(!readable(infile) || !writable(outfile) ) {
-		return PyErr_Format(PyExc_ValueError, "file objects not writeable/readable");
-	} 
 	
 	crc_init(&crc, 0xffffffffl);
 	while(encoded < bytes || bytes == 0){
@@ -217,35 +233,43 @@
 		} else {
 			in_ind = BLOCK;
 		}
-		read_bytes = fread(&read_buffer, 1, in_ind, infile);
-		if(read_bytes < 1) {
-                        break;
+		read_buffer = file_read(Py_infile, in_ind);
+		if(!read_buffer) {
+                        return NULL;
                 }
-		encoded_bytes = encode_buffer(&read_buffer[0], &write_buffer[0], read_bytes, &crc, &col);
-		if(fwrite(&write_buffer, 1, encoded_bytes, outfile) != encoded_bytes) {
+		if(PyBytes_GET_SIZE(read_buffer) < 1) {
+			Py_DECREF(read_buffer);
 			break;
 		}
-		encoded += read_bytes;
-	}
-	if(ferror(infile) || ferror(outfile)) {
-		return PyErr_Format(PyExc_IOError, "I/O Error while encoding");
+		encoded_bytes = encode_buffer(read_buffer, &write_buffer[0], &crc, &col);
+		encoded += PyBytes_GET_SIZE(read_buffer);
+		Py_DECREF(read_buffer);
+		if(!file_write(Py_outfile, write_buffer, encoded_bytes)) {
+			return NULL;
+		}
 	}
 	if(col > 0) {
-		fputc(CR, outfile);
-		fputc(LF, outfile);
+		if(!file_write(Py_outfile, (Byte *)"\r\n", 2)) {
+			return NULL;
+		}
+	}
+	result = PyObject_CallMethod(Py_outfile, "flush", NULL);
+	if(!result) {
+		return NULL;
 	}
-	fflush(outfile);
+	Py_DECREF(result);
 	return Py_BuildValue("(l,L)", encoded, (long long)crc.crc);
 }
 
 static int decode_buffer(
-		Byte *input_buffer, 
+		PyObject *Py_input_string, 
 		Byte *output_buffer, 
-		uInt bytes, 
 		Crc32 *crc, 
 		Bool *escape
 		)
 {
+	Byte *input_buffer = (Byte *)PyBytes_AS_STRING(Py_input_string);
+	uInt bytes = PyBytes_GET_SIZE(Py_input_string);
 	uInt read_ind;
 	uInt decoded_bytes;
 	Byte byte;
@@ -279,9 +303,8 @@
 {
 	PyObject *Py_input_string;
 	PyObject *Py_output_string;
-	PyObject *retval;
+	PyObject *retval = NULL;
 	
-	Byte *input_buffer = NULL;
 	Byte *output_buffer = NULL;
 	long long crc_value = 0xffffffffll;
 	uInt input_len = 0;
@@ -294,7 +317,7 @@
 				kwds,
 				"O!|Li", 
 				kwlist,
-				&PyString_Type,
+				&PyBytes_Type,
 				&Py_input_string, 
 				&crc_value,
 				&col
@@ -302,16 +325,20 @@
 		return NULL;
 
 	crc_init(&crc, (uInt)crc_value);
-	input_len = PyString_Size(Py_input_string);
-	input_buffer = (Byte *) PyString_AsString(Py_input_string);
+	input_len = PyBytes_Size(Py_input_string);
 	output_buffer = (Byte *) malloc((2 * input_len / LINESIZE + 1) * (LINESIZE + 2));
-	output_len = encode_buffer(input_buffer, output_buffer, input_len, &crc, &col);
-	Py_output_string = PyString_FromStringAndSize((char *)output_buffer, output_len);
-	retval = Py_BuildValue("(S,L,i)", Py_output_string, (long long)crc.crc, col);
+	if(!output_buffer)
+		return PyErr_NoMemory();
+	output_len = encode_buffer(Py_input_string, output_buffer, &crc, &col);
+	Py_output_string = PyBytes_FromStringAndSize((char *)output_buffer, output_len);
+	if(!Py_output_string)
+		goto out;
 
-	free(output_buffer);
+	retval = Py_BuildValue("(S,L,i)", Py_output_string, (long long)crc.crc, col);
 	Py_DECREF(Py_output_string);
 	
+out:
+	free(output_buffer);
 	return retval;
 }
 
@@ -321,33 +348,24 @@
 		PyObject* kwds
 		)
 {
-	Byte read_buffer[BLOCK];
+	PyObject *read_buffer;
 	Byte write_buffer[LONGBUFF];
 	uLong decoded = 0;
-	uInt decoded_bytes;
-	uInt read_bytes;
+	int decoded_bytes;
 	uLong read_max;
+	PyObject *result;
 	
 	Bool escape = 0;
 	uLong bytes = 0;
 	Crc32 crc;
 
-	FILE *infile = NULL, *outfile = NULL;
 	PyObject *Py_infile = NULL, *Py_outfile = NULL;
 	
-	if(!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|l", argnames, \
-				&PyFile_Type, &Py_infile, \
-				&PyFile_Type, &Py_outfile, \
+	if(!PyArg_ParseTupleAndKeywords(args, kwds, "OO|l", argnames, \
+				&Py_infile, \
+				&Py_outfile, \
 				&bytes)) return NULL;
 
-	infile = PyFile_AsFile(Py_infile);
-	outfile = PyFile_AsFile(Py_outfile);
-
-	if(!readable(infile) || !writable(outfile)) {
-		return PyErr_Format(PyExc_ValueError,
-				"file objects not writeable/readable");
-	} 
-
 	crc_init(&crc, 0xffffffffl);
 	while(decoded < bytes || bytes == 0){
 		if(bytes){
@@ -355,19 +373,27 @@
 		} else {
 			read_max=BLOCK;
 		};
-		read_bytes = fread((Byte *)&read_buffer, 1, read_max, infile);
-		if(read_bytes == 0) break;
-		decoded_bytes = decode_buffer(&read_buffer[0],
-				&write_buffer[0],read_bytes, &crc, &escape);
-		if(fwrite(&write_buffer[0],1,decoded_bytes,outfile)!=decoded_bytes){
+		read_buffer = file_read(Py_infile, read_max);
+		if(!read_buffer) {
+			return NULL;
+		}
+		if(PyBytes_GET_SIZE(read_buffer) == 0) {
+			Py_DECREF(read_buffer);
 			break;
 		}
+		decoded_bytes = decode_buffer(read_buffer,
+				&write_buffer[0], &crc, &escape);
+		Py_DECREF(read_buffer);
+		if(!file_write(Py_outfile, &write_buffer[0],decoded_bytes)){
+			return NULL;
+		}
 		decoded += decoded_bytes;
 	}
-	if(ferror(infile) || ferror(outfile)) {
-		return PyErr_Format(PyExc_IOError, "I/O Error while decoding");
+	result = PyObject_CallMethod(Py_outfile, "flush", NULL);
+	if(!result) {
+		return NULL;
 	}
-	fflush(outfile);
+	Py_DECREF(result);
 	return Py_BuildValue("(l,L)", decoded, (long long)crc.crc);
 }
 
@@ -379,9 +405,8 @@
 {
 	PyObject *Py_input_string;
 	PyObject *Py_output_string;
-	PyObject *retval;
+	PyObject *retval = NULL;
 	
-	Byte *input_buffer = NULL;
 	Byte *output_buffer = NULL;
 	long long crc_value = 0xffffffffll;
 	uInt input_len = 0;
@@ -394,29 +419,47 @@
 				kwds,
 				"O!|Li", 
 				kwlist,
-				&PyString_Type,
+				&PyBytes_Type,
 				&Py_input_string, 
 				&crc_value,
 				&escape
 				)) 
 		return NULL;
 	crc_init(&crc, (uInt)crc_value);
-	input_len = PyString_Size(Py_input_string);
-	input_buffer = (Byte *)PyString_AsString(Py_input_string);
+	input_len = PyBytes_Size(Py_input_string);
 	output_buffer = (Byte *)malloc( input_len );
-	output_len = decode_buffer(input_buffer, output_buffer, input_len, &crc, &escape);
-	Py_output_string = PyString_FromStringAndSize((char *)output_buffer, output_len);
+	if(!output_buffer)
+		return PyErr_NoMemory();
+	output_len = decode_buffer(Py_input_string, output_buffer, &crc, &escape);
+	Py_output_string = PyBytes_FromStringAndSize((char *)output_buffer, output_len);
+	if(!Py_output_string)
+		goto out;
+
 	retval = Py_BuildValue("(S,L,i)", Py_output_string, (long long)crc.crc, escape);
+	Py_DECREF(Py_output_string);
 	
+out:
 	free(output_buffer);
-	Py_DECREF(Py_output_string);
-
 	return retval;
 }
 
 
+#if PY_MAJOR_VERSION < 3
 void init_yenc()
 {
 	Py_InitModule3("_yenc", funcs, "Raw yenc operations");
 }
+#else
+PyObject* PyInit__yenc()
+{
+	static struct PyModuleDef moduledef = {
+		/* m_base */ PyModuleDef_HEAD_INIT,
+		/* m_name */ "_yenc",
+		/* m_doc */ "Raw yenc operations",
+		/* m_size */ -1,
+		/* m_methods */ funcs,
+	};
+	return PyModule_Create(&moduledef);
+}
+#endif
 
--- a/src/_yenc.h
+++ b/src/_yenc.h
@@ -19,9 +19,6 @@
  */ 
 
 #include <Python.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 /* Constants			*/
 #define	LINESIZE	128
@@ -38,8 +35,6 @@
  */
 #define LONGBUFF	( 2 * BLOCK / LINESIZE + 1) * ( LINESIZE + 2 ) 
 
-#define SMALLBUFF 	512
-
 #define ZERO		0x00
 #define CR		0x0d
 #define	LF		0x0a
@@ -48,15 +43,6 @@
 #define SPACE		0x20
 #define DOT             0x2e
 
-#define E_MODE		1
-#define E_EOF 		2
-#define E_IO		3
-
-#define E_MODE_MSG	"Invalide mode for '*file' arguments"
-#define E_IO_MSG	"I/O Error"
-
-#define _DDEBUG_
-
 
 /* Customized types		*/
 typedef unsigned long uLong;
@@ -69,7 +55,9 @@
 PyObject* decode_file(PyObject*, PyObject*, PyObject*);
 PyObject* encode_string(PyObject* ,PyObject* ,PyObject*);
 PyObject* decode_string(PyObject* ,PyObject* , PyObject*);
-void init_yenc(void);
-
-
 
+#if PY_MAJOR_VERSION < 3
+void init_yenc(void);
+#else
+PyObject* PyInit__yenc(void);
+#endif
--- a/test/test.py
+++ b/test/test.py
@@ -22,9 +22,11 @@
 ##=============================================================================
 
 import os
+import os.path
 import sys
 import time
 import logging
+import tempfile
 import unittest
 from binascii import crc32
 from stat import *
@@ -35,168 +37,185 @@
 BLOCK_SIZE = 4096
 
 
-class BaseTest(object):
+class BaseTest(unittest.TestCase):
     CMD_DATA = "dd if=/dev/urandom of=%s bs=1b count=%d" 
-    FILE_E = 'sampledata_e'
-    FILE_O = 'sampledata_o'
 
     def setUp(self):
-        os.system(BaseTest.CMD_DATA % (BaseTest.FILE_E, 128))
-        os.system(BaseTest.CMD_DATA % (BaseTest.FILE_O, 129))
+        self.open_file_e = tempfile.NamedTemporaryFile()
+        self.addCleanup(self.open_file_e.close)
+        self.open_file_o = tempfile.NamedTemporaryFile()
+        self.addCleanup(self.open_file_o.close)
+
+        self.FILE_E = self.open_file_e.name
+        self.FILE_O = self.open_file_o.name
+
+        os.system(self.CMD_DATA % (self.FILE_E, 128))
+        os.system(self.CMD_DATA % (self.FILE_O, 129))
 
     def tearDown(self):
-        os.unlink(BaseTest.FILE_E)
-        os.unlink(BaseTest.FILE_O)
-        for x in os.listdir('.'):
-            if x.endswith('.out') or x.endswith('.dec'):
-                os.unlink(x)
+        for basename in (self.FILE_E, self.FILE_O):
+            for x in ('.out', '.dec'):
+                if os.path.exists(basename + x):
+                    os.unlink(basename + x)
 
     def _readFile(self, filename):
-        file_in = open(filename, 'rb')
-
-        data = file_in.read()
-        file_in.close()
-        return data, "%08x" % (crc32(data) & 0xffffffffL)
+        with open(filename, 'rb') as file_in:
+            data = file_in.read()
+        return data, "%08x" % (crc32(data) & 0xffffffff)
 
 
 class TestLowLevel(unittest.TestCase):
 
     def testEncode(self):
-        e, c, z = _yenc.encode_string('Hello world!')
-        self.assertEquals(e, 'r\x8f\x96\x96\x99J\xa1\x99\x9c\x96\x8eK')
-        self.assertEquals(c, 3833259626L)
+        e, c, z = _yenc.encode_string(b'Hello world!')
+        self.assertEqual(e, b'r\x8f\x96\x96\x99J\xa1\x99\x9c\x96\x8eK')
+        self.assertEqual(c, 3833259626)
 
     def testDecode(self):
-        d, c, x = _yenc.decode_string('r\x8f\x96\x96\x99J\xa1\x99\x9c\x96\x8eK')
-        self.assertEquals(d, 'Hello world!')
-        self.assertEquals(c, 3833259626L)
+        d, c, x = _yenc.decode_string(b'r\x8f\x96\x96\x99J\xa1\x99\x9c\x96\x8eK')
+        self.assertEqual(d, b'Hello world!')
+        self.assertEqual(c, 3833259626)
 
 
-class TestFileFunctions(BaseTest, unittest.TestCase):
+class TestFileFunctions(BaseTest):
 
     def _testEncodeFile(self, filename):
         data, crc =  self._readFile(filename)
-        file_in = open(filename, 'rb')
-        file_out = open(filename + ".out", 'wb')
-
-        bytes_out, crc_out = yenc.encode(file_in, file_out, len(data))
+        with open(filename, 'rb') as file_in, \
+        open(filename + ".out", 'wb') as file_out:
+            bytes_out, crc_out = yenc.encode(file_in, file_out, len(data))
 
-        self.assertEquals(crc, crc_out)
+        self.assertEqual(crc, crc_out)
 
 
     def testEncodeE(self):
-        self._testEncodeFile(BaseTest.FILE_E)
+        self._testEncodeFile(self.FILE_E)
 
 
     def testEncodeO(self):
-        self._testEncodeFile(BaseTest.FILE_O)
+        self._testEncodeFile(self.FILE_O)
 
 
     def _testDecodeFile(self, filename):
         data, crc =  self._readFile(filename)
-        file_in = open(filename, 'rb')
-        file_out = open(filename + ".out", 'wb')
-
-        bytes_out, crc_out = yenc.encode(file_in, file_out, len(data))
-
-        file_in = open(filename + ".out", 'rb')
-        file_out = open(filename + ".dec", 'wb')
-        bytes_dec, crc_dec = yenc.decode(file_in, file_out)
+        
+        with open(filename, 'rb') as file_in, \
+        open(filename + ".out", 'wb') as file_out:
+            bytes_out, crc_out = yenc.encode(file_in, file_out, len(data))
+
+        with open(filename + ".out", 'rb') as file_in, \
+        open(filename + ".dec", 'wb') as file_out:
+            bytes_dec, crc_dec = yenc.decode(file_in, file_out)
 
-        self.assertEquals(crc, crc_dec)
+        self.assertEqual(crc, crc_dec)
 
     def testDecodeE(self):
-        self._testDecodeFile(BaseTest.FILE_E)
+        self._testDecodeFile(self.FILE_E)
 
     def testDecodeO(self):
-        self._testDecodeFile(BaseTest.FILE_O)
+        self._testDecodeFile(self.FILE_O)
+    
+    def testCrcIn(self):
+        """Exercise yenc.decode(crc_in=...) parameter"""
+        with open(self.FILE_E, 'rb') as plain, \
+        open(self.FILE_E + ".out", 'w+b') as encoded, \
+        open(os.devnull, 'wb') as null:
+            _, crc = yenc.encode(plain, encoded)
+            
+            # Correct CRC
+            encoded.seek(0)
+            yenc.decode(encoded, null, crc_in=crc)
+            
+            # Incorrect CRC
+            crc = format(int(crc, 16) ^ 0xffffffff, "08x")
+            encoded.seek(0)
+            with self.assertRaises(yenc.Error):
+                yenc.decode(encoded, null, crc_in=crc)
 
 
-class TestEncoderDecoderOnFile(BaseTest, unittest.TestCase):
+class TestEncoderDecoderOnFile(BaseTest):
 
     def _testEncoderDecoder(self, filename):
         file_data, crc =  self._readFile(filename)
 
         file_out = open(filename + '.out', 'wb')
-        file_in = open(filename, 'rb')
-        
         encoder = yenc.Encoder(file_out)
-        data = file_in.read(BLOCK_SIZE)
-        while(len(data)):
-            encoder.feed(data)
+        
+        with open(filename, 'rb') as file_in:
             data = file_in.read(BLOCK_SIZE)
-        file_in.close()
+            while len(data):
+                encoder.feed(data)
+                data = file_in.read(BLOCK_SIZE)
         encoder.terminate()
         logging.info("orig: %s enc: %s" %(crc, encoder.getCrc32()))
-        self.assertEquals(crc, encoder.getCrc32())
+        self.assertEqual(crc, encoder.getCrc32())
 
         # deleting forces files to be flushed
         del encoder
 
-        file_in = open(filename + '.out', 'rb')
         file_out = open(filename + '.dec', 'wb')
-
         decoder = yenc.Decoder(file_out)
-        data = file_in.read(BLOCK_SIZE)
-        while(len(data) > 0):
-            decoder.feed(data)
+        
+        with open(filename + '.out', 'rb') as file_in:
             data = file_in.read(BLOCK_SIZE)
-        file_in.close()
+            while len(data) > 0:
+                decoder.feed(data)
+                data = file_in.read(BLOCK_SIZE)
         decoder.flush()
         logging.info("orig: %s dec: %s" %(crc, decoder.getCrc32()))
-        self.assertEquals(crc, decoder.getCrc32())
+        self.assertEqual(crc, decoder.getCrc32())
 
         # deleting forces files to be flushed
         # if __del__ is not called further tests are going to fail
         del decoder
 
         data_dec, crc_dec = self._readFile(filename + '.dec')
-        self.assertEquals(file_data, data_dec)
-        self.assertEquals(crc, crc_dec)
+        self.assertEqual(file_data, data_dec)
+        self.assertEqual(crc, crc_dec)
 
     def testEncoderDecoderE(self):
-        self._testEncoderDecoder(BaseTest.FILE_E)
+        self._testEncoderDecoder(self.FILE_E)
 
     def testEncoderDecoderO(self):
-        self._testEncoderDecoder(BaseTest.FILE_O)
+        self._testEncoderDecoder(self.FILE_O)
 
     def testEncoderClosed(self):
         encoder = yenc.Encoder(open('afile.out', 'wb'))
-        encoder.feed('some data')
+        encoder.feed(b'some data')
         encoder.close()
         self.assertFalse(encoder._output_file)
-        self.assertRaises(IOError, encoder.feed, ('some data'))
+        self.assertRaises(IOError, encoder.feed, b'some data')
  
     def testEncoderTerminated(self):
         encoder = yenc.Encoder(open('afile.out', 'wb'))
         encoder.terminate()
-        self.assertRaises(IOError, encoder.feed, ('some data'))
+        self.assertRaises(IOError, encoder.feed, b'some data')
 
     def testDecoderClose(self):
         decoder = yenc.Decoder(open('afile.out', 'wb'))
-        decoder.feed('some data')
+        decoder.feed(b'some data')
         decoder.close()
         self.assertFalse(decoder._output_file)
 
 
-class TestEncoderDecoderInMemory(BaseTest, unittest.TestCase):
+class TestEncoderDecoderInMemory(unittest.TestCase):
 
     def testEncodeInMemory(self):
         """ Checks simple encoding in memory
         """
         encoder = yenc.Encoder()
-        encoder.feed('Hello world!')
-        self.assertEquals('r\x8f\x96\x96\x99J\xa1\x99\x9c\x96\x8eK', encoder.getEncoded())
-        self.assertEquals(encoder.getCrc32(), "%08x" % (crc32("Hello world!") & 0xffffffff))
+        encoder.feed(b'Hello world!')
+        self.assertEqual(b'r\x8f\x96\x96\x99J\xa1\x99\x9c\x96\x8eK', encoder.getEncoded())
+        self.assertEqual(encoder.getCrc32(), "%08x" % (crc32(b"Hello world!") & 0xffffffff))
 
     def testEncodeAndWriteInMemory(self):
         pass
 
     def testDecodeInMemory(self):
         decoder = yenc.Decoder()
-        decoder.feed('r\x8f\x96\x96\x99J\xa1\x99\x9c\x96\x8eK')
-        self.assertEquals("Hello world!", decoder.getDecoded())
-        self.assertEquals(decoder.getCrc32(), "%08x" % (crc32("Hello world!") & 0xffffffff))
+        decoder.feed(b'r\x8f\x96\x96\x99J\xa1\x99\x9c\x96\x8eK')
+        self.assertEqual(b"Hello world!", decoder.getDecoded())
+        self.assertEqual(decoder.getCrc32(), "%08x" % (crc32(b"Hello world!") & 0xffffffff))
 
     def testDecodeAndWriteInMemory(self):
         pass
@@ -216,9 +235,13 @@
     def testDecodeFlushInMemory(self):
         decoder = yenc.Decoder()
         self.assertRaises(ValueError, decoder.flush)
-    
+
+
+class TestMisc(unittest.TestCase):
+    def testError(self):
+        format(yenc.Error())
 
 
 if __name__ == "__main__":
-        logging.basicConfig(level=logging.INFO)
-	unittest.main()
+    logging.basicConfig(level=logging.INFO)
+    unittest.main()
