Anonymous avatar Anonymous committed ada973f

current state

Comments (0)

Files changed (10)

add-unit-tests-for-doctests

+# HG changeset patch
+# Parent 803f384a3f3c45a1aa8449556810ceafce37faa0
+
+diff -r 803f384a3f3c babel/messages/tests/catalog.py
+--- a/babel/messages/tests/catalog.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/messages/tests/catalog.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -15,9 +15,12 @@
+ import datetime
+ import doctest
+ import unittest
++import re
+ import sys
+ 
+ from babel.messages import catalog
++from babel.messages.catalog import Catalog, Message
++from babel.util import UTC
+ 
+ 
+ class MessageTestCase(unittest.TestCase):
+@@ -294,12 +297,189 @@
+             if key in ('POT-Creation-Date', 'PO-Revision-Date'):
+                 self.assertEqual(value, '2009-03-09 15:47-0700')
+ 
++
++class ConvertedDocTestsForCatalog(unittest.TestCase):
++
++    def test___setitem__(self):
++        # Add or update the message with the specified ID.
++        catalog = Catalog()
++        catalog[u'foo'] = Message(u'foo')
++        self.assertEqual('<Message %s (flags: [])>' % repr(u'foo'), 
++                         repr(catalog[u'foo']))
++        
++        catalog = Catalog()
++        catalog[u'foo'] = Message(u'foo', locations=[('main.py', 1)])
++        self.assertEqual([('main.py', 1)], catalog[u'foo'].locations)
++        catalog[u'foo'] = Message(u'foo', locations=[('utils.py', 5)])
++        self.assertEqual([('main.py', 1), ('utils.py', 5)], catalog[u'foo'].locations)
++        
++    def test_add(self):
++        # Add or update the message with the specified ID.
++        catalog = Catalog()
++        m = catalog.add(u'foo')
++        self.assertNotEqual(None, re.match('^<Message .*>$', repr(m)))
++        self.assertEqual(repr(catalog[u'foo']), '<Message %s (flags: [])>' % repr(u'foo'))
++        
++    def test_header_comment(self):
++        # The header comment for the catalog.
++        catalog = Catalog(project='Foobar', version='1.0', copyright_holder='Foo Company')
++        header_comment_regexp = u"""\
++^# Translations template for Foobar\.$(?:\\n|\\r\\n?)\
++^# Copyright \(C\) .*? Foo Company$(?:\\n|\\r\\n?)\
++^# This file is distributed under the same license as the Foobar project\.$(?:\\n|\\r\\n?)\
++^# FIRST AUTHOR <EMAIL@ADDRESS>, .*$(?:\\n|\\r\\n?)\
++^#\
++"""
++        matched = re.match(header_comment_regexp, catalog.header_comment, re.MULTILINE)
++        self.assertNotEqual(None, matched)
++
++        catalog = Catalog(project='Foobar', version='1.0', copyright_holder='Foo Company')
++        catalog.header_comment = """\
++# The POT for my really cool PROJECT project.
++# Copyright (C) 1990-2003 ORGANIZATION
++# This file is distributed under the same license as the PROJECT
++# project.
++#
++"""
++        header_comment_regexp = """\
++^# The POT for my really cool Foobar project.$(?:\\n|\\r\\n?)\
++^# Copyright \(C\) 1990-2003 Foo Company$(?:\\n|\\r\\n?)\
++^# This file is distributed under the same license as the Foobar$(?:\\n|\\r\\n?)\
++^# project.$(?:\\n|\\r\\n?)\
++^#\
++"""
++        matched = re.match(header_comment_regexp, catalog.header_comment, re.MULTILINE)
++        self.assertNotEqual(None, matched)
++
++    def test_mime_headers(self):
++        # The MIME headers of the catalog, used for the special ``msgid ""`` entry.
++        created = datetime.datetime(1990, 4, 1, 15, 30, tzinfo=UTC)
++        catalog = Catalog(project='Foobar', version='1.0',
++                          creation_date=created)
++        headers = ""
++        for name, value in catalog.mime_headers:
++            headers += '%s: %s\n' % (name, value)
++        mime_header_regexp = \
++u"""\
++^Project-Id-Version: Foobar 1\.0$(?:\\n|\\r\\n?)\
++^Report-Msgid-Bugs-To: EMAIL@ADDRESS$(?:\\n|\\r\\n?)\
++^POT-Creation-Date: 1990-04-01 15:30\+0000$(?:\\n|\\r\\n?)\
++^PO-Revision-Date: YEAR-MO-DA HO:MI\+ZONE$(?:\\n|\\r\\n?)\
++^Last-Translator: FULL NAME <EMAIL@ADDRESS>$(?:\\n|\\r\\n?)\
++^Language-Team: LANGUAGE <LL@li\.org>$(?:\\n|\\r\\n?)\
++^MIME-Version: 1\.0$(?:\\n|\\r\\n?)\
++^Content-Type: text/plain; charset=utf-8$(?:\\n|\\r\\n?)\
++^Content-Transfer-Encoding: 8bit$(?:\\n|\\r\\n?)\
++^Generated-By: Babel .*$(?:\\n|\\r\\n?)\
++"""
++        matched = re.match(mime_header_regexp, headers, re.MULTILINE)
++        self.assertNotEqual(None, matched)
++
++        revised = datetime.datetime(1990, 8, 3, 12, 0, tzinfo=UTC)
++        catalog = Catalog(locale='de_DE', project='Foobar', version='1.0',
++                          creation_date=created, revision_date=revised,
++                          last_translator='John Doe <jd@example.com>',
++                          language_team='de_DE <de@example.com>')
++        headers = ""
++        for name, value in catalog.mime_headers:
++            headers += '%s: %s\n' % (name, value)
++        mime_header_regexp = \
++u"""\
++^Project-Id-Version: Foobar 1\.0$(?:\\n|\\r\\n?)\
++^Report-Msgid-Bugs-To: EMAIL@ADDRESS$(?:\\n|\\r\\n?)\
++^POT-Creation-Date: 1990-04-01 15:30\+0000$(?:\\n|\\r\\n?)\
++^PO-Revision-Date: 1990-08-03 12:00\+0000$(?:\\n|\\r\\n?)\
++^Last-Translator: John Doe <jd@example\.com>$(?:\\n|\\r\\n?)\
++^Language-Team: de_DE <de@example\.com>$(?:\\n|\\r\\n?)\
++^Plural-Forms: nplurals=2; plural=\(n != 1\)$(?:\\n|\\r\\n?)\
++^MIME-Version: 1.0$(?:\\n|\\r\\n?)\
++^Content-Type: text/plain; charset=utf-8$(?:\\n|\\r\\n?)\
++^Content-Transfer-Encoding: 8bit$(?:\\n|\\r\\n?)\
++^Generated-By: Babel .*$(?:\\n|\\r\\n?)\
++"""
++        matched = re.match(mime_header_regexp, headers, re.MULTILINE)
++        self.assertNotEqual(None, matched)
++        
++    def test_num_plurals(self):
++        # The number of plurals used by the catalog or locale.
++        self.assertEqual(2, Catalog(locale='en').num_plurals)
++        self.assertEqual(3, Catalog(locale='ga').num_plurals)
++
++    def test_plural_expr(self):
++        # The plural expression used by the catalog or locale.
++        self.assertEqual('(n != 1)', Catalog(locale='en').plural_expr)
++        self.assertEqual('(n==1 ? 0 : n==2 ? 1 : 2)', Catalog(locale='ga').plural_expr)
++
++    def test_plural_forms(self):
++        # Return the plural forms declaration for the locale.
++        self.assertEqual('nplurals=2; plural=(n != 1)', Catalog(locale='en').plural_forms)
++        self.assertEqual('nplurals=2; plural=(n > 1)', Catalog(locale='pt_BR').plural_forms)
++
++    def test_update(self):
++        # Update the catalog based on the given template catalog.
++        template = Catalog()
++        m = template.add('green', locations=[('main.py', 99)])
++        self.assertNotEqual(None, re.match('^<Message .*>$', repr(m)))
++        m = template.add('blue', locations=[('main.py', 100)])
++        self.assertNotEqual(None, re.match('^<Message .*>$', repr(m)))
++        m = template.add(('salad', 'salads'), locations=[('util.py', 42)])
++        self.assertNotEqual(None, re.match('^<Message .*>$', repr(m)))
++        
++        catalog = Catalog(locale='de_DE')
++        m = catalog.add('blue', u'blau', locations=[('main.py', 98)])
++        self.assertNotEqual(None, re.match('^<Message .*>$', repr(m)))
++        m = catalog.add('head', u'Kopf', locations=[('util.py', 33)])
++        self.assertNotEqual(None, re.match('^<Message .*>$', repr(m)))
++        m = catalog.add(('salad', 'salads'), (u'Salat', u'Salate'),
++                        locations=[('util.py', 38)])
++        self.assertNotEqual(None, re.match('^<Message .*>$', repr(m)))
++
++        catalog.update(template)
++        self.assertEqual(3, len(catalog))
++
++        msg1 = catalog['green']
++        self.assertEqual(None, msg1.string)
++        self.assertEqual([('main.py', 99)], msg1.locations)
++
++        msg2 = catalog['blue']
++        self.assertEqual(u'blau', msg2.string)
++        self.assertEqual([('main.py', 100)], msg2.locations)
++
++        msg3 = catalog['salad']
++        self.assertEqual((u'Salat', u'Salate'), msg3.string)
++        self.assertEqual([('util.py', 42)], msg3.locations)
++        self.assertFalse('head' in catalog)
++        self.assertEqual('[<Message \'head\' (flags: [])>]', repr(catalog.obsolete.values()))
++
++
++class ConvertedDocTestsForMessage(unittest.TestCase):
++
++    def test_fuzzy(self):
++        # Whether the translation is fuzzy.
++        self.assertFalse(Message('foo').fuzzy)
++        msg = Message('foo', 'foo', flags=['fuzzy'])
++        self.assertTrue(msg.fuzzy)
++        self.assertEqual("<Message 'foo' (flags: ['fuzzy'])>", repr(msg))
++
++    def test_pluralizable(self):
++        # Whether the message is plurizable.
++        self.assertFalse(Message('foo').pluralizable)
++        self.assertTrue(Message(('foo', 'bar')).pluralizable)
++
++    def test_python_format(self):
++        # Whether the message contains Python-style parameters.
++        self.assertTrue(Message('foo %(name)s bar').python_format)
++        self.assertTrue(Message(('foo %(name)s', 'foo %(name)s')).python_format)
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+         suite.addTest(doctest.DocTestSuite(catalog, optionflags=doctest.ELLIPSIS))
+     suite.addTest(unittest.makeSuite(MessageTestCase))
+     suite.addTest(unittest.makeSuite(CatalogTestCase))
++    suite.addTest(unittest.makeSuite(ConvertedDocTestsForMessage))
++    suite.addTest(unittest.makeSuite(ConvertedDocTestsForCatalog))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/messages/tests/extract.py
+--- a/babel/messages/tests/extract.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/messages/tests/extract.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -533,6 +533,22 @@
+             sys.stderr = stderr
+ 
+ 
++class ConvertedDocTestsForExtract(unittest.TestCase):
++    def test_extract(self):
++        # Extract messages from the given file-like object using the specified
++        # extraction method.
++        source = bytes('''\
++# foo module
++def run(argv):
++    print _('Hello, world!')
++''')
++        msgcnt = 0
++        for message in extract.extract('python', BytesIO(source)):
++            msgcnt += 1
++        self.assertEqual(1, msgcnt)
++        self.assertEqual((3, u'Hello, world!', [], None), message)
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+@@ -540,6 +556,7 @@
+     suite.addTest(unittest.makeSuite(ExtractPythonTestCase))
+     suite.addTest(unittest.makeSuite(ExtractJavaScriptTestCase))
+     suite.addTest(unittest.makeSuite(ExtractTestCase))
++    suite.addTest(unittest.makeSuite(ConvertedDocTestsForExtract))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/messages/tests/frontend.py
+--- a/babel/messages/tests/frontend.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/messages/tests/frontend.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -685,6 +685,7 @@
+ 
+     def test_extract_with_mapping_file(self):
+         pot_file = self._pot_file()
++        print pot_file
+         self.cli.run(sys.argv + ['extract',
+             '--copyright-holder', 'FooBar, Inc.',
+             '--project', 'TestProject', '--version', '0.1',
+@@ -777,6 +778,7 @@
+        'date': format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ',
+                                tzinfo=LOCALTZ, locale='en')},
+        open(po_file, 'U').read())
++       # Test failure because of bad sorting!
+ 
+     def  _i18n_dir(self):
+         return os.path.join(self.datadir, 'project', 'i18n')
+@@ -919,6 +921,48 @@
+             if os.path.isfile(mo_file):
+                 os.unlink(mo_file)
+ 
++                
++class ConvertedDocTestsForFrontend(unittest.TestCase):
++    def test_messages_frontend_parse_keywords(self):
++        kw = frontend.parse_keywords(['_', 'dgettext:2', 'dngettext:2,3']).items()
++        kw.sort()
++        self.assertEqual(len(kw), 3)
++        self.assertEqual(kw[0],('_', None))
++        self.assertEqual(kw[1],('dgettext', (2,)))
++        self.assertEqual(kw[2],('dngettext', (2, 3)))
++ 
++    def test_messages_frontend_parse_mapping(self):
++        # Parse an extraction method mapping from a file-like object.
++        buf = StringIO('''
++[extractors]
++custom = mypackage.module:myfunc
++ 
++# Python source files
++[python: **.py]
++
++# Genshi templates
++[genshi: **/templates/**.html]
++include_attrs =
++[genshi: **/templates/**.txt]
++template_class = genshi.template:TextTemplate
++encoding = latin-1
++
++# Some custom extractor
++[custom: **/custom/*.*]
++''')
++
++        method_map, options_map = frontend.parse_mapping(buf)
++        self.assertEqual(len(method_map), 4)
++        self.assertEqual(method_map[0], ('**.py', 'python'))
++        self.assertEqual(options_map['**.py'],  {})
++        self.assertEqual(method_map[1], ('**/templates/**.html', 'genshi'))
++        self.assertEqual(options_map['**/templates/**.html']['include_attrs'], '')
++        self.assertEqual(method_map[2], ('**/templates/**.txt', 'genshi'))
++        self.assertEqual(options_map['**/templates/**.txt']['template_class'], 'genshi.template:TextTemplate')
++        self.assertEqual(options_map['**/templates/**.txt']['encoding'], 'latin-1')
++        self.assertEqual(method_map[3], ('**/custom/*.*', 'mypackage.module:myfunc'))
++        self.assertEqual(options_map['**/custom/*.*'], {})
++
+ 
+ def suite():
+     suite = unittest.TestSuite()
+@@ -928,6 +972,7 @@
+     suite.addTest(unittest.makeSuite(ExtractMessagesTestCase))
+     suite.addTest(unittest.makeSuite(InitCatalogTestCase))
+     suite.addTest(unittest.makeSuite(CommandLineInterfaceTestCase))
++    suite.addTest(unittest.makeSuite(ConvertedDocTestsForFrontend))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/messages/tests/plurals.py
+--- a/babel/messages/tests/plurals.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/messages/tests/plurals.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -17,10 +17,24 @@
+ 
+ from babel.messages import plurals
+ 
++class ConvertedDoctestsForPlural(unittest.TestCase):
++
++    def test_messages_plurals_get_plural(self):
++        self.assertEqual(plurals.get_plural(locale='en'), (2, '(n != 1)'))
++        self.assertEqual(plurals.get_plural(locale='ga'), (3, '(n==1 ? 0 : n==2 ? 1 : 2)'))
++    
++        tup = plurals.get_plural("ja")
++        self.assertEqual(tup.num_plurals, 1)
++        self.assertEqual(tup.plural_expr, '0')
++        self.assertEqual(tup.plural_forms, 'npurals=1; plural=0')
++        self.assertEqual(str(tup), 'npurals=1; plural=0')
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+         suite.addTest(doctest.DocTestSuite(plurals))
++    suite.addTest(unittest.makeSuite(ConvertedDoctestsForPlural))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/messages/tests/pofile.py
+--- a/babel/messages/tests/pofile.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/messages/tests/pofile.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -152,7 +152,7 @@
+         self.assertEqual(['This is an obsolete message'], message.user_comments)
+ 
+     def test_obsolete_message_ignored(self):
+-        buf = StringIO(r'''# This is an obsolete message
++        buf = BytesIO(bytes(r'''# This is an obsolete message
+ #~ msgid "foo"
+ #~ msgstr "Voh"
+ 
+@@ -160,7 +160,7 @@
+ #: main.py:1
+ msgid "bar"
+ msgstr "Bahr"
+-''')
++'''))
+         catalog = pofile.read_po(buf, ignore_obsolete=True)
+         self.assertEqual(1, len(catalog))
+         self.assertEqual(0, len(catalog.obsolete))
+@@ -526,6 +526,191 @@
+         self.assertEqual(catalog['missing line number'].locations, [])
+         self.assertEqual(catalog['broken line number'].locations, [])
+ 
++
++class messagesPofileDocTest(unittest.TestCase):
++    
++    def _test_messages_pofile_detect_encoding(self):
++        # Detect iso-8859-1 encoded file.
++        buf = u'''
++msgid ""
++msgstr ""
++"Project-Id-Version:  3.15\\n"
++"Report-Msgid-Bugs-To: Fliegender Zirkus <fliegender@zirkus.de>\\n"
++"POT-Creation-Date: 2007-09-27 11:19+0700\\n"
++"PO-Revision-Date: 2007-09-27 21:42-0700\\n"
++"Last-Translator: John <cleese@bavaria.de>\\n"
++"Language-Team: German Lang <de@babel.org>\\n"
++"Plural-Forms: nplurals=2; plural=(n != 1)\\n"
++"MIME-Version: 1.0\\n"
++"Content-Type: text/plain; charset=iso-8859-1\\n"
++"Content-Transfer-Encoding: 8bit\\n"
++"Generated-By: Babel 1.0dev-r313\\n"
++
++msgid "foo"
++msgstr "bär"'''.encode('iso-8859-1')
++
++        self.assertEqual(pofile.detect_encoding(buf, 'utf-8'), 'iso-8859-1')
++
++        # Detect utf-8 encoded file.
++        buf = u'''
++msgid ""
++msgstr ""
++"Project-Id-Version:  3.15\\n"
++"Report-Msgid-Bugs-To: Fliegender Zirkus <fliegender@zirkus.de>\\n"
++"POT-Creation-Date: 2007-09-27 11:19+0700\\n"
++"PO-Revision-Date: 2007-09-27 21:42-0700\\n"
++"Last-Translator: John <cleese@bavaria.de>\\n"
++"Language-Team: German Lang <de@babel.org>\\n"
++"Plural-Forms: nplurals=2; plural=(n != 1)\\n"
++"MIME-Version: 1.0\\n"
++"Content-Type: text/plain; charset=utf-8\\n"
++"Content-Transfer-Encoding: 8bit\\n"
++"Generated-By: Babel 1.0dev-r313\\n"
++
++msgid "foo"
++msgstr "bär"'''.encode('utf-8')
++        self.assertEqual(pofile.detect_encoding(buf, 'iso-8859-1'), 'utf-8')
++
++        # Detect iso-8859-1 default encoded file.
++        buf = u'''
++msgid ""
++msgstr ""
++"Project-Id-Version:  3.15\\n"
++"Report-Msgid-Bugs-To: Fliegender Zirkus <fliegender@zirkus.de>\\n"
++"POT-Creation-Date: 2007-09-27 11:19+0700\\n"
++"PO-Revision-Date: 2007-09-27 21:42-0700\\n"
++"Last-Translator: John <cleese@bavaria.de>\\n"
++"Language-Team: German Lang <de@babel.org>\\n"
++"Plural-Forms: nplurals=2; plural=(n != 1)\\n"
++"MIME-Version: 1.0\\n"
++"Content-Transfer-Encoding: 8bit\\n"
++"Generated-By: Babel 1.0dev-r313\\n"
++
++msgid "foo"
++msgstr "bär"'''.encode('iso-8859-1')
++
++        self.assertEqual(pofile.detect_encoding(buf, 'iso-8859-1'), 'iso-8859-1')
++
++        # Detect invalid encoding default to utf-8.
++        buf = u'''
++msgid ""
++msgstr ""
++"Project-Id-Version:  3.15\\n"
++"Report-Msgid-Bugs-To: Fliegender Zirkus <fliegender@zirkus.de>\\n"
++"POT-Creation-Date: 2007-09-27 11:19+0700\\n"
++"PO-Revision-Date: 2007-09-27 21:42-0700\\n"
++"Last-Translator: John <cleese@bavaria.de>\\n"
++"Language-Team: German Lang <de@babel.org>\\n"
++"Plural-Forms: nplurals=2; plural=(n != 1)\\n"
++"MIME-Version: 1.0\\n"
++"Content-Type: text/plain; charset=something-not-real\\n"
++"Content-Transfer-Encoding: 8bit\\n"
++"Generated-By: Babel 1.0dev-r313\\n"
++
++msgid "foo"
++msgstr "bär"'''.encode('utf-8')
++
++        self.assertEqual(pofile.detect_encoding(buf, 'utf-8'), 'utf-8')
++
++
++    def test_messages_pofile_unescape(self):
++        escaped = ur'"Say:\\n  \\"hello, world!\\"\\n"'
++        unescaped = u'''Say:
++  "hello, world!"
++'''
++        self.assertNotEqual(escaped, unescaped)
++        self.assertEqual(pofile.unescape(escaped), unescaped)
++
++    def test_messages_pofile_denormalize(self):
++        denormal = ur'''""
++"Say:\n"
++"  \"hello, world!\"\n"'''
++        normal = u'''Say:
++  "hello, world!"
++'''
++        self.assertNotEqual(denormal, normal)
++        self.assertEqual(pofile.denormalize(denormal), normal)
++
++        denormal = ur'''""
++"Say:\n"
++"  \"Lorem ipsum dolor sit "
++"amet, consectetur adipisicing"
++" elit, \"\n"'''
++        normal = u'''Say:
++  "Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
++'''
++        self.assertNotEqual(denormal, normal)
++        self.assertEqual(pofile.denormalize(denormal), normal)
++
++    def test_messages_pofile_read_po(self):
++        buf = StringIO('''
++#: main.py:1
++#, fuzzy, python-format
++msgid "foo %(name)s"
++msgstr ""
++
++# A user comment
++#. An auto comment
++#: main.py:3
++msgid "bar"
++msgid_plural "baz"
++msgstr[0] ""
++msgstr[1] ""
++''')
++        catalog = pofile.read_po(buf)
++        catalog.revision_date = datetime(2007, 04, 01)
++        i = 0
++        for message in catalog:
++            if message.id:
++                i += 1
++                if i == 1:
++                    self.assertEqual(message.id, u'foo %(name)s')
++                    self.assertEqual(message.string, u'')
++                    self.assertEqual(message.locations, [(u'main.py', 1)])
++                    self.assertEqual(message.flags, set([u'fuzzy', u'python-format']))
++                    self.assertEqual(message.user_comments, [])
++                    self.assertEqual(message.auto_comments, [])
++                elif i == 2:
++                    self.assertEqual(message.id, (u'bar', u'baz'))
++                    self.assertEqual(message.string, (u'', u''))
++                    self.assertEqual(message.locations, [(u'main.py', 3)])
++                    self.assertEqual(message.flags, set([]))
++                    self.assertEqual(message.user_comments, [u'A user comment'])
++                    self.assertEqual(message.auto_comments, [u'An auto comment'])
++                else:
++                    self.fail('Too many messages in catalog')
++
++    def test_messages_pofile_escape(self):
++        self.assertEqual(pofile.escape('Say:\n  "hello, world!"\n'), '"Say:\\n  \\"hello, world!\\"\\n"')
++
++    def test_messages_pofile_normalize(self):
++        self.assertEqual(pofile.normalize('Say:\n  "hello, world!"\n', width=None),
++                        '""\n"Say:\\n"\n"  \\"hello, world!\\"\\n"')
++
++        self.assertEqual(pofile.normalize('Say:\n  "Lorem ipsum dolor sit amet, consectetur adipisicing elit, "\n', 
++                            width=32), 
++                        '""\n"Say:\\n"\n"  \\"Lorem ipsum dolor sit "\n"amet, consectetur adipisicing"\n" elit, \\"\\n"')
++
++    def test_messages_pofile_write_po(self):
++        catalog = Catalog()
++        catalog.add(u'foo %(name)s', locations=[('main.py', 1)], flags=('fuzzy',))
++        catalog.add((u'bar', u'baz'), locations=[('main.py', 3)])
++        buf = BytesIO()
++        pofile.write_po(buf, catalog, omit_header=True)
++        self.assertEqual(buf.getvalue(), bytes('''#: main.py:1
++#, fuzzy, python-format
++msgid "foo %(name)s"
++msgstr ""
++
++#: main.py:3
++msgid "bar"
++msgid_plural "baz"
++msgstr[0] ""
++msgstr[1] ""
++
++'''))
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     import sys
+@@ -533,6 +718,7 @@
+         suite.addTest(doctest.DocTestSuite(pofile, optionflags=doctest.ELLIPSIS))
+     suite.addTest(unittest.makeSuite(ReadPoTestCase))
+     suite.addTest(unittest.makeSuite(WritePoTestCase))
++    suite.addTest(unittest.makeSuite(messagesPofileDocTest))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/tests/core.py
+--- a/babel/tests/core.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/tests/core.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -18,6 +18,7 @@
+ 
+ from babel import core
+ from babel.core import default_locale
++from babel.core import UnknownLocaleError,Locale,default_locale,get_global,negotiate_locale,parse_locale
+ 
+ class DefaultLocaleTest(unittest.TestCase):
+     
+@@ -45,11 +46,260 @@
+         # must not throw an exception
+         default_locale('LC_CTYPE')
+ 
++
++class coreDocTest(unittest.TestCase):
++
++    def test_core_Locale(self):
++        # Representation of a specific locale.
++        locale = Locale('en', 'US')
++        self.assertEqual(repr(locale),'<Locale "en_US">')
++        self.assertEqual(locale.display_name, u'English (United States)')
++        # A `Locale` object can also be instantiated from a raw locale string:
++        locale = Locale.parse('en-US', sep='-')
++        self.assertEqual(repr(locale),'<Locale "en_US">')
++        # `Locale` objects provide access to a collection of locale data, such as
++        #territory and language names, number and date format patterns, and more:
++        self.assertEqual(locale.number_symbols['decimal'], u'.')
++        # If a locale is requested for which no locale data is available, an
++        # `UnknownLocaleError` is raised:
++        self.assertRaises(UnknownLocaleError, Locale.parse, 'en_DE')
++
++    def test_core_Locale__init__(self):
++        #Initialize the locale object from the given identifier components.
++        locale = Locale('en', 'US')
++        self.assertEqual(locale.language, 'en')
++        self.assertEqual(locale.territory, 'US')
++
++    def test_core_Locale_currencies(self):
++        # Mapping of currency codes to translated currency names.
++        self.assertEqual(Locale('en').currencies['COP'], u'Colombian Peso')
++        self.assertEqual(Locale('de', 'DE').currencies['COP'], u'Kolumbianischer Peso')
++
++    def test_core_Locale_currency_formats(self):
++        # Locale patterns for currency number formatting.
++        
++        if sys.version_info >=(3, 0):
++            # This test should also work for Py2 as fare as I can tell, but fails because
++            # for some reason repr() on py2 is returning "<NumberPattern u'\\xa4#,##0.00'>"
++            self.assertEqual(repr(Locale('en', 'US').currency_formats[None]) ,'<NumberPattern %s>' % repr(u'\xa4#,##0.00'))
++        else:
++            self.assertEqual(Locale('en', 'US').currency_formats[None].pattern, u'\xa4#,##0.00' )
++        
++    def test_core_Locale_currency_symbols(self):
++        #Mapping of currency codes to symbols.
++        self.assertEqual(Locale('en', 'US').currency_symbols['USD'], u'$')
++        self.assertEqual(Locale('es', 'CO').currency_symbols['USD'], u'US$')
++
++    def test_core_Locale_date_formats(self):
++        # Locale patterns for date formatting.
++        self.assertEqual(repr(Locale('en', 'US').date_formats['short']), '<DateTimePattern %s>' % repr(u'M/d/yy'))
++        self.assertEqual(repr(Locale('fr', 'FR').date_formats['long']), '<DateTimePattern %s>' % repr(u'd MMMM y'))
++
++    def test_core_Locale_datetime_formats(self):
++        # Locale patterns for datetime formatting.
++        self.assertEqual(Locale('en').datetime_formats['full'], u'{1} {0}')
++        self.assertEqual(Locale('th').datetime_formats['medium'], u'{1}, {0}')
++
++    def test_core_Locale_days(self):
++        # Locale display names for weekdays.
++        self.assertEqual(Locale('de', 'DE').days['format']['wide'][3], u'Donnerstag')
++
++    def test_core_Locale_decimal_formats(self):
++        # Locale patterns for decimal number formatting.
++        self.assertEqual(repr(Locale('en', 'US').decimal_formats[None]), '<NumberPattern %s>' % repr(u'#,##0.###'))
++
++    def test_core_Locale_default(self):
++        # Return the system default locale for the specified category.
++        # Only clear down keys that exists, dont create ones that dont.
++        # Also, only set the first one we find.
++        # As opposed to the doctest whch arbitrarily makes all of these
++        # as valid keys in os.environ which masked a test error later on.
++        langset = False
++        for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG', 'LC_MESSAGES']:
++            if name in os.environ:
++                if not langset:
++                    os.environ[name] = 'fr_FR.UTF-8'
++                    langset = True
++                else:   
++                    os.environ[name] = ''
++        self.assertEqual(langset, True)
++        self.assertEqual(repr(Locale.default('LC_MESSAGES')), '<Locale "fr_FR">')
++        os.environ['LC_MESSAGES'] = 'de_DE.UTF-8'
++        self.assertEqual(repr(Locale.default('LC_MESSAGES')), '<Locale "de_DE">')
++
++    def test_core_Locale_display_name(self):
++        #The localized display name of the locale.
++        self.assertEqual(Locale('en').display_name, u'English')
++        self.assertEqual(Locale('en', 'US').display_name, u'English (United States)')
++        self.assertEqual(Locale('sv').display_name, u'svenska')
++
++    def test_core_Locale_english_name(self):
++        # The english display name of the locale.
++        self.assertEqual(Locale('de').english_name, u'German')
++        self.assertEqual(Locale('de', 'DE').english_name, u'German (Germany)')
++
++    def test_core_Locale_eras(self):
++        # Locale display names for eras.
++        self.assertEqual(Locale('en', 'US').eras['wide'][1], u'Anno Domini')
++        self.assertEqual(Locale('en', 'US').eras['abbreviated'][0], u'BC')
++
++    def test_core_Locale_first_week_day(self):
++        # The first day of a week, with 0 being Monday.
++        self.assertEqual(Locale('de', 'DE').first_week_day, 0)
++        self.assertEqual(Locale('en', 'US').first_week_day, 6)
++
++    def test_core_Locale_get_display_name(self):
++        # Return the display name of the locale using the given locale.
++        self.assertEqual(Locale('zh', 'CN', script='Hans').get_display_name('en'), u'Chinese (Simplified Han, China)')
++
++    def test_core_Locale_languages(self):
++        # Mapping of language codes to translated language names.
++        self.assertEqual(Locale('de', 'DE').languages['ja'], u'Japanisch')
++
++    def test_core_Locale_meta_zones(self):
++        # Locale display names for meta time zones.
++        self.assertEqual(Locale('en', 'US').meta_zones['Europe_Central']['long']['daylight'], u'Central European Summer Time')
++
++    def test_core_Locale_min_week_days(self):
++        # The minimum number of days in a week so that the week is counted as the
++        # first week of a year or month.
++        self.assertEqual(Locale('de', 'DE').min_week_days, 4)
++
++    def test_core_Locale_months(self):
++        # Locale display names for months.
++        self.assertEqual(Locale('de', 'DE').months['format']['wide'][10], u'Oktober')
++
++    def test_core_Locale_negotiate(self):
++        # Find the best match between available and requested locale strings.
++        
++        self.assertEqual(repr(Locale.negotiate(['de_DE', 'en_US'], ['de_DE', 'de_AT'])), '<Locale "de_DE">')
++        self.assertEqual(repr(Locale.negotiate(['de_DE', 'en_US'], ['en', 'de'])), '<Locale "de">')
++        self.assertEqual(Locale.negotiate(['de_DE', 'de'], ['en_US']), None)
++        self.assertEqual(repr(Locale.negotiate(['de-DE', 'de'], ['en-us', 'de-de'], sep='-')), '<Locale "de_DE">')
++
++    def test_core_Locale_number_symbols(self):
++        #Symbols used in number formatting.
++        self.assertEqual(Locale('fr', 'FR').number_symbols['decimal'], u',')
++
++    def test_core_Locale_parse(self):
++        #Create a `Locale` instance for the given locale identifier.
++        l = Locale.parse('de-DE', sep='-')
++        self.assertEqual(l.display_name, u'Deutsch (Deutschland)')
++        self.assertEqual(Locale.parse(l),l)
++
++    def test_core_Locale_percent_formats(self):
++        # Locale patterns for percent number formatting.
++        self.assertEqual(repr(Locale('en', 'US').percent_formats[None]), '<NumberPattern %s>'% repr(u'#,##0%'))
++
++    def test_core_Locale_periods(self):
++        # Locale display names for day periods (AM/PM).
++        self.assertEqual(Locale('en', 'US').periods['am'], u'AM')
++
++    def test_core_Locale_plural_form(self):
++        # Plural rules for the locale.
++        self.assertEqual(Locale('en').plural_form(1), 'one')
++        self.assertEqual(Locale('en').plural_form(0), 'other')
++        self.assertEqual(Locale('fr').plural_form(0), 'one')
++        self.assertEqual(Locale('ru').plural_form(100), 'many')
++
++    def test_core_Locale_quarters(self):
++        # Locale display names for quarters.
++        self.assertEqual(Locale('de', 'DE').quarters['format']['wide'][1], u'1. Quartal')
++
++    def test_core_Locale_scientific_formats(self):
++        # Locale patterns for scientific number formatting.
++        self.assertEqual(repr(Locale('en', 'US').scientific_formats[None]), '<NumberPattern %s>'% repr(u'#E0'))
++
++    def test_core_Locale_scripts(self):
++        # Mapping of script codes to translated script names.
++        self.assertEqual(Locale('en', 'US').scripts['Hira'], u'Hiragana')
++
++    def test_core_Locale_territories(self):
++        # Mapping of script codes to translated script names.
++        self.assertEqual(Locale('es', 'CO').territories['DE'], u'Alemania')
++
++    def test_core_Locale_time_formats(self):
++        # Locale patterns for time formatting.
++        self.assertEqual(repr(Locale('en', 'US').time_formats['short']), '<DateTimePattern %s>' % repr(u'h:mm a'))
++        self.assertEqual(repr(Locale('fr', 'FR').time_formats['long']), '<DateTimePattern %s>' % repr(u'HH:mm:ss z'))
++
++    def test_core_Locale_time_zones(self):
++        # Locale display names for time zones.
++        self.assertEqual(Locale('en', 'US').time_zones['Europe/London']['long']['daylight'], u'British Summer Time')
++        self.assertEqual(Locale('en', 'US').time_zones['America/St_Johns']['city'], u"St. John's")
++
++    def test_core_Locale_variants(self):
++        # Mapping of script codes to translated script names.
++        self.assertEqual(Locale('de', 'DE').variants['1901'], u'Alte deutsche Rechtschreibung')
++
++    def test_core_Locale_weekend_end(self):
++        #The day the weekend ends, with 0 being Monday.
++        self.assertEqual(Locale('de', 'DE').weekend_end, 6)
++
++    def test_core_Locale_weekend_start(self):
++        # The day the weekend starts, with 0 being Monday.
++        self.assertEqual(Locale('de', 'DE').weekend_start, 5)
++
++    def test_core_Locale_zone_formats(self):
++        # Patterns related to the formatting of time zones.
++        self.assertEqual(Locale('en', 'US').zone_formats['fallback'], u'%(1)s (%(0)s)')
++        self.assertEqual(Locale('pt', 'BR').zone_formats['region'], u'Hor\xe1rio %s')
++        
++    def test_core_default_locale(self):
++        # Returns the system default locale for a given category, based on
++        # environment variables.
++
++        # Return the system default locale for the specified category.
++        # Only clear down keys that exists, dont create ones that dont.
++        # Also, only set the first one we find.
++        # As opposed to the doctest whch arbitrarily makes all of these
++        # as valid keys in os.environ which masked a test error later on.
++        langset = False
++        for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG', 'LC_MESSAGES']:
++            if name in os.environ:
++                if not langset:
++                    os.environ[name] = 'fr_FR.UTF-8'
++                    langset = True
++                else:   
++                    os.environ[name] = ''
++        self.assertEqual(langset, True)
++        self.assertEqual(repr(Locale.default('LC_MESSAGES')), '<Locale "fr_FR">')
++        
++        os.environ['LC_MESSAGES'] = 'POSIX'
++        self.assertEqual(default_locale('LC_MESSAGES'), 'en_US_POSIX')
++
++    def test_core_get_global(self):
++        # Return the dictionary for the given key in the global data.
++        self.assertEqual(get_global('zone_aliases')['UTC'], 'Etc/GMT')
++        self.assertEqual(get_global('zone_territories')['Europe/Berlin'], 'DE')
++
++    def test_core_negotiate_locale(self):
++        # Find the best match between available and requested locale strings.
++        self.assertEqual(negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT']), 'de_DE')
++        self.assertEqual(negotiate_locale(['de_DE', 'en_US'], ['en', 'de']), 'de')
++        self.assertEqual(negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']), 'de_DE')
++        self.assertEqual(negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']), 'de_DE')
++        self.assertEqual(negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US']), 'ja_JP')
++        self.assertEqual(negotiate_locale(['no', 'sv'], ['nb_NO', 'sv_SE']), 'nb_NO')
++
++    def test_core_parse_locale(self):
++        # Parse a locale identifier into a tuple of the form:
++        # ``(language, territory, script, variant)``
++        self.assertEqual(parse_locale('zh_CN'), ('zh', 'CN', None, None))
++        self.assertEqual(parse_locale('zh_Hans_CN'), ('zh', 'CN', 'Hans', None))
++        self.assertEqual(parse_locale('zh-CN', sep='-'), ('zh', 'CN', None, None))
++        self.assertRaises(ValueError, parse_locale, 'not_a_LOCALE_String')
++        self.assertEqual(parse_locale('it_IT@euro'), ('it', 'IT', None, None))
++        self.assertEqual(parse_locale('en_US.UTF-8'), ('en', 'US', None, None))
++        self.assertEqual(parse_locale('de_DE.iso885915@euro'), ('de', 'DE', None, None))
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+         suite.addTest(doctest.DocTestSuite(core))
+     suite.addTest(unittest.makeSuite(DefaultLocaleTest))
++    suite.addTest(unittest.makeSuite(coreDocTest))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/tests/dates.py
+--- a/babel/tests/dates.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/tests/dates.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -20,6 +20,15 @@
+ 
+ from babel import dates
+ from babel.util import FixedOffsetTimezone
++from babel.core import Locale
++from babel.dates import DateTimeFormat, format_date, format_datetime, \
++                        format_time, format_timedelta, get_date_format, \
++                        get_datetime_format, get_day_names, get_era_names, \
++                        get_month_names, get_period_names, \
++                        get_quarter_names, get_time_format, \
++                        get_timezone_gmt, get_timezone_location, \
++                        get_timezone_name, parse_date, parse_pattern, \
++                        parse_time
+ 
+ 
+ class DateTimeFormatTestCase(unittest.TestCase):
+@@ -286,6 +295,154 @@
+         self.assertEqual('3:30:00 PM +0000', formatted_time)
+ 
+ 
++ 
++class datesDocTest(unittest.TestCase):
++ 
++    def test_dates_DateTimeFormat_get_week_number(self):
++        # Return the number of the week of a day within a period. 
++        format = DateTimeFormat(date(2006, 1, 8), Locale.parse('de_DE'))
++        self.assertEqual(format.get_week_number(6),1)
++        format = DateTimeFormat(date(2006, 1, 8), Locale.parse('en_US'))
++        self.assertEqual(format.get_week_number(6),2)
++
++    def test_dates_format_date(self):
++        # Return a date formatted according to the given pattern.
++        d = date(2007, 04, 01)
++        self.assertEqual(format_date(d, locale='en_US'), u'Apr 1, 2007')
++        self.assertEqual(format_date(d, format='full', locale='de_DE'), u'Sonntag, 1. April 2007')
++        self.assertEqual(format_date(d, "EEE, MMM d, ''yy", locale='en'), u"Sun, Apr 1, '07")
++
++    def test_dates_format_datetime(self):
++        # Return a date formatted according to the given pattern.
++        dt = datetime(2007, 04, 01, 15, 30)
++        self.assertEqual(format_datetime(dt, locale='en_US'), u'Apr 1, 2007 3:30:00 PM')
++        self.assertEqual(format_datetime(dt, 'full', tzinfo=timezone('Europe/Paris'), locale='fr_FR'),
++                    u'dimanche 1 avril 2007 17:30:00 Heure avanc\xe9e de l\u2019Europe centrale')
++        self.assertEqual(format_datetime(dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz",
++                             tzinfo=timezone('US/Eastern'), locale='en'), u'2007.04.01 AD at 11:30:00 EDT')
++    
++    def test_dates_format_time(self):
++        # Return a time formatted according to the given pattern.
++        t = time(15, 30)
++        self.assertEqual(format_time(t, locale='en_US'), u'3:30:00 PM')
++        self.assertEqual(format_time(t, format='short', locale='de_DE'), u'15:30')
++        self.assertEqual(format_time(t, "hh 'o''clock' a", locale='en'), u"03 o'clock PM")
++        t = datetime(2007, 4, 1, 15, 30)
++        tzinfo = timezone('Europe/Paris')
++        t = tzinfo.localize(t)
++        self.assertEqual(format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR'),
++                         u'15:30:00 Heure avanc\xe9e de l\u2019Europe centrale')
++        self.assertEqual(format_time(t, "hh 'o''clock' a, zzzz", tzinfo=timezone('US/Eastern'),
++                         locale='en'), u"09 o'clock AM, Eastern Daylight Time")
++    
++        t = time(15, 30)
++        self.assertEqual(format_time(t, format='full', tzinfo=timezone('Europe/Paris'),
++                         locale='fr_FR'), u'15:30:00 Heure normale de l\u2019Europe centrale')
++        self.assertEqual(format_time(t, format='full', tzinfo=timezone('US/Eastern'),
++                         locale='en_US'), u'3:30:00 PM Eastern Standard Time')
++
++    def test_dates_format_timedelta(self):
++        # Return a time delta according to the rules of the given locale.
++        self.assertEqual(format_timedelta(timedelta(weeks=12), locale='en_US'), u'3 mths')
++        self.assertEqual(format_timedelta(timedelta(seconds=1), locale='es'), u'1 s')
++        self.assertEqual(format_timedelta(timedelta(hours=3), granularity='day', locale='en_US'), u'1 day')
++        self.assertEqual(format_timedelta(timedelta(hours=23), threshold=0.9, locale='en_US'), u'1 day')
++        self.assertEqual(format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US'), u'23 hrs')
++
++    def test_dates_get_date_format(self):
++        # Return the date formatting patterns used by the locale for the specified format.
++        self.assertEqual(repr(get_date_format(locale='en_US')),'<DateTimePattern %s>'% repr(u'MMM d, y'))
++        self.assertEqual(repr(get_date_format('full', locale='de_DE')),'<DateTimePattern %s>'% repr(u'EEEE, d. MMMM y'))
++
++    def test_dates_get_datetime_format(self):
++        # Return the datetime formatting patterns used by the locale for the specified format.
++        self.assertEqual(get_datetime_format(locale='en_US'), u'{1} {0}')
++
++    def test_dates_get_day_names(self):
++        # Return the day names used by the locale for the specified format.
++        self.assertEqual(get_day_names('wide', locale='en_US')[1], u'Tuesday')
++        self.assertEqual(get_day_names('abbreviated', locale='es')[1], u'mar')
++        self.assertEqual(get_day_names('narrow', context='stand-alone', locale='de_DE')[1], u'D')
++
++    def test_dates_get_era_names(self):
++        # Return the era names used by the locale for the specified format.
++        self.assertEqual(get_era_names('wide', locale='en_US')[1], u'Anno Domini')
++        self.assertEqual(get_era_names('abbreviated', locale='de_DE')[1], u'n. Chr.')
++
++    def test_dates_get_month_names(self):
++        # Return the month names used by the locale for the specified format.
++        self.assertEqual(get_month_names('wide', locale='en_US')[1], u'January')
++        self.assertEqual(get_month_names('abbreviated', locale='es')[1], u'ene')
++        self.assertEqual(get_month_names('narrow', context='stand-alone', locale='de_DE')[1], u'J')
++
++    def test_dates_get_period_names(self):
++        # Return the names for day periods (AM/PM) used by the locale.
++        self.assertEqual(get_period_names(locale='en_US')['am'], u'AM')
++
++    def test_dates_get_quarter_names(self):
++        # Return the quarter names used by the locale for the specified format.
++        self.assertEqual(get_quarter_names('wide', locale='en_US')[1], u'1st quarter')
++        self.assertEqual(get_quarter_names('abbreviated', locale='de_DE')[1], u'Q1')
++
++    def test_dates_get_time_format(self):
++        # Return the time formatting patterns used by the locale for the specified format.
++        self.assertEqual(repr(get_time_format(locale='en_US')), '<DateTimePattern %s>' % repr(u'h:mm:ss a'))
++        self.assertEqual(repr(get_time_format('full', locale='de_DE')), '<DateTimePattern %s>' % repr(u'HH:mm:ss zzzz'))
++
++    def test_dates_get_timezone_gmt(self):
++        # Return the timezone associated with the given `datetime` object formatted
++        # as string indicating the offset from GMT.
++        dt = datetime(2007, 4, 1, 15, 30)
++        self.assertEqual(get_timezone_gmt(dt, locale='en'), u'GMT+00:00')
++        tz = timezone('America/Los_Angeles')
++        dt = datetime(2007, 4, 1, 15, 30, tzinfo=tz)
++        self.assertEqual(get_timezone_gmt(dt, locale='en'), u'GMT-08:00')
++        self.assertEqual(get_timezone_gmt(dt, 'short', locale='en'), u'-0800')
++        self.assertEqual(get_timezone_gmt(dt, 'long', locale='fr_FR'), u'UTC-08:00')
++
++    def test_dates_get_timezone_location(self):
++        # Return a representation of the given timezone using "location format".
++        tz = timezone('America/St_Johns')
++        self.assertEqual(get_timezone_location(tz, locale='de_DE'), u"Kanada (St. John's)")
++        tz = timezone('America/Mexico_City')
++        self.assertEqual(get_timezone_location(tz, locale='de_DE'), u'Mexiko (Mexiko-Stadt)')
++        tz = timezone('Europe/Berlin')
++        self.assertEqual(get_timezone_location(tz, locale='de_DE'), u'Deutschland')
++
++    def test_dates_get_timezone_name(self):
++        # Return the localized display name for the given timezone. 
++        dt = time(15, 30, tzinfo=timezone('America/Los_Angeles'))
++        self.assertEqual(get_timezone_name(dt, locale='en_US'), u'Pacific Standard Time')
++        self.assertEqual(get_timezone_name(dt, width='short', locale='en_US'),u'PST')
++        tz = timezone('America/Los_Angeles')
++        self.assertEqual(get_timezone_name(tz, locale='en_US'), u'Pacific Time')
++        self.assertEqual(get_timezone_name(tz, 'short', locale='en_US'), u'PT')
++        tz = timezone('Europe/Berlin')
++        self.assertEqual(get_timezone_name(tz, locale='de_DE'), u'Deutschland')
++        self.assertEqual(get_timezone_name(tz, locale='pt_BR'), u'Hor\xe1rio Alemanha')
++        tz = timezone('America/St_Johns')
++        self.assertEqual(get_timezone_name(tz, locale='de_DE'), u"Kanada (St. John's)")
++        tz = timezone('Europe/Paris')
++        self.assertEqual(get_timezone_name(tz, 'short', locale='fr_CA'), u'France')
++        self.assertEqual(get_timezone_name(tz, 'short', uncommon=True, locale='fr_CA'), u'HEC')
++
++    def test_dates_parse_date(self):
++        # Parse a date from a string.
++        self.assertEqual(repr(parse_date('4/1/04', locale='en_US')), 'datetime.date(2004, 4, 1)')
++        self.assertEqual(repr(parse_date('01.04.2004', locale='de_DE')), 'datetime.date(2004, 4, 1)')
++
++    def test_dates_parse_pattern(self):
++        # Parse date, time, and datetime format patterns.
++        self.assertEqual(parse_pattern("MMMMd").format, u'%(MMMM)s%(d)s')
++        self.assertEqual(parse_pattern("MMM d, yyyy").format, u'%(MMM)s %(d)s, %(yyyy)s')
++        self.assertEqual(parse_pattern("H:mm' Uhr 'z").format, u'%(H)s:%(mm)s Uhr %(z)s')
++        self.assertEqual(parse_pattern("hh' o''clock'").format, u"%(hh)s o'clock")
++
++    def test_dates_parse_time(self):
++        # Parse a time from a string.
++        self.assertEqual(repr(parse_time('15:30:00', locale='en_US')), 'datetime.time(15, 30)')
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+@@ -295,6 +452,7 @@
+     suite.addTest(unittest.makeSuite(FormatTimeTestCase))
+     suite.addTest(unittest.makeSuite(FormatTimedeltaTestCase))
+     suite.addTest(unittest.makeSuite(TimeZoneAdjustTestCase))
++    suite.addTest(unittest.makeSuite(datesDocTest))
+     return suite
+ 
+ 
+diff -r 803f384a3f3c babel/tests/localedata.py
+--- a/babel/tests/localedata.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/tests/localedata.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -65,11 +65,31 @@
+         }, dict(d.items()))
+ 
+ 
++class localedataDocTest(unittest.TestCase):
++    def test_localedata_load(self):
++        # Load the locale data for the given locale.
++        d = load('en_US')
++        self.assertEqual(d['languages']['sv'], u'Swedish')
++        d1 = load('en_US')
++        d2 = load('en_US')
++        self.assertEqual(d1 is d2,True)
++        
++    def test_localedata_merge(self):
++        # Merge the data from `dict2` into the `dict1` dictionary,
++        # making copies of nested dictionaries.
++        d = {1: 'foo', 3: 'baz'}
++        merge(d, {1: 'Foo', 2: 'Bar'})
++        items = d.items();
++        items.sort();
++        self.assertEqual(items,[(1, 'Foo'), (2, 'Bar'), (3, 'baz')])
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+         suite.addTest(doctest.DocTestSuite(localedata))
+     suite.addTest(unittest.makeSuite(MergeResolveTestCase))
++    suite.addTest(unittest.makeSuite(localedataDocTest))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/tests/numbers.py
+--- a/babel/tests/numbers.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/tests/numbers.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -150,12 +150,81 @@
+         self.assertEqual(Decimal('0.2'), numbers.bankersround(Decimal('0.15'), ndigits=1))
+ 
+ 
++class numbersDocTest(unittest.TestCase):
++    
++    def test_numbers_bankersround(self):
++        self.assertEqual(numbers.bankersround(5.5, 0), 6.0)
++        self.assertEqual(numbers.bankersround(6.5, 0), 6.0)
++        self.assertEqual(numbers.bankersround(-6.5, 0), -6.0)
++        self.assertEqual(numbers.bankersround(1234.0, -2), 1200.0)
++        
++    def test_numbers_format_currency(self):
++        self.assertEqual(numbers.format_currency(1099.98, 'USD', locale='en_US'), u'$1,099.98')
++        self.assertEqual(numbers.format_currency(1099.98, 'USD', locale='es_CO'), u'US$\xa01.099,98')
++        self.assertEqual(numbers.format_currency(1099.98, 'EUR', locale='de_DE'), u'1.099,98\xa0\u20ac')
++        self.assertEqual(numbers.format_currency(1099.98, 'EUR', u'\xa4\xa4 #,##0.00', locale='en_US'), u'EUR 1,099.98')
++        
++    def test_numbers_format_decimal(self):
++        self.assertEqual(numbers.format_decimal(1.2345, locale='en_US'), u'1.234')
++        self.assertEqual(numbers.format_decimal(1.2346, locale='en_US'), u'1.235')
++        self.assertEqual(numbers.format_decimal(-1.2346, locale='en_US'), u'-1.235')
++        self.assertEqual(numbers.format_decimal(1.2345, locale='sv_SE'), u'1,234')
++        self.assertEqual(numbers.format_decimal(1.2345, locale='de'), u'1,234')
++        self.assertEqual(numbers.format_decimal(12345.5, locale='en_US'), u'12,345.5')
++    
++    def test_numbers_format_number(self):
++        self.assertEqual(numbers.format_number(1099, locale='en_US'), u'1,099')
++        self.assertEqual(numbers.format_number(1099, locale='de_DE'), u'1.099')
++        
++    def test_numbers_format_percent(self):
++        self.assertEqual(numbers.format_percent(0.34, locale='en_US'), u'34%')
++        self.assertEqual(numbers.format_percent(25.1234, locale='en_US'), u'2,512%')
++        self.assertEqual(numbers.format_percent(25.1234, locale='sv_SE'), u'2\xa0512\xa0%')
++        self.assertEqual(numbers.format_percent(25.1234, u'#,##0\u2030', locale='en_US'), u'25,123\u2030')
++    
++    def test_numbers_format_scientific(self):
++        self.assertEqual(numbers.format_scientific(10000, locale='en_US'), u'1E4')
++        self.assertEqual(numbers.format_scientific(1234567, u'##0E00', locale='en_US'), u'1.23E06')
++        
++    def test_numbers_get_currency_name(self):
++        self.assertEqual(numbers.get_currency_name('USD', 'en_US'), u'US Dollar')
++        
++    def test_numbers_get_currency_symbol(self):
++        self.assertEqual(numbers.get_currency_symbol('USD', 'en_US'), u'$')
++        
++    def test_numbers_get_decimal_symbol(self):
++        self.assertEqual(numbers.get_decimal_symbol('en_US'), u'.')
++        
++    def test_numbers_get_exponential_symbol(self):
++        self.assertEqual(numbers.get_exponential_symbol('en_US'), u'E')
++        
++    def test_numbers_get_group_symbol(self):
++        self.assertEqual(numbers.get_group_symbol('en_US'), u',')
++        
++    def test_numbers_get_minus_sign_symbol(self):
++        self.assertEqual(numbers.get_minus_sign_symbol('en_US'), u'-')
++        
++    def test_numbers_get_plus_sign_symbol(self):
++        self.assertEqual(numbers.get_plus_sign_symbol('en_US'), u'+')
++        
++    def test_numbers_parse_decimal(self):
++        self.assertEqual(numbers.parse_decimal('1,099.98', locale='en_US'), 1099.98)
++        self.assertEqual(numbers.parse_decimal('1.099,98', locale='de'), 1099.98)
++        self.assertRaises(numbers.NumberFormatError, numbers.parse_decimal, '2,109,998', locale='de')
++    
++    def test_numbers_parse_number(self):
++        self.assertEqual(numbers.parse_number('1,099', locale='en_US'), 1099L)
++        self.assertEqual(numbers.parse_number('1.099', locale='de_DE'), 1099L)
++        self.assertRaises(numbers.NumberFormatError, numbers.parse_number, '1.099,98', locale='de')
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+         suite.addTest(doctest.DocTestSuite(numbers))
+     suite.addTest(unittest.makeSuite(FormatDecimalTestCase))
+     suite.addTest(unittest.makeSuite(BankersRoundTestCase))
++    suite.addTest(unittest.makeSuite(numbersDocTest))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/tests/plural.py
+--- a/babel/tests/plural.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/tests/plural.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -18,10 +18,52 @@
+ from babel import plural
+ 
+ 
++class pluralDocTest(unittest.TestCase):
++    def test_plural_PluralRule(self):
++        rule = plural.PluralRule({'one': 'n is 1'})
++        self.assertEqual(rule(1), 'one')
++        self.assertEqual(rule(2), 'other')
++        
++    def test_plural_rules(self):
++        rule = plural.PluralRule({'one': 'n is 1'})
++        self.assertEqual(rule.rules, {'one': 'n is 1'})
++        
++    def test_plural_cldr_modulo(self):
++        self.assertEqual(plural.cldr_modulo(-3, 5), -3)
++        self.assertEqual(plural.cldr_modulo(-3, -5), -3)
++        self.assertEqual(plural.cldr_modulo(3, 5), 3)
++    
++    def test_plural_in_range(self):
++        self.assertTrue(plural.in_range(1, 1, 3))
++        self.assertTrue(plural.in_range(3, 1, 3))
++        self.assertFalse(plural.in_range(1.2, 1, 4))
++        self.assertFalse(plural.in_range(10, 1, 4))
++ 
++    def test_plural_to_gettext(self):
++        self.assertEqual(plural.to_gettext({'one': 'n is 1', 'two': 'n is 2'}), 
++            'nplurals=3; plural=((n == 2) ? 1 : (n == 1) ? 0 : 2)')
++            
++    def test_plural_to_javascript(self):
++        self.assertEqual(plural.to_javascript({'one': 'n is 1'}), 
++            "(function(n) { return (n == 1) ? 'one' : 'other'; })")
++            
++    def test_plural_to_python(self):
++        func = plural.to_python({'one': 'n is 1', 'few': 'n in 2..4'})
++        self.assertEqual(func(1), 'one')
++        self.assertEqual(func(3), 'few')
++        
++    def test_plural_within_range(self):
++        self.assertTrue(plural.within_range(1, 1, 3))
++        self.assertTrue(plural.within_range(1.0, 1, 3))
++        self.assertTrue(plural.within_range(1.2, 1, 4))
++        self.assertFalse(plural.within_range(10, 1, 4))
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+         suite.addTest(doctest.DocTestSuite(plural))
++    suite.addTest(unittest.makeSuite(pluralDocTest))
+     return suite
+ 
+ 
+diff -r 803f384a3f3c babel/tests/support.py
+--- a/babel/tests/support.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/tests/support.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -22,6 +22,10 @@
+ from babel.messages import Catalog
+ from babel.messages.mofile import write_mo
+ 
++from datetime import date, datetime, timedelta
++from pytz import timezone
++from babel.util import UTC
++
+ class TranslationsTestCase(unittest.TestCase):
+     
+     def setUp(self):
+@@ -181,6 +185,63 @@
+         self.assertEqual(1, proxy.value)
+         self.assertEqual(2, proxy.value)
+ 
++class supportDocTest(unittest.TestCase):
++    
++    def test_support_Format(self):
++        fmt = support.Format('en_US', UTC)
++        self.assertEqual(fmt.date(date(2007, 4, 1)), u'Apr 1, 2007')
++        self.assertEqual(fmt.decimal(1.2345), u'1.234')
++        
++    def test_support_date(self):
++        fmt = support.Format('en_US')
++        self.assertEqual(fmt.date(date(2007, 4, 1)), u'Apr 1, 2007')
++        
++    def test_support_datetime(self):
++        fmt = support.Format('en_US', tzinfo=timezone('US/Eastern'))
++        self.assertEqual(fmt.datetime(datetime(2007, 4, 1, 15, 30)), u'Apr 1, 2007 11:30:00 AM')
++        
++    def test_support_decimal(self): 
++        fmt = support.Format('en_US')
++        self.assertEqual(fmt.decimal(1.2345), u'1.234')
++ 
++    def test_support_number(self):
++        fmt = support.Format('en_US')
++        self.assertEqual(fmt.number(1099), u'1,099')
++        
++    def test_support_percent(self):
++        fmt = support.Format('en_US')
++        self.assertEqual(fmt.percent(0.34), u'34%')
++        
++    def test_support_time(self): 
++        fmt = support.Format('en_US', tzinfo=timezone('US/Eastern'))
++        self.assertEqual(fmt.time(datetime(2007, 4, 1, 15, 30)), u'11:30:00 AM')
++        
++    def test_support_timedelta(self): 
++        fmt = support.Format('en_US')
++        self.assertEqual(fmt.timedelta(timedelta(weeks=11)), u'3 mths')
++        
++    def test_support_LazyProxy(self):
++        
++        def greeting(name='world'):
++            return 'Hello, %s!' % name
++            
++        lazy_greeting = support.LazyProxy(greeting, name='Joe')
++        self.assertEqual(lazy_greeting, 'Hello, Joe!')
++        self.assertEqual(u'  ' + lazy_greeting, u'  Hello, Joe!')
++        self.assertEqual(u'(%s)' % lazy_greeting, u'(Hello, Joe!)')
++        greetings = [
++            support.LazyProxy(greeting, 'world'),
++            support.LazyProxy(greeting, 'Joe'),
++            support.LazyProxy(greeting, 'universe'),
++        ]
++        greetings.sort()
++        self.assertEqual(greetings[0], 'Hello, Joe!')
++        self.assertEqual(greetings[1], 'Hello, universe!')
++        self.assertEqual(greetings[2], 'Hello, world!')
++        self.assertEqual(len(greetings), 3)
++
++
++
+ 
+ def suite():
+     suite = unittest.TestSuite()
+@@ -188,6 +249,7 @@
+         suite.addTest(doctest.DocTestSuite(support))
+     suite.addTest(unittest.makeSuite(TranslationsTestCase, 'test'))
+     suite.addTest(unittest.makeSuite(LazyProxyTestCase, 'test'))
++    suite.addTest(unittest.makeSuite(supportDocTest, 'test'))
+     return suite
+ 
+ if __name__ == '__main__':
+diff -r 803f384a3f3c babel/tests/util.py
+--- a/babel/tests/util.py	Thu Oct 06 20:22:30 2011 +0200
++++ b/babel/tests/util.py	Thu Oct 06 20:22:41 2011 +0200
+@@ -17,10 +17,25 @@
+ 
+ from babel import util
+ 
++class utilDocTest(unittest.TestCase):
++    
++    def test_util_distinct(self):
++        self.assertEqual(list(util.distinct([1, 2, 1, 3, 4, 4])), [1, 2, 3, 4])
++        self.assertEqual(list(util.distinct('foobar')), ['f', 'o', 'b', 'a', 'r'])
++        
++    def test_util_pathmatch(self):
++        self.assertTrue(util.pathmatch('**.py', 'bar.py'))
++        self.assertTrue(util.pathmatch('**.py', 'foo/bar/baz.py'))
++        self.assertFalse(util.pathmatch('**.py', 'templates/index.html'))
++        self.assertTrue(util.pathmatch('**/templates/*.html', 'templates/index.html'))
++        self.assertFalse(util.pathmatch('**/templates/*.html', 'templates/foo/bar.html'))
++
++
+ def suite():
+     suite = unittest.TestSuite()
+     if sys.version_info < (3, 0):
+         suite.addTest(doctest.DocTestSuite(util))
++    suite.addTest(unittest.makeSuite(utilDocTest))
+     return suite
+ 
+ if __name__ == '__main__':
+# HG changeset patch
+# Parent bbb648ab712eb0f2fce0653726bfe2159901b688
+diff -r bbb648ab712e -r 792de76a3b18 babel/messages/mofile.py
+--- a/babel/messages/mofile.py	Mon Mar 05 21:42:34 2012 +0100
++++ b/babel/messages/mofile.py	Mon Mar 05 22:22:22 2012 +0100
+@@ -210,7 +210,12 @@
+         koffsets += [l1, o1 + keystart]
+         voffsets += [l2, o2 + valuestart]
+     offsets = koffsets + voffsets
+-
++    
++    offsets_array = array.array("i", offsets)
++    if hasattr(offsets_array, 'tobytes'):
++        offsets_bytes = offsets_array.tobytes()
++    else:
++        offsets_bytes = offsets_array.tostring()
+     fileobj.write(struct.pack('Iiiiiii',
+         LE_MAGIC,                   # magic
+         0,                          # version
+@@ -218,4 +223,4 @@
+         7 * 4,                      # start of key index
+         7 * 4 + len(messages) * 8,  # start of value index
+         0, 0                        # size and offset of hash table
+-    ) + array.array("i", offsets).tostring() + ids + strs)
++    ) + offsets_bytes + ids + strs)
+# HG changeset patch
+# Parent 0ed735f4ea4b2eb54de0d84dc8896c73fab6088f
+
+diff -r 0ed735f4ea4b babel/messages/tests/catalog.py
+--- a/babel/messages/tests/catalog.py	Sun Oct 02 00:14:40 2011 +0200
++++ b/babel/messages/tests/catalog.py	Tue Oct 04 16:09:15 2011 +0200
+@@ -54,6 +54,16 @@
+         self.assertEqual(msg.locations, [('foo.py', 42)])
+         msg.flags.add('fuzzy')
+         assert not clone.fuzzy and msg.fuzzy
++    
++    def test_can_compare_messages_with_each_other(self):
++        msg1 = catalog.Message('foo')
++        msg2 = catalog.Message('bar')
++        self.assertTrue(msg1 > msg2)
++        self.assertFalse(msg1 <= msg2)
++        self.assertFalse(msg1 < msg2)
++        self.assertTrue(msg1 >= msg2)
++        self.assertFalse(msg1 == msg2)
++        self.assertTrue(msg1 != msg2)
+ 
+ 
+ class CatalogTestCase(unittest.TestCase):

fix-tests-for-python3

+# HG changeset patch
+# Parent d74f312f723fbbbcf57b46cec8bbb1669a353117
+
+diff -r d74f312f723f babel/compat.py
+--- a/babel/compat.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/compat.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -11,6 +11,8 @@
+ # individuals. For the exact contribution history, see the revision
+ # history and logs, available at http://babel.edgewall.org/log/.
+ 
++import sys
++
+ try:
+     from xml.etree import ElementTree
+ except ImportError:
+@@ -27,3 +29,102 @@
+ except ImportError:
+     import dummy_threading as threading
+ 
++try:
++    from UserDict import DictMixin
++except ImportError:
++    from collections import UserDict as DictMixin
++
++try:
++    from io import BytesIO
++except ImportError:
++    from StringIO import StringIO as BytesIO
++
++if sys.version_info > (3, 0):
++    bytes_class = b''.__class__
++    def _bytes(value):
++        if isinstance(value, bytes_class):
++            return value
++        return value.encode('utf-8')
++    bytes = _bytes
++else:
++    try:
++        bytes = bytes
++    except NameError:
++        bytes = str
++
++if sys.version_info > (3, 0):
++    from tokenize import tokenize as tokenize_python
++else:
++    from tokenize import generate_tokens as tokenize_python
++
++
++try:
++    from collections import OrderedDict as odict
++except ImportError:
++    class odict(dict):
++        """Ordered dict implementation.
++        
++        :see: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
++        """
++        def __init__(self, data=None):
++            dict.__init__(self, data or {})
++            self._keys = dict.keys(self)
++
++        def __delitem__(self, key):
++            dict.__delitem__(self, key)
++            self._keys.remove(key)
++
++        def __setitem__(self, key, item):
++            dict.__setitem__(self, key, item)
++            if key not in self._keys:
++                self._keys.append(key)
++
++        def __iter__(self):
++            return iter(self._keys)
++        iterkeys = __iter__
++
++        def clear(self):
++            dict.clear(self)
++            self._keys = []
++
++        def copy(self):
++            d = odict()
++            d.update(self)
++            return d
++
++        def items(self):
++            return zip(self._keys, self.values())
++
++        def iteritems(self):
++            return izip(self._keys, self.itervalues())
++
++        def keys(self):
++            return self._keys[:]
++
++        def pop(self, key, default=missing):
++            if default is missing:
++                return dict.pop(self, key)
++            elif key not in self:
++                return default
++            self._keys.remove(key)
++            return dict.pop(self, key, default)
++
++        def popitem(self, key):
++            self._keys.remove(key)
++            return dict.popitem(key)
++
++        def setdefault(self, key, failobj = None):
++            dict.setdefault(self, key, failobj)
++            if key not in self._keys:
++                self._keys.append(key)
++
++        def update(self, dict):
++            for (key, val) in dict.items():
++                self[key] = val
++
++        def values(self):
++            return map(self.get, self._keys)
++
++        def itervalues(self):
++            return imap(self.get, self._keys)
++
+diff -r d74f312f723f babel/localedata.py
+--- a/babel/localedata.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/localedata.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -19,9 +19,9 @@
+ 
+ import os
+ import cPickle as pickle
+-from UserDict import DictMixin
++import sys
+ 
+-from babel.compat import threading
++from babel.compat import DictMixin, threading
+ 
+ __all__ = ['exists', 'locale_identifiers', 'load']
+ __docformat__ = 'restructuredtext en'
+@@ -185,6 +185,10 @@
+ 
+     def __init__(self, data, base=None):
+         dict.__init__(self, data)
++        if hasattr(DictMixin, '__init__'):
++            # in Python 2 DictMixin does not have a constructor, but in Python 3
++            # is has and calling it is necessary
++            DictMixin.__init__(self,data)
+         if base is None:
+             base = data
+         self.base = base
+diff -r d74f312f723f babel/messages/catalog.py
+--- a/babel/messages/catalog.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/messages/catalog.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -19,6 +19,7 @@
+ from email import message_from_string
+ from copy import copy
+ import re
++import sys
+ import time
+ 
+ from babel import __version__ as VERSION
+@@ -101,7 +102,12 @@
+                     return self.id, obj.id[0]
+             return self.id, obj.id
+         this, other = values_to_compare()
+-        return cmp(this, other)
++        # Python 3 does not have 'cmp()' anymore, so just reimplement it...
++        if this < other:
++            return -1
++        elif this > other:
++            return 1
++        return 0
+ 
+     def __gt__(self, other):
+         return self.__cmp__(other) > 0
+@@ -609,6 +615,8 @@
+         elif id == '':
+             # special treatment for the header message
+             def _parse_header(header_string):
++                if sys.version_info >= (3, 0):
++                    return message_from_string(header_string)
+                 # message_from_string only works for str, not for unicode
+                 headers = message_from_string(header_string.encode('utf8'))
+                 decoded_headers = {}
+diff -r d74f312f723f babel/messages/extract.py
+--- a/babel/messages/extract.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/messages/extract.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -23,8 +23,9 @@
+ 
+ import os
+ import sys
+-from tokenize import generate_tokens, COMMENT, NAME, OP, STRING
++from tokenize import COMMENT, NAME, OP, STRING
+ 
++from babel.compat import tokenize_python
+ from babel.util import parse_encoding, pathmatch, relpath
+ from textwrap import dedent
+ 
+@@ -193,7 +194,7 @@
+     :return: the list of extracted messages
+     :rtype: `list`
+     """
+-    fileobj = open(filename, 'U')
++    fileobj = open(filename, 'Ub')
+     try:
+         return list(extract(method, fileobj, keywords, comment_tags, options,
+                             strip_comment_tags))
+@@ -352,9 +353,9 @@
+     in_def = in_translator_comments = False
+     comment_tag = None
+ 
+-    encoding = parse_encoding(fileobj) or options.get('encoding', 'iso-8859-1')
++    encoding = str(parse_encoding(fileobj) or options.get('encoding', 'iso-8859-1'))
+ 
+-    tokens = generate_tokens(fileobj.readline)
++    tokens = tokenize_python(fileobj.readline)
+     for tok, value, (lineno, _), _, _ in tokens:
+         if call_stack == -1 and tok == NAME and value in ('def', 'class'):
+             in_def = True
+@@ -372,8 +373,10 @@
+             in_def = False
+             continue
+         elif call_stack == -1 and tok == COMMENT:
++            if isinstance(value, bytes):
++                value = value.decode(encoding)
+             # Strip the comment token from the line
+-            value = value.decode(encoding)[1:].strip()
++            value = value[1:].strip()
+             if in_translator_comments and \
+                     translator_comments[-1][0] == lineno - 1:
+                 # We're already inside a translator comment, continue appending
+@@ -419,7 +422,7 @@
+                 # aid=617979&group_id=5470
+                 value = eval('# coding=%s\n%s' % (encoding, value),
+                              {'__builtins__':{}}, {})
+-                if isinstance(value, str):
++                if isinstance(value, bytes):
+                     value = value.decode(encoding)
+                 buf.append(value)
+             elif tok == OP and value == ',':
+diff -r d74f312f723f babel/messages/frontend.py
+--- a/babel/messages/frontend.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/messages/frontend.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -284,7 +284,7 @@
+ 
+     def run(self):
+         mappings = self._get_mappings()
+-        outfile = open(self.output_file, 'w')
++        outfile = open(self.output_file, 'wb')
+         try:
+             catalog = Catalog(project=self.distribution.get_name(),
+                               version=self.distribution.get_version(),
+@@ -447,7 +447,7 @@
+         catalog.locale = self._locale
+         catalog.fuzzy = False
+ 
+-        outfile = open(self.output_file, 'w')
++        outfile = open(self.output_file, 'wb')
+         try:
+             write_po(outfile, catalog)
+         finally:
+@@ -848,7 +848,7 @@
+             parser.error('incorrect number of arguments')
+ 
+         if options.output not in (None, '-'):
+-            outfile = open(options.output, 'w')
++            outfile = open(options.output, 'wb')
+         else:
+             outfile = sys.stdout
+ 
+@@ -981,7 +981,7 @@
+         self.log.info('creating catalog %r based on %r', options.output_file,
+                       options.input_file)
+ 
+-        outfile = open(options.output_file, 'w')
++        outfile = open(options.output_file, 'wb')
+         try:
+             write_po(outfile, catalog)
+         finally:
+diff -r d74f312f723f babel/messages/jslexer.py
+--- a/babel/messages/jslexer.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/messages/jslexer.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -24,7 +24,7 @@
+     '>>>=', '&', '&=', '|', '|=', '&&', '||', '^', '^=', '(', ')',
+     '[', ']', '{', '}', '!', '--', '++', '~', ',', ';', '.', ':'
+ ]
+-operators.sort(lambda a, b: cmp(-len(a), -len(b)))
++operators.sort(key=lambda a: -len(a))
+ 
+ escapes = {'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t'}
+ 
+diff -r d74f312f723f babel/messages/mofile.py
+--- a/babel/messages/mofile.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/messages/mofile.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -21,6 +21,7 @@
+ import array
+ import struct
+ 
++from babel.compat import bytes
+ from babel.messages.catalog import Catalog, Message
+ 
+ __all__ = ['read_mo', 'write_mo']
+@@ -84,21 +85,21 @@
+                 item = item.strip()
+                 if not item:
+                     continue
+-                if ':' in item:
+-                    key, value = item.split(':', 1)
++                if bytes(':') in item:
++                    key, value = item.split(bytes(':'), 1)
+                     lastkey = key = key.strip().lower()
+                     headers[key] = value.strip()
+                 elif lastkey:
+                     headers[lastkey] += '\n' + item
+ 
+-        if '\x04' in msg: # context
+-            ctxt, msg = msg.split('\x04')
++        if bytes('\x04') in msg: # context
++            ctxt, msg = msg.split(bytes('\x04'))
+         else:
+             ctxt = None
+ 
+-        if '\x00' in msg: # plural forms
+-            msg = msg.split('\x00')
+-            tmsg = tmsg.split('\x00')
++        if bytes('\x00') in msg: # plural forms
++            msg = msg.split(bytes('\x00'))
++            tmsg = tmsg.split(bytes('\x00'))
+             if catalog.charset:
+                 msg = [x.decode(catalog.charset) for x in msg]
+                 tmsg = [x.decode(catalog.charset) for x in tmsg]
+@@ -164,14 +165,14 @@
+         messages[1:] = [m for m in messages[1:] if not m.fuzzy]
+     messages.sort()
+ 
+-    ids = strs = ''
++    ids = strs = bytes('')
+     offsets = []
+ 
+     for message in messages:
+         # For each string, we need size and file offset.  Each string is NUL
+         # terminated; the NUL does not count into the size.
+         if message.pluralizable:
+-            msgid = '\x00'.join([
++            msgid = bytes('\x00').join([
+                 msgid.encode(catalog.charset) for msgid in message.id
+             ])
+             msgstrs = []
+@@ -180,7 +181,7 @@
+                     msgstrs.append(message.id[min(int(idx), 1)])
+                 else:
+                     msgstrs.append(string)
+-            msgstr = '\x00'.join([
++            msgstr = bytes('\x00').join([
+                 msgstr.encode(catalog.charset) for msgstr in msgstrs
+             ])
+         else:
+@@ -190,11 +191,11 @@
+             else:
+                 msgstr = message.string.encode(catalog.charset)
+         if message.context:
+-            msgid = '\x04'.join([message.context.encode(catalog.charset),
+-                                 msgid])
++            msgid = bytes('\x04').join([message.context.encode(catalog.charset),
++                                        msgid])
+         offsets.append((len(ids), len(msgid), len(strs), len(msgstr)))
+-        ids += msgid + '\x00'
+-        strs += msgstr + '\x00'
++        ids += msgid + bytes('\x00')
++        strs += msgstr + bytes('\x00')
+ 
+     # The header is 7 32-bit unsigned integers.  We don't use hash tables, so
+     # the keys start right after the index tables.
+diff -r d74f312f723f babel/messages/tests/catalog.py
+--- a/babel/messages/tests/catalog.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/messages/tests/catalog.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -15,6 +15,7 @@
+ import datetime
+ import doctest
+ import unittest
++import sys
+ 
+ from babel.messages import catalog
+ 
+@@ -295,7 +296,8 @@
+ 
+ def suite():
+     suite = unittest.TestSuite()
+-    suite.addTest(doctest.DocTestSuite(catalog, optionflags=doctest.ELLIPSIS))
++    if sys.version_info < (3, 0):
++        suite.addTest(doctest.DocTestSuite(catalog, optionflags=doctest.ELLIPSIS))
+     suite.addTest(unittest.makeSuite(MessageTestCase))
+     suite.addTest(unittest.makeSuite(CatalogTestCase))
+     return suite
+diff -r d74f312f723f babel/messages/tests/checkers.py
+--- a/babel/messages/tests/checkers.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/messages/tests/checkers.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -14,9 +14,9 @@
+ from datetime import datetime
+ import time
+ import unittest
+-from StringIO import StringIO
+ 
+ from babel import __version__ as VERSION
++from babel.compat import bytes, BytesIO
+ from babel.core import Locale, UnknownLocaleError
+ from babel.dates import format_datetime
+ from babel.messages import checkers
+@@ -79,7 +79,7 @@
+ 
+             # This test will fail for revisions <= 406 because so far
+             # catalog.num_plurals was neglected
+-            catalog = read_po(StringIO(po_file), _locale)
++            catalog = read_po(BytesIO(po_file), _locale)
+             message = catalog['foobar']
+             checkers.num_plurals(catalog, message)
+ 
+@@ -147,7 +147,7 @@
+ 
+             # This test will fail for revisions <= 406 because so far
+             # catalog.num_plurals was neglected
+-            catalog = read_po(StringIO(po_file), _locale)
++            catalog = read_po(BytesIO(po_file), _locale)
+             message = catalog['foobar']
+             checkers.num_plurals(catalog, message)
+ 
+@@ -198,7 +198,7 @@
+ 
+             # This test will fail for revisions <= 406 because so far
+             # catalog.num_plurals was neglected
+-            catalog = read_po(StringIO(po_file), _locale)
++            catalog = read_po(BytesIO(bytes(po_file)), _locale)
+             message = catalog['foobar']
+             checkers.num_plurals(catalog, message)
+ 
+@@ -250,7 +250,7 @@
+ 
+             # This test will fail for revisions <= 406 because so far
+             # catalog.num_plurals was neglected
+-            catalog = read_po(StringIO(po_file), _locale)
++            catalog = read_po(BytesIO(bytes(po_file)), _locale)
+             message = catalog['foobar']
+             checkers.num_plurals(catalog, message)
+ 
+@@ -303,7 +303,7 @@
+ 
+             # This test will fail for revisions <= 406 because so far
+             # catalog.num_plurals was neglected
+-            catalog = read_po(StringIO(po_file), _locale)
++            catalog = read_po(BytesIO(bytes(po_file)), _locale)
+             message = catalog['foobar']
+             checkers.num_plurals(catalog, message)
+ 
+@@ -357,7 +357,7 @@
+ 
+             # This test will fail for revisions <= 406 because so far
+             # catalog.num_plurals was neglected
+-            catalog = read_po(StringIO(po_file), _locale)
++            catalog = read_po(BytesIO(bytes(po_file)), _locale)
+             message = catalog['foobar']
+             checkers.num_plurals(catalog, message)
+ 
+diff -r d74f312f723f babel/messages/tests/extract.py
+--- a/babel/messages/tests/extract.py	Tue Oct 04 16:09:15 2011 +0200
++++ b/babel/messages/tests/extract.py	Thu Oct 06 20:22:30 2011 +0200
+@@ -17,13 +17,14 @@
+ import sys
+ import unittest
+ 
++from babel.compat import bytes, BytesIO
+ from babel.messages import extract
+ 
+ 
+ class ExtractPythonTestCase(unittest.TestCase):
+ 
+     def test_nested_calls(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ msg1 = _(i18n_arg.replace(r'\"', '"'))
+ msg2 = ungettext(i18n_arg.replace(r'\"', '"'), multi_arg.replace(r'\"', '"'), 2)
+ msg3 = ungettext("Babel", multi_arg.replace(r'\"', '"'), 2)
+@@ -34,7 +35,7 @@
+ msg8 = gettext('Rabbit')
+ msg9 = dgettext('wiki', model.addPage())
+ msg10 = dngettext(getDomain(), 'Page', 'Pages', 3)
+-""")
++"""))
+         messages = list(extract.extract_python(buf,
+                                                extract.DEFAULT_KEYWORDS.keys(),
+                                                [], {}))
+@@ -52,18 +53,18 @@
+                          messages)
+ 
+     def test_nested_comments(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ msg = ngettext('pylon',  # TRANSLATORS: shouldn't be
+                'pylons', # TRANSLATORS: seeing this
+                count)
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('ngettext',),
+                                                ['TRANSLATORS:'], {}))
+         self.assertEqual([(1, 'ngettext', (u'pylon', u'pylons', None), [])],
+                          messages)
+ 
+     def test_comments_with_calls_that_spawn_multiple_lines(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ # NOTE: This Comment SHOULD Be Extracted
+ add_notice(req, ngettext("Catalog deleted.",
+                          "Catalogs deleted.", len(selected)))
+@@ -79,7 +80,7 @@
+ # NOTE: And This One Too
+ add_notice(req, ngettext("Bar deleted.",
+                          "Bars deleted.", len(selected)))
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('ngettext','_'), ['NOTE:'],
+ 
+                                                {'strip_comment_tags':False}))
+@@ -102,7 +103,7 @@
+                          messages[3])
+ 
+     def test_declarations(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ class gettext(object):
+     pass
+ def render_body(context,x,y=_('Page arg 1'),z=_('Page arg 2'),**pageargs):
+@@ -111,7 +112,7 @@
+     pass
+ class Meta:
+     verbose_name = _('log entry')
+-""")
++"""))
+         messages = list(extract.extract_python(buf,
+                                                extract.DEFAULT_KEYWORDS.keys(),
+                                                [], {}))
+@@ -121,24 +122,24 @@
+                          messages)
+ 
+     def test_multiline(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ msg1 = ngettext('pylon',
+                 'pylons', count)
+ msg2 = ngettext('elvis',
+                 'elvises',
+                  count)
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('ngettext',), [], {}))
+         self.assertEqual([(1, 'ngettext', (u'pylon', u'pylons', None), []),
+                           (3, 'ngettext', (u'elvis', u'elvises', None), [])],
+                          messages)
+ 
+     def test_triple_quoted_strings(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ msg1 = _('''pylons''')
+ msg2 = ngettext(r'''elvis''', \"\"\"elvises\"\"\", count)
+ msg2 = ngettext(\"\"\"elvis\"\"\", 'elvises', count)
+-""")
++"""))
+         messages = list(extract.extract_python(buf,
+                                                extract.DEFAULT_KEYWORDS.keys(),
+                                                [], {}))
+@@ -148,11 +149,11 @@
+                          messages)
+ 
+     def test_multiline_strings(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ _('''This module provides internationalization and localization
+ support for your Python programs by providing an interface to the GNU
+ gettext message catalog library.''')
+-""")
++"""))
+         messages = list(extract.extract_python(buf,
+                                                extract.DEFAULT_KEYWORDS.keys(),
+                                                [], {}))
+@@ -164,73 +165,73 @@
+             messages)
+ 
+     def test_concatenated_strings(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ foobar = _('foo' 'bar')
+-""")
++"""))
+         messages = list(extract.extract_python(buf,
+                                                extract.DEFAULT_KEYWORDS.keys(),
+                                                [], {}))
+         self.assertEqual(u'foobar', messages[0][2])
+ 
+     def test_unicode_string_arg(self):
+-        buf = StringIO("msg = _(u'Foo Bar')")
++        buf = BytesIO(bytes("msg = _(u'Foo Bar')"))
+         messages = list(extract.extract_python(buf, ('_',), [], {}))
+         self.assertEqual(u'Foo Bar', messages[0][2])
+ 
+     def test_comment_tag(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # NOTE: A translation comment
+ msg = _(u'Foo Bar')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Foo Bar', messages[0][2])
+         self.assertEqual([u'NOTE: A translation comment'], messages[0][3])
+ 
+     def test_comment_tag_multiline(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # NOTE: A translation comment
+ # with a second line
+ msg = _(u'Foo Bar')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Foo Bar', messages[0][2])
+         self.assertEqual([u'NOTE: A translation comment', u'with a second line'],
+                          messages[0][3])
+ 
+     def test_translator_comments_with_previous_non_translator_comments(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # This shouldn't be in the output
+ # because it didn't start with a comment tag
+ # NOTE: A translation comment
+ # with a second line
+ msg = _(u'Foo Bar')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Foo Bar', messages[0][2])
+         self.assertEqual([u'NOTE: A translation comment', u'with a second line'],
+                          messages[0][3])
+ 
+     def test_comment_tags_not_on_start_of_comment(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # This shouldn't be in the output
+ # because it didn't start with a comment tag
+ # do NOTE: this will not be a translation comment
+ # NOTE: This one will be
+ msg = _(u'Foo Bar')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Foo Bar', messages[0][2])
+         self.assertEqual([u'NOTE: This one will be'], messages[0][3])
+ 
+     def test_multiple_comment_tags(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # NOTE1: A translation comment for tag1
+ # with a second line
+ msg = _(u'Foo Bar1')
+ 
+ # NOTE2: A translation comment for tag2
+ msg = _(u'Foo Bar2')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',),
+                                                ['NOTE1:', 'NOTE2:'], {}))
+         self.assertEqual(u'Foo Bar1', messages[0][2])
+@@ -240,28 +241,28 @@
+         self.assertEqual([u'NOTE2: A translation comment for tag2'], messages[1][3])
+ 
+     def test_two_succeeding_comments(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # NOTE: one
+ # NOTE: two
+ msg = _(u'Foo Bar')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Foo Bar', messages[0][2])
+         self.assertEqual([u'NOTE: one', u'NOTE: two'], messages[0][3])
+ 
+     def test_invalid_translator_comments(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # NOTE: this shouldn't apply to any messages
+ hello = 'there'
+ 
+ msg = _(u'Foo Bar')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Foo Bar', messages[0][2])
+         self.assertEqual([], messages[0][3])
+ 
+     def test_invalid_translator_comments2(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # NOTE: Hi!
+ hithere = _('Hi there!')
+ 
+@@ -270,7 +271,7 @@
+ 
+ # this (NOTE:) should not show up either
+ hello = _('Hello')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Hi there!', messages[0][2])
+         self.assertEqual([u'NOTE: Hi!'], messages[0][3])
+@@ -278,36 +279,36 @@
+         self.assertEqual([], messages[1][3])
+ 
+     def test_invalid_translator_comments3(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # NOTE: Hi,
+ 
+ # there!
+ hithere = _('Hi there!')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Hi there!', messages[0][2])
+         self.assertEqual([], messages[0][3])
+ 
+     def test_comment_tag_with_leading_space(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+   #: A translation comment
+   #: with leading spaces
+ msg = _(u'Foo Bar')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), [':'], {}))
+         self.assertEqual(u'Foo Bar', messages[0][2])
+         self.assertEqual([u': A translation comment', u': with leading spaces'],
+                          messages[0][3])
+ 
+     def test_different_signatures(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ foo = _('foo', 'bar')
+ n = ngettext('hello', 'there', n=3)
+ n = ngettext(n=3, 'hello', 'there')
+ n = ngettext(n=3, *messages)
+ n = ngettext()
+ n = ngettext('foo')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_', 'ngettext'), [], {}))
+         self.assertEqual((u'foo', u'bar'), messages[0][2])
+         self.assertEqual((u'hello', u'there', None), messages[1][2])
+@@ -317,51 +318,51 @@
+         self.assertEqual(('foo'), messages[5][2])
+ 
+     def test_utf8_message(self):
+-        buf = StringIO("""
++        buf = BytesIO(bytes("""
+ # NOTE: hello
+ msg = _('Bonjour à tous')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'],
+                                                {'encoding': 'utf-8'}))
+         self.assertEqual(u'Bonjour à tous', messages[0][2])
+         self.assertEqual([u'NOTE: hello'], messages[0][3])
+ 
+     def test_utf8_message_with_magic_comment(self):
+-        buf = StringIO("""# -*- coding: utf-8 -*-
++        buf = BytesIO(bytes("""# -*- coding: utf-8 -*-
+ # NOTE: hello
+ msg = _('Bonjour à tous')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Bonjour à tous', messages[0][2])
+         self.assertEqual([u'NOTE: hello'], messages[0][3])
+ 
+     def test_utf8_message_with_utf8_bom(self):
+-        buf = StringIO(codecs.BOM_UTF8 + """
++        buf = BytesIO(codecs.BOM_UTF8 + bytes("""
+ # NOTE: hello
+ msg = _('Bonjour à tous')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Bonjour à tous', messages[0][2])
+         self.assertEqual([u'NOTE: hello'], messages[0][3])
+ 
+     def test_utf8_raw_strings_match_unicode_strings(self):
+-        buf = StringIO(codecs.BOM_UTF8 + """
++        buf = BytesIO(codecs.BOM_UTF8 + bytes("""
+ msg = _('Bonjour à tous')
+ msgu = _(u'Bonjour à tous')
+-""")
++"""))
+         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
+         self.assertEqual(u'Bonjour à tous', messages[0][2])
+         self.assertEqual(messages[0][2], messages[1][2])
+ 
+     def test_extract_strip_comment_tags(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ #: This is a comment with a very simple
+ #: prefix specified
+ _('Servus')
+ 
+ # NOTE: This is a multiline comment with
+ # a prefix too
+-_('Babatschi')""")
++_('Babatschi')"""))
+         messages = list(extract.extract('python', buf, comment_tags=['NOTE:', ':'],
+                                         strip_comment_tags=True))
+         self.assertEqual(u'Servus', messages[0][1])
+@@ -375,11 +376,11 @@
+ class ExtractJavaScriptTestCase(unittest.TestCase):
+ 
+     def test_simple_extract(self):
+-        buf = StringIO("""\
++        buf = BytesIO(bytes("""\
+ msg1 = _('simple')
+ msg2 = gettext('simple')
+ msg3 = ngettext('s', 'p', 42)
+-        """)
++        """))
+         messages = \
+             list(extract.extract('javascript', buf, extract.DEFAULT_KEYWORDS,
+                                  [], {}))
+@@ -389,7 +390,7 @@
+                           (3, ('s', 'p'), [], None)], messages)