Repository: python-babel/babel (python)

Issue:
A bug in support.Translations class prevent merge method to work with python 3
This code sample works well in python 2:

    from babel.support import Translations
    translations = Translations.load("path/to/translations", ["en"])
    other_translations = Translations.load("other/path/to/translations", ["en"])
    translations.merge(other_translations)

Switching to python 3 make it to throw an AttributeError ('filter' object has no attribute 'extend') at babel/support.py:590:

    self.files.extend(translations.files)

It turns out that self.files is set at babel/support.py:284:

    self.files = filter(None, [getattr(fp, 'name', None)])

The filter method returns a list in python 2 but python 3 version returns a kind of generator, which has not extend method. Make a list from the filter fix the issue, but I'm not sure what side effects it could have:

    self.files = list(filter(None, [getattr(fp, 'name', None)]))


Pull request description:
Flatten NullTranslations.files into a list
`filter` is special in Python 3, and latter usages would error out in
strange ways.

Fixes #92
Fixes #162

Patch:
diff --git a/babel/support.py b/babel/support.py
index 3b4869c..e803138 100644
--- a/babel/support.py
+++ b/babel/support.py
@@ -298,7 +298,7 @@ class NullTranslations(gettext.NullTranslations, object):
         self._catalog = {}
         self.plural = lambda n: int(n != 1)
         super(NullTranslations, self).__init__(fp=fp)
-        self.files = filter(None, [getattr(fp, 'name', None)])
+        self.files = list(filter(None, [getattr(fp, 'name', None)]))
         self.domain = self.DEFAULT_DOMAIN
         self._domains = {}
 


Test patch:
diff --git a/tests/test_support.py b/tests/test_support.py
index 4647f6b..efdddeb 100644
--- a/tests/test_support.py
+++ b/tests/test_support.py
@@ -22,7 +22,7 @@ from datetime import date, datetime, timedelta
 from babel import support
 from babel.messages import Catalog
 from babel.messages.mofile import write_mo
-from babel._compat import BytesIO
+from babel._compat import BytesIO, PY2
 
 
 @pytest.mark.usefixtures("os_environ")
@@ -328,3 +328,28 @@ def test_lazy_proxy():
         u"Hello, universe!",
         u"Hello, world!",
     ]
+
+
+def test_catalog_merge_files():
+    # Refs issues #92, #162
+    t1 = support.Translations()
+    assert t1.files == []
+    t1._catalog["foo"] = "bar"
+    if PY2:
+        # Explicitly use the pure-Python `StringIO` class, as we need to
+        # augment it with the `name` attribute, which we can't do for
+        # `babel._compat.BytesIO`, which is `cStringIO.StringIO` under
+        # `PY2`...
+        from StringIO import StringIO
+        fp = StringIO()
+    else:
+        fp = BytesIO()
+    write_mo(fp, Catalog())
+    fp.seek(0)
+    fp.name = "pro.mo"
+    t2 = support.Translations(fp)
+    assert t2.files == ["pro.mo"]
+    t2._catalog["bar"] = "quux"
+    t1.merge(t2)
+    assert t1.files == ["pro.mo"]
+    assert set(t1._catalog.keys()) == set(('', 'foo', 'bar'))