| # Copyright (c) 2010-2024 Benjamin Peterson | |
| # | |
| # 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 operator | |
| import sys | |
| import types | |
| import unittest | |
| import abc | |
| import pytest | |
| import six | |
| def test_add_doc(): | |
| def f(): | |
| """Icky doc""" | |
| pass | |
| six._add_doc(f, """New doc""") | |
| assert f.__doc__ == "New doc" | |
| def test_import_module(): | |
| from logging import handlers | |
| m = six._import_module("logging.handlers") | |
| assert m is handlers | |
| def test_integer_types(): | |
| assert isinstance(1, six.integer_types) | |
| assert isinstance(-1, six.integer_types) | |
| assert isinstance(six.MAXSIZE + 23, six.integer_types) | |
| assert not isinstance(.1, six.integer_types) | |
| def test_string_types(): | |
| assert isinstance("hi", six.string_types) | |
| assert isinstance(six.u("hi"), six.string_types) | |
| assert issubclass(six.text_type, six.string_types) | |
| def test_class_types(): | |
| class X: | |
| pass | |
| class Y(object): | |
| pass | |
| assert isinstance(X, six.class_types) | |
| assert isinstance(Y, six.class_types) | |
| assert not isinstance(X(), six.class_types) | |
| def test_text_type(): | |
| assert type(six.u("hi")) is six.text_type | |
| def test_binary_type(): | |
| assert type(six.b("hi")) is six.binary_type | |
| def test_MAXSIZE(): | |
| try: | |
| # This shouldn't raise an overflow error. | |
| six.MAXSIZE.__index__() | |
| except AttributeError: | |
| # Before Python 2.6. | |
| pass | |
| pytest.raises( | |
| (ValueError, OverflowError), | |
| operator.mul, [None], six.MAXSIZE + 1) | |
| def test_lazy(): | |
| if six.PY3: | |
| html_name = "html.parser" | |
| else: | |
| html_name = "HTMLParser" | |
| assert html_name not in sys.modules | |
| mod = six.moves.html_parser | |
| assert sys.modules[html_name] is mod | |
| assert "htmlparser" not in six._MovedItems.__dict__ | |
| try: | |
| import _tkinter | |
| except ImportError: | |
| have_tkinter = False | |
| else: | |
| have_tkinter = True | |
| have_gdbm = True | |
| try: | |
| import gdbm | |
| except ImportError: | |
| try: | |
| import dbm.gnu | |
| except ImportError: | |
| have_gdbm = False | |
| have_ndbm = True | |
| try: | |
| import dbm | |
| except ImportError: | |
| try: | |
| import dbm.ndbm | |
| except ImportError: | |
| have_ndbm = False | |
| @pytest.mark.parametrize("item_name", | |
| [item.name for item in six._moved_attributes]) | |
| def test_move_items(item_name): | |
| """Ensure that everything loads correctly.""" | |
| try: | |
| item = getattr(six.moves, item_name) | |
| if isinstance(item, types.ModuleType): | |
| __import__("six.moves." + item_name) | |
| except ImportError: | |
| if item_name == "winreg" and not sys.platform.startswith("win"): | |
| pytest.skip("Windows only module") | |
| if item_name.startswith("tkinter"): | |
| if not have_tkinter: | |
| pytest.skip("requires tkinter") | |
| if item_name == "tkinter_tix" and sys.version_info >= (3, 13): | |
| pytest.skip("tkinter.tix removed from Python 3.13") | |
| if item_name == "dbm_gnu" and not have_gdbm: | |
| pytest.skip("requires gdbm") | |
| if item_name == "dbm_ndbm": | |
| pytest.skip("requires ndbm") | |
| raise | |
| assert item_name in dir(six.moves) | |
| @pytest.mark.parametrize("item_name", | |
| [item.name for item in six._urllib_parse_moved_attributes]) | |
| def test_move_items_urllib_parse(item_name): | |
| """Ensure that everything loads correctly.""" | |
| assert item_name in dir(six.moves.urllib.parse) | |
| getattr(six.moves.urllib.parse, item_name) | |
| @pytest.mark.parametrize("item_name", | |
| [item.name for item in six._urllib_error_moved_attributes]) | |
| def test_move_items_urllib_error(item_name): | |
| """Ensure that everything loads correctly.""" | |
| assert item_name in dir(six.moves.urllib.error) | |
| getattr(six.moves.urllib.error, item_name) | |
| @pytest.mark.parametrize("item_name", | |
| [item.name for item in six._urllib_request_moved_attributes]) | |
| def test_move_items_urllib_request(item_name): | |
| """Ensure that everything loads correctly.""" | |
| assert item_name in dir(six.moves.urllib.request) | |
| getattr(six.moves.urllib.request, item_name) | |
| @pytest.mark.parametrize("item_name", | |
| [item.name for item in six._urllib_response_moved_attributes]) | |
| def test_move_items_urllib_response(item_name): | |
| """Ensure that everything loads correctly.""" | |
| assert item_name in dir(six.moves.urllib.response) | |
| getattr(six.moves.urllib.response, item_name) | |
| @pytest.mark.parametrize("item_name", | |
| [item.name for item in six._urllib_robotparser_moved_attributes]) | |
| def test_move_items_urllib_robotparser(item_name): | |
| """Ensure that everything loads correctly.""" | |
| assert item_name in dir(six.moves.urllib.robotparser) | |
| getattr(six.moves.urllib.robotparser, item_name) | |
| def test_import_moves_error_1(): | |
| from six.moves.urllib.parse import urljoin | |
| from six import moves | |
| # In 1.4.1: AttributeError: 'Module_six_moves_urllib_parse' object has no attribute 'urljoin' | |
| assert moves.urllib.parse.urljoin | |
| def test_import_moves_error_2(): | |
| from six import moves | |
| assert moves.urllib.parse.urljoin | |
| # In 1.4.1: ImportError: cannot import name urljoin | |
| from six.moves.urllib.parse import urljoin | |
| def test_import_moves_error_3(): | |
| from six.moves.urllib.parse import urljoin | |
| # In 1.4.1: ImportError: cannot import name urljoin | |
| from six.moves.urllib_parse import urljoin | |
| def test_from_imports(): | |
| from six.moves.queue import Queue | |
| assert isinstance(Queue, six.class_types) | |
| from six.moves.configparser import ConfigParser | |
| assert isinstance(ConfigParser, six.class_types) | |
| def test_filter(): | |
| from six.moves import filter | |
| f = filter(lambda x: x % 2, range(10)) | |
| assert six.advance_iterator(f) == 1 | |
| def test_filter_false(): | |
| from six.moves import filterfalse | |
| f = filterfalse(lambda x: x % 3, range(10)) | |
| assert six.advance_iterator(f) == 0 | |
| assert six.advance_iterator(f) == 3 | |
| assert six.advance_iterator(f) == 6 | |
| def test_map(): | |
| from six.moves import map | |
| assert six.advance_iterator(map(lambda x: x + 1, range(2))) == 1 | |
| def test_getoutput(): | |
| from six.moves import getoutput | |
| output = getoutput('dir' if sys.platform.startswith('win') else 'echo foo') | |
| assert output != '' | |
| def test_zip(): | |
| from six.moves import zip | |
| assert six.advance_iterator(zip(range(2), range(2))) == (0, 0) | |
| def test_zip_longest(): | |
| from six.moves import zip_longest | |
| it = zip_longest(range(2), range(1)) | |
| assert six.advance_iterator(it) == (0, 0) | |
| assert six.advance_iterator(it) == (1, None) | |
| class TestCustomizedMoves: | |
| def teardown_method(self, meth): | |
| try: | |
| del six._MovedItems.spam | |
| except AttributeError: | |
| pass | |
| try: | |
| del six.moves.__dict__["spam"] | |
| except KeyError: | |
| pass | |
| def test_moved_attribute(self): | |
| attr = six.MovedAttribute("spam", "foo", "bar") | |
| if six.PY3: | |
| assert attr.mod == "bar" | |
| else: | |
| assert attr.mod == "foo" | |
| assert attr.attr == "spam" | |
| attr = six.MovedAttribute("spam", "foo", "bar", "lemma") | |
| assert attr.attr == "lemma" | |
| attr = six.MovedAttribute("spam", "foo", "bar", "lemma", "theorm") | |
| if six.PY3: | |
| assert attr.attr == "theorm" | |
| else: | |
| assert attr.attr == "lemma" | |
| def test_moved_module(self): | |
| attr = six.MovedModule("spam", "foo") | |
| if six.PY3: | |
| assert attr.mod == "spam" | |
| else: | |
| assert attr.mod == "foo" | |
| attr = six.MovedModule("spam", "foo", "bar") | |
| if six.PY3: | |
| assert attr.mod == "bar" | |
| else: | |
| assert attr.mod == "foo" | |
| def test_custom_move_module(self): | |
| attr = six.MovedModule("spam", "six", "six") | |
| six.add_move(attr) | |
| six.remove_move("spam") | |
| assert not hasattr(six.moves, "spam") | |
| attr = six.MovedModule("spam", "six", "six") | |
| six.add_move(attr) | |
| from six.moves import spam | |
| assert spam is six | |
| six.remove_move("spam") | |
| assert not hasattr(six.moves, "spam") | |
| def test_custom_move_attribute(self): | |
| attr = six.MovedAttribute("spam", "six", "six", "u", "u") | |
| six.add_move(attr) | |
| six.remove_move("spam") | |
| assert not hasattr(six.moves, "spam") | |
| attr = six.MovedAttribute("spam", "six", "six", "u", "u") | |
| six.add_move(attr) | |
| from six.moves import spam | |
| assert spam is six.u | |
| six.remove_move("spam") | |
| assert not hasattr(six.moves, "spam") | |
| def test_empty_remove(self): | |
| pytest.raises(AttributeError, six.remove_move, "eggs") | |
| def test_get_unbound_function(): | |
| class X(object): | |
| def m(self): | |
| pass | |
| assert six.get_unbound_function(X.m) is X.__dict__["m"] | |
| def test_get_method_self(): | |
| class X(object): | |
| def m(self): | |
| pass | |
| x = X() | |
| assert six.get_method_self(x.m) is x | |
| pytest.raises(AttributeError, six.get_method_self, 42) | |
| def test_get_method_function(): | |
| class X(object): | |
| def m(self): | |
| pass | |
| x = X() | |
| assert six.get_method_function(x.m) is X.__dict__["m"] | |
| pytest.raises(AttributeError, six.get_method_function, hasattr) | |
| def test_get_function_closure(): | |
| def f(): | |
| x = 42 | |
| def g(): | |
| return x | |
| return g | |
| cell = six.get_function_closure(f())[0] | |
| assert type(cell).__name__ == "cell" | |
| def test_get_function_code(): | |
| def f(): | |
| pass | |
| assert isinstance(six.get_function_code(f), types.CodeType) | |
| if not hasattr(sys, "pypy_version_info"): | |
| pytest.raises(AttributeError, six.get_function_code, hasattr) | |
| def test_get_function_defaults(): | |
| def f(x, y=3, b=4): | |
| pass | |
| assert six.get_function_defaults(f) == (3, 4) | |
| def test_get_function_globals(): | |
| def f(): | |
| pass | |
| assert six.get_function_globals(f) is globals() | |
| def test_dictionary_iterators(monkeypatch): | |
| def stock_method_name(iterwhat): | |
| """Given a method suffix like "lists" or "values", return the name | |
| of the dict method that delivers those on the version of Python | |
| we're running in.""" | |
| if six.PY3: | |
| return iterwhat | |
| return 'iter' + iterwhat | |
| class MyDict(dict): | |
| if not six.PY3: | |
| def lists(self, **kw): | |
| return [1, 2, 3] | |
| def iterlists(self, **kw): | |
| return iter([1, 2, 3]) | |
| f = MyDict.iterlists | |
| del MyDict.iterlists | |
| setattr(MyDict, stock_method_name('lists'), f) | |
| d = MyDict(zip(range(10), reversed(range(10)))) | |
| for name in "keys", "values", "items", "lists": | |
| meth = getattr(six, "iter" + name) | |
| it = meth(d) | |
| assert not isinstance(it, list) | |
| assert list(it) == list(getattr(d, name)()) | |
| pytest.raises(StopIteration, six.advance_iterator, it) | |
| record = [] | |
| def with_kw(*args, **kw): | |
| record.append(kw["kw"]) | |
| return old(*args) | |
| old = getattr(MyDict, stock_method_name(name)) | |
| monkeypatch.setattr(MyDict, stock_method_name(name), with_kw) | |
| meth(d, kw=42) | |
| assert record == [42] | |
| monkeypatch.undo() | |
| def test_dictionary_views(): | |
| d = dict(zip(range(10), (range(11, 20)))) | |
| for name in "keys", "values", "items": | |
| meth = getattr(six, "view" + name) | |
| view = meth(d) | |
| assert set(view) == set(getattr(d, name)()) | |
| def test_advance_iterator(): | |
| assert six.next is six.advance_iterator | |
| l = [1, 2] | |
| it = iter(l) | |
| assert six.next(it) == 1 | |
| assert six.next(it) == 2 | |
| pytest.raises(StopIteration, six.next, it) | |
| pytest.raises(StopIteration, six.next, it) | |
| def test_iterator(): | |
| class myiter(six.Iterator): | |
| def __next__(self): | |
| return 13 | |
| assert six.advance_iterator(myiter()) == 13 | |
| class myitersub(myiter): | |
| def __next__(self): | |
| return 14 | |
| assert six.advance_iterator(myitersub()) == 14 | |
| def test_callable(): | |
| class X: | |
| def __call__(self): | |
| pass | |
| def method(self): | |
| pass | |
| assert six.callable(X) | |
| assert six.callable(X()) | |
| assert six.callable(test_callable) | |
| assert six.callable(hasattr) | |
| assert six.callable(X.method) | |
| assert six.callable(X().method) | |
| assert not six.callable(4) | |
| assert not six.callable("string") | |
| def test_create_bound_method(): | |
| class X(object): | |
| pass | |
| def f(self): | |
| return self | |
| x = X() | |
| b = six.create_bound_method(f, x) | |
| assert isinstance(b, types.MethodType) | |
| assert b() is x | |
| def test_create_unbound_method(): | |
| class X(object): | |
| pass | |
| def f(self): | |
| return self | |
| u = six.create_unbound_method(f, X) | |
| pytest.raises(TypeError, u) | |
| if six.PY2: | |
| assert isinstance(u, types.MethodType) | |
| x = X() | |
| assert f(x) is x | |
| if six.PY3: | |
| def test_b(): | |
| data = six.b("\xff") | |
| assert isinstance(data, bytes) | |
| assert len(data) == 1 | |
| assert data == bytes([255]) | |
| def test_u(): | |
| s = six.u("hi \u0439 \U00000439 \\ \\\\ \n") | |
| assert isinstance(s, str) | |
| assert s == "hi \u0439 \U00000439 \\ \\\\ \n" | |
| else: | |
| def test_b(): | |
| data = six.b("\xff") | |
| assert isinstance(data, str) | |
| assert len(data) == 1 | |
| assert data == "\xff" | |
| def test_u(): | |
| s = six.u("hi \u0439 \U00000439 \\ \\\\ \n") | |
| assert isinstance(s, unicode) | |
| assert s == "hi \xd0\xb9 \xd0\xb9 \\ \\\\ \n".decode("utf8") | |
| def test_u_escapes(): | |
| s = six.u("\u1234") | |
| assert len(s) == 1 | |
| def test_unichr(): | |
| assert six.u("\u1234") == six.unichr(0x1234) | |
| assert type(six.u("\u1234")) is type(six.unichr(0x1234)) | |
| def test_int2byte(): | |
| assert six.int2byte(3) == six.b("\x03") | |
| pytest.raises(Exception, six.int2byte, 256) | |
| def test_byte2int(): | |
| assert six.byte2int(six.b("\x03")) == 3 | |
| assert six.byte2int(six.b("\x03\x04")) == 3 | |
| pytest.raises(IndexError, six.byte2int, six.b("")) | |
| def test_bytesindex(): | |
| assert six.indexbytes(six.b("hello"), 3) == ord("l") | |
| def test_bytesiter(): | |
| it = six.iterbytes(six.b("hi")) | |
| assert six.next(it) == ord("h") | |
| assert six.next(it) == ord("i") | |
| pytest.raises(StopIteration, six.next, it) | |
| def test_StringIO(): | |
| fp = six.StringIO() | |
| fp.write(six.u("hello")) | |
| assert fp.getvalue() == six.u("hello") | |
| def test_BytesIO(): | |
| fp = six.BytesIO() | |
| fp.write(six.b("hello")) | |
| assert fp.getvalue() == six.b("hello") | |
| def test_exec_(): | |
| def f(): | |
| l = [] | |
| six.exec_("l.append(1)") | |
| assert l == [1] | |
| f() | |
| ns = {} | |
| six.exec_("x = 42", ns) | |
| assert ns["x"] == 42 | |
| glob = {} | |
| loc = {} | |
| six.exec_("global y; y = 42; x = 12", glob, loc) | |
| assert glob["y"] == 42 | |
| assert "x" not in glob | |
| assert loc["x"] == 12 | |
| assert "y" not in loc | |
| def test_reraise(): | |
| def get_next(tb): | |
| if six.PY3: | |
| return tb.tb_next.tb_next | |
| else: | |
| return tb.tb_next | |
| e = Exception("blah") | |
| try: | |
| raise e | |
| except Exception: | |
| tp, val, tb = sys.exc_info() | |
| try: | |
| six.reraise(tp, val, tb) | |
| except Exception: | |
| tp2, value2, tb2 = sys.exc_info() | |
| assert tp2 is Exception | |
| assert value2 is e | |
| assert tb is get_next(tb2) | |
| try: | |
| six.reraise(tp, val) | |
| except Exception: | |
| tp2, value2, tb2 = sys.exc_info() | |
| assert tp2 is Exception | |
| assert value2 is e | |
| assert tb2 is not tb | |
| try: | |
| six.reraise(tp, val, tb2) | |
| except Exception: | |
| tp2, value2, tb3 = sys.exc_info() | |
| assert tp2 is Exception | |
| assert value2 is e | |
| assert get_next(tb3) is tb2 | |
| try: | |
| six.reraise(tp, None, tb) | |
| except Exception: | |
| tp2, value2, tb2 = sys.exc_info() | |
| assert tp2 is Exception | |
| assert value2 is not val | |
| assert isinstance(value2, Exception) | |
| assert tb is get_next(tb2) | |
| def test_raise_from(): | |
| try: | |
| try: | |
| raise Exception("blah") | |
| except Exception: | |
| ctx = sys.exc_info()[1] | |
| f = Exception("foo") | |
| six.raise_from(f, None) | |
| except Exception: | |
| tp, val, tb = sys.exc_info() | |
| if sys.version_info[:2] > (3, 0): | |
| # We should have done a raise f from None equivalent. | |
| assert val.__cause__ is None | |
| assert val.__context__ is ctx | |
| # And that should suppress the context on the exception. | |
| assert val.__suppress_context__ | |
| # For all versions the outer exception should have raised successfully. | |
| assert str(val) == "foo" | |
| def test_print_(): | |
| save = sys.stdout | |
| out = sys.stdout = six.moves.StringIO() | |
| try: | |
| six.print_("Hello,", "person!") | |
| finally: | |
| sys.stdout = save | |
| assert out.getvalue() == "Hello, person!\n" | |
| out = six.StringIO() | |
| six.print_("Hello,", "person!", file=out) | |
| assert out.getvalue() == "Hello, person!\n" | |
| out = six.StringIO() | |
| six.print_("Hello,", "person!", file=out, end="") | |
| assert out.getvalue() == "Hello, person!" | |
| out = six.StringIO() | |
| six.print_("Hello,", "person!", file=out, sep="X") | |
| assert out.getvalue() == "Hello,Xperson!\n" | |
| out = six.StringIO() | |
| six.print_(six.u("Hello,"), six.u("person!"), file=out) | |
| result = out.getvalue() | |
| assert isinstance(result, six.text_type) | |
| assert result == six.u("Hello, person!\n") | |
| six.print_("Hello", file=None) # This works. | |
| out = six.StringIO() | |
| six.print_(None, file=out) | |
| assert out.getvalue() == "None\n" | |
| class FlushableStringIO(six.StringIO): | |
| def __init__(self): | |
| six.StringIO.__init__(self) | |
| self.flushed = False | |
| def flush(self): | |
| self.flushed = True | |
| out = FlushableStringIO() | |
| six.print_("Hello", file=out) | |
| assert not out.flushed | |
| six.print_("Hello", file=out, flush=True) | |
| assert out.flushed | |
| def test_print_exceptions(): | |
| pytest.raises(TypeError, six.print_, x=3) | |
| pytest.raises(TypeError, six.print_, end=3) | |
| pytest.raises(TypeError, six.print_, sep=42) | |
| def test_with_metaclass(): | |
| class Meta(type): | |
| pass | |
| class X(six.with_metaclass(Meta)): | |
| pass | |
| assert type(X) is Meta | |
| assert issubclass(X, object) | |
| class Base(object): | |
| pass | |
| class X(six.with_metaclass(Meta, Base)): | |
| pass | |
| assert type(X) is Meta | |
| assert issubclass(X, Base) | |
| class Base2(object): | |
| pass | |
| class X(six.with_metaclass(Meta, Base, Base2)): | |
| pass | |
| assert type(X) is Meta | |
| assert issubclass(X, Base) | |
| assert issubclass(X, Base2) | |
| assert X.__mro__ == (X, Base, Base2, object) | |
| class X(six.with_metaclass(Meta)): | |
| pass | |
| class MetaSub(Meta): | |
| pass | |
| class Y(six.with_metaclass(MetaSub, X)): | |
| pass | |
| assert type(Y) is MetaSub | |
| assert Y.__mro__ == (Y, X, object) | |
| def test_with_metaclass_typing(): | |
| try: | |
| import typing | |
| except ImportError: | |
| pytest.skip("typing module required") | |
| class Meta(type): | |
| pass | |
| if sys.version_info[:2] < (3, 7): | |
| # Generics with custom metaclasses were broken on older versions. | |
| class Meta(Meta, typing.GenericMeta): | |
| pass | |
| T = typing.TypeVar('T') | |
| class G(six.with_metaclass(Meta, typing.Generic[T])): | |
| pass | |
| class GA(six.with_metaclass(abc.ABCMeta, typing.Generic[T])): | |
| pass | |
| assert isinstance(G, Meta) | |
| assert isinstance(GA, abc.ABCMeta) | |
| assert G[int] is not G[G[int]] | |
| assert GA[int] is not GA[GA[int]] | |
| assert G.__bases__ == (typing.Generic,) | |
| assert G.__orig_bases__ == (typing.Generic[T],) | |
| @pytest.mark.skipif("sys.version_info[:2] < (3, 7)") | |
| def test_with_metaclass_pep_560(): | |
| class Meta(type): | |
| pass | |
| class A: | |
| pass | |
| class B: | |
| pass | |
| class Fake: | |
| def __mro_entries__(self, bases): | |
| return (A, B) | |
| fake = Fake() | |
| class G(six.with_metaclass(Meta, fake)): | |
| pass | |
| class GA(six.with_metaclass(abc.ABCMeta, fake)): | |
| pass | |
| assert isinstance(G, Meta) | |
| assert isinstance(GA, abc.ABCMeta) | |
| assert G.__bases__ == (A, B) | |
| assert G.__orig_bases__ == (fake,) | |
| @pytest.mark.skipif("sys.version_info[:2] < (3, 0)") | |
| def test_with_metaclass_prepare(): | |
| """Test that with_metaclass causes Meta.__prepare__ to be called with the correct arguments.""" | |
| class MyDict(dict): | |
| pass | |
| class Meta(type): | |
| @classmethod | |
| def __prepare__(cls, name, bases): | |
| namespace = MyDict(super().__prepare__(name, bases), cls=cls, bases=bases) | |
| namespace['namespace'] = namespace | |
| return namespace | |
| class Base(object): | |
| pass | |
| bases = (Base,) | |
| class X(six.with_metaclass(Meta, *bases)): | |
| pass | |
| assert getattr(X, 'cls', type) is Meta | |
| assert getattr(X, 'bases', ()) == bases | |
| assert isinstance(getattr(X, 'namespace', {}), MyDict) | |
| def test_wraps(): | |
| def f(g): | |
| @six.wraps(g) | |
| def w(): | |
| return 42 | |
| return w | |
| def k(): | |
| pass | |
| original_k = k | |
| k = f(f(k)) | |
| assert hasattr(k, '__wrapped__') | |
| k = k.__wrapped__ | |
| assert hasattr(k, '__wrapped__') | |
| k = k.__wrapped__ | |
| assert k is original_k | |
| assert not hasattr(k, '__wrapped__') | |
| def f(g, assign, update): | |
| def w(): | |
| return 42 | |
| w.glue = {"foo": "bar"} | |
| w.xyzzy = {"qux": "quux"} | |
| return six.wraps(g, assign, update)(w) | |
| k.glue = {"melon": "egg"} | |
| k.turnip = 43 | |
| k = f(k, ["turnip", "baz"], ["glue", "xyzzy"]) | |
| assert k.__name__ == "w" | |
| assert k.turnip == 43 | |
| assert not hasattr(k, "baz") | |
| assert k.glue == {"melon": "egg", "foo": "bar"} | |
| assert k.xyzzy == {"qux": "quux"} | |
| def test_wraps_raises_on_missing_updated_field_on_wrapper(): | |
| """Ensure six.wraps doesn't ignore missing attrs wrapper. | |
| Because that's what happens in Py3's functools.update_wrapper. | |
| """ | |
| def wrapped(): | |
| pass | |
| def wrapper(): | |
| pass | |
| with pytest.raises(AttributeError, match='has no attribute.*xyzzy'): | |
| six.wraps(wrapped, [], ['xyzzy'])(wrapper) | |
| def test_add_metaclass(): | |
| class Meta(type): | |
| pass | |
| class X: | |
| "success" | |
| X = six.add_metaclass(Meta)(X) | |
| assert type(X) is Meta | |
| assert issubclass(X, object) | |
| assert X.__module__ == __name__ | |
| assert X.__doc__ == "success" | |
| class Base(object): | |
| pass | |
| class X(Base): | |
| pass | |
| X = six.add_metaclass(Meta)(X) | |
| assert type(X) is Meta | |
| assert issubclass(X, Base) | |
| class Base2(object): | |
| pass | |
| class X(Base, Base2): | |
| pass | |
| X = six.add_metaclass(Meta)(X) | |
| assert type(X) is Meta | |
| assert issubclass(X, Base) | |
| assert issubclass(X, Base2) | |
| # Test a second-generation subclass of a type. | |
| class Meta1(type): | |
| m1 = "m1" | |
| class Meta2(Meta1): | |
| m2 = "m2" | |
| class Base: | |
| b = "b" | |
| Base = six.add_metaclass(Meta1)(Base) | |
| class X(Base): | |
| x = "x" | |
| X = six.add_metaclass(Meta2)(X) | |
| assert type(X) is Meta2 | |
| assert issubclass(X, Base) | |
| assert type(Base) is Meta1 | |
| assert "__dict__" not in vars(X) | |
| instance = X() | |
| instance.attr = "test" | |
| assert vars(instance) == {"attr": "test"} | |
| assert instance.b == Base.b | |
| assert instance.x == X.x | |
| # Test a class with slots. | |
| class MySlots(object): | |
| __slots__ = ["a", "b"] | |
| MySlots = six.add_metaclass(Meta1)(MySlots) | |
| assert MySlots.__slots__ == ["a", "b"] | |
| instance = MySlots() | |
| instance.a = "foo" | |
| pytest.raises(AttributeError, setattr, instance, "c", "baz") | |
| # Test a class with string for slots. | |
| class MyStringSlots(object): | |
| __slots__ = "ab" | |
| MyStringSlots = six.add_metaclass(Meta1)(MyStringSlots) | |
| assert MyStringSlots.__slots__ == "ab" | |
| instance = MyStringSlots() | |
| instance.ab = "foo" | |
| pytest.raises(AttributeError, setattr, instance, "a", "baz") | |
| pytest.raises(AttributeError, setattr, instance, "b", "baz") | |
| class MySlotsWeakref(object): | |
| __slots__ = "__weakref__", | |
| MySlotsWeakref = six.add_metaclass(Meta)(MySlotsWeakref) | |
| assert type(MySlotsWeakref) is Meta | |
| @pytest.mark.skipif("sys.version_info[:2] < (3, 3)") | |
| def test_add_metaclass_nested(): | |
| # Regression test for https://github.com/benjaminp/six/issues/259 | |
| class Meta(type): | |
| pass | |
| class A: | |
| class B: pass | |
| expected = 'test_add_metaclass_nested.<locals>.A.B' | |
| assert A.B.__qualname__ == expected | |
| class A: | |
| @six.add_metaclass(Meta) | |
| class B: pass | |
| assert A.B.__qualname__ == expected | |
| def test_assertCountEqual(): | |
| class TestAssertCountEqual(unittest.TestCase): | |
| def test(self): | |
| with self.assertRaises(AssertionError): | |
| six.assertCountEqual(self, (1, 2), [3, 4, 5]) | |
| six.assertCountEqual(self, (1, 2), [2, 1]) | |
| TestAssertCountEqual('test').test() | |
| def test_assertRegex(): | |
| class TestAssertRegex(unittest.TestCase): | |
| def test(self): | |
| with self.assertRaises(AssertionError): | |
| six.assertRegex(self, 'test', r'^a') | |
| six.assertRegex(self, 'test', r'^t') | |
| TestAssertRegex('test').test() | |
| def test_assertNotRegex(): | |
| class TestAssertNotRegex(unittest.TestCase): | |
| def test(self): | |
| with self.assertRaises(AssertionError): | |
| six.assertNotRegex(self, 'test', r'^t') | |
| six.assertNotRegex(self, 'test', r'^a') | |
| TestAssertNotRegex('test').test() | |
| def test_assertRaisesRegex(): | |
| class TestAssertRaisesRegex(unittest.TestCase): | |
| def test(self): | |
| with six.assertRaisesRegex(self, AssertionError, '^Foo'): | |
| raise AssertionError('Foo') | |
| with self.assertRaises(AssertionError): | |
| with six.assertRaisesRegex(self, AssertionError, r'^Foo'): | |
| raise AssertionError('Bar') | |
| TestAssertRaisesRegex('test').test() | |
| def test_python_2_unicode_compatible(): | |
| @six.python_2_unicode_compatible | |
| class MyTest(object): | |
| def __str__(self): | |
| return six.u('hello') | |
| def __bytes__(self): | |
| return six.b('hello') | |
| my_test = MyTest() | |
| if six.PY2: | |
| assert str(my_test) == six.b("hello") | |
| assert unicode(my_test) == six.u("hello") | |
| elif six.PY3: | |
| assert bytes(my_test) == six.b("hello") | |
| assert str(my_test) == six.u("hello") | |
| assert getattr(six.moves.builtins, 'bytes', str)(my_test) == six.b("hello") | |
| class EnsureTests: | |
| # grinning face emoji | |
| UNICODE_EMOJI = six.u("\U0001F600") | |
| BINARY_EMOJI = b"\xf0\x9f\x98\x80" | |
| def test_ensure_binary_raise_type_error(self): | |
| with pytest.raises(TypeError): | |
| six.ensure_str(8) | |
| def test_errors_and_encoding(self): | |
| six.ensure_binary(self.UNICODE_EMOJI, encoding='latin-1', errors='ignore') | |
| with pytest.raises(UnicodeEncodeError): | |
| six.ensure_binary(self.UNICODE_EMOJI, encoding='latin-1', errors='strict') | |
| def test_ensure_binary_raise(self): | |
| converted_unicode = six.ensure_binary(self.UNICODE_EMOJI, encoding='utf-8', errors='strict') | |
| converted_binary = six.ensure_binary(self.BINARY_EMOJI, encoding="utf-8", errors='strict') | |
| if six.PY2: | |
| # PY2: unicode -> str | |
| assert converted_unicode == self.BINARY_EMOJI and isinstance(converted_unicode, str) | |
| # PY2: str -> str | |
| assert converted_binary == self.BINARY_EMOJI and isinstance(converted_binary, str) | |
| else: | |
| # PY3: str -> bytes | |
| assert converted_unicode == self.BINARY_EMOJI and isinstance(converted_unicode, bytes) | |
| # PY3: bytes -> bytes | |
| assert converted_binary == self.BINARY_EMOJI and isinstance(converted_binary, bytes) | |
| def test_ensure_str(self): | |
| converted_unicode = six.ensure_str(self.UNICODE_EMOJI, encoding='utf-8', errors='strict') | |
| converted_binary = six.ensure_str(self.BINARY_EMOJI, encoding="utf-8", errors='strict') | |
| if six.PY2: | |
| # PY2: unicode -> str | |
| assert converted_unicode == self.BINARY_EMOJI and isinstance(converted_unicode, str) | |
| # PY2: str -> str | |
| assert converted_binary == self.BINARY_EMOJI and isinstance(converted_binary, str) | |
| else: | |
| # PY3: str -> str | |
| assert converted_unicode == self.UNICODE_EMOJI and isinstance(converted_unicode, str) | |
| # PY3: bytes -> str | |
| assert converted_binary == self.UNICODE_EMOJI and isinstance(converted_unicode, str) | |
| def test_ensure_text(self): | |
| converted_unicode = six.ensure_text(self.UNICODE_EMOJI, encoding='utf-8', errors='strict') | |
| converted_binary = six.ensure_text(self.BINARY_EMOJI, encoding="utf-8", errors='strict') | |
| if six.PY2: | |
| # PY2: unicode -> unicode | |
| assert converted_unicode == self.UNICODE_EMOJI and isinstance(converted_unicode, unicode) | |
| # PY2: str -> unicode | |
| assert converted_binary == self.UNICODE_EMOJI and isinstance(converted_unicode, unicode) | |
| else: | |
| # PY3: str -> str | |
| assert converted_unicode == self.UNICODE_EMOJI and isinstance(converted_unicode, str) | |
| # PY3: bytes -> str | |
| assert converted_binary == self.UNICODE_EMOJI and isinstance(converted_unicode, str) |