Remove DateValue, drop Py<3.6, fix tests.
authorRadek Czajka <rczajka@rczajka.pl>
Tue, 13 Jun 2023 12:25:43 +0000 (14:25 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Tue, 13 Jun 2023 12:27:53 +0000 (14:27 +0200)
23 files changed:
src/librarian/__init__.py
src/librarian/builders/epub.py
src/librarian/builders/html.py
src/librarian/builders/txt.py
src/librarian/dcparser.py
src/librarian/elements/base.py
src/librarian/html.py
src/librarian/meta/types/date.py [deleted file]
src/librarian/meta/types/wluri.py
src/librarian/picture.py
src/librarian/res/text/template.txt
src/librarian/text.py
tests/files/dcparser/andersen_brzydkie_kaczatko.out
tests/files/dcparser/kochanowski_piesn7.out
tests/files/dcparser/mickiewicz_rybka.out
tests/files/dcparser/sofokles_antygona.out
tests/files/text/asnyk_miedzy_nami_expected.html
tests/files/text/asnyk_miedzy_nami_expected.txt
tests/test_dcparser.py
tests/test_epub.py
tests/test_html_fragments.py
tests/test_text.py
tox.ini

index 68afe74..35c54e0 100644 (file)
@@ -15,6 +15,9 @@ import six
 from six.moves.urllib.request import FancyURLopener
 from .util import makedirs
 
+# Compatibility imports.
+from .meta.types.wluri import WLURI
+
 
 @six.python_2_unicode_compatible
 class UnicodeException(Exception):
@@ -77,17 +80,12 @@ class EmptyNamespace(XMLNamespace):
 XMLNS = XMLNamespace('http://www.w3.org/XML/1998/namespace')
 RDFNS = XMLNamespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
 DCNS = XMLNamespace('http://purl.org/dc/elements/1.1/')
-XINS = XMLNamespace("http://www.w3.org/2001/XInclude")
 XHTMLNS = XMLNamespace("http://www.w3.org/1999/xhtml")
-NCXNS = XMLNamespace("http://www.daisy.org/z3986/2005/ncx/")
-OPFNS = XMLNamespace("http://www.idpf.org/2007/opf")
 PLMETNS = XMLNamespace("http://dl.psnc.pl/schemas/plmet/")
 
 WLNS = EmptyNamespace()
 
 
-
-
 class DocProvider(object):
     """Base class for a repository of XML files.
 
@@ -111,75 +109,6 @@ class DirDocProvider(DocProvider):
         return open(os.path.join(self.dir, fname), 'rb')
 
 
-from . import dcparser
-from .meta.types.wluri import WLURI
-
-
-DEFAULT_BOOKINFO = dcparser.BookInfo(
-    {
-        RDFNS('about'): u'http://wiki.wolnepodreczniki.pl/Lektury:Template'
-    },
-    {
-        DCNS('creator'): [u'Some, Author'],
-        DCNS('title'): [u'Some Title'],
-        DCNS('subject.period'): [u'Unknown'],
-        DCNS('subject.type'): [u'Unknown'],
-        DCNS('subject.genre'): [u'Unknown'],
-        DCNS('date'): ['1970-01-01'],
-        DCNS('language'): [u'pol'],
-        # DCNS('date'): [creation_date],
-        DCNS('publisher'): [u"Fundacja Nowoczesna Polska"],
-        DCNS('description'):
-        [u"""Publikacja zrealizowana w ramach projektu
-        Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa
-        wykonana przez Bibliotekę Narodową z egzemplarza
-        pochodzącego ze zbiorów BN."""],
-        DCNS('identifier.url'): [WLURI.example],
-        DCNS('rights'):
-        [u"Domena publiczna - zm. [OPIS STANU PRAWNEGO TEKSTU]"]
-    }
-)
-
-
-def xinclude_forURI(uri):
-    e = etree.Element(XINS("include"))
-    e.set("href", uri)
-    return etree.tostring(e, encoding='unicode')
-
-
-def wrap_text(ocrtext, creation_date, bookinfo=DEFAULT_BOOKINFO):
-    """Wrap the text within the minimal XML structure with a DC template."""
-    bookinfo.created_at = creation_date
-
-    dcstring = etree.tostring(
-        bookinfo.to_etree(),  method='xml', encoding='unicode',
-        pretty_print=True
-    )
-
-    return u'<utwor>\n' + dcstring + u'\n<plain-text>\n' + ocrtext + \
-        u'\n</plain-text>\n</utwor>'
-
-
-def serialize_raw(element):
-    b = u'' + (element.text or '')
-
-    for child in element.iterchildren():
-        e = etree.tostring(child, method='xml', encoding='unicode',
-                           pretty_print=True)
-        b += e
-
-    return b
-
-
-SERIALIZERS = {
-    'raw': serialize_raw,
-}
-
-
-def serialize_children(element, format='raw'):
-    return SERIALIZERS[format](element)
-
-
 def get_resource(path):
     return os.path.join(os.path.dirname(__file__), path)
 
index 902b460..8544846 100644 (file)
@@ -11,8 +11,6 @@ import librarian.epub
 from librarian.fonts import strip_font
 
 
-
-
 class Xhtml:
     def __init__(self):
         self.element = etree.XML('''<html xmlns="http://www.w3.org/1999/xhtml"><head><link rel="stylesheet" href="style.css" type="text/css"/><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>WolneLektury.pl</title></head><body/></html>''')
@@ -61,8 +59,6 @@ class Builder:
     def forget_fragment(self, name):
         del self.cursors[name]
 
-
-
     @property
     def base_url(self):
         if self._base_url is not None:
@@ -80,6 +76,7 @@ class Builder:
 class EpubBuilder(Builder):
     file_extension = 'epub'
     isbn_field = 'isbn_epub'
+    orphans = True
 
     def __init__(self, *args, **kwargs):
         self.chars = set()
index 5096e28..ed222d3 100644 (file)
@@ -18,6 +18,7 @@ class HtmlBuilder:
     with_footnotes = True
     with_nota_red = True
     no_externalities = False
+    orphans = True
 
     def __init__(self, base_url=None):
         self._base_url = base_url
index 8dba4ae..b796c9b 100644 (file)
@@ -48,21 +48,23 @@ class TxtBuilder:
     file_extension = "txt"
     identifier = "txt"
 
+    orphans = False
+    
     default_license_description = {
         "pol": (
             "Wszystkie zasoby Wolnych Lektur możesz swobodnie wykorzystywać, "
             "publikować i rozpowszechniać pod warunkiem zachowania warunków "
             "licencji i zgodnie z Zasadami wykorzystania Wolnych Lektur.\n"
-            "Ten utwór jest w domenie publicznej. "
+            "Ten utwór jest w domenie publicznej.\n"
             "Wszystkie materiały dodatkowe (przypisy, motywy literackie) są "
-            "udostępnione na Licencji Wolnej Sztuki 1.3 "
-            "(https://artlibre.org/licence/lal/pl/).\n"
+            "udostępnione na Licencji Wolnej Sztuki 1.3: "
+            "https://artlibre.org/licence/lal/pl/\n"
             "Fundacja Nowoczesna Polska zastrzega sobie prawa do wydania "
             "krytycznego zgodnie z art. Art.99(2) Ustawy o prawach autorskich "
-            "i prawach pokrewnych. Wykorzystując zasoby z Wolnych Lektur, "
+            "i prawach pokrewnych.\nWykorzystując zasoby z Wolnych Lektur, "
             "należy pamiętać o zapisach licencji oraz zasadach, które "
-            "spisaliśmy w Zasadach wykorzystania Wolnych Lektur "
-            "(https://wolnelektury.pl/info/zasady-wykorzystania/). Zapoznaj "
+            "spisaliśmy w Zasadach wykorzystania Wolnych Lektur: "
+            "https://wolnelektury.pl/info/zasady-wykorzystania/\nZapoznaj "
             "się z nimi, zanim udostępnisz dalej nasze książki."
         )
     }
@@ -74,19 +76,19 @@ class TxtBuilder:
             "licencji i zgodnie z Zasadami wykorzystania Wolnych Lektur.\n"
             "Ten utwór jest jest udostępniony na licencji {meta.license_description} ({meta.license}). "
             "Wszystkie materiały dodatkowe (przypisy, motywy literackie) są "
-            "udostępnione na Licencji Wolnej Sztuki 1.3 "
-            "(https://artlibre.org/licence/lal/pl/).\n"
+            "udostępnione na Licencji Wolnej Sztuki 1.3: "
+            "https://artlibre.org/licence/lal/pl/\n"
             "Fundacja Nowoczesna Polska zastrzega sobie prawa do wydania "
             "krytycznego zgodnie z art. Art.99(2) Ustawy o prawach autorskich "
-            "i prawach pokrewnych. Wykorzystując zasoby z Wolnych Lektur, "
+            "i prawach pokrewnych.\nWykorzystując zasoby z Wolnych Lektur, "
             "należy pamiętać o zapisach licencji oraz zasadach, które "
-            "spisaliśmy w Zasadach wykorzystania Wolnych Lektur "
-            "(https://wolnelektury.pl/info/zasady-wykorzystania/). Zapoznaj "
+            "spisaliśmy w Zasadach wykorzystania Wolnych Lektur: "
+            "https://wolnelektury.pl/info/zasady-wykorzystania/\nZapoznaj "
             "się z nimi, zanim udostępnisz dalej nasze książki."
         )
     }
 
-    def __init__(self):
+    def __init__(self, base_url=None):
         self.fragments = {
             None: TxtFragment(),
             'header': TxtFragment()
index b89abd1..2edba6c 100644 (file)
@@ -15,7 +15,6 @@ import lxml.etree as etree
 from lxml.etree import XMLSyntaxError
 
 from librarian.meta.types.bool import BoolValue
-from librarian.meta.types.date import DateValue
 from librarian.meta.types.person import Person
 from librarian.meta.types.wluri import WLURI
 from librarian.meta.types import text
@@ -121,7 +120,7 @@ class WorkInfo(six.with_metaclass(DCInfo, object)):
         Field(DCNS('contributor.thanks'), 'thanks', required=False),
 
         Field(DCNS('date'), 'created_at'),
-        Field(DCNS('date.pd'), 'released_to_public_domain_at', DateValue,
+        Field(DCNS('date.pd'), 'released_to_public_domain_at',
               required=False),
         Field(DCNS('publisher'), 'publisher', multiple=True),
 
index d17747d..6036d16 100644 (file)
@@ -104,7 +104,8 @@ class WLElement(etree.ElementBase):
                 newt += builder.hyphenator.inserted(w, u'\u00AD')
             text = newt
 
-        text = re.sub(r'(?<=\s\w)\s+', u'\u00A0', text)
+        if builder.orphans:
+            text = re.sub(r'(?<=\s\w)\s+', u'\u00A0', text)
 
         return text
 
index fddeb2f..6ed5c52 100644 (file)
@@ -241,7 +241,8 @@ def extract_fragments(input_filename):
                 for parent in parents:
                     fragment.append('start', parent)
 
-                open_fragments[fragment.id] = fragment
+                if fragment.id not in open_fragments:
+                    open_fragments[fragment.id] = fragment
 
             # Close existing fragment
             else:
diff --git a/src/librarian/meta/types/date.py b/src/librarian/meta/types/date.py
deleted file mode 100644 (file)
index 7094fc3..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-from datetime import date
-import re
-import time
-from .base import MetaValue
-
-
-class DateValue(MetaValue):
-    @classmethod
-    def from_text(cls, text):
-        """
-        Dates for digitization of pictures. It seems we need the following:
-        ranges:                '1350-1450',
-        centuries:     "XVIII w.'
-        half centuries/decades: '2 poł. XVIII w.', 'XVII w., l. 20'
-        later-then: 'po 1450'
-        circa 'ok. 1813-1814', 'ok.1876-ok.1886
-        turn: 1893/1894
-
-        For now we will translate this to some single date
-        losing information of course.
-        """
-        try:
-            # check out the "N. poł X w." syntax
-            century_format = (
-                "(?:([12]) *poł[.]? +)?([MCDXVI]+) *w[.,]*(?: *l[.]? *([0-9]+))?"
-            )
-            vague_format = "(?:po *|ok. *)?([0-9]{4})(-[0-9]{2}-[0-9]{2})?"
-
-            m = re.match(century_format, text)
-            m2 = re.match(vague_format, text)
-            if m:
-                half = m.group(1)
-                decade = m.group(3)
-                century = roman_to_int(m.group(2))
-                if half is not None:
-                    if decade is not None:
-                        raise ValueError(
-                            "Bad date format. "
-                            "Cannot specify both half and decade of century."
-                        )
-                    half = int(half)
-                    t = ((century*100 + (half-1)*50), 1, 1)
-                else:
-                    decade = int(decade or 0)
-                    t = ((century*100 + decade), 1, 1)
-            elif m2:
-                year = m2.group(1)
-                mon_day = m2.group(2)
-                if mon_day:
-                    t = time.strptime(year + mon_day, "%Y-%m-%d")
-                else:
-                    t = time.strptime(year, '%Y')
-            else:
-                raise ValueError
-
-            return cls(date(t[0], t[1], t[2]))
-        except ValueError:
-            raise ValueError("Unrecognized date format. Try YYYY-MM-DD or YYYY.")
index 92c6017..422b18e 100644 (file)
@@ -4,7 +4,7 @@ from .base import MetaValue
 
 class WLURI(MetaValue):
     """Represents a WL URI. Extracts slug from it."""
-    example = 'http://wolnelektury.pl/katalog/lektura/template/'
+    template = 'http://wolnelektury.pl/katalog/lektura/%s/'
     _re_wl_uri = re.compile(
         r'http://(www\.)?wolnelektury.pl/katalog/lektur[ay]/'
         '(?P<slug>[-a-z0-9]+)/?$'
@@ -13,12 +13,12 @@ class WLURI(MetaValue):
     def __init__(self, slug, uri=None):
         """Contructs an URI from slug.
 
-        >>> print(WLURI.from_slug('a-slug').uri)
+        >>> print(WLURI('a-slug').uri)
         http://wolnelektury.pl/katalog/lektura/a-slug/
 
         """
         if uri is None:
-            uri = 'http://wolnelektury.pl/katalog/lektura/%s/' % slug
+            uri = self.template % slug
         self.uri = uri
         return super().__init__(slug)
 
index 3897d1a..113cece 100644 (file)
@@ -17,11 +17,7 @@ class WLPictureURI(WLURI):
     _re_wl_uri = re.compile(
         'http://wolnelektury.pl/katalog/obraz/(?P<slug>[-a-z0-9]+)/?$'
     )
-
-    @classmethod
-    def from_slug(cls, slug):
-        uri = 'http://wolnelektury.pl/katalog/obraz/%s/' % slug
-        return cls(uri)
+    template = 'http://wolnelektury.pl/katalog/obraz/%s/'
 
 
 def as_wlpictureuri_strict(text):
index fa1429c..c85dabf 100644 (file)
@@ -7,6 +7,6 @@ Wersja lektury w opracowaniu merytorycznym i krytycznym (przypisy i motywy) dost
 
 Utwór opracowany został w ramach projektu Wolne Lektury przez fundację Nowoczesna Polska.
 
-%(license_description)s.%(source)s%(publisher)s
+%(license_description)s%(source)s%(publisher)s
 
 %(description)s%(contributors)s%(funders)s%(isbn)s
index 5b03525..d4eaf34 100644 (file)
@@ -81,7 +81,7 @@ def transform(wldoc, flags=None, **options):
                 "należy pamiętać o zapisach licencji oraz zasadach, które "
                 "spisaliśmy w Zasadach wykorzystania Wolnych Lektur: "
                 "https://wolnelektury.pl/info/zasady-wykorzystania/\nZapoznaj "
-                "się z nimi, zanim udostępnisz dalej nasze książki"
+                "się z nimi, zanim udostępnisz dalej nasze książki."
             )
             license_description = "\n".join(license_description)
 
index 9f07b39..32a1d3c 100644 (file)
@@ -9,7 +9,7 @@
     'kind': u'Epika',
     'source_url': u'http://www.polona.pl/dlibra/doccontent2?id=3563&dirids=4',
     'translators': [u'Niewiadomska, Cecylia'],
-    'released_to_public_domain_at': u'1925-01-01',
+    'released_to_public_domain_at': u'1925',
     'epoch': u'Romantyzm',
     'genre': u'Baśń',
     'technical_editors': [u'Gałecki, Dariusz'],
index 96198a3..f6526ad 100644 (file)
@@ -8,7 +8,7 @@
     'title': u'Pieśń VII (Trudna rada w tej mierze: przyjdzie się rozjechać...)',
     'kind': u'Liryka',
     'source_url': u'http://www.polona.pl/Content/1499',
-    'released_to_public_domain_at': u'1584-01-01',
+    'released_to_public_domain_at': u'1584',
     'epoch': u'Renesans',
     'genre': u'Pieśń',
     'technical_editors': [u'Gałecki, Dariusz'],
index f3c76c0..d13ebf7 100644 (file)
@@ -9,7 +9,7 @@
     'title': u'Rybka',
     'kind': u'Liryka',
     'source_url': u'http://www.polona.pl/Content/2222',
-    'released_to_public_domain_at': u'1855-01-01',
+    'released_to_public_domain_at': u'1855',
     'epoch': u'Romantyzm',
     'genre': u'Ballada',
     'technical_editors': [u'Sutkowska, Olga'],
index 477988f..86bf068 100644 (file)
@@ -10,7 +10,7 @@
     'kind': u'Dramat',
     'source_url': u'http://www.polona.pl/Content/3768',
     'translators': [u'Morawski, Kazimierz'],
-    'released_to_public_domain_at': u'1925-01-01',
+    'released_to_public_domain_at': u'1925',
     'epoch': u'Starożytność',
     'genre': u'Tragedia',
     'technical_editors': [u'Gałecki, Dariusz'],
index 02b9a2f..99c729a 100644 (file)
 <a name="f1" class="target"> </a><a href="#f1" class="anchor">1</a><div class="verse">Między nami <a class="theme-begin" fid="1" name="m1">Nicość</a>nic nie było!<span class="theme-end" fid="1"></span>
 </div>
 <div class="verse">Żadnych zwierzeń, wyznań żadnych!</div>
-<div class="verse">Nic nas z sobą nie łączyło —</div>
+<div class="verse">Nic nas z sobą nie łączyło —</div>
 <div class="verse">Prócz wiosennych marzeń zdradnych;</div>
 </div>
 <div class="stanza" id="sec5">
 <a name="f5" class="target"> </a><a href="#f5" class="anchor">5</a><div class="verse">
-<a class="theme-begin" fid="1189062528872" name="m1189062528872">Natura</a>Prócz tych woni, barw i blasków,</div>
-<div class="verse">Unoszących się w przestrzeni;</div>
+<a class="theme-begin" fid="1189062528872" name="m1189062528872">Natura</a>Prócz tych woni, barw i blasków,</div>
+<div class="verse">Unoszących się w przestrzeni;</div>
 <div class="verse">Prócz szumiących śpiewem lasków</div>
 <div class="verse">I tej świeżej łąk zieleni;</div>
 </div>
 <div class="stanza" id="sec6">
-<div class="verse">Prócz tych kaskad i potoków,</div>
+<div class="verse">Prócz tych kaskad i potoków,</div>
 <a name="f10" class="target"> </a><a href="#f10" class="anchor">10</a><div class="verse">Zraszających każdy parów,</div>
 <div class="verse">Prócz girlandy tęcz, obłoków,</div>
 <div class="verse">Prócz natury słodkich czarów;</div>
@@ -34,7 +34,7 @@
 <div class="stanza" id="sec7">
 <div class="verse">Prócz tych wspólnych, jasnych zdrojów,</div>
 <div class="verse">Z których serce zachwyt piło;</div>
-<a name="f15" class="target"> </a><a href="#f15" class="anchor">15</a><div class="verse">Prócz pierwiosnków i powojów,—</div>
+<a name="f15" class="target"> </a><a href="#f15" class="anchor">15</a><div class="verse">Prócz pierwiosnków i powojów,—</div>
 <div class="verse">Między nami nic nie było!<span class="theme-end" fid="1189062528872"></span><span class="theme-end" fid="1189062500041"></span>
 </div>
 </div>
index 92cc1bd..8baee83 100644 (file)
@@ -33,7 +33,12 @@ Wersja lektury w opracowaniu merytorycznym i krytycznym (przypisy i motywy) dost
 \r
 Utwór opracowany został w ramach projektu Wolne Lektury przez fundację Nowoczesna Polska.\r
 \r
-Ten utwór nie jest objęty majątkowym prawem autorskim i znajduje się w domenie publicznej, co oznacza że możesz go swobodnie wykorzystywać, publikować i rozpowszechniać. Jeśli utwór opatrzony jest dodatkowymi materiałami (przypisy, motywy literackie etc.), które podlegają prawu autorskiemu, to te dodatkowe materiały udostępnione są na licencji Creative Commons Uznanie Autorstwa – Na Tych Samych Warunkach 3.0 PL (http://creativecommons.org/licenses/by-sa/3.0/).\r
+Wszystkie zasoby Wolnych Lektur możesz swobodnie wykorzystywać, publikować i rozpowszechniać pod warunkiem zachowania warunków licencji i zgodnie z Zasadami wykorzystania Wolnych Lektur.\r
+Ten utwór jest w domenie publicznej.\r
+Wszystkie materiały dodatkowe (przypisy, motywy literackie) są udostępnione na Licencji Wolnej Sztuki 1.3: https://artlibre.org/licence/lal/pl/\r
+Fundacja Nowoczesna Polska zastrzega sobie prawa do wydania krytycznego zgodnie z art. Art.99(2) Ustawy o prawach autorskich i prawach pokrewnych.\r
+Wykorzystując zasoby z Wolnych Lektur, należy pamiętać o zapisach licencji oraz zasadach, które spisaliśmy w Zasadach wykorzystania Wolnych Lektur: https://wolnelektury.pl/info/zasady-wykorzystania/\r
+Zapoznaj się z nimi, zanim udostępnisz dalej nasze książki.\r
 \r
 Tekst opracowany na podstawie: (Asnyk, Adam) El...y (1838-1897), Poezye, t. 3,  Gebethner i Wolff, wyd. nowe poprzedzone słowem wstępnym St. Krzemińskiego, Warszawa, 1898\r
 \r
index 4dab764..64e98d7 100644 (file)
@@ -48,14 +48,3 @@ def check_serialize(xml_file):
 def test_serialize():
     for fixture in get_all_fixtures('dcparser', '*.xml'):
         yield check_serialize, fixture
-
-
-def test_asdate():
-    assert_equals(dcparser.as_date(u"2010-10-03"), date(2010, 10, 3))
-    assert_equals(dcparser.as_date(u"2011"), date(2011, 1, 1))
-    assert_equals(dcparser.as_date(u"2 poł. XIX w."), date(1950, 1, 1))
-    assert_equals(dcparser.as_date(u"XVII w., l. 20"), date(1720, 1, 1))
-    assert_equals(dcparser.as_date(u"po 1460"), date(1460, 1, 1))
-    assert_equals(dcparser.as_date(u"ok. 1813-1814"), date(1813, 1, 1))
-    assert_equals(dcparser.as_date(u"ok.1876-ok.1886"), date(1876, 1, 1))
-    assert_equals(dcparser.as_date(u"1893/1894"), date(1893, 1, 1))
index 9ff1b41..c7a262b 100644 (file)
@@ -78,7 +78,7 @@ def test_transform():
     )
     assert_equals(
         book.get_metadata(DC, "creator"),
-        [('Adam Asnyk', {"id": "creator"})]
+        [('Adam Asnyk', {"id": "creator0"})]
     )
     assert_equals(
         book.get_metadata(DC, "publisher"),
index b3915fd..5c89d68 100644 (file)
@@ -17,7 +17,7 @@ class FragmentsTest(unittest.TestCase):
         expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_fragments.html')
 
         closed_fragments, open_fragments = extract_fragments(
-            get_fixture('text', 'asnyk_miedzy_nami_expected.html'))
+            get_fixture('text', 'asnyk_miedzy_nami_expected.legacy.html'))
         assert not open_fragments
         fragments_text = u"\n\n".join(u"%s: %s\n%s" % (f.id, f.themes, f) for f in sorted(closed_fragments.values(), key=lambda f: f.id))
         self.assertEqual(fragments_text, open(expected_output_file_path, 'rb').read().decode('utf-8'))
index 4cb2b7b..c566a48 100644 (file)
@@ -5,6 +5,7 @@
 #
 from __future__ import unicode_literals
 
+import unittest
 from librarian import NoDublinCore
 from librarian.builders import builders
 from librarian.parser import WLDocument as LegacyWLDocument
@@ -13,34 +14,39 @@ from nose.tools import *
 from .utils import get_fixture
 
 
-def test_transform_legacy():
-    expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_expected.txt')
+class TextTests(unittest.TestCase):
+    maxDiff = None
 
-    text = LegacyWLDocument.from_file(
-            get_fixture('text', 'miedzy-nami-nic-nie-bylo.xml')
-        ).as_text().get_bytes()
+    def test_transform_legacy(self):
+        expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_expected.txt')
 
-    assert_equal(text, open(expected_output_file_path, 'rb').read())
+        text = LegacyWLDocument.from_file(
+            get_fixture('text', 'miedzy-nami-nic-nie-bylo.xml')
+        ).as_text().get_bytes().decode('utf-8')
 
+        with open(expected_output_file_path, 'rb') as f:
+            self.assertEqual(text, f.read().decode('utf-8'))
 
-def test_transform():
-    expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_expected.txt')
+    def test_transform(self):
+        expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_expected.txt')
 
-    text = WLDocument(
-        filename=get_fixture('text', 'miedzy-nami-nic-nie-bylo.xml')
-    ).build(builders['txt']).get_bytes()
+        text = WLDocument(
+            filename=get_fixture('text', 'miedzy-nami-nic-nie-bylo.xml')
+        ).build(builders['txt']).get_bytes().decode('utf-8')
 
-    assert_equal(text, open(expected_output_file_path, 'rb').read())
+        with open(expected_output_file_path, 'rb') as f:
+            self.assertEqual(text, f.read().decode('utf-8'))
 
     
-def test_transform_raw():
-    expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_expected_raw.txt')
+    def test_transform_raw(self):
+        expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_expected_raw.txt')
 
-    text = LegacyWLDocument.from_file(
+        text = LegacyWLDocument.from_file(
             get_fixture('text', 'miedzy-nami-nic-nie-bylo.xml')
-        ).as_text(flags=['raw-text']).get_bytes()
+        ).as_text(flags=['raw-text']).get_bytes().decode('utf-8')
 
-    assert_equal(text, open(expected_output_file_path, 'rb').read())
+        with open(expected_output_file_path, 'rb') as f:
+            self.assertEqual(text, f.read().decode('utf-8'))
 
 
 @raises(NoDublinCore)
diff --git a/tox.ini b/tox.ini
index d1fb9b3..e508014 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,7 @@
 [tox]
 envlist =
     clean,
-    py{27,35,36,37,38},
+    py{36,37,38,39},
     stats
 
 [testenv]