diff --git a/i18nspector-0.26.tar.gz b/i18nspector-0.26.tar.gz deleted file mode 100644 index 3c4d59d..0000000 --- a/i18nspector-0.26.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:86dbaedbb8c2e3e834b308025cb5161e3ba0a8fcd575ac0a4f778e0f92e59d6f -size 151236 diff --git a/i18nspector-0.26.tar.gz.asc b/i18nspector-0.26.tar.gz.asc deleted file mode 100644 index 7ae7d73..0000000 --- a/i18nspector-0.26.tar.gz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEzbWhJDrNtjAJrQchLU6zpgFUdfUFAl9vbh4ACgkQLU6zpgFU -dfWqJhAAkRzLUT2C9R92kWKUgC5Law7w2itAKOAdC39fY4G7E6U5IyakMh5bf/vv -X/KsqbnNenla14q0Ll/3aY1zQB5tD5K4dr6QiQtQxIzEIBuz3FrQj4Y2S7qwmp1N -5SprRLbs8EHrqJ3JVuM4OAbSnppHX0QpSKgYrZlB1r0HV06p4QMTvf4G4rBLcF0v -ejrS0+fin1IxncUzq4ZrutW4x30XRbJ3IFTmr0i4y08nivUbJtU/vpGZP6uvyXjy -hpvfotkrpa1f2Am5UjQtvh74wl22o9Y4z0EKG2z0ewXQCljr7BVhiTEOvY1e1JU8 -eVncD34RfSIc2LtVyNxrs9YA7XoKKOpgrDRIibeBXDrcOksTRiFGkWqIxFBQnK59 -tFau95ZR/ODL/GZCQVDkl+RaWKFhN24I1vkKN7lX+p3so0RD/csn6HiKhDuBebQv -3iToXtCehZQAKpMCXoVDEYx1yYp6gcvGU6jT1qZGRswuqLs+6qM/XgPDU4JFm5tW -WP6M8pglI18w3nCIc0g14DSpaVutEWKJwWpuFUT3jBqqRTGzWW/rEoQCVVFxACfn -3doXKNBNy4JKExgR0FxP1dgrivWThWC+1hVELl+R02SROv3mWjdf+2pPRGyNuVhW -ENQLP1H9H/jtJHHR0FjfYBSAOUEbBHrLjJszKSv2oIKpPVoPdTQ= -=9tJH ------END PGP SIGNATURE----- diff --git a/i18nspector-0.27.1.tar.gz b/i18nspector-0.27.1.tar.gz new file mode 100644 index 0000000..8a52052 --- /dev/null +++ b/i18nspector-0.27.1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c705acb8b863c30ef1eed92f655b31f51955919d75efbd292242c5f1f2dc9c9d +size 153732 diff --git a/i18nspector-0.27.1.tar.gz.asc b/i18nspector-0.27.1.tar.gz.asc new file mode 100644 index 0000000..1ddc6cd --- /dev/null +++ b/i18nspector-0.27.1.tar.gz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEzbWhJDrNtjAJrQchLU6zpgFUdfUFAmL1ZhoACgkQLU6zpgFU +dfUfzg/7BoporS07RvjlY5HPGN+KYzK5CCM+5anOTlwomzhwBZv3VyCMqKM5mI2Y +zORY+El1vHnQEyMJpu+OmJNw5v1lvBHjpvgi78GXhSCiqRW3wyXRh7E38dlGbJsW +VvOv8TIhsUmNRvVQDh0JM0X9GDgMaX9GW83uDCccav/lrorOEG8eoCAdYnubrSC4 +vNf8U4jEoRdDaCIwURLr6PTsduwPnq79DmGzO//GpYoUmaA5Eiogj0IQLxT3Z6bQ +xsq2sD8NnRlxt7QX5sqzQGIxY3frPt6eN92zxMWpELLJaDIINY4b97fq65lplCmd +4eXGNib+VnGs+fIAPbKAnJ8/NUUFQafDGyEV37RbN3ICJFYOKULH7xjikfNvcPxk +ZAfasH/q4G4hi61JIUkRcq+ybHnzKO4ZvF48BN2eIdGUHfOywyl06JffULqxmqnw +sIA+yDK3YW2LP7FtKSgvVuMB8jdOXbEu1rU5guq1awMCJNWBG0857XMywVkJnFsx +LS7H753cGXjWVnggdV0D2h1QKVuc8WIaUWJ7Av/BmgqjdSZa66nimWb6MbH5LOBL +SKxjJ1M7QMaK06Io9gn6T/EsIyRvSJ81lerRksr7qJ5UBq10/HA7uSRy8Lau5JMX +Qu5aJk7yLHiTIPsXvuS8mWhrnktfDpYTwKobVT/dvJMsqgs0SKs= +=GtDG +-----END PGP SIGNATURE----- diff --git a/i18nspector.changes b/i18nspector.changes index 0d76123..f70eddc 100644 --- a/i18nspector.changes +++ b/i18nspector.changes @@ -1,3 +1,22 @@ +------------------------------------------------------------------- +Tue Apr 2 06:50:39 UTC 2024 - ming li + +- Update to version 0.27.1: + * Use uppercase for metavars in the help message. + * Improve the test suite. + + Make it possible to use pytest as the test harness. + + Skip glibc-supported C.* locales. + + Remove switch-to-pytest.patch + + update to version 0.27 + * Recognize the “markdown-text” message flag. + * Drop support for Python < 3.6. + * Make “-j auto” take CPU affinity into account. + * Stop using deprecated abc.abstractproperty(). + * Improve documentation: + + Update Docutils homepage URL. + ------------------------------------------------------------------- Mon Nov 22 05:39:58 UTC 2021 - Steve Kowalik diff --git a/i18nspector.spec b/i18nspector.spec index c62a2df..830883f 100644 --- a/i18nspector.spec +++ b/i18nspector.spec @@ -1,7 +1,7 @@ # # spec file for package i18nspector # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: i18nspector -Version: 0.26 +Version: 0.27.1 Release: 0 Summary: Tool for Checking gettext POT/PO/MO Files License: MIT @@ -26,8 +26,6 @@ URL: https://jwilk.net/software/i18nspector Source0: https://github.com/jwilk/i18nspector/releases/download/%{version}/%{name}-%{version}.tar.gz Source1: https://github.com/jwilk/i18nspector/releases/download/%{version}/%{name}-%{version}.tar.gz.asc Source2: %{name}.keyring -# PATCH-FIX-UPSTREAM Adapted from gh#jwilk/i18nspector/pull/8 -Patch0: switch-to-pytest.patch BuildRequires: python3-devel >= 3.4 # Requires for tests. BuildRequires: python3-curses @@ -35,12 +33,8 @@ BuildRequires: python3-polib BuildRequires: python3-pytest BuildRequires: python3-rply # -Requires: python3-polib -Requires: python3-rply BuildArch: noarch -%if 0%{?suse_version} && 0%{?suse_version} < 1230 Requires: python3 >= 3.4 -%endif %description i18nspector is a tool for checking translation templates (POT), message diff --git a/switch-to-pytest.patch b/switch-to-pytest.patch deleted file mode 100644 index 135c4d0..0000000 --- a/switch-to-pytest.patch +++ /dev/null @@ -1,4672 +0,0 @@ -From b84035ee74cc646ddbabbb99d565e50973fe7b1d Mon Sep 17 00:00:00 2001 -From: Stuart Prescott -Date: Thu, 28 Oct 2021 16:39:36 +1100 -Subject: [PATCH 01/10] Change tests from nose to pytest - -Some of the 'yield' tests are easy to refactor into parameterised tests -while others would take a fair amount of rethinking and/or additional -helper functions. At this stage, the relevant functions are simply decorated -with a 'collect_yielded' decorator that collects all the yield tests at -collection time and sets them up as a parameterised test to be run later. ---- - pytest.ini | 4 + - tests/conftest.py | 21 ++ - tests/run-tests | 21 +- - tests/test_changelog.py | 33 +- - tests/test_domains.py | 23 +- - tests/test_encodings.py | 117 +++--- - tests/test_gettext.py | 134 ++++--- - tests/test_iconv.py | 11 +- - tests/test_ling.py | 566 +++++++++++++++--------------- - tests/test_misc.py | 49 ++- - tests/test_moparser.py | 21 +- - tests/test_polib4us.py | 14 +- - tests/test_strformat_c.py | 404 +++++++++++---------- - tests/test_strformat_perlbrace.py | 19 +- - tests/test_strformat_pybrace.py | 99 +++--- - tests/test_strformat_python.py | 198 ++++++----- - tests/test_tags.py | 27 +- - tests/test_terminal.py | 8 +- - tests/test_version.py | 8 +- - tests/test_xml.py | 12 +- - tests/tools.py | 41 ++- - 21 files changed, 935 insertions(+), 895 deletions(-) - create mode 100644 pytest.ini - create mode 100644 tests/conftest.py - -Index: i18nspector-0.26/pytest.ini -=================================================================== ---- /dev/null -+++ i18nspector-0.26/pytest.ini -@@ -0,0 +1,4 @@ -+[pytest] -+testpaths = tests -+python_classes = test_* -+python_functions = test *_test test_* -Index: i18nspector-0.26/tests/conftest.py -=================================================================== ---- /dev/null -+++ i18nspector-0.26/tests/conftest.py -@@ -0,0 +1,19 @@ -+ -+import os -+import tempfile -+ -+ -+def pytest_sessionstart(session): -+ envvar = 'XDG_CACHE_HOME' -+ old_xdg_cache_home = os.environ.get(envvar, None) -+ xdg_temp_dir = tempfile.TemporaryDirectory(prefix='i18nspector.tests.') # pylint: disable=consider-using-with -+ os.environ[envvar] = xdg_temp_dir.name -+ -+ def cleanup(): -+ xdg_temp_dir.cleanup() -+ if old_xdg_cache_home is None: -+ del os.environ[envvar] -+ else: -+ os.environ[envvar] = old_xdg_cache_home -+ -+ session.config.add_cleanup(cleanup) -Index: i18nspector-0.26/tests/run-tests -=================================================================== ---- i18nspector-0.26.orig/tests/run-tests -+++ i18nspector-0.26/tests/run-tests -@@ -1,6 +1,6 @@ --#!/usr/bin/env python3 -+#!/bin/bash - --# Copyright © 2013-2016 Jakub Wilk -+# Copyright © 2021 Stuart Prescott - # - # Permission is hereby granted, free of charge, to any person obtaining a copy - # of this software and associated documentation files (the “Software”), to deal -@@ -20,19 +20,6 @@ - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - # SOFTWARE. - --import os --import sys --import tempfile -+set -e - --import nose -- --sys.path[0] += '/..' -- --from tests import blackbox_tests -- --if __name__ == '__main__': -- with tempfile.TemporaryDirectory(prefix='i18nspector.tests.') as tmpdir: -- os.environ['XDG_CACHE_HOME'] = tmpdir -- nose.main(addplugins=[blackbox_tests.Plugin()]) -- --# vim:ts=4 sts=4 sw=4 et -+pytest -rsx "$@" -Index: i18nspector-0.26/tests/test_changelog.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_changelog.py -+++ i18nspector-0.26/tests/test_changelog.py -@@ -21,11 +21,9 @@ - import os - import re - --from nose.tools import ( -- assert_not_equal, --) -- - import lib.tags -+from . import tools -+ - - here = os.path.dirname(__file__) - docdir = os.path.join(here, os.pardir, 'doc') -@@ -52,32 +50,39 @@ rename_re = re.compile( - r'([\w-]+) [(]from ([\w-]+)[)]' - ) - --def test_tags(): -- path = os.path.join(docdir, 'changelog') -- with open(path, 'rt', encoding='UTF-8') as file: -- changelog = file.read() -- summaries = summary_re.findall(changelog) -- changelog_tags = set() -+ -+@tools.collect_yielded -+def test_tag_paramerisation(): - def add(info, tag): - del info - if tag in changelog_tags: - raise AssertionError('changelog adds tag twice: ' + tag) - changelog_tags.add(tag) -+ - def remove(info, tag): - del info - if tag not in changelog_tags: - raise AssertionError('changelog removes non-existent tag: ' + tag) - changelog_tags.remove(tag) -+ - def rename(info, removed_tag, added_tag): -- assert_not_equal(removed_tag, added_tag) -+ assert removed_tag != added_tag - remove(info, removed_tag) - add(info, added_tag) -+ - def check(info, tag): - del info - if tag not in changelog_tags: - raise AssertionError('tag not in changelog: ' + tag) - if tag not in data_tags: - raise AssertionError('changelog adds unknown tag: ' + tag) -+ -+ path = os.path.join(docdir, 'changelog') -+ with open(path, 'rt', encoding='UTF-8') as file: -+ changelog = file.read() -+ summaries = summary_re.findall(changelog) -+ changelog_tags = set() -+ - for summary in reversed(summaries): - match = summary_details_re.match(summary) - for key, lines in match.groupdict().items(): -@@ -86,16 +91,17 @@ def test_tags(): - lines = [l[8:] for l in lines.splitlines()] - if key == 'added': - for tag in lines: -- yield add, 'add', tag -+ yield add, ('add', tag) - elif key == 'renamed': - for line in lines: - added_tag, removed_tag = rename_re.match(line).groups() -- yield rename, 'rename', removed_tag, added_tag -+ yield rename, ('rename', removed_tag, added_tag) - else: - assert False - data_tags = frozenset(tag.name for tag in lib.tags.iter_tags()) - for tag in sorted(changelog_tags | data_tags): -- yield check, 'check', tag -+ yield check, ('check', tag) -+ - - def test_trailing_whitespace(): - path = os.path.join(docdir, 'changelog') -Index: i18nspector-0.26/tests/test_domains.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_domains.py -+++ i18nspector-0.26/tests/test_domains.py -@@ -18,12 +18,7 @@ - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - # SOFTWARE. - --from nose.tools import ( -- assert_false, -- assert_is, -- assert_raises, -- assert_true, --) -+import pytest - - import lib.domains as M - -@@ -32,9 +27,9 @@ class test_special_domains: - def t(self, domain, special=True): - result = M.is_special_domain(domain) - if special: -- assert_true(result) -+ assert result - else: -- assert_false(result) -+ assert not result - - def test_ok(self): - self.t('test.jwilk.net', False) -@@ -68,9 +63,9 @@ class test_special_domain_emails: - def t(self, email, special=True): - result = M.is_email_in_special_domain(email) - if special: -- assert_true(result) -+ assert result - else: -- assert_false(result) -+ assert not result - - def test_valid(self): - self.t('jwilk@test.jwilk.net', False) -@@ -79,14 +74,14 @@ class test_special_domain_emails: - self.t('jwilk@example.net') - - def test_no_at(self): -- with assert_raises(ValueError): -+ with pytest.raises(ValueError): - self.t('jwilk%jwilk.net') - - class test_dotless_domains: - - def t(self, domain, dotless=True): - result = M.is_dotless_domain(domain) -- assert_is(result, dotless) -+ assert result is dotless - - def test_dotless(self): - self.t('net') -@@ -99,7 +94,7 @@ class test_dotless_emails: - - def t(self, email, dotless=True): - result = M.is_email_in_dotless_domain(email) -- assert_is(result, dotless) -+ assert result is dotless - - def test_dotless(self): - self.t('jwilk@net') -@@ -108,7 +103,7 @@ class test_dotless_emails: - self.t('jwilk@example.net', False) - - def test_no_at(self): -- with assert_raises(ValueError): -+ with pytest.raises(ValueError): - self.t('jwilk%jwilk.net') - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_encodings.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_encodings.py -+++ i18nspector-0.26/tests/test_encodings.py -@@ -20,79 +20,85 @@ - - import curses.ascii - import sys -+import unittest - --import nose --from nose.tools import ( -- assert_equal, -- assert_false, -- assert_is_none, -- assert_not_in, -- assert_raises, -- assert_true, --) -+import pytest - - import lib.encodings as E - - from . import tools - -+ -+# methods using the tools.collect_yielded decorator don't have a 'self' -+# since they end up being run before 'self' exists. pylint doesn't -+# understand this unusual situation -+# pylint: disable=no-method-argument -+ -+ - class test_is_portable_encoding: - - def test_found(self): -- assert_true(E.is_portable_encoding('ISO-8859-2')) -+ assert E.is_portable_encoding('ISO-8859-2') - - def test_found_(self): -- assert_true(E.is_portable_encoding('ISO_8859-2')) -+ assert E.is_portable_encoding('ISO_8859-2') - - def test_found_nonpython(self): -- assert_false(E.is_portable_encoding('KOI8-T')) -- assert_true(E.is_portable_encoding('KOI8-T', python=False)) -+ assert not E.is_portable_encoding('KOI8-T') -+ assert E.is_portable_encoding('KOI8-T', python=False) - - def test_notfound(self): -- assert_false(E.is_portable_encoding('ISO-8859-16')) -- assert_false(E.is_portable_encoding('ISO-8859-16', python=False)) -+ assert not E.is_portable_encoding('ISO-8859-16') -+ assert not E.is_portable_encoding('ISO-8859-16', python=False) - - class test_propose_portable_encoding: - - def test_identity(self): - encoding = 'ISO-8859-2' - portable_encoding = E.propose_portable_encoding(encoding) -- assert_equal(portable_encoding, encoding) -+ assert portable_encoding == encoding - -- def test_found(self): -+ @tools.collect_yielded -+ def test_found(): - def t(encoding, expected_portable_encoding): - portable_encoding = E.propose_portable_encoding(encoding) -- assert_equal(portable_encoding, expected_portable_encoding) -- yield t, 'ISO8859-2', 'ISO-8859-2' -- yield t, 'ISO_8859-2', 'ISO-8859-2' -- yield t, 'Windows-1250', 'CP1250' -+ assert portable_encoding == expected_portable_encoding -+ yield t, ('ISO8859-2', 'ISO-8859-2') -+ yield t, ('ISO_8859-2', 'ISO-8859-2') -+ yield t, ('Windows-1250', 'CP1250') - - def test_notfound(self): - portable_encoding = E.propose_portable_encoding('ISO-8859-16') -- assert_is_none(portable_encoding) -+ assert portable_encoding is None -+ -+ -+def _test_missing(encoding): -+ assert not E.is_ascii_compatible_encoding(encoding) -+ with pytest.raises(E.EncodingLookupError): -+ E.is_ascii_compatible_encoding(encoding, missing_ok=False) -+ - - class test_ascii_compatibility: - -- def test_portable(self): -+ @tools.collect_yielded -+ def test_portable(): - def t(encoding): -- assert_true(E.is_ascii_compatible_encoding(encoding)) -- assert_true(E.is_ascii_compatible_encoding(encoding, missing_ok=False)) -+ assert E.is_ascii_compatible_encoding(encoding) -+ assert E.is_ascii_compatible_encoding(encoding, missing_ok=False) - for encoding in E.get_portable_encodings(): - yield t, encoding - -- def test_incompatible(self): -+ @tools.collect_yielded -+ def test_incompatible(): - def t(encoding): -- assert_false(E.is_ascii_compatible_encoding(encoding)) -- assert_false(E.is_ascii_compatible_encoding(encoding, missing_ok=False)) -+ assert not E.is_ascii_compatible_encoding(encoding) -+ assert not E.is_ascii_compatible_encoding(encoding, missing_ok=False) - yield t, 'UTF-7' - yield t, 'UTF-16' - -- def _test_missing(self, encoding): -- assert_false(E.is_ascii_compatible_encoding(encoding)) -- with assert_raises(E.EncodingLookupError): -- E.is_ascii_compatible_encoding(encoding, missing_ok=False) -- -- def test_non_text(self): -- t = self._test_missing -+ @tools.collect_yielded -+ def test_non_text(): -+ t = _test_missing - yield t, 'base64_codec' - yield t, 'bz2_codec' - yield t, 'hex_codec' -@@ -102,7 +108,8 @@ class test_ascii_compatibility: - yield t, 'zlib_codec' - - def test_missing(self): -- self._test_missing('eggs') -+ _test_missing('eggs') -+ - - class test_get_character_name: - -@@ -110,44 +117,44 @@ class test_get_character_name: - for i in range(ord('a'), ord('z')): - u = chr(i) - name = E.get_character_name(u) -- assert_equal(name, 'LATIN SMALL LETTER ' + u.upper()) -+ assert name == 'LATIN SMALL LETTER ' + u.upper() - u = chr(i).upper() - name = E.get_character_name(u) -- assert_equal(name, 'LATIN CAPITAL LETTER ' + u) -+ assert name == 'LATIN CAPITAL LETTER ' + u - - def test_c0(self): - for i, curses_name in zip(range(0, 0x20), curses.ascii.controlnames): - u = chr(i) - name = E.get_character_name(u) - expected_name = 'control character ' + curses_name -- assert_equal(name, expected_name) -+ assert name == expected_name - - def test_del(self): - name = E.get_character_name('\x7F') -- assert_equal(name, 'control character DEL') -+ assert name == 'control character DEL' - - def test_c1(self): - for i in range(0x80, 0xA0): - u = chr(i) - name = E.get_character_name(u) -- assert_true(name.startswith('control character ')) -+ assert name.startswith('control character ') - - def test_uniqueness(self): - names = set() - for i in range(0, 0x100): - u = chr(i) - name = E.get_character_name(u) -- assert_not_in(name, names) -+ assert name not in names - names.add(name) - - def test_non_character(self): - name = E.get_character_name('\uFFFE') -- assert_equal(name, 'non-character') -+ assert name == 'non-character' - name = E.get_character_name('\uFFFF') -- assert_equal(name, 'non-character') -+ assert name == 'non-character' - - def test_lookup_error(self): -- with assert_raises(ValueError): -+ with pytest.raises(ValueError): - E.get_character_name('\uE000') - - class test_extra_encoding: -@@ -164,13 +171,13 @@ class test_extra_encoding: - except LookupError: - pass - else: -- raise nose.SkipTest( -+ raise unittest.SkipTest( - 'python{ver[0]}.{ver[1]} supports the {enc} encoding'.format( - ver=sys.version_info, - enc=encoding - ) - ) -- with assert_raises(LookupError): -+ with pytest.raises(LookupError): - dec() - E.install_extra_encodings() - enc() -@@ -181,7 +188,7 @@ class test_extra_encoding: - E.install_extra_encodings() - encoding = '8859-2' - portable_encoding = E.propose_portable_encoding(encoding) -- assert_equal('ISO-' + encoding, portable_encoding) -+ assert 'ISO-' + encoding == portable_encoding - - @tools.fork_isolation - def test_not_allowed(self): -@@ -193,14 +200,14 @@ class test_extra_encoding: - except LookupError: - pass - else: -- raise nose.SkipTest( -+ raise unittest.SkipTest( - 'python{ver[0]}.{ver[1]} supports the {enc} encoding'.format( - ver=sys.version_info, - enc=encoding - ) - ) - E.install_extra_encodings() -- with assert_raises(LookupError): -+ with pytest.raises(LookupError): - enc() - - _viscii_unicode = 'Ti\u1EBFng Vi\u1EC7t' -@@ -211,13 +218,13 @@ class test_extra_encoding: - E.install_extra_encodings() - u = self._viscii_unicode - b = u.encode('VISCII') -- assert_equal(b, self._viscii_bytes) -+ assert b == self._viscii_bytes - - @tools.fork_isolation - def test_8b_encode_error(self): - E.install_extra_encodings() - u = self._viscii_unicode -- with assert_raises(UnicodeEncodeError): -+ with pytest.raises(UnicodeEncodeError): - u.encode('KOI8-RU') - - @tools.fork_isolation -@@ -225,13 +232,13 @@ class test_extra_encoding: - E.install_extra_encodings() - b = self._viscii_bytes - u = b.decode('VISCII') -- assert_equal(u, self._viscii_unicode) -+ assert u == self._viscii_unicode - - @tools.fork_isolation - def test_8b_decode_error(self): - E.install_extra_encodings() - b = self._viscii_bytes -- with assert_raises(UnicodeDecodeError): -+ with pytest.raises(UnicodeDecodeError): - b.decode('KOI8-T') - - _euc_tw_unicode = '\u4E2D\u6587' -@@ -242,13 +249,13 @@ class test_extra_encoding: - E.install_extra_encodings() - u = self._euc_tw_unicode - b = u.encode('EUC-TW') -- assert_equal(b, self._euc_tw_bytes) -+ assert b == self._euc_tw_bytes - - @tools.fork_isolation - def test_mb_encode_error(self): - E.install_extra_encodings() - u = self._viscii_unicode -- with assert_raises(UnicodeEncodeError): -+ with pytest.raises(UnicodeEncodeError): - u.encode('EUC-TW') - - @tools.fork_isolation -@@ -256,13 +263,13 @@ class test_extra_encoding: - E.install_extra_encodings() - b = self._euc_tw_bytes - u = b.decode('EUC-TW') -- assert_equal(u, self._euc_tw_unicode) -+ assert u == self._euc_tw_unicode - - @tools.fork_isolation - def test_mb_decode_error(self): - E.install_extra_encodings() - b = self._viscii_bytes -- with assert_raises(UnicodeDecodeError): -+ with pytest.raises(UnicodeDecodeError): - b.decode('EUC-TW') - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_gettext.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_gettext.py -+++ i18nspector-0.26/tests/test_gettext.py -@@ -20,16 +20,7 @@ - - import datetime - --from nose.tools import ( -- assert_equal, -- assert_false, -- assert_is_instance, -- assert_is_none, -- assert_is_not_none, -- assert_less, -- assert_raises, -- assert_true, --) -+import pytest - - import lib.gettext as M - -@@ -38,21 +29,21 @@ class test_header_fields: - def test_nonempty(self): - # XXX Update this number after editing data/header-fields: - expected = 12 -- assert_equal(len(M.header_fields), expected) -+ assert len(M.header_fields) == expected - - def test_no_x(self): - for field in M.header_fields: -- assert_false(field.startswith('X-')) -+ assert not field.startswith('X-') - - def test_valid(self): - for field in M.header_fields: -- assert_true(M.is_valid_field_name(field)) -+ assert M.is_valid_field_name(field) - - class test_header_parser: - - def t(self, message, expected): - parsed = list(M.parse_header(message)) -- assert_equal(parsed, expected) -+ assert parsed == expected - - def test_ok(self): - self.t( -@@ -91,8 +82,8 @@ class test_plural_exp: - def t(self, s, n=None, fn=None): - f = M.parse_plural_expression(s) - if n is not None: -- assert_is_not_none(fn) -- assert_equal(f(n), fn) -+ assert fn is not None -+ assert f(n) == fn - - def test_const(self): - n = 42 -@@ -101,7 +92,7 @@ class test_plural_exp: - def test_const_overflow(self): - m = (1 << 32) - 1 - self.t(str(m), m, m) -- with assert_raises(OverflowError): -+ with pytest.raises(OverflowError): - self.t(str(m + 1), m + 1, False) - self.t(str(m + 1), m + 42, False) - -@@ -112,7 +103,7 @@ class test_plural_exp: - def test_var_overflow(self): - m = (1 << 32) - 1 - self.t('n', m, m) -- with assert_raises(OverflowError): -+ with pytest.raises(OverflowError): - self.t('n', m + 1, False) - self.t('42', m + 1, 42) - -@@ -122,7 +113,7 @@ class test_plural_exp: - def test_add_overflow(self): - m = (1 << 32) - 1 - self.t('n + 42', m - 42, m) -- with assert_raises(OverflowError): -+ with pytest.raises(OverflowError): - self.t('n + 42', m - 41, False) - self.t('n + 42', m - 23, False) - -@@ -130,7 +121,7 @@ class test_plural_exp: - self.t('n - 23', 37, 14) - - def test_sub_overflow(self): -- with assert_raises(OverflowError): -+ with pytest.raises(OverflowError): - self.t('n - 23', 6, False) - - def test_mul(self): -@@ -140,7 +131,7 @@ class test_plural_exp: - m = (1 << 32) - 1 - assert m % 17 == 0 - self.t('n * 17', m / 17, m) -- with assert_raises(OverflowError): -+ with pytest.raises(OverflowError): - self.t('n * 17', (m / 17) + 1, False) - self.t('n * 2', (m + 1) / 2, False) - -@@ -148,14 +139,14 @@ class test_plural_exp: - self.t('105 / n', 17, 6) - - def test_div_by_0(self): -- with assert_raises(ZeroDivisionError): -+ with pytest.raises(ZeroDivisionError): - self.t('105 / n', 0, False) - - def test_mod(self): - self.t('105 % n', 17, 3) - - def test_mod_by_0(self): -- with assert_raises(ZeroDivisionError): -+ with pytest.raises(ZeroDivisionError): - self.t('105 % n', 0, False) - - def test_and(self): -@@ -228,75 +219,75 @@ class test_plural_exp: - self.t('(2 ? 3 : 7) ? 23 : 37') - - def test_badly_nested_conditional(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('2 ? (3 : 7 ? ) : 23') - - def test_unary_minus(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('-37') -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('23 + (-37)') - - def test_unary_plus(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('+42') -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('23 + (+37)') - - def test_func_call(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('n(42)') -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('42(n)') - - def test_unbalanced_parentheses(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('(6 * 7') -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6 * 7)') -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6) * (7') - - def test_dangling_binop(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6 +') - - def test_junk_token(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6 # 7') - - def test_shift(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6 << 7') -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6 >> 7') - - def test_pow(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6 ** 7') - - def test_floor_div(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6 // 7') - - def test_tuple(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('()') -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('(6, 7)') - - def test_starred(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('*42') - - def test_exotic_whitespace(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('6 *\xA07') - - def test_empty(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('') -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t(' ') - - class test_codomain: -@@ -308,9 +299,9 @@ class test_codomain: - cd = f.codomain() - if min_ is None: - assert max_ is None -- assert_is_none(cd) -+ assert cd is None - else: -- assert_equal(cd, (min_, max_)) -+ assert cd == (min_, max_) - - def test_num(self): - self.t('0', 0, 0) -@@ -530,9 +521,9 @@ class test_period: - op = f.period() - if offset is None: - assert period is None -- assert_is_none(op) -+ assert op is None - else: -- assert_equal(op, (offset, period)) -+ assert op == (offset, period) - - def test_num(self): - self.t('42', 0, 1) -@@ -619,20 +610,20 @@ class test_plural_forms: - - def t(self, s, *, n, ljunk='', rjunk=''): - if ljunk or rjunk: -- with assert_raises(self.error): -+ with pytest.raises(self.error): - M.parse_plural_forms(s) - else: - (n0, expr0) = M.parse_plural_forms(s) - del expr0 -- assert_equal(n0, n) -+ assert n0 == n - (n1, expr1, ljunk1, rjunk1) = M.parse_plural_forms(s, strict=False) # pylint: disable=unbalanced-tuple-unpacking - del expr1 -- assert_equal(n1, n) -- assert_equal(ljunk1, ljunk) -- assert_equal(rjunk1, rjunk) -+ assert n1 == n -+ assert ljunk1 == ljunk -+ assert rjunk1 == rjunk - - def test_nplurals_0(self): -- with assert_raises(self.error): -+ with pytest.raises(self.error): - self.t('nplurals=0; plural=0;', n=0) - - def test_nplurals_positive(self): -@@ -651,15 +642,15 @@ class test_fix_date_format: - - def t(self, old, expected): - if expected is None: -- with assert_raises(M.DateSyntaxError): -+ with pytest.raises(M.DateSyntaxError): - M.fix_date_format(old) - else: - new = M.fix_date_format(old) -- assert_is_not_none(new) -- assert_equal(new, expected) -+ assert new is not None -+ assert new == expected - - def tbp(self, old): -- with assert_raises(M.BoilerplateDate): -+ with pytest.raises(M.BoilerplateDate): - M.fix_date_format(old) - - def test_boilerplate(self): -@@ -710,10 +701,9 @@ class test_fix_date_format: - self.t('2002-01-01 03:05', None) - - def test_tz_hint(self): -- assert_equal( -- M.fix_date_format('2002-01-01 03:05', tz_hint='+0900'), -- '2002-01-01 03:05+0900', -- ) -+ assert ( -+ M.fix_date_format('2002-01-01 03:05', tz_hint='+0900') == -+ '2002-01-01 03:05+0900') - - def test_gmt_before_tz(self): - self.t( -@@ -762,30 +752,30 @@ class test_parse_date: - t = staticmethod(M.parse_date) - - def test_nonexistent(self): -- with assert_raises(M.DateSyntaxError): -+ with pytest.raises(M.DateSyntaxError): - self.t('2010-02-29 19:49+0200') - - def test_existent(self): - d = self.t('2003-09-08 21:26+0200') -- assert_equal(d.second, 0) -- assert_is_instance(d, datetime.datetime) -- assert_equal(str(d), '2003-09-08 21:26:00+02:00') -+ assert d.second == 0 -+ assert isinstance(d, datetime.datetime) -+ assert str(d) == '2003-09-08 21:26:00+02:00' - - def test_epoch(self): - d = self.t('2008-04-03 16:06+0300') -- assert_less(M.epoch, d) -+ assert M.epoch < d - - class test_string_formats: - - def test_nonempty(self): - # XXX Update this number after editing data/string-formats: - expected = 28 -- assert_equal(len(M.string_formats), expected) -+ assert len(M.string_formats) == expected - - def test_lowercase(self): - for s in M.string_formats: -- assert_is_instance(s, str) -- assert_true(s) -- assert_equal(s, s.lower()) -+ assert isinstance(s, str) -+ assert s -+ assert s == s.lower() - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_iconv.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_iconv.py -+++ i18nspector-0.26/tests/test_iconv.py -@@ -18,10 +18,7 @@ - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - # SOFTWARE. - --from nose.tools import ( -- assert_equal, -- assert_raises, --) -+import pytest - - import lib.iconv as M - -@@ -32,11 +29,11 @@ class _test: - - def test_encode(self): - b = M.encode(self.u, self.e) -- assert_equal(b, self.b) -+ assert b == self.b - - def test_decode(self): - u = M.decode(self.b, self.e) -- assert_equal(u, self.u) -+ assert u == self.u - - class test_iso2(_test): - u = 'Żrą łódź? Część miń!' -@@ -50,7 +47,7 @@ class test_tcvn(_test): - - def test_incomplete_char(): - b = 'Ę'.encode('UTF-8')[:1] -- with assert_raises(UnicodeDecodeError): -+ with pytest.raises(UnicodeDecodeError): - M.decode(b, 'UTF-8') - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_ling.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_ling.py -+++ i18nspector-0.26/tests/test_ling.py -@@ -18,19 +18,9 @@ - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - # SOFTWARE. - --import nose --from nose.tools import ( -- assert_equal, -- assert_false, -- assert_in, -- assert_is, -- assert_is_instance, -- assert_is_none, -- assert_not_equal, -- assert_not_in, -- assert_raises, -- assert_true, --) -+import unittest -+ -+import pytest - - import lib.encodings - import lib.ling -@@ -45,12 +35,12 @@ class test_fix_codes: - - def t(self, l1, l2): - lang = L.parse_language(l1) -- assert_equal(str(lang), l1) -+ assert str(lang) == l1 - if l1 == l2: -- assert_is_none(lang.fix_codes()) -+ assert lang.fix_codes() is None - else: -- assert_is(lang.fix_codes(), True) -- assert_equal(str(lang), l2) -+ assert lang.fix_codes() is True -+ assert str(lang) == l2 - - def test_2_to_2(self): - self.t('grc', 'grc') -@@ -69,18 +59,18 @@ class test_fix_codes: - self.t('gre_GR', 'el_GR') - - def test_ll_not_found(self): -- with assert_raises(L.FixingLanguageCodesFailed): -+ with pytest.raises(L.FixingLanguageCodesFailed): - self.t('ry', '') - - def test_cc_not_found(self): -- with assert_raises(L.FixingLanguageCodesFailed): -+ with pytest.raises(L.FixingLanguageCodesFailed): - self.t('el_RY', '') - - def test_language_repr(): - # Language.__repr__() is never used by i18nspector itself, - # but it's useful for debugging test failures. - lng = T('el') -- assert_equal(repr(lng), '') -+ assert repr(lng) == '' - - class test_language_equality: - -@@ -89,47 +79,47 @@ class test_language_equality: - def test_eq(self): - l1 = T('el', 'GR') - l2 = T('el', 'GR') -- assert_equal(l1, l2) -- assert_equal(l2, l1) -+ assert l1 == l2 -+ assert l2 == l1 - - def test_ne(self): - l1 = T('el') - l2 = T('el', 'GR') -- assert_not_equal(l1, l2) -- assert_not_equal(l2, l1) -+ assert l1 != l2 -+ assert l2 != l1 - - def test_ne_other_type(self): - l1 = T('el') -- assert_not_equal(l1, 42) -- assert_not_equal(42, l1) -+ assert l1 != 42 -+ assert 42 != l1 # pylint: disable=misplaced-comparison-constant - - def test_almost_equal(self): - l1 = T('el') - l2 = T('el', 'GR') -- assert_true(l1.is_almost_equal(l2)) -- assert_true(l2.is_almost_equal(l1)) -+ assert l1.is_almost_equal(l2) -+ assert l2.is_almost_equal(l1) - - def test_not_almost_equal(self): - l1 = T('el', 'GR') - l2 = T('grc', 'GR') -- assert_false(l1.is_almost_equal(l2)) -- assert_false(l2.is_almost_equal(l1)) -+ assert not l1.is_almost_equal(l2) -+ assert not l2.is_almost_equal(l1) - - def test_not_almost_equal_other_type(self): - l1 = T('el') -- with assert_raises(TypeError): -+ with pytest.raises(TypeError): - l1.is_almost_equal(42) - - class test_remove_encoding: - - def t(self, l1, l2): - lang = L.parse_language(l1) -- assert_equal(str(lang), l1) -+ assert str(lang) == l1 - if l1 == l2: -- assert_is_none(lang.remove_encoding()) -+ assert lang.remove_encoding() is None - else: -- assert_is(lang.remove_encoding(), True) -- assert_equal(str(lang), l2) -+ assert lang.remove_encoding() is True -+ assert str(lang) == l2 - - def test_without_encoding(self): - self.t('el', 'el') -@@ -141,12 +131,12 @@ class test_remove_nonlinguistic_modifier - - def t(self, l1, l2): - lang = L.parse_language(l1) -- assert_equal(str(lang), l1) -+ assert str(lang) == l1 - if l1 == l2: -- assert_is_none(lang.remove_nonlinguistic_modifier()) -+ assert lang.remove_nonlinguistic_modifier() is None - else: -- assert_is(lang.remove_nonlinguistic_modifier(), True) -- assert_equal(str(lang), l2) -+ assert lang.remove_nonlinguistic_modifier() is True -+ assert str(lang) == l2 - - def test_quot(self): - self.t('en@quot', 'en@quot') -@@ -162,18 +152,18 @@ class test_lookup_territory_code: - - def test_found(self): - cc = L.lookup_territory_code('GR') -- assert_equal(cc, 'GR') -+ assert cc == 'GR' - - def test_not_found(self): - cc = L.lookup_territory_code('RG') -- assert_is_none(cc) -+ assert cc is None - - class test_get_language_for_name: - - def t(self, name, expected): - lang = L.get_language_for_name(name) -- assert_is_instance(lang, T) -- assert_equal(str(lang), expected) -+ assert isinstance(lang, T) -+ assert str(lang) == expected - - def test_found(self): - self.t('Greek', 'el') -@@ -194,69 +184,75 @@ class test_get_language_for_name: - self.t('Pashto, Pushto', 'ps') - - def test_lone_comma(self): -- with assert_raises(LookupError): -+ with pytest.raises(LookupError): - self.t(',', None) - - def test_not_found(self): -- with assert_raises(LookupError): -+ with pytest.raises(LookupError): - self.t('Nadsat', None) - - class test_parse_language: - - def test_ll(self): - lang = L.parse_language('el') -- assert_equal(lang.language_code, 'el') -- assert_is_none(lang.territory_code) -- assert_is_none(lang.encoding) -- assert_is_none(lang.modifier) -+ assert lang.language_code == 'el' -+ assert lang.territory_code is None -+ assert lang.encoding is None -+ assert lang.modifier is None - - def test_lll(self): - lang = L.parse_language('ell') -- assert_equal(lang.language_code, 'ell') -- assert_is_none(lang.territory_code) -- assert_is_none(lang.encoding) -- assert_is_none(lang.modifier) -+ assert lang.language_code == 'ell' -+ assert lang.territory_code is None -+ assert lang.encoding is None -+ assert lang.modifier is None - - def test_ll_cc(self): - lang = L.parse_language('el_GR') -- assert_equal(lang.language_code, 'el') -- assert_equal(lang.territory_code, 'GR') -- assert_is_none(lang.encoding) -- assert_is_none(lang.modifier) -+ assert lang.language_code == 'el' -+ assert lang.territory_code == 'GR' -+ assert lang.encoding is None -+ assert lang.modifier is None - - def test_ll_cc_enc(self): - lang = L.parse_language('el_GR.UTF-8') -- assert_equal(lang.language_code, 'el') -- assert_equal(lang.territory_code, 'GR') -- assert_equal(lang.encoding, 'UTF-8') -- assert_is_none(lang.modifier) -+ assert lang.language_code == 'el' -+ assert lang.territory_code == 'GR' -+ assert lang.encoding == 'UTF-8' -+ assert lang.modifier is None - - def test_ll_cc_modifier(self): - lang = L.parse_language('en_US@quot') -- assert_equal(lang.language_code, 'en') -- assert_equal(lang.territory_code, 'US') -- assert_is_none(lang.encoding) -- assert_equal(lang.modifier, 'quot') -+ assert lang.language_code == 'en' -+ assert lang.territory_code == 'US' -+ assert lang.encoding is None -+ assert lang.modifier == 'quot' - - def test_syntax_error(self): -- with assert_raises(L.LanguageSyntaxError): -+ with pytest.raises(L.LanguageSyntaxError): - L.parse_language('GR') - - class test_get_primary_languages: - - def test_found(self): - langs = L.get_primary_languages() -- assert_in('el', langs) -+ assert 'el' in langs - - def test_not_found(self): - langs = L.get_primary_languages() -- assert_not_in('ry', langs) -+ assert 'ry' not in langs - -- def test_iso_639(self): -+ # methods using the tools.collect_yielded decorator don't have a 'self' -+ # since they end up being run before 'self' exists. pylint doesn't -+ # understand this unusual situation -+ -+ @tools.collect_yielded -+ def test_iso_639(): -+ # pylint: disable=no-method-argument - def t(lang_str): - lang = L.parse_language(lang_str) -- assert_is_none(lang.fix_codes()) -- assert_equal(str(lang), lang_str) -+ assert lang.fix_codes() is None -+ assert str(lang) == lang_str - for lang_str in L.get_primary_languages(): - yield t, lang_str - -@@ -267,34 +263,30 @@ class test_get_plural_forms: - return lang.get_plural_forms() - - def test_found_ll(self): -- assert_equal( -- self.t('el'), -- ['nplurals=2; plural=n != 1;'] -- ) -+ assert ( -+ self.t('el') == -+ ['nplurals=2; plural=n != 1;']) - - def test_found_ll_cc(self): -- assert_equal( -- self.t('el_GR'), -- ['nplurals=2; plural=n != 1;'] -- ) -+ assert ( -+ self.t('el_GR') == -+ ['nplurals=2; plural=n != 1;']) - - def test_en_ca(self): -- assert_equal( -- self.t('en'), -- self.t('en_CA'), -- ) -+ assert ( -+ self.t('en') == -+ self.t('en_CA')) - - def test_pt_br(self): -- assert_not_equal( -- self.t('pt'), -- self.t('pt_BR'), -- ) -+ assert ( -+ self.t('pt') != -+ self.t('pt_BR')) - - def test_not_known(self): -- assert_is_none(self.t('la')) -+ assert self.t('la') is None - - def test_not_found(self): -- assert_is_none(self.t('ry')) -+ assert self.t('ry') is None - - class test_principal_territory: - -@@ -302,115 +294,116 @@ class test_principal_territory: - # el -> el_GR - lang = L.parse_language('el') - cc = lang.get_principal_territory_code() -- assert_equal(cc, 'GR') -+ assert cc == 'GR' - - def test_remove_2(self): - # el_GR -> el - lang = L.parse_language('el_GR') -- assert_equal(str(lang), 'el_GR') -+ assert str(lang) == 'el_GR' - rc = lang.remove_principal_territory_code() -- assert_is(rc, True) -- assert_equal(str(lang), 'el') -+ assert rc is True -+ assert str(lang) == 'el' - - def test_found_3(self): - # ang -> ang_GB - lang = L.parse_language('ang') - cc = lang.get_principal_territory_code() -- assert_equal(cc, 'GB') -+ assert cc == 'GB' - - def test_remove_3(self): - # ang_GB -> ang - lang = L.parse_language('ang_GB') -- assert_equal(str(lang), 'ang_GB') -+ assert str(lang) == 'ang_GB' - rc = lang.remove_principal_territory_code() -- assert_is(rc, True) -- assert_equal(str(lang), 'ang') -+ assert rc is True -+ assert str(lang) == 'ang' - - def test_no_principal_territory_code(self): - # en -/-> en_US - lang = L.parse_language('en') - cc = lang.get_principal_territory_code() -- assert_is_none(cc) -+ assert cc is None - - def test_no_remove_principal_territory_code(self): - # en_US -/-> en - lang = L.parse_language('en_US') -- assert_equal(str(lang), 'en_US') -+ assert str(lang) == 'en_US' - rc = lang.remove_principal_territory_code() -- assert_is_none(rc) -- assert_equal(str(lang), 'en_US') -+ assert rc is None -+ assert str(lang) == 'en_US' - - def test_not_found(self): - lang = L.parse_language('ry') - cc = lang.get_principal_territory_code() -- assert_equal(cc, None) -+ assert cc is None - - class test_unrepresentable_characters: - - def test_ll_bad(self): - lang = L.parse_language('pl') - result = lang.get_unrepresentable_characters('ISO-8859-1') -- assert_not_equal(result, []) -+ assert result != [] - - def test_ll_ok(self): - lang = L.parse_language('pl') - result = lang.get_unrepresentable_characters('ISO-8859-2') -- assert_equal(result, []) -+ assert result == [] - - def test_ll_cc_bad(self): - lang = L.parse_language('pl_PL') - result = lang.get_unrepresentable_characters('ISO-8859-1') -- assert_not_equal(result, []) -+ assert result != [] - - def test_ll_cc_ok(self): - lang = L.parse_language('pl_PL') - result = lang.get_unrepresentable_characters('ISO-8859-2') -- assert_equal(result, []) -+ assert result == [] - - def test_ll_mod_bad(self): - lang = L.parse_language('en@quot') - result = lang.get_unrepresentable_characters('ISO-8859-1') -- assert_not_equal(result, []) -+ assert result != [] - - def test_ll_mod_ok(self): - lang = L.parse_language('en@quot') - result = lang.get_unrepresentable_characters('UTF-8') -- assert_equal(result, []) -+ assert result == [] - - def test_ll_cc_mod_bad(self): - lang = L.parse_language('en_US@quot') - result = lang.get_unrepresentable_characters('ISO-8859-1') -- assert_not_equal(result, []) -+ assert result != [] - - def test_ll_cc_mod_ok(self): - lang = L.parse_language('en_US@quot') - result = lang.get_unrepresentable_characters('UTF-8') -- assert_equal(result, []) -+ assert result == [] - - def test_ll_optional(self): - # U+0178 (LATIN CAPITAL LETTER Y WITH DIAERESIS) is not representable - # in ISO-8859-1, but we normally turn a blind eye to this. - lang = L.parse_language('fr') - result = lang.get_unrepresentable_characters('ISO-8859-1') -- assert_equal(result, []) -+ assert result == [] - result = lang.get_unrepresentable_characters('ISO-8859-1', strict=True) -- assert_not_equal(result, []) -+ assert result != [] - - def test_ll_not_found(self): - lang = L.parse_language('ry') - result = lang.get_unrepresentable_characters('ISO-8859-1') -- assert_is_none(result) -+ assert result is None - - @tools.fork_isolation - def test_extra_encoding(self): - encoding = 'GEORGIAN-PS' - lang = L.parse_language('pl') -- with assert_raises(LookupError): -+ with pytest.raises(LookupError): - ''.encode(encoding) - E.install_extra_encodings() - result = lang.get_unrepresentable_characters(encoding) -- assert_not_equal(result, []) -+ assert result != [] - -+@tools.collect_yielded - def test_glibc_supported(): - def t(l): - lang = L.parse_language(l) -@@ -419,16 +412,16 @@ def test_glibc_supported(): - except L.FixingLanguageCodesFailed: - # FIXME: some ISO-639-3 codes are not recognized yet - if len(l.split('_')[0]) == 3: -- raise nose.SkipTest('expected failure') -+ raise unittest.SkipTest('expected failure') - reason = locales_to_skip.get(l) - if reason is not None: -- raise nose.SkipTest(reason) -+ raise unittest.SkipTest(reason) - raise - assert_equal(str(lang), l) - try: - file = open('/usr/share/i18n/SUPPORTED', encoding='ASCII') - except OSError as exc: -- raise nose.SkipTest(exc) -+ raise unittest.SkipTest(exc) - locales = set() - with file: - for line in file: -@@ -452,6 +445,7 @@ def test_glibc_supported(): - for l in sorted(locales): - yield t, l - -+@tools.collect_yielded - def test_poedit(): - # https://github.com/vslavik/poedit/blob/v1.8.1-oss/src/language_impl_legacy.h - # There won't be any new names in this table, -@@ -459,178 +453,178 @@ def test_poedit(): - def t(name, poedit_ll): - poedit_ll = L.parse_language(poedit_ll) - ll = L.get_language_for_name(name) -- assert_equal(ll, poedit_ll) -+ assert ll == poedit_ll - def x(name, poedit_ll): - poedit_ll = L.parse_language(poedit_ll) -- with assert_raises(LookupError): -+ with pytest.raises(LookupError): - L.get_language_for_name(name) -- raise nose.SkipTest('expected failure') -- yield t, 'Abkhazian', 'ab' -- yield t, 'Afar', 'aa' -- yield t, 'Afrikaans', 'af' -- yield t, 'Albanian', 'sq' -- yield t, 'Amharic', 'am' -- yield t, 'Arabic', 'ar' -- yield t, 'Armenian', 'hy' -- yield t, 'Assamese', 'as' -- yield t, 'Avestan', 'ae' -- yield t, 'Aymara', 'ay' -- yield t, 'Azerbaijani', 'az' -- yield t, 'Bashkir', 'ba' -- yield t, 'Basque', 'eu' -- yield t, 'Belarusian', 'be' -- yield t, 'Bengali', 'bn' -- yield t, 'Bislama', 'bi' -- yield t, 'Bosnian', 'bs' -- yield t, 'Breton', 'br' -- yield t, 'Bulgarian', 'bg' -- yield t, 'Burmese', 'my' -- yield t, 'Catalan', 'ca' -- yield t, 'Chamorro', 'ch' -- yield t, 'Chechen', 'ce' -- yield t, 'Chichewa; Nyanja', 'ny' -- yield t, 'Chinese', 'zh' -- yield t, 'Church Slavic', 'cu' -- yield t, 'Chuvash', 'cv' -- yield t, 'Cornish', 'kw' -- yield t, 'Corsican', 'co' -- yield t, 'Croatian', 'hr' -- yield t, 'Czech', 'cs' -- yield t, 'Danish', 'da' -- yield t, 'Dutch', 'nl' -- yield t, 'Dzongkha', 'dz' -- yield t, 'English', 'en' -- yield t, 'Esperanto', 'eo' -- yield t, 'Estonian', 'et' -- yield t, 'Faroese', 'fo' -- yield t, 'Fijian', 'fj' -- yield t, 'Finnish', 'fi' -- yield t, 'French', 'fr' -- yield t, 'Frisian', 'fy' -- yield t, 'Friulian', 'fur' -- yield t, 'Gaelic', 'gd' -- yield t, 'Galician', 'gl' -- yield t, 'Georgian', 'ka' -- yield t, 'German', 'de' -- yield t, 'Greek', 'el' -- yield t, 'Guarani', 'gn' -- yield t, 'Gujarati', 'gu' -- yield t, 'Hausa', 'ha' -- yield t, 'Hebrew', 'he' -- yield t, 'Herero', 'hz' -- yield t, 'Hindi', 'hi' -- yield t, 'Hiri Motu', 'ho' -- yield t, 'Hungarian', 'hu' -- yield t, 'Icelandic', 'is' -- yield t, 'Indonesian', 'id' -- yield t, 'Interlingua', 'ia' -- yield t, 'Interlingue', 'ie' -- yield t, 'Inuktitut', 'iu' -- yield t, 'Inupiaq', 'ik' -- yield t, 'Irish', 'ga' -- yield t, 'Italian', 'it' -- yield t, 'Japanese', 'ja' -- yield t, 'Javanese', 'jv' # https://github.com/vslavik/poedit/pull/193 -- yield t, 'Kalaallisut', 'kl' -- yield t, 'Kannada', 'kn' -- yield t, 'Kashmiri', 'ks' -- yield t, 'Kazakh', 'kk' -- yield t, 'Khmer', 'km' -- yield t, 'Kikuyu', 'ki' -- yield t, 'Kinyarwanda', 'rw' -- yield t, 'Komi', 'kv' -- yield t, 'Korean', 'ko' -- yield t, 'Kuanyama', 'kj' -- yield t, 'Kurdish', 'ku' -- yield t, 'Kyrgyz', 'ky' -- yield t, 'Lao', 'lo' -- yield t, 'Latin', 'la' -- yield t, 'Latvian', 'lv' -- yield t, 'Letzeburgesch', 'lb' -- yield t, 'Lingala', 'ln' -- yield t, 'Lithuanian', 'lt' -- yield t, 'Macedonian', 'mk' -- yield t, 'Malagasy', 'mg' -- yield t, 'Malay', 'ms' -- yield t, 'Malayalam', 'ml' -- yield t, 'Maltese', 'mt' -- yield t, 'Maori', 'mi' -- yield t, 'Marathi', 'mr' -- yield t, 'Marshall', 'mh' -- yield t, 'Moldavian', 'ro_MD' # XXX poedit uses deprecated "mo" -- yield t, 'Mongolian', 'mn' -- yield t, 'Nauru', 'na' -- yield t, 'Navajo', 'nv' -- yield t, 'Ndebele, South', 'nr' -- yield t, 'Ndonga', 'ng' -- yield t, 'Nepali', 'ne' -- yield t, 'Northern Sami', 'se' -- yield t, 'Norwegian Bokmal', 'nb' -- yield t, 'Norwegian Nynorsk', 'nn' -- yield t, 'Occitan', 'oc' -- yield t, 'Oriya', 'or' -- yield t, 'Ossetian; Ossetic', 'os' -- yield t, 'Pali', 'pi' -- yield t, 'Panjabi', 'pa' -- yield t, 'Pashto, Pushto', 'ps' -- yield t, 'Persian', 'fa' -- yield t, 'Polish', 'pl' -- yield t, 'Portuguese', 'pt' -- yield t, 'Quechua', 'qu' -- yield t, 'Rhaeto-Romance', 'rm' -- yield t, 'Romanian', 'ro' -- yield t, 'Rundi', 'rn' -- yield t, 'Russian', 'ru' -- yield t, 'Samoan', 'sm' -- yield t, 'Sangro', 'sg' -- yield t, 'Sanskrit', 'sa' -- yield t, 'Sardinian', 'sc' -- yield t, 'Serbian', 'sr' -- yield t, 'Sesotho', 'st' -- yield t, 'Setswana', 'tn' -- yield t, 'Shona', 'sn' -- yield t, 'Sindhi', 'sd' -- yield t, 'Sinhalese', 'si' -- yield t, 'Siswati', 'ss' -- yield t, 'Slovak', 'sk' -- yield t, 'Slovenian', 'sl' -- yield t, 'Somali', 'so' -- yield t, 'Spanish', 'es' -- yield t, 'Sundanese', 'su' -- yield t, 'Swahili', 'sw' -- yield t, 'Swedish', 'sv' -- yield t, 'Tagalog', 'tl' -- yield t, 'Tahitian', 'ty' -- yield t, 'Tajik', 'tg' -- yield t, 'Tamil', 'ta' -- yield t, 'Tatar', 'tt' -- yield t, 'Telugu', 'te' -- yield t, 'Thai', 'th' -- yield t, 'Tibetan', 'bo' -- yield t, 'Tigrinya', 'ti' -- yield t, 'Tonga', 'to' -- yield t, 'Tsonga', 'ts' -- yield t, 'Turkish', 'tr' -- yield t, 'Turkmen', 'tk' -- yield t, 'Twi', 'tw' -- yield t, 'Ukrainian', 'uk' -- yield t, 'Urdu', 'ur' -- yield t, 'Uyghur', 'ug' -- yield t, 'Uzbek', 'uz' -- yield t, 'Vietnamese', 'vi' -- yield t, 'Volapuk', 'vo' -- yield t, 'Walloon', 'wa' -- yield t, 'Welsh', 'cy' -- yield t, 'Wolof', 'wo' -- yield t, 'Xhosa', 'xh' -- yield t, 'Yiddish', 'yi' -- yield t, 'Yoruba', 'yo' -- yield t, 'Zhuang', 'za' -- yield t, 'Zulu', 'zu' -+ raise unittest.SkipTest('expected failure') -+ yield t, ('Abkhazian', 'ab') -+ yield t, ('Afar', 'aa') -+ yield t, ('Afrikaans', 'af') -+ yield t, ('Albanian', 'sq') -+ yield t, ('Amharic', 'am') -+ yield t, ('Arabic', 'ar') -+ yield t, ('Armenian', 'hy') -+ yield t, ('Assamese', 'as') -+ yield t, ('Avestan', 'ae') -+ yield t, ('Aymara', 'ay') -+ yield t, ('Azerbaijani', 'az') -+ yield t, ('Bashkir', 'ba') -+ yield t, ('Basque', 'eu') -+ yield t, ('Belarusian', 'be') -+ yield t, ('Bengali', 'bn') -+ yield t, ('Bislama', 'bi') -+ yield t, ('Bosnian', 'bs') -+ yield t, ('Breton', 'br') -+ yield t, ('Bulgarian', 'bg') -+ yield t, ('Burmese', 'my') -+ yield t, ('Catalan', 'ca') -+ yield t, ('Chamorro', 'ch') -+ yield t, ('Chechen', 'ce') -+ yield t, ('Chichewa; Nyanja', 'ny') -+ yield t, ('Chinese', 'zh') -+ yield t, ('Church Slavic', 'cu') -+ yield t, ('Chuvash', 'cv') -+ yield t, ('Cornish', 'kw') -+ yield t, ('Corsican', 'co') -+ yield t, ('Croatian', 'hr') -+ yield t, ('Czech', 'cs') -+ yield t, ('Danish', 'da') -+ yield t, ('Dutch', 'nl') -+ yield t, ('Dzongkha', 'dz') -+ yield t, ('English', 'en') -+ yield t, ('Esperanto', 'eo') -+ yield t, ('Estonian', 'et') -+ yield t, ('Faroese', 'fo') -+ yield t, ('Fijian', 'fj') -+ yield t, ('Finnish', 'fi') -+ yield t, ('French', 'fr') -+ yield t, ('Frisian', 'fy') -+ yield t, ('Friulian', 'fur') -+ yield t, ('Gaelic', 'gd') -+ yield t, ('Galician', 'gl') -+ yield t, ('Georgian', 'ka') -+ yield t, ('German', 'de') -+ yield t, ('Greek', 'el') -+ yield t, ('Guarani', 'gn') -+ yield t, ('Gujarati', 'gu') -+ yield t, ('Hausa', 'ha') -+ yield t, ('Hebrew', 'he') -+ yield t, ('Herero', 'hz') -+ yield t, ('Hindi', 'hi') -+ yield t, ('Hiri Motu', 'ho') -+ yield t, ('Hungarian', 'hu') -+ yield t, ('Icelandic', 'is') -+ yield t, ('Indonesian', 'id') -+ yield t, ('Interlingua', 'ia') -+ yield t, ('Interlingue', 'ie') -+ yield t, ('Inuktitut', 'iu') -+ yield t, ('Inupiaq', 'ik') -+ yield t, ('Irish', 'ga') -+ yield t, ('Italian', 'it') -+ yield t, ('Japanese', 'ja') -+ yield t, ('Javanese', 'jv') # https://github.com/vslavik/poedit/pull/193 -+ yield t, ('Kalaallisut', 'kl') -+ yield t, ('Kannada', 'kn') -+ yield t, ('Kashmiri', 'ks') -+ yield t, ('Kazakh', 'kk') -+ yield t, ('Khmer', 'km') -+ yield t, ('Kikuyu', 'ki') -+ yield t, ('Kinyarwanda', 'rw') -+ yield t, ('Komi', 'kv') -+ yield t, ('Korean', 'ko') -+ yield t, ('Kuanyama', 'kj') -+ yield t, ('Kurdish', 'ku') -+ yield t, ('Kyrgyz', 'ky') -+ yield t, ('Lao', 'lo') -+ yield t, ('Latin', 'la') -+ yield t, ('Latvian', 'lv') -+ yield t, ('Letzeburgesch', 'lb') -+ yield t, ('Lingala', 'ln') -+ yield t, ('Lithuanian', 'lt') -+ yield t, ('Macedonian', 'mk') -+ yield t, ('Malagasy', 'mg') -+ yield t, ('Malay', 'ms') -+ yield t, ('Malayalam', 'ml') -+ yield t, ('Maltese', 'mt') -+ yield t, ('Maori', 'mi') -+ yield t, ('Marathi', 'mr') -+ yield t, ('Marshall', 'mh') -+ yield t, ('Moldavian', 'ro_MD') # XXX poedit uses deprecated "mo" -+ yield t, ('Mongolian', 'mn') -+ yield t, ('Nauru', 'na') -+ yield t, ('Navajo', 'nv') -+ yield t, ('Ndebele, South', 'nr') -+ yield t, ('Ndonga', 'ng') -+ yield t, ('Nepali', 'ne') -+ yield t, ('Northern Sami', 'se') -+ yield t, ('Norwegian Bokmal', 'nb') -+ yield t, ('Norwegian Nynorsk', 'nn') -+ yield t, ('Occitan', 'oc') -+ yield t, ('Oriya', 'or') -+ yield t, ('Ossetian; Ossetic', 'os') -+ yield t, ('Pali', 'pi') -+ yield t, ('Panjabi', 'pa') -+ yield t, ('Pashto, Pushto', 'ps') -+ yield t, ('Persian', 'fa') -+ yield t, ('Polish', 'pl') -+ yield t, ('Portuguese', 'pt') -+ yield t, ('Quechua', 'qu') -+ yield t, ('Rhaeto-Romance', 'rm') -+ yield t, ('Romanian', 'ro') -+ yield t, ('Rundi', 'rn') -+ yield t, ('Russian', 'ru') -+ yield t, ('Samoan', 'sm') -+ yield t, ('Sangro', 'sg') -+ yield t, ('Sanskrit', 'sa') -+ yield t, ('Sardinian', 'sc') -+ yield t, ('Serbian', 'sr') -+ yield t, ('Sesotho', 'st') -+ yield t, ('Setswana', 'tn') -+ yield t, ('Shona', 'sn') -+ yield t, ('Sindhi', 'sd') -+ yield t, ('Sinhalese', 'si') -+ yield t, ('Siswati', 'ss') -+ yield t, ('Slovak', 'sk') -+ yield t, ('Slovenian', 'sl') -+ yield t, ('Somali', 'so') -+ yield t, ('Spanish', 'es') -+ yield t, ('Sundanese', 'su') -+ yield t, ('Swahili', 'sw') -+ yield t, ('Swedish', 'sv') -+ yield t, ('Tagalog', 'tl') -+ yield t, ('Tahitian', 'ty') -+ yield t, ('Tajik', 'tg') -+ yield t, ('Tamil', 'ta') -+ yield t, ('Tatar', 'tt') -+ yield t, ('Telugu', 'te') -+ yield t, ('Thai', 'th') -+ yield t, ('Tibetan', 'bo') -+ yield t, ('Tigrinya', 'ti') -+ yield t, ('Tonga', 'to') -+ yield t, ('Tsonga', 'ts') -+ yield t, ('Turkish', 'tr') -+ yield t, ('Turkmen', 'tk') -+ yield t, ('Twi', 'tw') -+ yield t, ('Ukrainian', 'uk') -+ yield t, ('Urdu', 'ur') -+ yield t, ('Uyghur', 'ug') -+ yield t, ('Uzbek', 'uz') -+ yield t, ('Vietnamese', 'vi') -+ yield t, ('Volapuk', 'vo') -+ yield t, ('Walloon', 'wa') -+ yield t, ('Welsh', 'cy') -+ yield t, ('Wolof', 'wo') -+ yield t, ('Xhosa', 'xh') -+ yield t, ('Yiddish', 'yi') -+ yield t, ('Yoruba', 'yo') -+ yield t, ('Zhuang', 'za') -+ yield t, ('Zulu', 'zu') - # TODO: -- yield x, '(Afan) Oromo', 'om' -- yield x, 'Bihari', 'bh' # "bh" is marked as collective in ISO-639-2 -- yield x, 'Serbian (Latin)', 'sr_RS@latin' -- yield x, 'Serbo-Croatian', 'sh' # "sh" is deprecated in favor of "sr", "hr", "bs" -+ yield x, ('(Afan) Oromo', 'om') -+ yield x, ('Bihari', 'bh') # "bh" is marked as collective in ISO-639-2 -+ yield x, ('Serbian (Latin)', 'sr_RS@latin') -+ yield x, ('Serbo-Croatian', 'sh') # "sh" is deprecated in favor of "sr", "hr", "bs" - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_misc.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_misc.py -+++ i18nspector-0.26/tests/test_misc.py -@@ -24,14 +24,7 @@ import stat - import tempfile - import time - --from nose.tools import ( -- assert_almost_equal, -- assert_equal, -- assert_is_instance, -- assert_is_not_none, -- assert_raises, -- assert_true, --) -+import pytest - - import lib.misc as M - -@@ -40,11 +33,11 @@ from . import tools - class test_unsorted: - - def t(self, lst, expected): -- assert_is_instance(lst, list) -+ assert isinstance(lst, list) - r = M.unsorted(lst) -- assert_equal(r, expected) -+ assert r == expected - r = M.unsorted(x for x in lst) -- assert_equal(r, expected) -+ assert r == expected - - def test_0(self): - self.t([], None) -@@ -71,7 +64,7 @@ class test_unsorted: - while True: - yield 23 - r = M.unsorted(iterable()) -- assert_equal(r, (37, 23)) -+ assert r == (37, 23) - - class test_check_sorted: - -@@ -79,25 +72,24 @@ class test_check_sorted: - M.check_sorted([17, 23, 37]) - - def test_unsorted(self): -- with assert_raises(M.DataIntegrityError) as cm: -+ with pytest.raises(M.DataIntegrityError) as cm: - M.check_sorted([23, 37, 17]) -- assert_equal(str(cm.exception), '37 > 17') -+ assert str(cm.value) == '37 > 17' - - def test_sorted_vk(): - lst = ['eggs', 'spam', 'ham'] - d = dict(enumerate(lst)) -- assert_equal( -- lst, -- list(M.sorted_vk(d)) -- ) -+ assert ( -+ lst == -+ list(M.sorted_vk(d))) - - class test_utc_now: - - def test_types(self): - now = M.utc_now() -- assert_is_instance(now, datetime.datetime) -- assert_is_not_none(now.tzinfo) -- assert_equal(now.tzinfo.utcoffset(now), datetime.timedelta(0)) -+ assert isinstance(now, datetime.datetime) -+ assert now.tzinfo is not None -+ assert now.tzinfo.utcoffset(now) == datetime.timedelta(0) - - @tools.fork_isolation - def test_tz_resistance(self): -@@ -108,18 +100,17 @@ class test_utc_now: - now1 = t('Etc/GMT-4') - now2 = t('Etc/GMT+2') - tdelta = (now1 - now2).total_seconds() -- assert_almost_equal(tdelta, 0, delta=0.75) -+ assert abs(tdelta - 0) <= 0.75 - - class test_format_range: - - def t(self, x, y, max, expected): # pylint: disable=redefined-builtin -- assert_equal( -- M.format_range(range(x, y), max=max), -- expected -- ) -+ assert ( -+ M.format_range(range(x, y), max=max) == -+ expected) - - def test_max_is_lt_4(self): -- with assert_raises(ValueError): -+ with pytest.raises(ValueError): - self.t(5, 10, 3, None) - - def test_len_lt_max(self): -@@ -139,7 +130,7 @@ def test_throwaway_tempdir(): - with M.throwaway_tempdir('test'): - d = tempfile.gettempdir() - st = os.stat(d) -- assert_equal(stat.S_IMODE(st.st_mode), 0o700) -- assert_true(stat.S_ISDIR(st.st_mode)) -+ assert stat.S_IMODE(st.st_mode) == 0o700 -+ assert stat.S_ISDIR(st.st_mode) - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_moparser.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_moparser.py -+++ i18nspector-0.26/tests/test_moparser.py -@@ -20,10 +20,7 @@ - - import random - --from nose.tools import ( -- assert_equal, -- assert_raises, --) -+import pytest - - import lib.moparser as M - -@@ -38,21 +35,21 @@ def parser_for_bytes(data): - class test_magic: - - def test_value(self): -- assert_equal(M.little_endian_magic, b'\xDE\x12\x04\x95') -- assert_equal(M.big_endian_magic, b'\x95\x04\x12\xDE') -+ assert M.little_endian_magic == b'\xDE\x12\x04\x95' -+ assert M.big_endian_magic == b'\x95\x04\x12\xDE' - - def test_short(self): - for j in range(0, 3): - data = M.little_endian_magic[:j] -- with assert_raises(M.SyntaxError) as cm: -+ with pytest.raises(M.SyntaxError) as cm: - parser_for_bytes(data) -- assert_equal(str(cm.exception), 'unexpected magic') -+ assert str(cm.value) == 'unexpected magic' - - def test_full(self): - for magic in {M.little_endian_magic, M.big_endian_magic}: -- with assert_raises(M.SyntaxError) as cm: -+ with pytest.raises(M.SyntaxError) as cm: - parser_for_bytes(magic) -- assert_equal(str(cm.exception), 'truncated file') -+ assert str(cm.value) == 'truncated file' - - def test_random(self): - while True: -@@ -62,8 +59,8 @@ class test_magic: - if random_magic in {M.little_endian_magic, M.big_endian_magic}: - continue - break -- with assert_raises(M.SyntaxError) as cm: -+ with pytest.raises(M.SyntaxError) as cm: - parser_for_bytes(random_magic) -- assert_equal(str(cm.exception), 'unexpected magic') -+ assert str(cm.value) == 'unexpected magic' - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_polib4us.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_polib4us.py -+++ i18nspector-0.26/tests/test_polib4us.py -@@ -20,11 +20,6 @@ - - import polib - --from nose.tools import ( -- assert_list_equal, -- assert_true, --) -- - import lib.polib4us as M - - from . import tools -@@ -50,7 +45,7 @@ msgstr "b" - file.write(s) - file.flush() - po = polib.pofile(file.name) -- assert_true(po[-1].obsolete) -+ assert po[-1].obsolete - t() - M.install_patches() - t() -@@ -60,9 +55,8 @@ def test_flag_splitting(): - M.install_patches() - e = polib.POEntry() - e.flags = ['fuzzy,c-format'] -- assert_list_equal( -- e.flags, -- ['fuzzy', 'c-format'] -- ) -+ assert ( -+ e.flags == -+ ['fuzzy', 'c-format']) - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_strformat_c.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_strformat_c.py -+++ i18nspector-0.26/tests/test_strformat_c.py -@@ -21,22 +21,25 @@ - import os - import struct - import sys -+import unittest - import unittest.mock - --import nose --from nose.tools import ( -- assert_equal, -- assert_greater, -- assert_is_instance, -- assert_raises, -- assert_sequence_equal, --) -+import pytest - - import lib.strformat.c as M - -+from . import tools -+ -+ -+# methods using the tools.collect_yielded decorator don't have a 'self' -+# since they end up being run before 'self' exists. pylint doesn't -+# understand this unusual situation -+# pylint: disable=no-method-argument -+ -+ - def test_INT_MAX(): - struct.pack('=i', M.INT_MAX) -- with assert_raises(struct.error): -+ with pytest.raises(struct.error): - struct.pack('=i', M.INT_MAX + 1) - - def is_glibc(): -@@ -49,121 +52,134 @@ def is_glibc(): - def test_NL_ARGMAX(): - plat = sys.platform - if plat.startswith('linux') and is_glibc(): -- assert_equal( -- M.NL_ARGMAX, -- os.sysconf('SC_NL_ARGMAX') -- ) -+ assert ( -+ M.NL_ARGMAX == -+ os.sysconf('SC_NL_ARGMAX')) - else: -- raise nose.SkipTest('Test specific to Linux with glibc') -+ raise unittest.SkipTest('Test specific to Linux with glibc') - - small_NL_ARGMAX = unittest.mock.patch('lib.strformat.c.NL_ARGMAX', 42) - # Setting NL_ARGMAX to a small number makes the *_index_out_of_range() tests - # much faster. - - def test_lone_percent(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('%') - - def test_invalid_conversion_spec(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('%!') - - def test_add_argument(): - fmt = M.FormatString('%s') -- with assert_raises(RuntimeError): -+ with pytest.raises(RuntimeError): - fmt.add_argument(2, None) - - def test_text(): - fmt = M.FormatString('eggs%dbacon%dspam') -- assert_equal(len(fmt), 5) -+ assert len(fmt) == 5 - fmt = list(fmt) -- assert_equal(fmt[0], 'eggs') -- assert_equal(fmt[2], 'bacon') -- assert_equal(fmt[4], 'spam') -+ assert fmt[0] == 'eggs' -+ assert fmt[2] == 'bacon' -+ assert fmt[4] == 'spam' - - class test_types: - -- def t(self, s, tp, warn_type=None, integer=False): -+ @staticmethod -+ def t(s, tp, warn_type=None, integer=False): - fmt = M.FormatString(s) - [conv] = fmt -- assert_is_instance(conv, M.Conversion) -- assert_equal(conv.type, tp) -+ assert isinstance(conv, M.Conversion) -+ assert conv.type == tp - if tp == 'void': -- assert_sequence_equal(fmt.arguments, []) -+ assert fmt.arguments == [] - else: - [[arg]] = fmt.arguments -- assert_equal(arg.type, tp) -+ assert arg.type == tp - if warn_type is None: -- assert_sequence_equal(fmt.warnings, []) -+ assert fmt.warnings == [] - else: - [warning] = fmt.warnings -- assert_is_instance(warning, warn_type) -- assert_equal(conv.integer, integer) -+ assert isinstance(warning, warn_type) -+ assert conv.integer == integer - -- def test_integer(self): -- def t(s, tp, warn_type=None): -+ @tools.collect_yielded -+ def test_integer(): -+ def t(s, tp, suffix, warn_type=None): - integer = not suffix -- self.t(s, tp + suffix, warn_type, integer) -+ test_types.t(s, tp + suffix, warn_type, integer) - for c in 'din': - suffix = '' - if c == 'n': - suffix = ' *' -- yield t, ('%hh' + c), 'signed char' -- yield t, ('%h' + c), 'short int' -- yield t, ('%' + c), 'int' -- yield t, ('%l' + c), 'long int' -- yield t, ('%ll' + c), 'long long int' -- yield t, ('%L' + c), 'long long int', M.NonPortableConversion -- yield t, ('%q' + c), 'long long int', M.NonPortableConversion -- yield t, ('%j' + c), 'intmax_t' -- yield t, ('%z' + c), 'ssize_t' -- yield t, ('%Z' + c), 'ssize_t', M.NonPortableConversion -- yield t, ('%t' + c), 'ptrdiff_t' -+ yield t, (('%hh' + c), 'signed char', suffix) -+ yield t, (('%h' + c), 'short int', suffix) -+ yield t, (('%' + c), 'int', suffix) -+ yield t, (('%l' + c), 'long int', suffix) -+ yield t, (('%ll' + c), 'long long int', suffix) -+ yield t, (('%L' + c), 'long long int', suffix, M.NonPortableConversion) -+ yield t, (('%q' + c), 'long long int', suffix, M.NonPortableConversion) -+ yield t, (('%j' + c), 'intmax_t', suffix) -+ yield t, (('%z' + c), 'ssize_t', suffix) -+ yield t, (('%Z' + c), 'ssize_t', suffix, M.NonPortableConversion) -+ yield t, (('%t' + c), 'ptrdiff_t', suffix) - for c in 'ouxX': - suffix = '' -- yield t, ('%hh' + c), 'unsigned char' -- yield t, ('%h' + c), 'unsigned short int' -- yield t, ('%' + c), 'unsigned int' -- yield t, ('%l' + c), 'unsigned long int' -- yield t, ('%ll' + c), 'unsigned long long int' -- yield t, ('%L' + c), 'unsigned long long int', M.NonPortableConversion -- yield t, ('%q' + c), 'unsigned long long int', M.NonPortableConversion -- yield t, ('%j' + c), 'uintmax_t' -- yield t, ('%z' + c), 'size_t' -- yield t, ('%Z' + c), 'size_t', M.NonPortableConversion -- yield t, ('%t' + c), '[unsigned ptrdiff_t]' -+ yield t, (('%hh' + c), 'unsigned char', suffix) -+ yield t, (('%h' + c), 'unsigned short int', suffix) -+ yield t, (('%' + c), 'unsigned int', suffix) -+ yield t, (('%l' + c), 'unsigned long int', suffix) -+ yield t, (('%ll' + c), 'unsigned long long int', suffix) -+ yield t, (('%L' + c), 'unsigned long long int', suffix, M.NonPortableConversion) -+ yield t, (('%q' + c), 'unsigned long long int', suffix, M.NonPortableConversion) -+ yield t, (('%j' + c), 'uintmax_t', suffix) -+ yield t, (('%z' + c), 'size_t', suffix) -+ yield t, (('%Z' + c), 'size_t', suffix, M.NonPortableConversion) -+ yield t, (('%t' + c), '[unsigned ptrdiff_t]', suffix) -+ -+ @tools.collect_yielded -+ def test_double(): -+ def t(*args): -+ test_types.t(*args) - -- def test_double(self): -- t = self.t - for c in 'aefgAEFG': -- yield t, ('%' + c), 'double' -- yield t, ('%l' + c), 'double', M.NonPortableConversion -- yield t, ('%L' + c), 'long double' -- -- def test_char(self): -- t = self.t -- yield t, '%c', 'char' -- yield t, '%lc', 'wint_t' -- yield t, '%C', 'wint_t', M.NonPortableConversion -- yield t, '%s', 'const char *' -- yield t, '%ls', 'const wchar_t *' -- yield t, '%S', 'const wchar_t *', M.NonPortableConversion -- -- def test_void(self): -- t = self.t -- yield t, '%p', 'void *' -- yield t, '%m', 'void' -- yield t, '%%', 'void' -+ yield t, (('%' + c), 'double') -+ yield t, (('%l' + c), 'double', M.NonPortableConversion) -+ yield t, (('%L' + c), 'long double') -+ -+ @tools.collect_yielded -+ def test_char(): -+ def t(*args): -+ test_types.t(*args) -+ -+ yield t, ('%c', 'char') -+ yield t, ('%lc', 'wint_t') -+ yield t, ('%C', 'wint_t', M.NonPortableConversion) -+ yield t, ('%s', 'const char *') -+ yield t, ('%ls', 'const wchar_t *') -+ yield t, ('%S', 'const wchar_t *', M.NonPortableConversion) -+ -+ @tools.collect_yielded -+ def test_void(): -+ def t(*args): -+ test_types.t(*args) -+ -+ yield t, ('%p', 'void *') -+ yield t, ('%m', 'void') -+ yield t, ('%%', 'void') - -- def test_c99_macros(self): -+ @tools.collect_yielded -+ def test_c99_macros(): - # pylint: disable=undefined-loop-variable - def _t(s, tp): -- return self.t(s, tp, integer=True) -+ return test_types.t(s, tp, integer=True) - def t(s, tp): - return ( - _t, -- '%<{macro}>'.format(macro=s.format(c=c, n=n)), -- ('u' if unsigned else '') + tp.format(n=n) -+ ( -+ '%<{macro}>'.format(macro=s.format(c=c, n=n)), -+ ('u' if unsigned else '') + tp.format(n=n) -+ ) - ) - # pylint: enable=undefined-loop-variable - for c in 'diouxX': -@@ -175,64 +191,71 @@ class test_types: - yield t('PRI{c}MAX', 'intmax_t') - yield t('PRI{c}PTR', 'intptr_t') - -+_lengths = ['hh', 'h', 'l', 'll', 'q', 'j', 'z', 't', 'L'] -+ - class test_invalid_length: -- def t(self, s): -- with assert_raises(M.LengthError): -+ @staticmethod -+ def t(s): -+ with pytest.raises(M.LengthError): - M.FormatString(s) - -- _lengths = ['hh', 'h', 'l', 'll', 'q', 'j', 'z', 't', 'L'] -- -- def test_double(self): -- t = self.t -+ @tools.collect_yielded -+ def test_double(): -+ def t(*args): -+ test_invalid_length.t(*args) - for c in 'aefgAEFG': -- for l in self._lengths: -+ for l in _lengths: - if l in 'lL': - continue - yield t, ('%' + l + c) - -- def test_char(self): -- t = self.t -+ @tools.collect_yielded -+ def test_char(): -+ def t(*args): -+ test_invalid_length.t(*args) - for c in 'cs': -- for l in self._lengths: -+ for l in _lengths: - if l != 'l': - yield t, '%' + l + c - yield t, ('%' + l + c.upper()) - -- def test_void(self): -- t = self.t -+ @tools.collect_yielded -+ def test_void(): -+ def t(*args): -+ test_invalid_length.t(*args) - for c in 'pm%': -- for l in self._lengths: -+ for l in _lengths: - yield t, ('%' + l + c) - - class test_numeration: - - def test_percent(self): -- with assert_raises(M.ForbiddenArgumentIndex): -+ with pytest.raises(M.ForbiddenArgumentIndex): - M.FormatString('%1$%') - - def test_errno(self): - # FIXME? - fmt = M.FormatString('%1$m') -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.arguments), 0) -+ assert len(fmt) == 1 -+ assert len(fmt.arguments) == 0 - - def test_swapped(self): - fmt = M.FormatString('%2$s%1$d') -- assert_equal(len(fmt), 2) -+ assert len(fmt) == 2 - [a1], [a2] = fmt.arguments -- assert_equal(a1.type, 'int') -- assert_equal(a2.type, 'const char *') -+ assert a1.type == 'int' -+ assert a2.type == 'const char *' - - def test_numbering_mixture(self): - def t(s): -- with assert_raises(M.ArgumentNumberingMixture): -+ with pytest.raises(M.ArgumentNumberingMixture): - M.FormatString(s) - t('%s%2$s') - t('%2$s%s') - - @small_NL_ARGMAX - def test_index_out_of_range(self): -- with assert_raises(M.ArgumentRangeError): -+ with pytest.raises(M.ArgumentRangeError): - M.FormatString('%0$d') - def fs(n): - s = ''.join( -@@ -241,17 +264,17 @@ class test_numeration: - ) - return M.FormatString(s) - fmt = fs(M.NL_ARGMAX) -- assert_equal(len(fmt), M.NL_ARGMAX) -- assert_equal(len(fmt.arguments), M.NL_ARGMAX) -- with assert_raises(M.ArgumentRangeError): -+ assert len(fmt) == M.NL_ARGMAX -+ assert len(fmt.arguments) == M.NL_ARGMAX -+ with pytest.raises(M.ArgumentRangeError): - fs(M.NL_ARGMAX + 1) - - def test_initial_gap(self): -- with assert_raises(M.MissingArgument): -+ with pytest.raises(M.MissingArgument): - M.FormatString('%2$d') - - def test_gap(self): -- with assert_raises(M.MissingArgument): -+ with pytest.raises(M.MissingArgument): - M.FormatString('%3$d%1$d') - - class test_redundant_flag: -@@ -259,7 +282,7 @@ class test_redundant_flag: - def t(self, s): - fmt = M.FormatString(s) - [exc] = fmt.warnings -- assert_is_instance(exc, M.RedundantFlag) -+ assert isinstance(exc, M.RedundantFlag) - - def test_duplicate(self): - self.t('%--17d') -@@ -274,87 +297,114 @@ class test_redundant_flag: - - class test_expected_flag: - -- def t(self, s): -+ @staticmethod -+ def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - -- def test_hash(self): -+ @tools.collect_yielded -+ def test_hash(): -+ def t(*args): -+ test_expected_flag.t(*args) - for c in 'oxXaAeEfFgG': -- yield self.t, ('%#' + c) -+ yield t, ('%#' + c) - -- def test_zero(self): -+ @tools.collect_yielded -+ def test_zero(): -+ def t(*args): -+ test_expected_flag.t(*args) - for c in 'diouxXaAeEfFgG': -- yield self.t, ('%0' + c) -+ yield t, ('%0' + c) - -- def test_apos(self): -+ @tools.collect_yielded -+ def test_apos(): -+ def t(*args): -+ test_expected_flag.t(*args) - for c in 'diufFgG': -- yield self.t, ("%'" + c) -+ yield t, ("%'" + c) - -- def test_other(self): -+ @tools.collect_yielded -+ def test_other(): -+ def t(*args): -+ test_expected_flag.t(*args) - for flag in '- +I': - for c in 'diouxXaAeEfFgGcCsSpm': -- yield self.t, ('%' + flag + c) -+ yield t, ('%' + flag + c) - - class test_unexpected_flag: - -- def t(self, s): -- with assert_raises(M.FlagError): -+ @staticmethod -+ def t(s): -+ with pytest.raises(M.FlagError): - M.FormatString(s) - -- def test_hash(self): -+ @tools.collect_yielded -+ def test_hash(): -+ def t(*args): -+ test_unexpected_flag.t(*args) - for c in 'dicCsSnpm%': -- yield self.t, ('%#' + c) -+ yield t, ('%#' + c) - -- def test_zero(self): -+ @tools.collect_yielded -+ def test_zero(): -+ def t(*args): -+ test_unexpected_flag.t(*args) - for c in 'cCsSnpm%': -- yield self.t, ('%0' + c) -+ yield t, ('%0' + c) - -- def test_apos(self): -+ @tools.collect_yielded -+ def test_apos(): -+ def t(*args): -+ test_unexpected_flag.t(*args) - for c in 'oxXaAeEcCsSnpm%': -- yield self.t, ("%'" + c) -+ yield t, ("%'" + c) - -- def test_other(self): -+ @tools.collect_yielded -+ def test_other(): -+ def t(*args): -+ test_unexpected_flag.t(*args) - for c in '%n': - for flag in '- +I': -- yield self.t, ('%' + flag + c) -+ yield t, ('%' + flag + c) - - class test_width: - -- def test_ok(self): -+ @tools.collect_yielded -+ def test_ok(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - for c in 'diouxXaAeEfFgGcCsSp': - yield t, ('%1' + c) - yield t, '%1m' # FIXME? - - def test_invalid(self): - for c in '%n': -- with assert_raises(M.WidthError): -+ with pytest.raises(M.WidthError): - M.FormatString('%1' + c) - - def test_too_large(self): - fmt = M.FormatString('%{0}d'.format(M.INT_MAX)) -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.arguments), 1) -- with assert_raises(M.WidthRangeError): -+ assert len(fmt) == 1 -+ assert len(fmt.arguments) == 1 -+ with pytest.raises(M.WidthRangeError): - M.FormatString('%{0}d'.format(M.INT_MAX + 1)) - - def test_variable(self): - fmt = M.FormatString('%*s') -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.arguments), 2) -+ assert len(fmt) == 1 -+ assert len(fmt.arguments) == 2 - [a1], [a2] = fmt.arguments -- assert_equal(a1.type, 'int') -- assert_equal(a2.type, 'const char *') -+ assert a1.type == 'int' -+ assert a2.type == 'const char *' - - def _test_index(self, i): - fmt = M.FormatString('%2$*{0}$s'.format(i)) -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.arguments), 2) -+ assert len(fmt) == 1 -+ assert len(fmt.arguments) == 2 - [a1], [a2] = fmt.arguments -- assert_equal(a1.type, 'int') -- assert_equal(a2.type, 'const char *') -+ assert a1.type == 'int' -+ assert a2.type == 'const char *' - - def test_index(self): - self._test_index(1) -@@ -365,7 +415,7 @@ class test_width: - - @small_NL_ARGMAX - def test_index_out_of_range(self): -- with assert_raises(M.ArgumentRangeError): -+ with pytest.raises(M.ArgumentRangeError): - M.FormatString('%1$*0$s') - def fs(n): - s = ''.join( -@@ -374,14 +424,14 @@ class test_width: - ) + '%1$*{0}$s'.format(n) - return M.FormatString(s) - fmt = fs(M.NL_ARGMAX) -- assert_equal(len(fmt), M.NL_ARGMAX - 1) -- assert_equal(len(fmt.arguments), M.NL_ARGMAX) -- with assert_raises(M.ArgumentRangeError): -+ assert len(fmt) == M.NL_ARGMAX - 1 -+ assert len(fmt.arguments) == M.NL_ARGMAX -+ with pytest.raises(M.ArgumentRangeError): - fs(M.NL_ARGMAX + 1) - - def test_numbering_mixture(self): - def t(s): -- with assert_raises(M.ArgumentNumberingMixture): -+ with pytest.raises(M.ArgumentNumberingMixture): - M.FormatString(s) - t('%1$*s') - t('%*1$s') -@@ -390,58 +440,62 @@ class test_width: - - class test_precision: - -- def test_ok(self): -+ @tools.collect_yielded -+ def test_ok(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - for c in 'diouxXaAeEfFgGsS': - yield t, ('%.1' + c) - -- def test_redundant_0(self): -+ @tools.collect_yielded -+ def test_redundant_0(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - [warning] = fmt.warnings -- assert_is_instance(warning, M.RedundantFlag) -+ assert isinstance(warning, M.RedundantFlag) - for c in 'diouxX': - yield t, ('%0.1' + c) - -- def test_non_redundant_0(self): -+ @tools.collect_yielded -+ def test_non_redundant_0(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -- assert_sequence_equal(fmt.warnings, []) -+ assert len(fmt) == 1 -+ assert fmt.warnings == [] - for c in 'aAeEfFgG': - yield t, ('%0.1' + c) - -- def test_unexpected(self): -+ @tools.collect_yielded -+ def test_unexpected(): - def t(s): -- with assert_raises(M.PrecisionError): -+ with pytest.raises(M.PrecisionError): - M.FormatString(s) - for c in 'cCpnm%': - yield t, ('%.1' + c) - - def test_too_large(self): - fmt = M.FormatString('%.{0}f'.format(M.INT_MAX)) -- assert_equal(len(fmt), 1) -- with assert_raises(M.PrecisionRangeError): -+ assert len(fmt) == 1 -+ with pytest.raises(M.PrecisionRangeError): - M.FormatString('%.{0}f'.format(M.INT_MAX + 1)) - - def test_variable(self): - fmt = M.FormatString('%.*f') -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.arguments), 2) -+ assert len(fmt) == 1 -+ assert len(fmt.arguments) == 2 - [a1], [a2] = fmt.arguments -- assert_equal(a1.type, 'int') -- assert_equal(a2.type, 'double') -+ assert a1.type == 'int' -+ assert a2.type == 'double' - - def _test_index(self, i): - fmt = M.FormatString('%2$.*{0}$f'.format(i)) -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.arguments), 2) -+ assert len(fmt) == 1 -+ assert len(fmt.arguments) == 2 - [a1], [a2] = fmt.arguments -- assert_equal(a1.type, 'int') -- assert_equal(a2.type, 'double') -+ assert a1.type == 'int' -+ assert a2.type == 'double' - - def test_index(self): - self._test_index(1) -@@ -452,7 +506,7 @@ class test_precision: - - @small_NL_ARGMAX - def test_index_out_of_range(self): -- with assert_raises(M.ArgumentRangeError): -+ with pytest.raises(M.ArgumentRangeError): - M.FormatString('%1$.*0$f') - def fs(n): - s = ''.join( -@@ -461,14 +515,14 @@ class test_precision: - ) + '%1$.*{0}$f'.format(n) - return M.FormatString(s) - fmt = fs(M.NL_ARGMAX) -- assert_equal(len(fmt), M.NL_ARGMAX - 1) -- assert_equal(len(fmt.arguments), M.NL_ARGMAX) -- with assert_raises(M.ArgumentRangeError): -+ assert len(fmt) == M.NL_ARGMAX - 1 -+ assert len(fmt.arguments) == M.NL_ARGMAX -+ with pytest.raises(M.ArgumentRangeError): - fs(M.NL_ARGMAX + 1) - - def test_numbering_mixture(self): - def t(s): -- with assert_raises(M.ArgumentNumberingMixture): -+ with pytest.raises(M.ArgumentNumberingMixture): - M.FormatString(s) - t('%1$.*f') - t('%.*1$f') -@@ -481,15 +535,15 @@ class test_type_compatibility: - def t(s, tp): - fmt = M.FormatString(s) - [args] = fmt.arguments -- assert_greater(len(args), 1) -+ assert len(args) > 1 - for arg in args: -- assert_equal(arg.type, tp) -+ assert arg.type == tp - t('%1$d%1$d', 'int') - t('%1$d%1$i', 'int') - - def test_mismatch(self): - def t(s): -- with assert_raises(M.ArgumentTypeMismatch): -+ with pytest.raises(M.ArgumentTypeMismatch): - M.FormatString(s) - t('%1$d%1$hd') - t('%1$d%1$u') -@@ -498,11 +552,11 @@ class test_type_compatibility: - @small_NL_ARGMAX - def test_too_many_conversions(): - def t(s): -- with assert_raises(M.ArgumentRangeError): -+ with pytest.raises(M.ArgumentRangeError): - M.FormatString(s) - s = M.NL_ARGMAX * '%d' - fmt = M.FormatString(s) -- assert_equal(len(fmt), M.NL_ARGMAX) -+ assert len(fmt) == M.NL_ARGMAX - t(s + '%f') - t(s + '%*f') - t(s + '%.*f') -@@ -512,7 +566,7 @@ class test_get_last_integer_conversion: - def test_overflow(self): - fmt = M.FormatString('%s%d') - for n in [-1, 0, 3]: -- with assert_raises(IndexError): -+ with pytest.raises(IndexError): - fmt.get_last_integer_conversion(n=n) - - def t(self, s, n, tp=M.Conversion): -@@ -520,7 +574,7 @@ class test_get_last_integer_conversion: - conv = fmt.get_last_integer_conversion(n=n) - if tp is None: - tp = type(tp) -- assert_is_instance(conv, tp) -+ assert isinstance(conv, tp) - return conv - - def test_okay(self): -Index: i18nspector-0.26/tests/test_strformat_perlbrace.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_strformat_perlbrace.py -+++ i18nspector-0.26/tests/test_strformat_perlbrace.py -@@ -18,40 +18,37 @@ - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - # SOFTWARE. - --from nose.tools import ( -- assert_equal, -- assert_raises, --) -+import pytest - - import lib.strformat.perlbrace as M - - def test_lone_lcb(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('{') - - def test_lone_rcb(): - M.FormatString('}') - - def test_invalid_field(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('{@}') - - def test_text(): - fmt = M.FormatString('bacon') -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - [fld] = fmt -- assert_equal(fld, 'bacon') -+ assert fld == 'bacon' - - class test_named_arguments: - - def test_good(self): - fmt = M.FormatString('{spam}') -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - [fld] = fmt -- assert_equal(fld, '{spam}') -+ assert fld == '{spam}' - - def test_bad(self): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('{3ggs}') - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_strformat_pybrace.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_strformat_pybrace.py -+++ i18nspector-0.26/tests/test_strformat_pybrace.py -@@ -21,18 +21,13 @@ - import struct - import unittest.mock - --from nose.tools import ( -- assert_equal, -- assert_is, -- assert_is_instance, -- assert_raises, --) -+import pytest - - import lib.strformat.pybrace as M - - def test_SSIZE_MAX(): - struct.pack('=i', M.SSIZE_MAX) -- with assert_raises(struct.error): -+ with pytest.raises(struct.error): - struct.pack('=i', M.SSIZE_MAX + 1) - - small_SSIZE_MAX = unittest.mock.patch('lib.strformat.pybrace.SSIZE_MAX', 42) -@@ -40,31 +35,31 @@ small_SSIZE_MAX = unittest.mock.patch('l - # a very large number of arguments without running out of memory. - - def test_lone_lcb(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('{') - - def test_lone_rcb(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('}') - - def test_invalid_field(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('{@}') - - def test_add_argument(): - fmt = M.FormatString('{}') -- with assert_raises(RuntimeError): -+ with pytest.raises(RuntimeError): - fmt.add_argument(None, None) -- with assert_raises(RuntimeError): -+ with pytest.raises(RuntimeError): - fmt.add_argument('eggs', None) - - def test_text(): - fmt = M.FormatString('eggs{}bacon{}spam') -- assert_equal(len(fmt), 5) -+ assert len(fmt) == 5 - fmt = list(fmt) -- assert_equal(fmt[0], 'eggs') -- assert_equal(fmt[2], 'bacon') -- assert_equal(fmt[4], 'spam') -+ assert fmt[0] == 'eggs' -+ assert fmt[2] == 'bacon' -+ assert fmt[4] == 'spam' - - class test_types: - -@@ -72,12 +67,12 @@ class test_types: - types = frozenset(tp.__name__ for tp in types) - fmt = M.FormatString('{:' + k + '}') - [fld] = fmt -- assert_is_instance(fld, M.Field) -- assert_equal(fld.types, types) -- assert_equal(len(fmt.argument_map), 1) -+ assert isinstance(fld, M.Field) -+ assert fld.types == types -+ assert len(fmt.argument_map) == 1 - [(key, [afld])] = fmt.argument_map.items() -- assert_equal(key, 0) -- assert_is(fld, afld) -+ assert key == 0 -+ assert fld is afld - - def test_default(self): - self.t('', int, float, str) -@@ -102,12 +97,12 @@ class test_conversion: - types = frozenset(tp.__name__ for tp in types) - fmt = M.FormatString('{!' + c + ':' + k + '}') - [fld] = fmt -- assert_is_instance(fld, M.Field) -- assert_equal(fld.types, types) -- assert_equal(len(fmt.argument_map), 1) -+ assert isinstance(fld, M.Field) -+ assert fld.types == types -+ assert len(fmt.argument_map) == 1 - [(key, [afld])] = fmt.argument_map.items() -- assert_equal(key, 0) -- assert_is(fld, afld) -+ assert key == 0 -+ assert fld is afld - - def test_default(self): - for c in 'sra': -@@ -120,11 +115,11 @@ class test_conversion: - def test_numeric(self): - for c in 'sra': - for k in 'bcdoxXneEfFgG': -- with assert_raises(M.FormatTypeMismatch): -+ with pytest.raises(M.FormatTypeMismatch): - self.t(c, k, int) - - def test_bad(self): -- with assert_raises(M.ConversionError): -+ with pytest.raises(M.ConversionError): - self.t('z', '') - - class test_numbered_arguments: -@@ -134,12 +129,12 @@ class test_numbered_arguments: - - def t(self, s, *types): - fmt = M.FormatString(s) -- assert_equal(len(fmt), len(types)) -- assert_equal(len(fmt.argument_map), len(types)) -+ assert len(fmt) == len(types) -+ assert len(fmt.argument_map) == len(types) - for (key, args), (xkey, xtype) in zip(sorted(fmt.argument_map.items()), enumerate(types)): - [arg] = args -- assert_equal(key, xkey) -- assert_equal(arg.types, frozenset({xtype.__name__})) -+ assert key == xkey -+ assert arg.types == frozenset({xtype.__name__}) - - def test_unnumbered(self): - self.t('{:d}{:f}', int, float) -@@ -151,9 +146,9 @@ class test_numbered_arguments: - self.t('{1:d}{0:f}', float, int) - - def test_mixed(self): -- with assert_raises(M.ArgumentNumberingMixture): -+ with pytest.raises(M.ArgumentNumberingMixture): - self.t('{0:d}{:f}') -- with assert_raises(M.ArgumentNumberingMixture): -+ with pytest.raises(M.ArgumentNumberingMixture): - self.t('{:d}{0:f}') - - def test_numbered_out_of_range(self): -@@ -161,7 +156,7 @@ class test_numbered_arguments: - s = ('{' + str(i) + '}') - M.FormatString(s) - t(M.SSIZE_MAX) -- with assert_raises(M.ArgumentRangeError): -+ with pytest.raises(M.ArgumentRangeError): - t(M.SSIZE_MAX + 1) - - @small_SSIZE_MAX -@@ -170,7 +165,7 @@ class test_numbered_arguments: - s = '{}' * i - M.FormatString(s) - t(M.SSIZE_MAX + 1) -- with assert_raises(M.ArgumentRangeError): -+ with pytest.raises(M.ArgumentRangeError): - t(M.SSIZE_MAX + 2) - - class test_named_arguments: -@@ -179,21 +174,21 @@ class test_named_arguments: - fmt = M.FormatString('{spam}') - [fld] = fmt - [(aname, [afld])] = fmt.argument_map.items() -- assert_equal(aname, 'spam') -- assert_is(fld, afld) -+ assert aname == 'spam' -+ assert fld is afld - - def test_bad(self): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('{3ggs}') - - class test_format_spec: - - def test_bad_char(self): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('{:@}') - - def test_bad_letter(self): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('{:Z}') - - def test_comma(self): -@@ -203,7 +198,7 @@ class test_format_spec: - for k in 'bcdoxXeEfFgG': - t(k) - for k in 'ns': -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - t(k) - - def test_alt_sign(self): -@@ -213,7 +208,7 @@ class test_format_spec: - t(c, '') - for k in 'bcdoxXneEfFgG': - t(c, k) -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - t(c, 's') - - def test_align(self): -@@ -228,7 +223,7 @@ class test_format_spec: - t(c, '') - for k in 'bcdoxXneEfFgG': - t(c, k) -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - t(c, 's') - - def test_width(self): -@@ -239,7 +234,7 @@ class test_format_spec: - for k in 'bcdoxXneEfFgGs\0': - for i in 4, 37, M.SSIZE_MAX: - t(i, k) -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - t(M.SSIZE_MAX + 1, k) - - def test_precision(self): -@@ -250,11 +245,11 @@ class test_format_spec: - for k in 'neEfFgGs\0': - for i in {4, 37, M.SSIZE_MAX}: - t(i, k) -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - t(M.SSIZE_MAX + 1, k) - for k in 'bcdoxX': - for i in {4, 37, M.SSIZE_MAX, M.SSIZE_MAX + 1}: -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - t(i, k) - - def test_type_compat(self): -@@ -262,7 +257,7 @@ class test_format_spec: - s = '{0:' + k1 + '}{0:' + k2 + '}' - M.FormatString(s) - def e(k1, k2): -- with assert_raises(M.ArgumentTypeMismatch): -+ with pytest.raises(M.ArgumentTypeMismatch): - t(k1, k2) - ks = 'bcdoxXneEfFgGs' - compat = [ -@@ -291,13 +286,13 @@ class test_format_spec: - s = '{' + str(v) + ':{' + str(f) + '}}' - return M.FormatString(s) - fmt = t() -- assert_equal(len(fmt.argument_map), 2) -+ assert len(fmt.argument_map) == 2 - t(v=0, f=M.SSIZE_MAX) -- with assert_raises(M.ArgumentRangeError): -+ with pytest.raises(M.ArgumentRangeError): - t(v=0, f=(M.SSIZE_MAX + 1)) -- with assert_raises(M.ArgumentNumberingMixture): -+ with pytest.raises(M.ArgumentNumberingMixture): - t(v=0) -- with assert_raises(M.ArgumentNumberingMixture): -+ with pytest.raises(M.ArgumentNumberingMixture): - t(f=0) - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_strformat_python.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_strformat_python.py -+++ i18nspector-0.26/tests/test_strformat_python.py -@@ -20,53 +20,55 @@ - - import struct - --from nose.tools import ( -- assert_equal, -- assert_greater, -- assert_is_instance, -- assert_raises, -- assert_sequence_equal, --) -+import pytest - - import lib.strformat.python as M -+from . import tools -+ -+ -+# methods using the tools.collect_yielded decorator don't have a 'self' -+# since they end up being run before 'self' exists. pylint doesn't -+# understand this unusual situation -+# pylint: disable=no-method-argument -+ - - def test_SSIZE_MAX(): - struct.pack('=i', M.SSIZE_MAX) -- with assert_raises(struct.error): -+ with pytest.raises(struct.error): - struct.pack('=i', M.SSIZE_MAX + 1) - - def test_lone_percent(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('%') - - def test_invalid_conversion_spec(): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - M.FormatString('%!') - - def test_add_argument(): - fmt = M.FormatString('%s') -- with assert_raises(RuntimeError): -+ with pytest.raises(RuntimeError): - fmt.add_argument(None, None) -- with assert_raises(RuntimeError): -+ with pytest.raises(RuntimeError): - fmt.add_argument('eggs', None) - - def test_text(): - fmt = M.FormatString('eggs%dbacon%dspam') -- assert_equal(len(fmt), 5) -+ assert len(fmt) == 5 - fmt = list(fmt) -- assert_equal(fmt[0], 'eggs') -- assert_equal(fmt[2], 'bacon') -- assert_equal(fmt[4], 'spam') -+ assert fmt[0] == 'eggs' -+ assert fmt[2] == 'bacon' -+ assert fmt[4] == 'spam' - - class test_map: - - def t(self, key): - s = '%(' + key + ')s' - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -- assert_sequence_equal(fmt.seq_arguments, []) -+ assert len(fmt) == 1 -+ assert fmt.seq_arguments == [] - [pkey] = fmt.map_arguments.keys() -- assert_equal(key, pkey) -+ assert key == pkey - - def test_simple(self): - self.t('eggs') -@@ -75,69 +77,82 @@ class test_map: - self.t('eggs(ham)spam') - - def test_unbalanced_parens(self): -- with assert_raises(M.Error): -+ with pytest.raises(M.Error): - self.t('eggs(ham') - - class test_types: - -- def t(self, s, tp, warn_type=None): -+ @staticmethod -+ def t( s, tp, warn_type=None): - fmt = M.FormatString(s) - [conv] = fmt -- assert_is_instance(conv, M.Conversion) -- assert_equal(conv.type, tp) -- assert_equal(len(fmt.map_arguments), 0) -+ assert isinstance(conv, M.Conversion) -+ assert conv.type == tp -+ assert len(fmt.map_arguments) == 0 - if tp == 'None': -- assert_sequence_equal(fmt.seq_arguments, []) -+ assert fmt.seq_arguments == [] - else: - [arg] = fmt.seq_arguments -- assert_equal(arg.type, tp) -+ assert arg.type == tp - if warn_type is None: -- assert_equal(len(fmt.warnings), 0) -+ assert len(fmt.warnings) == 0 - else: - [warning] = fmt.warnings -- assert_is_instance(warning, warn_type) -+ assert isinstance(warning, warn_type) - -- def test_integer(self): -- t = self.t -+ @tools.collect_yielded -+ def test_integer(): -+ def t(*args): -+ test_types.t(*args) - for c in 'oxXdi': -- yield t, '%' + c, 'int' -- yield t, '%u', 'int', M.ObsoleteConversion -+ yield t, ('%' + c, 'int') -+ yield t, ('%u', 'int', M.ObsoleteConversion) - -- def test_float(self): -- t = self.t -+ @tools.collect_yielded -+ def test_float(): -+ def t(*args): -+ test_types.t(*args) - for c in 'eEfFgG': -- yield t, '%' + c, 'float' -- -- def test_str(self): -- t = self.t -- yield t, '%c', 'chr' -- yield t, '%s', 'str' -+ yield t, ('%' + c, 'float') - -- def test_repr(self): -- t = self.t -+ @tools.collect_yielded -+ def test_str(): -+ def t(*args): -+ test_types.t(*args) -+ yield t, ('%c', 'chr') -+ yield t, ('%s', 'str') -+ -+ @tools.collect_yielded -+ def test_repr(): -+ def t(*args): -+ test_types.t(*args) - for c in 'ra': -- yield t, '%' + c, 'object' -+ yield t, ('%' + c, 'object') - -- def test_void(self): -- yield self.t, '%%', 'None' -+ @tools.collect_yielded -+ def test_void(): -+ def t(*args): -+ test_types.t(*args) -+ yield t, ('%%', 'None') - -+@tools.collect_yielded - def test_length(): - def t(l): - fmt = M.FormatString('%' + l + 'd') - [warning] = fmt.warnings -- assert_is_instance(warning, M.RedundantLength) -+ assert isinstance(warning, M.RedundantLength) - for l in 'hlL': - yield t, l - - class test_indexing: - - def test_percent(self): -- with assert_raises(M.ForbiddenArgumentKey): -+ with pytest.raises(M.ForbiddenArgumentKey): - M.FormatString('%(eggs)%') - - def test_indexing_mixture(self): - def t(s): -- with assert_raises(M.ArgumentIndexingMixture): -+ with pytest.raises(M.ArgumentIndexingMixture): - M.FormatString(s) - t('%s%(eggs)s') - t('%(eggs)s%s') -@@ -149,7 +164,7 @@ class test_multiple_flags: - def t(self, s): - fmt = M.FormatString(s) - [exc] = fmt.warnings -- assert_is_instance(exc, M.RedundantFlag) -+ assert isinstance(exc, M.RedundantFlag) - - def test_duplicate(self): - self.t('%--17d') -@@ -160,108 +175,114 @@ class test_multiple_flags: - def test_plus_space(self): - self.t('%+ d') - -+@tools.collect_yielded - def test_single_flag(): - - def t(s, expected): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - if expected: -- assert_sequence_equal(fmt.warnings, []) -+ assert fmt.warnings == [] - else: - [exc] = fmt.warnings -- assert_is_instance(exc, M.RedundantFlag) -+ assert isinstance(exc, M.RedundantFlag) - - for c in 'dioxXeEfFgGcrsa%': -- yield t, ('%#' + c), (c in 'oxXeEfFgG') -+ yield t, (('%#' + c), (c in 'oxXeEfFgG')) - for flag in '0 +': -- yield t, ('%' + flag + c), (c in 'dioxXeEfFgG') -- yield t, ('%-' + c), True -+ yield t, (('%' + flag + c), (c in 'dioxXeEfFgG')) -+ yield t, (('%-' + c), True) - - class test_width: - -- def test_ok(self): -+ @tools.collect_yielded -+ def test_ok(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -- assert_sequence_equal(fmt.warnings, []) -+ assert len(fmt) == 1 -+ assert fmt.warnings == [] - for c in 'dioxXeEfFgGcrsa%': - yield t, ('%1' + c) - - def test_too_large(self): - fmt = M.FormatString('%{0}d'.format(M.SSIZE_MAX)) -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.seq_arguments), 1) -- assert_equal(len(fmt.map_arguments), 0) -- with assert_raises(M.WidthRangeError): -+ assert len(fmt) == 1 -+ assert len(fmt.seq_arguments) == 1 -+ assert len(fmt.map_arguments) == 0 -+ with pytest.raises(M.WidthRangeError): - M.FormatString('%{0}d'.format(M.SSIZE_MAX + 1)) - - def test_variable(self): - fmt = M.FormatString('%*s') -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.map_arguments), 0) -+ assert len(fmt) == 1 -+ assert len(fmt.map_arguments) == 0 - [a1, a2] = fmt.seq_arguments -- assert_equal(a1.type, 'int') -- assert_equal(a2.type, 'str') -+ assert a1.type == 'int' -+ assert a2.type == 'str' - - def test_indexing_mixture(self): - def t(s): -- with assert_raises(M.ArgumentIndexingMixture): -+ with pytest.raises(M.ArgumentIndexingMixture): - M.FormatString(s) - t('%*s%(eggs)s') - t('%(eggs)s%*s') - - class test_precision: - -- def test_ok(self): -+ @tools.collect_yielded -+ def test_ok(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - for c in 'dioxXeEfFgGrsa': - yield t, ('%.1' + c) - -- def test_redundant_0(self): -+ @tools.collect_yielded -+ def test_redundant_0(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - [warning] = fmt.warnings -- assert_is_instance(warning, M.RedundantFlag) -+ assert isinstance(warning, M.RedundantFlag) - for c in 'dioxX': - yield t, ('%0.1' + c) - -- def test_non_redundant_0(self): -+ @tools.collect_yielded -+ def test_non_redundant_0(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -- assert_sequence_equal(fmt.warnings, []) -+ assert len(fmt) == 1 -+ assert fmt.warnings == [] - for c in 'eEfFgG': - yield t, ('%0.1' + c) - -- def test_unexpected(self): -+ @tools.collect_yielded -+ def test_unexpected(): - def t(s): - fmt = M.FormatString(s) -- assert_equal(len(fmt), 1) -+ assert len(fmt) == 1 - [warning] = fmt.warnings -- assert_is_instance(warning, M.RedundantPrecision) -+ assert isinstance(warning, M.RedundantPrecision) - for c in 'c%': - yield t, ('%.1' + c) - - def test_too_large(self): - fmt = M.FormatString('%.{0}f'.format(M.SSIZE_MAX)) -- assert_equal(len(fmt), 1) -- with assert_raises(M.PrecisionRangeError): -+ assert len(fmt) == 1 -+ with pytest.raises(M.PrecisionRangeError): - M.FormatString('%.{0}f'.format(M.SSIZE_MAX + 1)) - - def test_variable(self): - fmt = M.FormatString('%.*f') -- assert_equal(len(fmt), 1) -- assert_equal(len(fmt.map_arguments), 0) -+ assert len(fmt) == 1 -+ assert len(fmt.map_arguments) == 0 - [a1, a2] = fmt.seq_arguments -- assert_equal(a1.type, 'int') -- assert_equal(a2.type, 'float') -+ assert a1.type == 'int' -+ assert a2.type == 'float' - - def test_indexing_mixture(self): - def t(s): -- with assert_raises(M.ArgumentIndexingMixture): -+ with pytest.raises(M.ArgumentIndexingMixture): - M.FormatString(s) - t('%.*f%(eggs)f') - t('%(eggs)f%.*f') -@@ -271,26 +292,26 @@ class test_type_compatibility: - def test_okay(self): - def t(s, tp): - fmt = M.FormatString(s) -- assert_equal(len(fmt.seq_arguments), 0) -+ assert len(fmt.seq_arguments) == 0 - [args] = fmt.map_arguments.values() -- assert_greater(len(args), 1) -+ assert len(args) > 1 - for arg in args: -- assert_equal(arg.type, tp) -+ assert arg.type == tp - t('%(eggs)d%(eggs)d', 'int') - t('%(eggs)d%(eggs)i', 'int') - - def test_mismatch(self): - def t(s): -- with assert_raises(M.ArgumentTypeMismatch): -+ with pytest.raises(M.ArgumentTypeMismatch): - M.FormatString(s) - t('%(eggs)d%(eggs)s') - - def test_seq_conversions(): - def t(s, n): - fmt = M.FormatString(s) -- assert_equal(len(fmt.seq_conversions), n) -+ assert len(fmt.seq_conversions) == n - for arg in fmt.seq_conversions: -- assert_is_instance(arg, M.Conversion) -+ assert isinstance(arg, M.Conversion) - t('%d', 1) - t('%d%d', 2) - t('eggs%dham', 1) -Index: i18nspector-0.26/tests/test_tags.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_tags.py -+++ i18nspector-0.26/tests/test_tags.py -@@ -24,26 +24,20 @@ import inspect - import operator - import pkgutil - --from nose.tools import ( -- assert_equal, -- assert_false, -- assert_is_instance, -- assert_raises, -- assert_true, --) -+import pytest - - import lib.check - import lib.tags as M -+from . import tools - - class test_escape: - - def t(self, s, expected): - result = M.safe_format('{}', s) -- assert_is_instance(result, M.safestr) -- assert_equal( -- result, -- expected -- ) -+ assert isinstance(result, M.safestr) -+ assert ( -+ result == -+ expected) - - def test_safe(self): - s = 'fox' -@@ -75,6 +69,7 @@ def ast_to_tagnames(node): - if ok: - yield node.args[0].s - -+@tools.collect_yielded - def test_consistency(): - source_tagnames = set() - def visit_mod(modname): -@@ -117,13 +112,13 @@ class test_enums: - for op in operators: - for i, x in enumerate(keys): - for j, y in enumerate(keys): -- assert_equal(op(x, y), op(i, j)) -+ assert op(x, y) == op(i, j) - if op is operator.eq: -- assert_false(op(x, j)) -+ assert not op(x, j) - elif op is operator.ne: -- assert_true(op(x, j)) -+ assert op(x, j) - else: -- with assert_raises(TypeError): -+ with pytest.raises(TypeError): - op(x, j) - - def test_severities(self): -Index: i18nspector-0.26/tests/test_terminal.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_terminal.py -+++ i18nspector-0.26/tests/test_terminal.py -@@ -23,17 +23,13 @@ import os - import pty - import sys - --from nose.tools import ( -- assert_equal, --) -- - import lib.terminal as T - - from . import tools - - def test_strip_delay(): - def t(s, r=b''): -- assert_equal(T._strip_delay(s), r) # pylint: disable=protected-access -+ assert T._strip_delay(s) == r # pylint: disable=protected-access - t(b'$<1>') - t(b'$<2/>') - t(b'$<3*>') -@@ -56,7 +52,7 @@ def assert_tseq_equal(s, expected): - # but not their subclasses. We don't want detailed comparisons, - # because diff could contain control characters. - pass -- assert_equal(S(expected), S(s)) -+ assert S(expected) == S(s) - - def test_dummy(): - t = assert_tseq_equal -Index: i18nspector-0.26/tests/test_version.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_version.py -+++ i18nspector-0.26/tests/test_version.py -@@ -20,10 +20,6 @@ - - import os - --from nose.tools import ( -- assert_equal, --) -- - from lib.cli import __version__ - - here = os.path.dirname(__file__) -@@ -34,7 +30,7 @@ def test_changelog(): - with open(path, 'rt', encoding='UTF-8') as file: - line = file.readline() - changelog_version = line.split()[1].strip('()') -- assert_equal(changelog_version, __version__) -+ assert changelog_version == __version__ - - def test_manpage(): - path = os.path.join(docdir, 'manpage.rst') -@@ -44,6 +40,6 @@ def test_manpage(): - if line.startswith(':version:'): - manpage_version = line.split()[-1] - break -- assert_equal(manpage_version, __version__) -+ assert manpage_version == __version__ - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/test_xml.py -=================================================================== ---- i18nspector-0.26.orig/tests/test_xml.py -+++ i18nspector-0.26/tests/test_xml.py -@@ -21,11 +21,7 @@ - import re - import xml.etree.ElementTree as etree - --from nose.tools import ( -- assert_is_none, -- assert_is_not_none, -- assert_raises, --) -+import pytest - - import lib.xml as M - -@@ -44,7 +40,7 @@ class test_well_formed: - class test_malformed: - - def t(self, s): -- with assert_raises(M.SyntaxError): -+ with pytest.raises(M.SyntaxError): - M.check_fragment(s) - - def test_non_xml_character(self): -@@ -73,7 +69,7 @@ class test_name_re(): - def test_good(self): - def t(s): - match = self.regexp.match(s) -- assert_is_not_none(match) -+ assert match is not None - t(':') - t('_') - t('e') -@@ -85,7 +81,7 @@ class test_name_re(): - def test_bad(self): - def t(s): - match = self.regexp.match(s) -- assert_is_none(match) -+ assert match is None - t('') - t('0') - t('-') -Index: i18nspector-0.26/tests/tools.py -=================================================================== ---- i18nspector-0.26.orig/tests/tools.py -+++ i18nspector-0.26/tests/tools.py -@@ -1,4 +1,5 @@ - # Copyright © 2012-2019 Jakub Wilk -+# Copyright © 2021 Stuart Prescott - # - # Permission is hereby granted, free of charge, to any person obtaining a copy - # of this software and associated documentation files (the “Software”), to deal -@@ -23,8 +24,9 @@ import os - import sys - import tempfile - import traceback -+import unittest - --import nose -+import pytest - - temporary_file = functools.partial( - tempfile.NamedTemporaryFile, -@@ -52,8 +54,8 @@ def fork_isolation(f): - EXIT_SKIP_TEST = 102 - - exit = os._exit # pylint: disable=redefined-builtin,protected-access -- # sys.exit() can't be used here, because nose catches all exceptions, -- # including SystemExit -+ # sys.exit() can't be used here, because the test harness catches all -+ # exceptions, including SystemExit - - # pylint:disable=consider-using-sys-exit - -@@ -66,7 +68,7 @@ def fork_isolation(f): - os.close(readfd) - try: - f(*args, **kwargs) -- except nose.SkipTest as exc: -+ except unittest.SkipTest as exc: - s = str(exc).encode('UTF-8') - with os.fdopen(writefd, 'wb') as fp: - fp.write(s) -@@ -90,7 +92,7 @@ def fork_isolation(f): - if status == (EXIT_EXCEPTION << 8): - raise IsolatedException() from Exception('\n\n' + msg) - elif status == (EXIT_SKIP_TEST << 8): -- raise nose.SkipTest(msg) -+ raise unittest.SkipTest(msg) - elif status == 0 and msg == '': - pass - else: -@@ -100,6 +102,37 @@ def fork_isolation(f): - - return wrapper - -+ -+def collect_yielded(collect_func): -+ # figure out if this is a function or method being wrapped -+ # as the wrapper needs a slightly different signature -+ if collect_func.__name__ != collect_func.__qualname__: -+ # method -+ @pytest.mark.parametrize("func, test_args", -+ list(collect_func())) -+ def yield_tester(self, func, test_args): -+ # pylint: disable=unused-argument -+ # self is unused here -+ if isinstance(test_args, (tuple, list)): -+ func(*test_args) -+ elif isinstance(test_args, (str, int, float)): -+ func(test_args) -+ else: -+ raise ValueError("args must be either a tuple or a str") -+ else: -+ # function -+ @pytest.mark.parametrize("func, test_args", -+ list(collect_func())) -+ def yield_tester(func, test_args): -+ if isinstance(test_args, (tuple, list)): -+ func(*test_args) -+ elif isinstance(test_args, (str, int, float)): -+ func(test_args) -+ else: -+ raise ValueError("args must be either a tuple or a str") -+ return yield_tester -+ -+ - basedir = os.path.join( - os.path.dirname(__file__), - os.pardir, -Index: i18nspector-0.26/.coveragerc -=================================================================== ---- i18nspector-0.26.orig/.coveragerc -+++ i18nspector-0.26/.coveragerc -@@ -1,5 +1,11 @@ - [run] - branch = true -+omit = -+ *_boot*.py -+ */lib/python*/* -+ */dist-packages/* -+ */tests/* -+source = lib - - [report] - show_missing = true -Index: i18nspector-0.26/private/update-branch-coverage -=================================================================== ---- i18nspector-0.26.orig/private/update-branch-coverage -+++ i18nspector-0.26/private/update-branch-coverage -@@ -1,6 +1,6 @@ --#!/usr/bin/env python3 -+#!/bin/bash - --# Copyright © 2014-2018 Jakub Wilk -+# Copyright © 2021 Stuart Prescott - # - # Permission is hereby granted, free of charge, to any person obtaining a copy - # of this software and associated documentation files (the “Software”), to deal -@@ -20,55 +20,22 @@ - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - # SOFTWARE. - --import glob --import io --import os --import sys -- --import nose --import nose.plugins.cover -- --class Coverage(nose.plugins.cover.Coverage): -- -- stream = None -- -- def report(self, stream): -- return super().report(self.stream) -- --basedir = os.path.join( -- os.path.dirname(__file__), -- os.pardir, --) -- --def main(): -- os.chdir(basedir) -- module_glob = os.path.join('tests', 'test_*.py') -- modules = glob.glob(module_glob) -- argv = [ -- sys.argv[0], -- '--with-coverage', -- '--cover-package=lib', -- '--cover-erase', -- ] + modules -- path = os.path.join( -- 'tests', -- 'coverage' -- ) -- plugin = Coverage() -- report_stream = plugin.stream = io.StringIO() -- print('Generated automatically by private/update-branch-coverage. ' -- 'Do not edit.\n', file=report_stream) -- ok = nose.run(argv=argv, plugins=[plugin]) -- if not ok: -- sys.exit(1) -- report_stream.seek(0) -- with open(path + '.tmp', 'wt', encoding='ASCII') as file: -- for line in report_stream: -- line = line.rstrip() -- print(line, file=file) -- os.rename(path + '.tmp', path) - --if __name__ == '__main__': -- main() -+set -e -+ -+basedir=$(dirname "$0") -+ -+cd "$basedir/.." -+ -+pytest --cov lib/ --cov-branch -rsx -v tests --ignore tests/blackbox_tests -+ -+( -+ set -e -+ echo 'Generated automatically by private/update-branch-coverage. Do not edit.' -+ echo -+ python3-coverage report -+) > tests/coverage.tmp -+ -+mv tests/coverage.tmp tests/coverage - - # vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/blackbox_tests/__init__.py -=================================================================== ---- i18nspector-0.26.orig/tests/blackbox_tests/__init__.py -+++ i18nspector-0.26/tests/blackbox_tests/__init__.py -@@ -1,415 +0,0 @@ --# Copyright © 2012-2017 Jakub Wilk --# --# Permission is hereby granted, free of charge, to any person obtaining a copy --# of this software and associated documentation files (the “Software”), to deal --# in the Software without restriction, including without limitation the rights --# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell --# copies of the Software, and to permit persons to whom the Software is --# furnished to do so, subject to the following conditions: --# --# The above copyright notice and this permission notice shall be included in --# all copies or substantial portions of the Software. --# --# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE --# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, --# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE --# SOFTWARE. -- --import difflib --import inspect --import io --import multiprocessing as mp --import os --import re --import shlex --import signal --import subprocess as ipc --import sys --import traceback --import unittest -- --import nose --import nose.plugins -- --from .. import tools -- --here = os.path.dirname(__file__) -- --# ---------------------------------------- -- --def this(): -- ''' -- Return function that called this function. (Hopefully!) -- ''' -- return globals()[inspect.stack()[1][0].f_code.co_name] -- --# ---------------------------------------- -- --_parse_etag = re.compile(r'([A-Z]): (([\w-]+).*)').match -- --def parse_etag(contents, path): -- match = _parse_etag(contents) -- if match is None: -- return -- t = ETag(match.group(1), path, match.group(2)) -- return t -- --def etags_from_tagstring(obj, path): -- try: -- docstring = obj.tagstring -- except AttributeError: -- return -- for line in docstring.splitlines(): -- line = line.lstrip() -- t = parse_etag(line, path) -- if t is not None: -- yield t -- --def tagstring(s): -- def update(x): -- x.tagstring = s -- return x -- return update -- --# ---------------------------------------- -- --class ETag(): -- -- _ellipsis = '<...>' -- _split = re.compile('({})'.format(re.escape(_ellipsis))).split -- -- def __init__(self, code, path, rest): -- self._s = s = '{code}: {path}: {rest}'.format( -- code=code, -- path=path, -- rest=rest, -- ) -- self.tag = rest.split(None, 1)[0] -- regexp = ''.join( -- '.*' if chunk == self._ellipsis else re.escape(chunk) -- for chunk in self._split(s) -- ) -- self._regexp = re.compile('^{}$'.format(regexp)) -- -- def __eq__(self, other): -- if isinstance(other, str): -- return self._regexp.match(other) -- else: -- return NotImplemented -- -- def __str__(self): -- return self._s -- -- def __repr__(self): -- return repr(self._s) -- --# ---------------------------------------- -- --def _get_signal_names(): -- data = dict( -- (name, getattr(signal, name)) -- for name in dir(signal) -- if re.compile('^SIG[A-Z0-9]*$').match(name) -- ) -- try: -- if data['SIGABRT'] == data['SIGIOT']: -- del data['SIGIOT'] -- except KeyError: -- pass -- try: -- if data['SIGCHLD'] == data['SIGCLD']: -- del data['SIGCLD'] -- except KeyError: -- pass -- for name, n in data.items(): -- yield n, name -- --_signal_names = dict(_get_signal_names()) -- --def get_signal_name(n): -- try: -- return _signal_names[n] -- except KeyError: -- return str(n) -- --# ---------------------------------------- -- --test_file_extensions = ('.mo', '.po', '.pot', '.pop') --# .pop is a special extension to trigger unknown-file-type -- --class Plugin(nose.plugins.Plugin): -- -- name = 'po-plugin' -- enabled = True -- -- def options(self, parser, env): -- pass -- -- def wantFile(self, path): -- if path.endswith(test_file_extensions): -- if path.startswith(os.path.join(os.path.abspath(here), '')): -- return True -- -- def loadTestsFromFile(self, path): -- if self.wantFile(path): -- yield TestCase(path) -- -- def wantFunction(self, func): -- if getattr(func, 'redundant', False): -- return False -- --class TestCase(unittest.TestCase): -- -- def __init__(self, path): -- super().__init__('_test') -- self.path = os.path.relpath(path) -- -- def _test(self): -- _test_file(self.path, basedir=None) -- -- def __str__(self): -- return self.path -- --class SubprocessError(Exception): -- pass -- --def queue_get(queue, process): -- ''' -- Remove and return an item from the queue. -- Block until the process terminates. -- ''' -- while True: -- try: -- return queue.get(timeout=1) -- # This semi-active waiting is ugly, but there doesn't seem be any -- # obvious better way. -- except mp.queues.Empty: -- if process.exitcode is None: -- continue -- else: -- raise -- --def run_i18nspector(options, path): -- commandline = os.environ.get('I18NSPECTOR_COMMANDLINE') -- if commandline is None: -- # We cheat here a bit, because exec(3)ing is very expensive. -- # Let's load the needed Python modules, and use multiprocessing to -- # “emulate” the command execution. -- import lib.cli # pylint: disable=import-outside-toplevel -- assert lib.cli # make pyflakes happy -- prog = os.path.join(here, os.pardir, os.pardir, 'i18nspector') -- commandline = [sys.executable, prog] -- queue = mp.Queue() -- child = mp.Process( -- target=_mp_run_i18nspector, -- args=(prog, options, path, queue) -- ) -- child.start() -- [stdout, stderr] = ( -- s.splitlines() -- for s in queue_get(queue, child) -- ) -- child.join() -- rc = child.exitcode -- else: -- commandline = shlex.split(commandline) -- commandline += options -- commandline += [path] -- fixed_env = dict(os.environ, PYTHONIOENCODING='UTF-8') -- child = ipc.Popen(commandline, stdout=ipc.PIPE, stderr=ipc.PIPE, env=fixed_env) -- stdout, stderr = ( -- s.decode('UTF-8').splitlines() -- for s in child.communicate() -- ) -- rc = child.poll() -- assert isinstance(rc, int) -- if rc == 0: -- return stdout -- if rc < 0: -- message = ['command was interrupted by signal {sig}'.format(sig=get_signal_name(-rc))] # pylint: disable=invalid-unary-operand-type -- else: -- message = ['command exited with status {rc}'.format(rc=rc)] -- message += [''] -- if stdout: -- message += ['stdout:'] -- message += ['| ' + s for s in stdout] + [''] -- else: -- message += ['stdout: (empty)'] -- if stderr: -- message += ['stderr:'] -- message += ['| ' + s for s in stderr] -- else: -- message += ['stderr: (empty)'] -- raise SubprocessError('\n'.join(message)) -- --def _mp_run_i18nspector(prog, options, path, queue): -- with open(prog, 'rt', encoding='UTF-8') as file: -- code = file.read() -- sys.argv = [prog] + list(options) + [path] -- orig_stdout = sys.stdout -- orig_stderr = sys.stderr -- code = compile(code, prog, 'exec') -- io_stdout = io.StringIO() -- io_stderr = io.StringIO() -- gvars = dict( -- __file__=prog, -- ) -- (sys.stdout, sys.stderr) = (io_stdout, io_stderr) -- try: -- try: -- exec(code, gvars) # pylint: disable=exec-used -- finally: -- (sys.stdout, sys.stderr) = (orig_stdout, orig_stderr) -- stdout = io_stdout.getvalue() -- stderr = io_stderr.getvalue() -- except SystemExit: -- queue.put([stdout, stderr]) -- raise -- except: # pylint: disable=bare-except -- exctp, exc, tb = sys.exc_info() -- stderr += ''.join( -- traceback.format_exception(exctp, exc, tb) -- ) -- queue.put([stdout, stderr]) -- sys.exit(1) -- raise # hi, pydiatra! -- else: -- queue.put([stdout, stderr]) -- sys.exit(0) -- --def assert_emit_tags(path, etags, *, options=()): -- etags = list(etags) -- stdout = run_i18nspector(options, path) -- expected_failure = os.path.basename(path).startswith('xfail-') -- if stdout != etags: -- if expected_failure: -- raise nose.SkipTest('expected failure') -- str_etags = [str(x) for x in etags] -- message = ['Tags differ:', ''] -- diff = list( -- difflib.unified_diff(str_etags, stdout, n=9999) -- ) -- message += diff[3:] -- raise AssertionError('\n'.join(message)) -- elif expected_failure: -- raise AssertionError('unexpected success') -- --class TestFileSyntaxError(Exception): -- pass -- --def _parse_test_header_file(file, path, *, comments_only): -- etags = [] -- options = [] -- for n, line in enumerate(file): -- orig_line = line -- if comments_only: -- if n == 0 and line.startswith('#!'): -- continue -- if line.startswith('# '): -- line = line[2:] -- else: -- break -- if line.startswith('--'): -- options += shlex.split(line) -- else: -- etag = parse_etag(line, path) -- if etag is None: -- if comments_only: -- break -- else: -- raise TestFileSyntaxError(orig_line) -- etags += [etag] -- return etags, options -- --def _parse_test_headers(path): -- # .tags: -- try: -- file = open(path + '.tags', encoding='UTF-8') -- except FileNotFoundError: -- pass -- else: -- with file: -- return _parse_test_header_file(file, path, comments_only=False) -- # .gen: -- try: -- file = open(path + '.gen', encoding='UTF-8', errors='ignore') -- except FileNotFoundError: -- pass -- else: -- with file: -- return _parse_test_header_file(file, path, comments_only=True) -- # : -- with open(path, 'rt', encoding='UTF-8', errors='ignore') as file: -- return _parse_test_header_file(file, path, comments_only=True) -- --def _test_file(path, basedir=here): -- if basedir is not None: -- path = os.path.relpath(os.path.join(basedir, path), start=os.getcwd()) -- options = [] -- etags, options = _parse_test_headers(path) -- assert_emit_tags(path, etags, options=options) -- --def get_coverage_for_file(path): -- etags, options = _parse_test_headers(path) -- del options -- return (t.tag for t in etags) -- --def get_coverage_for_function(fn): -- for etag in etags_from_tagstring(fn, ''): -- yield etag.tag -- --def _get_test_filenames(): -- for root, dirnames, filenames in os.walk(here): -- del dirnames -- for filename in filenames: -- if not filename.endswith(test_file_extensions): -- continue -- yield os.path.join(root, filename) -- --def test_file(): -- for filename in _get_test_filenames(): -- path = os.path.relpath(filename, start=here) -- yield _test_file, path --test_file.redundant = True # not needed if the plugin is enabled -- --@tagstring(''' --E: os-error No such file or directory --''') --def test_os_error_no_such_file(): -- with tools.temporary_directory() as tmpdir: -- path = os.path.join(tmpdir, 'nonexistent.po') -- expected = etags_from_tagstring(this(), path) -- assert_emit_tags(path, expected) -- --@tagstring(''' --E: os-error Permission denied --''') --def test_os_error_permission_denied(): -- if os.getuid() == 0: -- raise nose.SkipTest('this test must not be run as root') -- with tools.temporary_directory() as tmpdir: -- path = os.path.join(tmpdir, 'denied.po') -- with open(path, 'wb'): -- pass -- os.chmod(path, 0) -- expected = etags_from_tagstring(this(), path) -- assert_emit_tags(path, expected) -- --# ---------------------------------------- -- --def get_coverage(): -- coverage = set() -- for filename in _get_test_filenames(): -- for tag in get_coverage_for_file(filename): -- coverage.add(tag) -- for objname, obj in globals().items(): -- if not objname.startswith('test_'): -- continue -- for tag in get_coverage_for_function(obj): -- coverage.add(tag) -- return coverage -- --# vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/blackbox_tests/conftest.py -=================================================================== ---- /dev/null -+++ i18nspector-0.26/tests/blackbox_tests/conftest.py -@@ -0,0 +1,424 @@ -+# Copyright © 2012-2021 Jakub Wilk -+# Copyright © 2021 Stuart Prescott -+# -+# Permission is hereby granted, free of charge, to any person obtaining a copy -+# of this software and associated documentation files (the “Software”), to deal -+# in the Software without restriction, including without limitation the rights -+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+# copies of the Software, and to permit persons to whom the Software is -+# furnished to do so, subject to the following conditions: -+# -+# The above copyright notice and this permission notice shall be included in -+# all copies or substantial portions of the Software. -+# -+# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -+# SOFTWARE. -+ -+import difflib -+import inspect -+import io -+import multiprocessing as mp -+import os -+import re -+import shlex -+import signal -+import subprocess as ipc -+import sys -+import traceback -+import unittest -+ -+import pytest -+ -+ -+here = os.path.dirname(__file__) -+ -+ -+# ---------------------------------------- -+ -+def this(): -+ ''' -+ Return function that called this function. (Hopefully!) -+ ''' -+ return globals()[inspect.stack()[1][0].f_code.co_name] -+ -+# ---------------------------------------- -+ -+_parse_etag = re.compile(r'([A-Z]): (([\w-]+).*)').match -+ -+def parse_etag(contents, path): -+ match = _parse_etag(contents) -+ if match is None: -+ return -+ t = ETag(match.group(1), path, match.group(2)) -+ return t -+ -+def etags_from_tagstring(obj, path): -+ try: -+ docstring = obj.tagstring -+ except AttributeError: -+ return -+ for line in docstring.splitlines(): -+ line = line.lstrip() -+ t = parse_etag(line, path) -+ if t is not None: -+ yield t -+ -+def tagstring(s): -+ def update(x): -+ x.tagstring = s -+ return x -+ return update -+ -+# ---------------------------------------- -+ -+class ETag(): -+ -+ _ellipsis = '<...>' -+ _split = re.compile('({})'.format(re.escape(_ellipsis))).split -+ -+ def __init__(self, code, path, rest): -+ self._s = s = '{code}: {path}: {rest}'.format( -+ code=code, -+ path=path, -+ rest=rest, -+ ) -+ self.tag = rest.split(None, 1)[0] -+ regexp = ''.join( -+ '.*' if chunk == self._ellipsis else re.escape(chunk) -+ for chunk in self._split(s) -+ ) -+ self._regexp = re.compile('^{}$'.format(regexp)) -+ -+ def __eq__(self, other): -+ if isinstance(other, str): -+ return self._regexp.match(other) -+ else: -+ return NotImplemented -+ -+ def __str__(self): -+ return self._s -+ -+ def __repr__(self): -+ return repr(self._s) -+ -+# ---------------------------------------- -+ -+def _get_signal_names(): -+ data = dict( -+ (name, getattr(signal, name)) -+ for name in dir(signal) -+ if re.compile('^SIG[A-Z0-9]*$').match(name) -+ ) -+ try: -+ if data['SIGABRT'] == data['SIGIOT']: -+ del data['SIGIOT'] -+ except KeyError: -+ pass -+ try: -+ if data['SIGCHLD'] == data['SIGCLD']: -+ del data['SIGCLD'] -+ except KeyError: -+ pass -+ for name, n in data.items(): -+ yield n, name -+ -+_signal_names = dict(_get_signal_names()) -+ -+def get_signal_name(n): -+ try: -+ return _signal_names[n] -+ except KeyError: -+ return str(n) -+ -+# ---------------------------------------- -+ -+# Here, a pytest hook function (pytest_collect_file) is used to decide that -+# the .mo/.po/.pot files in the directory are actually test items. -+# For each file that is found, a PoTestFile is created. For each PoTestFile, -+# a testable item (PoTestItem) is created; if needed, there could be be -+# more than one PoTestItem per PoTestFile, but the code currently only -+# creates a single test. -+ -+test_file_extensions = ('.mo', '.po', '.pot', '.pop') -+# .pop is a special extension to trigger unknown-file-type -+ -+ -+def pytest_collect_file(parent, path): -+ """hook function that decides that po files are actually tests""" -+ if path.ext in test_file_extensions: -+ return PoTestFile.from_parent(parent, fspath=path) -+ -+ -+class PoTestFile(pytest.File): -+ """Class to yield one or more test items that are contained in the file -+ -+ In this implementation, only one test is yielded. -+ """ -+ # pylint: disable=abstract-method -+ # pylint gets confused about this subclassing and emits warnings about -+ # (unneeded) abstract methods not being defined. -+ # https://github.com/pytest-dev/pytest/issues/7591 -+ -+ def collect(self): -+ # record the po file's basename and its full filesystem path as -+ # strings; pytest will happily use Path objects but the rest of -+ # stdlib on older Python versions only wants str not Path. -+ fname = str(self.fspath) -+ name = os.path.basename(fname) -+ yield PoTestItem.from_parent(self, name=name, filename=fname) -+ -+ -+class PoTestItem(pytest.Item): -+ """Class that represents a single test.""" -+ # pylint: disable=abstract-method -+ -+ def __init__(self, name, parent, filename): -+ super().__init__(name, parent) -+ self.filename = filename -+ -+ def runtest(self): -+ """run the test -+ -+ The intended i18nspector error tags are extracted from the file, -+ i18nspector is run on the file and the sets of tags are asserted -+ to be identical. Exceptions may also be raised for various failure -+ modes of i18nspector or other test harness failures. -+ """ -+ etags, options = self.parse_test_headers() -+ assert_emit_tags(self.filename, etags, options=options) -+ -+ def repr_failure(self, excinfo, style=None): -+ """Presentation of test failures for when self.runtest() raises an exception.""" -+ if isinstance(excinfo.value, PoTestFileException): -+ return "\n".join( -+ [ -+ "blackbox test failed for " + self.name, -+ str(excinfo.value), -+ ] -+ ) -+ else: -+ return "\n".join( -+ [ -+ "blackbox test error for " + self.name, -+ str(excinfo.value), -+ ] -+ ) -+ -+ def reportinfo(self): -+ """ header for test failure/error/stdout reporting """ -+ return self.fspath, 0, "blackbox test: " + self.name -+ -+ def _parse_test_header_file(self, fh, path, *, comments_only): -+ etags = [] -+ options = [] -+ for n, line in enumerate(fh): -+ orig_line = line -+ if comments_only: -+ if n == 0 and line.startswith('#!'): -+ continue -+ if line.startswith('# '): -+ line = line[2:] -+ else: -+ break -+ if line.startswith('--'): -+ options += shlex.split(line) -+ else: -+ etag = parse_etag(line, path) -+ if etag is None: -+ if comments_only: -+ break -+ else: -+ raise TestFileSyntaxError(orig_line) -+ etags += [etag] -+ return etags, options -+ -+ def parse_test_headers(self): -+ path = self.filename -+ # .tags: -+ try: -+ fh = open(path + '.tags', encoding='UTF-8') # pylint: disable=consider-using-with -+ except FileNotFoundError: -+ pass -+ else: -+ with fh: -+ return self._parse_test_header_file(fh, path, comments_only=False) -+ # .gen: -+ try: -+ fh = open(path + '.gen', encoding='UTF-8', errors='ignore') # pylint: disable=consider-using-with -+ except FileNotFoundError: -+ pass -+ else: -+ with fh: -+ return self._parse_test_header_file(fh, path, comments_only=True) -+ # : -+ with open(path, 'rt', encoding='UTF-8', errors='ignore') as fh: -+ return self._parse_test_header_file(fh, path, comments_only=True) -+ -+ -+class PoTestFileException(AssertionError): -+ """Custom exception for error reporting.""" -+ -+ -+class TestFileSyntaxError(Exception): -+ pass -+ -+ -+def queue_get(queue, process): -+ ''' -+ Remove and return an item from the queue. -+ Block until the process terminates. -+ ''' -+ while True: -+ try: -+ return queue.get(timeout=1) -+ # This semi-active waiting is ugly, but there doesn't seem be any -+ # obvious better way. -+ except mp.queues.Empty: -+ if process.exitcode is None: -+ continue -+ else: -+ raise -+ -+def run_i18nspector(options, path): -+ commandline = os.environ.get('I18NSPECTOR_COMMANDLINE') -+ if commandline is None: -+ # We cheat here a bit, because exec(3)ing is very expensive. -+ # Let's load the needed Python modules, and use multiprocessing to -+ # “emulate” the command execution. -+ import lib.cli # pylint: disable=import-outside-toplevel -+ assert lib.cli # make pyflakes happy -+ prog = os.path.join(here, os.pardir, os.pardir, 'i18nspector') -+ commandline = [sys.executable, prog] -+ queue = mp.Queue() -+ child = mp.Process( -+ target=_mp_run_i18nspector, -+ args=(prog, options, path, queue) -+ ) -+ child.start() -+ [stdout, stderr] = ( -+ s.splitlines() -+ for s in queue_get(queue, child) -+ ) -+ child.join() -+ rc = child.exitcode -+ else: -+ commandline = shlex.split(commandline) -+ commandline += options -+ commandline += [path] -+ fixed_env = dict(os.environ, PYTHONIOENCODING='UTF-8') -+ with ipc.Popen(commandline, stdout=ipc.PIPE, stderr=ipc.PIPE, env=fixed_env) as child: -+ stdout, stderr = ( -+ s.decode('UTF-8').splitlines() -+ for s in child.communicate() -+ ) -+ rc = child.poll() -+ assert isinstance(rc, int) -+ if rc == 0: -+ return stdout -+ if rc < 0: -+ message = ['command was interrupted by signal {sig}'.format(sig=get_signal_name(-rc))] # pylint: disable=invalid-unary-operand-type -+ else: -+ message = ['command exited with status {rc}'.format(rc=rc)] -+ message += [''] -+ if stdout: -+ message += ['stdout:'] -+ message += ['| ' + s for s in stdout] + [''] -+ else: -+ message += ['stdout: (empty)'] -+ if stderr: -+ message += ['stderr:'] -+ message += ['| ' + s for s in stderr] -+ else: -+ message += ['stderr: (empty)'] -+ raise PoTestFileException('\n'.join(message)) -+ -+def _mp_run_i18nspector(prog, options, path, queue): -+ with open(prog, 'rt', encoding='UTF-8') as file: -+ code = file.read() -+ sys.argv = [prog] + list(options) + [path] -+ orig_stdout = sys.stdout -+ orig_stderr = sys.stderr -+ code = compile(code, prog, 'exec') -+ io_stdout = io.StringIO() -+ io_stderr = io.StringIO() -+ gvars = dict( -+ __file__=prog, -+ ) -+ (sys.stdout, sys.stderr) = (io_stdout, io_stderr) -+ try: -+ try: -+ exec(code, gvars) # pylint: disable=exec-used -+ finally: -+ (sys.stdout, sys.stderr) = (orig_stdout, orig_stderr) -+ stdout = io_stdout.getvalue() -+ stderr = io_stderr.getvalue() -+ except SystemExit: -+ queue.put([stdout, stderr]) -+ raise -+ except: # pylint: disable=bare-except -+ exctp, exc, tb = sys.exc_info() -+ stderr += ''.join( -+ traceback.format_exception(exctp, exc, tb) -+ ) -+ queue.put([stdout, stderr]) -+ sys.exit(1) -+ raise # hi, pydiatra! -+ else: -+ queue.put([stdout, stderr]) -+ sys.exit(0) -+ -+def assert_emit_tags(path, etags, *, options=()): -+ etags = list(etags) -+ stdout = run_i18nspector(options, path) -+ expected_failure = os.path.basename(path).startswith('xfail-') -+ if stdout != etags: -+ if expected_failure: -+ raise unittest.SkipTest('expected failure') -+ str_etags = [str(x) for x in etags] -+ message = ['Tags differ:', ''] -+ diff = list( -+ difflib.unified_diff(str_etags, stdout, n=9999) -+ ) -+ message += diff[3:] -+ raise PoTestFileException('\n'.join(message)) -+ elif expected_failure: -+ raise PoTestFileException('unexpected success') -+ -+ -+# ---------------------------------------- -+# Tag coverage recording code -+ -+class PofileRecorderPlugin: -+ """pytest plugin to just record the test files that are collected""" -+ def __init__(self): -+ self.collected = [] -+ -+ def pytest_collection_modifyitems(self, items): -+ for item in items: -+ self.collected.append((item.nodeid, item)) -+ -+ -+def get_coverage(): -+ # ask pytest to enumerate the tests it knows of in this directory -+ directory = os.path.dirname(__file__) -+ pofile_recorder = PofileRecorderPlugin() -+ pytest.main(['--collect-only', '-p', 'no:terminal', directory], plugins=[pofile_recorder]) -+ -+ coverage = set() -+ for _, item in pofile_recorder.collected: -+ if isinstance(item, PoTestItem): -+ etags, _ = item.parse_test_headers() -+ for t in etags: -+ coverage.add(t.tag) -+ else: -+ for tag in etags_from_tagstring(item.function, ''): -+ coverage.add(tag.tag) -+ -+ return coverage -+ -+# vim:ts=4 sts=4 sw=4 et -Index: i18nspector-0.26/tests/blackbox_tests/test_errors.py -=================================================================== ---- /dev/null -+++ i18nspector-0.26/tests/blackbox_tests/test_errors.py -@@ -0,0 +1,36 @@ -+import inspect -+import os.path -+import unittest -+ -+from .conftest import tagstring, etags_from_tagstring, assert_emit_tags -+from .. import tools -+ -+ -+def this(): -+ ''' -+ Return function that called this function. (Hopefully!) -+ ''' -+ return globals()[inspect.stack()[1][0].f_code.co_name] -+ -+@tagstring(''' -+E: os-error No such file or directory -+''') -+def test_os_error_no_such_file(): -+ with tools.temporary_directory() as tmpdir: -+ path = os.path.join(tmpdir, 'nonexistent.po') -+ expected = etags_from_tagstring(this(), path) -+ assert_emit_tags(path, expected) -+ -+@tagstring(''' -+E: os-error Permission denied -+''') -+def test_os_error_permission_denied(): -+ if os.getuid() == 0: -+ raise unittest.SkipTest('this test must not be run as root') -+ with tools.temporary_directory() as tmpdir: -+ path = os.path.join(tmpdir, 'denied.po') -+ with open(path, 'wb'): -+ pass -+ os.chmod(path, 0) -+ expected = etags_from_tagstring(this(), path) -+ assert_emit_tags(path, expected) -Index: i18nspector-0.26/private/update-tag-coverage -=================================================================== ---- i18nspector-0.26.orig/private/update-tag-coverage -+++ i18nspector-0.26/private/update-tag-coverage -@@ -21,12 +21,14 @@ - # SOFTWARE. - - import os -+import os.path - import sys - - sys.path[0] += '/..' - - from lib import tags --from tests import blackbox_tests -+from tests.blackbox_tests import conftest as blackbox_tests -+ - - def main(): - coverage = blackbox_tests.get_coverage()