1# Copyright (c) 2010-2020 Benjamin Peterson
2#
3# Permission is hereby granted, free of charge, to any person obtaining a copy
4# of this software and associated documentation files (the "Software"), to deal
5# in the Software without restriction, including without limitation the rights
6# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7# copies of the Software, and to permit persons to whom the Software is
8# furnished to do so, subject to the following conditions:
9#
10# The above copyright notice and this permission notice shall be included in all
11# copies or substantial portions of the Software.
12#
13# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19# SOFTWARE.
20
21import operator
22import sys
23import types
24import unittest
25import abc
26
27import pytest
28
29import six
30
31
32def test_add_doc():
33    def f():
34        """Icky doc"""
35        pass
36    six._add_doc(f, """New doc""")
37    assert f.__doc__ == "New doc"
38
39
40def test_import_module():
41    from logging import handlers
42    m = six._import_module("logging.handlers")
43    assert m is handlers
44
45
46def test_integer_types():
47    assert isinstance(1, six.integer_types)
48    assert isinstance(-1, six.integer_types)
49    assert isinstance(six.MAXSIZE + 23, six.integer_types)
50    assert not isinstance(.1, six.integer_types)
51
52
53def test_string_types():
54    assert isinstance("hi", six.string_types)
55    assert isinstance(six.u("hi"), six.string_types)
56    assert issubclass(six.text_type, six.string_types)
57
58
59def test_class_types():
60    class X:
61        pass
62    class Y(object):
63        pass
64    assert isinstance(X, six.class_types)
65    assert isinstance(Y, six.class_types)
66    assert not isinstance(X(), six.class_types)
67
68
69def test_text_type():
70    assert type(six.u("hi")) is six.text_type
71
72
73def test_binary_type():
74    assert type(six.b("hi")) is six.binary_type
75
76
77def test_MAXSIZE():
78    try:
79        # This shouldn't raise an overflow error.
80        six.MAXSIZE.__index__()
81    except AttributeError:
82        # Before Python 2.6.
83        pass
84    pytest.raises(
85        (ValueError, OverflowError),
86        operator.mul, [None], six.MAXSIZE + 1)
87
88
89def test_lazy():
90    if six.PY3:
91        html_name = "html.parser"
92    else:
93        html_name = "HTMLParser"
94    assert html_name not in sys.modules
95    mod = six.moves.html_parser
96    assert sys.modules[html_name] is mod
97    assert "htmlparser" not in six._MovedItems.__dict__
98
99
100try:
101    import _tkinter
102except ImportError:
103    have_tkinter = False
104else:
105    have_tkinter = True
106
107have_gdbm = True
108try:
109    import gdbm
110except ImportError:
111    try:
112        import dbm.gnu
113    except ImportError:
114        have_gdbm = False
115
116@pytest.mark.parametrize("item_name",
117                          [item.name for item in six._moved_attributes])
118def test_move_items(item_name):
119    """Ensure that everything loads correctly."""
120    try:
121        item = getattr(six.moves, item_name)
122        if isinstance(item, types.ModuleType):
123            __import__("six.moves." + item_name)
124    except ImportError:
125        if item_name == "winreg" and not sys.platform.startswith("win"):
126            pytest.skip("Windows only module")
127        if item_name.startswith("tkinter"):
128            if not have_tkinter:
129                pytest.skip("requires tkinter")
130        if item_name.startswith("dbm_gnu") and not have_gdbm:
131            pytest.skip("requires gdbm")
132        raise
133    assert item_name in dir(six.moves)
134
135
136@pytest.mark.parametrize("item_name",
137                          [item.name for item in six._urllib_parse_moved_attributes])
138def test_move_items_urllib_parse(item_name):
139    """Ensure that everything loads correctly."""
140    assert item_name in dir(six.moves.urllib.parse)
141    getattr(six.moves.urllib.parse, item_name)
142
143
144@pytest.mark.parametrize("item_name",
145                          [item.name for item in six._urllib_error_moved_attributes])
146def test_move_items_urllib_error(item_name):
147    """Ensure that everything loads correctly."""
148    assert item_name in dir(six.moves.urllib.error)
149    getattr(six.moves.urllib.error, item_name)
150
151
152@pytest.mark.parametrize("item_name",
153                          [item.name for item in six._urllib_request_moved_attributes])
154def test_move_items_urllib_request(item_name):
155    """Ensure that everything loads correctly."""
156    assert item_name in dir(six.moves.urllib.request)
157    getattr(six.moves.urllib.request, item_name)
158
159
160@pytest.mark.parametrize("item_name",
161                          [item.name for item in six._urllib_response_moved_attributes])
162def test_move_items_urllib_response(item_name):
163    """Ensure that everything loads correctly."""
164    assert item_name in dir(six.moves.urllib.response)
165    getattr(six.moves.urllib.response, item_name)
166
167
168@pytest.mark.parametrize("item_name",
169                          [item.name for item in six._urllib_robotparser_moved_attributes])
170def test_move_items_urllib_robotparser(item_name):
171    """Ensure that everything loads correctly."""
172    assert item_name in dir(six.moves.urllib.robotparser)
173    getattr(six.moves.urllib.robotparser, item_name)
174
175
176def test_import_moves_error_1():
177    from six.moves.urllib.parse import urljoin
178    from six import moves
179    # In 1.4.1: AttributeError: 'Module_six_moves_urllib_parse' object has no attribute 'urljoin'
180    assert moves.urllib.parse.urljoin
181
182
183def test_import_moves_error_2():
184    from six import moves
185    assert moves.urllib.parse.urljoin
186    # In 1.4.1: ImportError: cannot import name urljoin
187    from six.moves.urllib.parse import urljoin
188
189
190def test_import_moves_error_3():
191    from six.moves.urllib.parse import urljoin
192    # In 1.4.1: ImportError: cannot import name urljoin
193    from six.moves.urllib_parse import urljoin
194
195
196def test_from_imports():
197    from six.moves.queue import Queue
198    assert isinstance(Queue, six.class_types)
199    from six.moves.configparser import ConfigParser
200    assert isinstance(ConfigParser, six.class_types)
201
202
203def test_filter():
204    from six.moves import filter
205    f = filter(lambda x: x % 2, range(10))
206    assert six.advance_iterator(f) == 1
207
208
209def test_filter_false():
210    from six.moves import filterfalse
211    f = filterfalse(lambda x: x % 3, range(10))
212    assert six.advance_iterator(f) == 0
213    assert six.advance_iterator(f) == 3
214    assert six.advance_iterator(f) == 6
215
216def test_map():
217    from six.moves import map
218    assert six.advance_iterator(map(lambda x: x + 1, range(2))) == 1
219
220
221def test_getoutput():
222    from six.moves import getoutput
223    output = getoutput('echo "foo"')
224    assert output == 'foo'
225
226
227def test_zip():
228    from six.moves import zip
229    assert six.advance_iterator(zip(range(2), range(2))) == (0, 0)
230
231
232def test_zip_longest():
233    from six.moves import zip_longest
234    it = zip_longest(range(2), range(1))
235
236    assert six.advance_iterator(it) == (0, 0)
237    assert six.advance_iterator(it) == (1, None)
238
239
240class TestCustomizedMoves:
241
242    def teardown_method(self, meth):
243        try:
244            del six._MovedItems.spam
245        except AttributeError:
246            pass
247        try:
248            del six.moves.__dict__["spam"]
249        except KeyError:
250            pass
251
252
253    def test_moved_attribute(self):
254        attr = six.MovedAttribute("spam", "foo", "bar")
255        if six.PY3:
256            assert attr.mod == "bar"
257        else:
258            assert attr.mod == "foo"
259        assert attr.attr == "spam"
260        attr = six.MovedAttribute("spam", "foo", "bar", "lemma")
261        assert attr.attr == "lemma"
262        attr = six.MovedAttribute("spam", "foo", "bar", "lemma", "theorm")
263        if six.PY3:
264            assert attr.attr == "theorm"
265        else:
266            assert attr.attr == "lemma"
267
268
269    def test_moved_module(self):
270        attr = six.MovedModule("spam", "foo")
271        if six.PY3:
272            assert attr.mod == "spam"
273        else:
274            assert attr.mod == "foo"
275        attr = six.MovedModule("spam", "foo", "bar")
276        if six.PY3:
277            assert attr.mod == "bar"
278        else:
279            assert attr.mod == "foo"
280
281
282    def test_custom_move_module(self):
283        attr = six.MovedModule("spam", "six", "six")
284        six.add_move(attr)
285        six.remove_move("spam")
286        assert not hasattr(six.moves, "spam")
287        attr = six.MovedModule("spam", "six", "six")
288        six.add_move(attr)
289        from six.moves import spam
290        assert spam is six
291        six.remove_move("spam")
292        assert not hasattr(six.moves, "spam")
293
294
295    def test_custom_move_attribute(self):
296        attr = six.MovedAttribute("spam", "six", "six", "u", "u")
297        six.add_move(attr)
298        six.remove_move("spam")
299        assert not hasattr(six.moves, "spam")
300        attr = six.MovedAttribute("spam", "six", "six", "u", "u")
301        six.add_move(attr)
302        from six.moves import spam
303        assert spam is six.u
304        six.remove_move("spam")
305        assert not hasattr(six.moves, "spam")
306
307
308    def test_empty_remove(self):
309        pytest.raises(AttributeError, six.remove_move, "eggs")
310
311
312def test_get_unbound_function():
313    class X(object):
314        def m(self):
315            pass
316    assert six.get_unbound_function(X.m) is X.__dict__["m"]
317
318
319def test_get_method_self():
320    class X(object):
321        def m(self):
322            pass
323    x = X()
324    assert six.get_method_self(x.m) is x
325    pytest.raises(AttributeError, six.get_method_self, 42)
326
327
328def test_get_method_function():
329    class X(object):
330        def m(self):
331            pass
332    x = X()
333    assert six.get_method_function(x.m) is X.__dict__["m"]
334    pytest.raises(AttributeError, six.get_method_function, hasattr)
335
336
337def test_get_function_closure():
338    def f():
339        x = 42
340        def g():
341            return x
342        return g
343    cell = six.get_function_closure(f())[0]
344    assert type(cell).__name__ == "cell"
345
346
347def test_get_function_code():
348    def f():
349        pass
350    assert isinstance(six.get_function_code(f), types.CodeType)
351    if not hasattr(sys, "pypy_version_info"):
352        pytest.raises(AttributeError, six.get_function_code, hasattr)
353
354
355def test_get_function_defaults():
356    def f(x, y=3, b=4):
357        pass
358    assert six.get_function_defaults(f) == (3, 4)
359
360
361def test_get_function_globals():
362    def f():
363        pass
364    assert six.get_function_globals(f) is globals()
365
366
367def test_dictionary_iterators(monkeypatch):
368    def stock_method_name(iterwhat):
369        """Given a method suffix like "lists" or "values", return the name
370        of the dict method that delivers those on the version of Python
371        we're running in."""
372        if six.PY3:
373            return iterwhat
374        return 'iter' + iterwhat
375
376    class MyDict(dict):
377        if not six.PY3:
378            def lists(self, **kw):
379                return [1, 2, 3]
380        def iterlists(self, **kw):
381            return iter([1, 2, 3])
382    f = MyDict.iterlists
383    del MyDict.iterlists
384    setattr(MyDict, stock_method_name('lists'), f)
385
386    d = MyDict(zip(range(10), reversed(range(10))))
387    for name in "keys", "values", "items", "lists":
388        meth = getattr(six, "iter" + name)
389        it = meth(d)
390        assert not isinstance(it, list)
391        assert list(it) == list(getattr(d, name)())
392        pytest.raises(StopIteration, six.advance_iterator, it)
393        record = []
394        def with_kw(*args, **kw):
395            record.append(kw["kw"])
396            return old(*args)
397        old = getattr(MyDict, stock_method_name(name))
398        monkeypatch.setattr(MyDict, stock_method_name(name), with_kw)
399        meth(d, kw=42)
400        assert record == [42]
401        monkeypatch.undo()
402
403
404def test_dictionary_views():
405    d = dict(zip(range(10), (range(11, 20))))
406    for name in "keys", "values", "items":
407        meth = getattr(six, "view" + name)
408        view = meth(d)
409        assert set(view) == set(getattr(d, name)())
410
411
412def test_advance_iterator():
413    assert six.next is six.advance_iterator
414    l = [1, 2]
415    it = iter(l)
416    assert six.next(it) == 1
417    assert six.next(it) == 2
418    pytest.raises(StopIteration, six.next, it)
419    pytest.raises(StopIteration, six.next, it)
420
421
422def test_iterator():
423    class myiter(six.Iterator):
424        def __next__(self):
425            return 13
426    assert six.advance_iterator(myiter()) == 13
427    class myitersub(myiter):
428        def __next__(self):
429            return 14
430    assert six.advance_iterator(myitersub()) == 14
431
432
433def test_callable():
434    class X:
435        def __call__(self):
436            pass
437        def method(self):
438            pass
439    assert six.callable(X)
440    assert six.callable(X())
441    assert six.callable(test_callable)
442    assert six.callable(hasattr)
443    assert six.callable(X.method)
444    assert six.callable(X().method)
445    assert not six.callable(4)
446    assert not six.callable("string")
447
448
449def test_create_bound_method():
450    class X(object):
451        pass
452    def f(self):
453        return self
454    x = X()
455    b = six.create_bound_method(f, x)
456    assert isinstance(b, types.MethodType)
457    assert b() is x
458
459
460def test_create_unbound_method():
461    class X(object):
462        pass
463
464    def f(self):
465        return self
466    u = six.create_unbound_method(f, X)
467    pytest.raises(TypeError, u)
468    if six.PY2:
469        assert isinstance(u, types.MethodType)
470    x = X()
471    assert f(x) is x
472
473
474if six.PY3:
475
476    def test_b():
477        data = six.b("\xff")
478        assert isinstance(data, bytes)
479        assert len(data) == 1
480        assert data == bytes([255])
481
482
483    def test_u():
484        s = six.u("hi \u0439 \U00000439 \\ \\\\ \n")
485        assert isinstance(s, str)
486        assert s == "hi \u0439 \U00000439 \\ \\\\ \n"
487
488else:
489
490    def test_b():
491        data = six.b("\xff")
492        assert isinstance(data, str)
493        assert len(data) == 1
494        assert data == "\xff"
495
496
497    def test_u():
498        s = six.u("hi \u0439 \U00000439 \\ \\\\ \n")
499        assert isinstance(s, unicode)
500        assert s == "hi \xd0\xb9 \xd0\xb9 \\ \\\\ \n".decode("utf8")
501
502
503def test_u_escapes():
504    s = six.u("\u1234")
505    assert len(s) == 1
506
507
508def test_unichr():
509    assert six.u("\u1234") == six.unichr(0x1234)
510    assert type(six.u("\u1234")) is type(six.unichr(0x1234))
511
512
513def test_int2byte():
514    assert six.int2byte(3) == six.b("\x03")
515    pytest.raises(Exception, six.int2byte, 256)
516
517
518def test_byte2int():
519    assert six.byte2int(six.b("\x03")) == 3
520    assert six.byte2int(six.b("\x03\x04")) == 3
521    pytest.raises(IndexError, six.byte2int, six.b(""))
522
523
524def test_bytesindex():
525    assert six.indexbytes(six.b("hello"), 3) == ord("l")
526
527
528def test_bytesiter():
529    it = six.iterbytes(six.b("hi"))
530    assert six.next(it) == ord("h")
531    assert six.next(it) == ord("i")
532    pytest.raises(StopIteration, six.next, it)
533
534
535def test_StringIO():
536    fp = six.StringIO()
537    fp.write(six.u("hello"))
538    assert fp.getvalue() == six.u("hello")
539
540
541def test_BytesIO():
542    fp = six.BytesIO()
543    fp.write(six.b("hello"))
544    assert fp.getvalue() == six.b("hello")
545
546
547def test_exec_():
548    def f():
549        l = []
550        six.exec_("l.append(1)")
551        assert l == [1]
552    f()
553    ns = {}
554    six.exec_("x = 42", ns)
555    assert ns["x"] == 42
556    glob = {}
557    loc = {}
558    six.exec_("global y; y = 42; x = 12", glob, loc)
559    assert glob["y"] == 42
560    assert "x" not in glob
561    assert loc["x"] == 12
562    assert "y" not in loc
563
564
565def test_reraise():
566    def get_next(tb):
567        if six.PY3:
568            return tb.tb_next.tb_next
569        else:
570            return tb.tb_next
571    e = Exception("blah")
572    try:
573        raise e
574    except Exception:
575        tp, val, tb = sys.exc_info()
576    try:
577        six.reraise(tp, val, tb)
578    except Exception:
579        tp2, value2, tb2 = sys.exc_info()
580        assert tp2 is Exception
581        assert value2 is e
582        assert tb is get_next(tb2)
583    try:
584        six.reraise(tp, val)
585    except Exception:
586        tp2, value2, tb2 = sys.exc_info()
587        assert tp2 is Exception
588        assert value2 is e
589        assert tb2 is not tb
590    try:
591        six.reraise(tp, val, tb2)
592    except Exception:
593        tp2, value2, tb3 = sys.exc_info()
594        assert tp2 is Exception
595        assert value2 is e
596        assert get_next(tb3) is tb2
597    try:
598        six.reraise(tp, None, tb)
599    except Exception:
600        tp2, value2, tb2 = sys.exc_info()
601        assert tp2 is Exception
602        assert value2 is not val
603        assert isinstance(value2, Exception)
604        assert tb is get_next(tb2)
605
606
607def test_raise_from():
608    try:
609        try:
610            raise Exception("blah")
611        except Exception:
612            ctx = sys.exc_info()[1]
613            f = Exception("foo")
614            six.raise_from(f, None)
615    except Exception:
616        tp, val, tb = sys.exc_info()
617    if sys.version_info[:2] > (3, 0):
618        # We should have done a raise f from None equivalent.
619        assert val.__cause__ is None
620        assert val.__context__ is ctx
621        # And that should suppress the context on the exception.
622        assert val.__suppress_context__
623    # For all versions the outer exception should have raised successfully.
624    assert str(val) == "foo"
625
626
627def test_print_():
628    save = sys.stdout
629    out = sys.stdout = six.moves.StringIO()
630    try:
631        six.print_("Hello,", "person!")
632    finally:
633        sys.stdout = save
634    assert out.getvalue() == "Hello, person!\n"
635    out = six.StringIO()
636    six.print_("Hello,", "person!", file=out)
637    assert out.getvalue() == "Hello, person!\n"
638    out = six.StringIO()
639    six.print_("Hello,", "person!", file=out, end="")
640    assert out.getvalue() == "Hello, person!"
641    out = six.StringIO()
642    six.print_("Hello,", "person!", file=out, sep="X")
643    assert out.getvalue() == "Hello,Xperson!\n"
644    out = six.StringIO()
645    six.print_(six.u("Hello,"), six.u("person!"), file=out)
646    result = out.getvalue()
647    assert isinstance(result, six.text_type)
648    assert result == six.u("Hello, person!\n")
649    six.print_("Hello", file=None) # This works.
650    out = six.StringIO()
651    six.print_(None, file=out)
652    assert out.getvalue() == "None\n"
653    class FlushableStringIO(six.StringIO):
654        def __init__(self):
655            six.StringIO.__init__(self)
656            self.flushed = False
657        def flush(self):
658            self.flushed = True
659    out = FlushableStringIO()
660    six.print_("Hello", file=out)
661    assert not out.flushed
662    six.print_("Hello", file=out, flush=True)
663    assert out.flushed
664
665
666def test_print_exceptions():
667    pytest.raises(TypeError, six.print_, x=3)
668    pytest.raises(TypeError, six.print_, end=3)
669    pytest.raises(TypeError, six.print_, sep=42)
670
671
672def test_with_metaclass():
673    class Meta(type):
674        pass
675    class X(six.with_metaclass(Meta)):
676        pass
677    assert type(X) is Meta
678    assert issubclass(X, object)
679    class Base(object):
680        pass
681    class X(six.with_metaclass(Meta, Base)):
682        pass
683    assert type(X) is Meta
684    assert issubclass(X, Base)
685    class Base2(object):
686        pass
687    class X(six.with_metaclass(Meta, Base, Base2)):
688        pass
689    assert type(X) is Meta
690    assert issubclass(X, Base)
691    assert issubclass(X, Base2)
692    assert X.__mro__ == (X, Base, Base2, object)
693    class X(six.with_metaclass(Meta)):
694        pass
695    class MetaSub(Meta):
696        pass
697    class Y(six.with_metaclass(MetaSub, X)):
698        pass
699    assert type(Y) is MetaSub
700    assert Y.__mro__ == (Y, X, object)
701
702
703def test_with_metaclass_typing():
704    try:
705        import typing
706    except ImportError:
707        pytest.skip("typing module required")
708    class Meta(type):
709        pass
710    if sys.version_info[:2] < (3, 7):
711        # Generics with custom metaclasses were broken on older versions.
712        class Meta(Meta, typing.GenericMeta):
713            pass
714    T = typing.TypeVar('T')
715    class G(six.with_metaclass(Meta, typing.Generic[T])):
716        pass
717    class GA(six.with_metaclass(abc.ABCMeta, typing.Generic[T])):
718        pass
719    assert isinstance(G, Meta)
720    assert isinstance(GA, abc.ABCMeta)
721    assert G[int] is not G[G[int]]
722    assert GA[int] is not GA[GA[int]]
723    assert G.__bases__ == (typing.Generic,)
724    assert G.__orig_bases__ == (typing.Generic[T],)
725
726
727@pytest.mark.skipif("sys.version_info[:2] < (3, 7)")
728def test_with_metaclass_pep_560():
729    class Meta(type):
730        pass
731    class A:
732        pass
733    class B:
734        pass
735    class Fake:
736        def __mro_entries__(self, bases):
737            return (A, B)
738    fake = Fake()
739    class G(six.with_metaclass(Meta, fake)):
740        pass
741    class GA(six.with_metaclass(abc.ABCMeta, fake)):
742        pass
743    assert isinstance(G, Meta)
744    assert isinstance(GA, abc.ABCMeta)
745    assert G.__bases__ == (A, B)
746    assert G.__orig_bases__ == (fake,)
747
748
749@pytest.mark.skipif("sys.version_info[:2] < (3, 0)")
750def test_with_metaclass_prepare():
751    """Test that with_metaclass causes Meta.__prepare__ to be called with the correct arguments."""
752
753    class MyDict(dict):
754        pass
755
756    class Meta(type):
757
758        @classmethod
759        def __prepare__(cls, name, bases):
760            namespace = MyDict(super().__prepare__(name, bases), cls=cls, bases=bases)
761            namespace['namespace'] = namespace
762            return namespace
763
764    class Base(object):
765        pass
766
767    bases = (Base,)
768
769    class X(six.with_metaclass(Meta, *bases)):
770        pass
771
772    assert getattr(X, 'cls', type) is Meta
773    assert getattr(X, 'bases', ()) == bases
774    assert isinstance(getattr(X, 'namespace', {}), MyDict)
775
776
777def test_wraps():
778    def f(g):
779        @six.wraps(g)
780        def w():
781            return 42
782        return w
783    def k():
784        pass
785    original_k = k
786    k = f(f(k))
787    assert hasattr(k, '__wrapped__')
788    k = k.__wrapped__
789    assert hasattr(k, '__wrapped__')
790    k = k.__wrapped__
791    assert k is original_k
792    assert not hasattr(k, '__wrapped__')
793
794    def f(g, assign, update):
795        def w():
796            return 42
797        w.glue = {"foo": "bar"}
798        w.xyzzy = {"qux": "quux"}
799        return six.wraps(g, assign, update)(w)
800    k.glue = {"melon": "egg"}
801    k.turnip = 43
802    k = f(k, ["turnip", "baz"], ["glue", "xyzzy"])
803    assert k.__name__ == "w"
804    assert k.turnip == 43
805    assert not hasattr(k, "baz")
806    assert k.glue == {"melon": "egg", "foo": "bar"}
807    assert k.xyzzy == {"qux": "quux"}
808
809
810def test_wraps_raises_on_missing_updated_field_on_wrapper():
811    """Ensure six.wraps doesn't ignore missing attrs wrapper.
812
813    Because that's what happens in Py3's functools.update_wrapper.
814    """
815    def wrapped():
816        pass
817
818    def wrapper():
819        pass
820
821    with pytest.raises(AttributeError, match='has no attribute.*xyzzy'):
822        six.wraps(wrapped, [], ['xyzzy'])(wrapper)
823
824
825
826def test_add_metaclass():
827    class Meta(type):
828        pass
829    class X:
830        "success"
831    X = six.add_metaclass(Meta)(X)
832    assert type(X) is Meta
833    assert issubclass(X, object)
834    assert X.__module__ == __name__
835    assert X.__doc__ == "success"
836    class Base(object):
837        pass
838    class X(Base):
839        pass
840    X = six.add_metaclass(Meta)(X)
841    assert type(X) is Meta
842    assert issubclass(X, Base)
843    class Base2(object):
844        pass
845    class X(Base, Base2):
846        pass
847    X = six.add_metaclass(Meta)(X)
848    assert type(X) is Meta
849    assert issubclass(X, Base)
850    assert issubclass(X, Base2)
851
852    # Test a second-generation subclass of a type.
853    class Meta1(type):
854        m1 = "m1"
855    class Meta2(Meta1):
856        m2 = "m2"
857    class Base:
858        b = "b"
859    Base = six.add_metaclass(Meta1)(Base)
860    class X(Base):
861        x = "x"
862    X = six.add_metaclass(Meta2)(X)
863    assert type(X) is Meta2
864    assert issubclass(X, Base)
865    assert type(Base) is Meta1
866    assert "__dict__" not in vars(X)
867    instance = X()
868    instance.attr = "test"
869    assert vars(instance) == {"attr": "test"}
870    assert instance.b == Base.b
871    assert instance.x == X.x
872
873    # Test a class with slots.
874    class MySlots(object):
875        __slots__ = ["a", "b"]
876    MySlots = six.add_metaclass(Meta1)(MySlots)
877
878    assert MySlots.__slots__ == ["a", "b"]
879    instance = MySlots()
880    instance.a = "foo"
881    pytest.raises(AttributeError, setattr, instance, "c", "baz")
882
883    # Test a class with string for slots.
884    class MyStringSlots(object):
885        __slots__ = "ab"
886    MyStringSlots = six.add_metaclass(Meta1)(MyStringSlots)
887    assert MyStringSlots.__slots__ == "ab"
888    instance = MyStringSlots()
889    instance.ab = "foo"
890    pytest.raises(AttributeError, setattr, instance, "a", "baz")
891    pytest.raises(AttributeError, setattr, instance, "b", "baz")
892
893    class MySlotsWeakref(object):
894        __slots__ = "__weakref__",
895    MySlotsWeakref = six.add_metaclass(Meta)(MySlotsWeakref)
896    assert type(MySlotsWeakref) is Meta
897
898
899@pytest.mark.skipif("sys.version_info[:2] < (3, 3)")
900def test_add_metaclass_nested():
901    # Regression test for https://github.com/benjaminp/six/issues/259
902    class Meta(type):
903        pass
904
905    class A:
906        class B: pass
907
908    expected = 'test_add_metaclass_nested.<locals>.A.B'
909
910    assert A.B.__qualname__ == expected
911
912    class A:
913        @six.add_metaclass(Meta)
914        class B: pass
915
916    assert A.B.__qualname__ == expected
917
918
919def test_assertCountEqual():
920    class TestAssertCountEqual(unittest.TestCase):
921        def test(self):
922            with self.assertRaises(AssertionError):
923                six.assertCountEqual(self, (1, 2), [3, 4, 5])
924
925            six.assertCountEqual(self, (1, 2), [2, 1])
926
927    TestAssertCountEqual('test').test()
928
929
930def test_assertRegex():
931    class TestAssertRegex(unittest.TestCase):
932        def test(self):
933            with self.assertRaises(AssertionError):
934                six.assertRegex(self, 'test', r'^a')
935
936            six.assertRegex(self, 'test', r'^t')
937
938    TestAssertRegex('test').test()
939
940
941def test_assertNotRegex():
942    class TestAssertNotRegex(unittest.TestCase):
943        def test(self):
944            with self.assertRaises(AssertionError):
945                six.assertNotRegex(self, 'test', r'^t')
946
947            six.assertNotRegex(self, 'test', r'^a')
948
949    TestAssertNotRegex('test').test()
950
951
952def test_assertRaisesRegex():
953    class TestAssertRaisesRegex(unittest.TestCase):
954        def test(self):
955            with six.assertRaisesRegex(self, AssertionError, '^Foo'):
956                raise AssertionError('Foo')
957
958            with self.assertRaises(AssertionError):
959                with six.assertRaisesRegex(self, AssertionError, r'^Foo'):
960                    raise AssertionError('Bar')
961
962    TestAssertRaisesRegex('test').test()
963
964
965def test_python_2_unicode_compatible():
966    @six.python_2_unicode_compatible
967    class MyTest(object):
968        def __str__(self):
969            return six.u('hello')
970
971        def __bytes__(self):
972            return six.b('hello')
973
974    my_test = MyTest()
975
976    if six.PY2:
977        assert str(my_test) == six.b("hello")
978        assert unicode(my_test) == six.u("hello")
979    elif six.PY3:
980        assert bytes(my_test) == six.b("hello")
981        assert str(my_test) == six.u("hello")
982
983    assert getattr(six.moves.builtins, 'bytes', str)(my_test) == six.b("hello")
984
985
986class EnsureTests:
987
988    # grinning face emoji
989    UNICODE_EMOJI = six.u("\U0001F600")
990    BINARY_EMOJI = b"\xf0\x9f\x98\x80"
991
992    def test_ensure_binary_raise_type_error(self):
993        with pytest.raises(TypeError):
994            six.ensure_str(8)
995
996    def test_errors_and_encoding(self):
997        six.ensure_binary(self.UNICODE_EMOJI, encoding='latin-1', errors='ignore')
998        with pytest.raises(UnicodeEncodeError):
999            six.ensure_binary(self.UNICODE_EMOJI, encoding='latin-1', errors='strict')
1000
1001    def test_ensure_binary_raise(self):
1002        converted_unicode = six.ensure_binary(self.UNICODE_EMOJI, encoding='utf-8', errors='strict')
1003        converted_binary = six.ensure_binary(self.BINARY_EMOJI, encoding="utf-8", errors='strict')
1004        if six.PY2:
1005            # PY2: unicode -> str
1006            assert converted_unicode == self.BINARY_EMOJI and isinstance(converted_unicode, str)
1007            # PY2: str -> str
1008            assert converted_binary == self.BINARY_EMOJI and isinstance(converted_binary, str)
1009        else:
1010            # PY3: str -> bytes
1011            assert converted_unicode == self.BINARY_EMOJI and isinstance(converted_unicode, bytes)
1012            # PY3: bytes -> bytes
1013            assert converted_binary == self.BINARY_EMOJI and isinstance(converted_binary, bytes)
1014
1015    def test_ensure_str(self):
1016        converted_unicode = six.ensure_str(self.UNICODE_EMOJI, encoding='utf-8', errors='strict')
1017        converted_binary = six.ensure_str(self.BINARY_EMOJI, encoding="utf-8", errors='strict')
1018        if six.PY2:
1019            # PY2: unicode -> str
1020            assert converted_unicode == self.BINARY_EMOJI and isinstance(converted_unicode, str)
1021            # PY2: str -> str
1022            assert converted_binary == self.BINARY_EMOJI and isinstance(converted_binary, str)
1023        else:
1024            # PY3: str -> str
1025            assert converted_unicode == self.UNICODE_EMOJI and isinstance(converted_unicode, str)
1026            # PY3: bytes -> str
1027            assert converted_binary == self.UNICODE_EMOJI and isinstance(converted_unicode, str)
1028
1029    def test_ensure_text(self):
1030        converted_unicode = six.ensure_text(self.UNICODE_EMOJI, encoding='utf-8', errors='strict')
1031        converted_binary = six.ensure_text(self.BINARY_EMOJI, encoding="utf-8", errors='strict')
1032        if six.PY2:
1033            # PY2: unicode -> unicode
1034            assert converted_unicode == self.UNICODE_EMOJI and isinstance(converted_unicode, unicode)
1035            # PY2: str -> unicode
1036            assert converted_binary == self.UNICODE_EMOJI and isinstance(converted_unicode, unicode)
1037        else:
1038            # PY3: str -> str
1039            assert converted_unicode == self.UNICODE_EMOJI and isinstance(converted_unicode, str)
1040            # PY3: bytes -> str
1041            assert converted_binary == self.UNICODE_EMOJI and isinstance(converted_unicode, str)
1042