Repository: mitmproxy/mitmproxy

Issue:
type object 'Sequence' has no attribute '__args__' using python3.5.0
##### Steps to reproduce the problem:

1.pyenv global 3.5.0
2.pip3.5 install mitmproxy
3.mitmproxy -b 10.19.84.140 -p 9999  --no-upstream-cert


##### Any other comments? What have you tried so far?
trying to install from brew, and problem still by raising the py3.6.0 problem.
0.15  runs perfectly under python2.7.10


##### System information

Traceback (most recent call last):
  File "/usr/local/var/pyenv/versions/3.5.0/bin/mitmproxy", line 11, in <module>
    sys.exit(mitmproxy())
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/tools/main.py", line 67, in mitmproxy
    console_options = options.Options()
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/options.py", line 117, in __init__
    self.client_replay = client_replay
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/optmanager.py", line 111, in __setattr__
    self._typecheck(attr, value)
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/optmanager.py", line 122, in _typecheck
    typecheck.check_type(attr, value, expected_type)
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/utils/typecheck.py", line 55, in check_type
    T = typeinfo.__args__[0]
AttributeError: type object 'Sequence' has no attribute '__args__'
<!--
Traceback (most recent call last):
  File "/usr/local/var/pyenv/versions/3.5.0/bin/mitmproxy", line 11, in <module>
    sys.exit(mitmproxy())
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/tools/main.py", line 67, in mitmproxy
    console_options = options.Options()
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/options.py", line 117, in __init__
    self.client_replay = client_replay
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/optmanager.py", line 111, in __setattr__
    self._typecheck(attr, value)
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/optmanager.py", line 122, in _typecheck
    typecheck.check_type(attr, value, expected_type)
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/site-packages/mitmproxy/utils/typecheck.py", line 55, in check_type
    T = typeinfo.__args__[0]
AttributeError: type object 'Sequence' has no attribute '__args__'
-->



Discussion:
I have a similar error, trying to use it on Kali Linux (Debian based distro).

Traceback (most recent call last):
  File "/usr/local/bin/mitmproxy", line 11, in <module>
    sys.exit(mitmproxy())
  File "/usr/local/lib/python3.5/dist-packages/mitmproxy/tools/main.py", line 67, in mitmproxy
    console_options = options.Options()
  File "/usr/local/lib/python3.5/dist-packages/mitmproxy/options.py", line 123, in __init__
    self.rfile = rfile
  File "/usr/local/lib/python3.5/dist-packages/mitmproxy/optmanager.py", line 111, in __setattr__
    self._typecheck(attr, value)
  File "/usr/local/lib/python3.5/dist-packages/mitmproxy/optmanager.py", line 122, in _typecheck
    typecheck.check_type(attr, value, expected_type)
  File "/usr/local/lib/python3.5/dist-packages/mitmproxy/utils/typecheck.py", line 29, in check_type
    types = typeinfo.__union_params__
AttributeError: '_Union' object has no attribute '__union_params__'
What's your output of `python --version`?
mine:
```
~ » python --version
Python 3.5.0
```
Thanks. Looks like 3.5.2 works, but 3.5.0 doesn't.

@CapitanShinChan: What's your output of `pip freeze`?

Resolving patch:
diff --git a/mitmproxy/utils/typecheck.py b/mitmproxy/utils/typecheck.py
--- a/mitmproxy/utils/typecheck.py
+++ b/mitmproxy/utils/typecheck.py
@@ -1,5 +1,4 @@
 import typing
-import sys
 
 
 def check_type(attr_name: str, value: typing.Any, typeinfo: type) -> None:
@@ -25,10 +24,11 @@ def check_type(attr_name: str, value: typing.Any, typeinfo: type) -> None:
     typename = str(typeinfo)
 
     if typename.startswith("typing.Union"):
-        if sys.version_info < (3, 6):
-            types = typeinfo.__union_params__
-        else:
+        try:
             types = typeinfo.__args__
+        except AttributeError:
+            # Python 3.5.x
+            types = typeinfo.__union_params__
 
         for T in types:
             try:
@@ -39,10 +39,11 @@ def check_type(attr_name: str, value: typing.Any, typeinfo: type) -> None:
                 return
         raise e
     elif typename.startswith("typing.Tuple"):
-        if sys.version_info < (3, 6):
-            types = typeinfo.__tuple_params__
-        else:
+        try:
             types = typeinfo.__args__
+        except AttributeError:
+            # Python 3.5.x
+            types = typeinfo.__tuple_params__
 
         if not isinstance(value, (tuple, list)):
             raise e
@@ -52,7 +53,11 @@ def check_type(attr_name: str, value: typing.Any, typeinfo: type) -> None:
             check_type("{}[{}]".format(attr_name, i), x, T)
         return
     elif typename.startswith("typing.Sequence"):
-        T = typeinfo.__args__[0]
+        try:
+            T = typeinfo.__args__[0]
+        except AttributeError:
+            # Python 3.5.0
+            T = typeinfo.__parameters__[0]
         if not isinstance(value, (tuple, list)):
             raise e
         for v in value:
@@ -60,6 +65,8 @@ def check_type(attr_name: str, value: typing.Any, typeinfo: type) -> None:
     elif typename.startswith("typing.IO"):
         if hasattr(value, "read"):
             return
+        else:
+            raise e
     elif not isinstance(value, typeinfo):
         raise e
 


Test patch:
diff --git a/test/mitmproxy/utils/test_typecheck.py b/test/mitmproxy/utils/test_typecheck.py
--- a/test/mitmproxy/utils/test_typecheck.py
+++ b/test/mitmproxy/utils/test_typecheck.py
@@ -1,6 +1,9 @@
+import io
 import typing
 
+import mock
 import pytest
+
 from mitmproxy.utils import typecheck
 
 
@@ -57,3 +60,17 @@ def test_check_sequence():
         typecheck.check_type("foo", [10, "foo"], typing.Sequence[int])
     with pytest.raises(TypeError):
         typecheck.check_type("foo", [b"foo"], typing.Sequence[str])
+    with pytest.raises(TypeError):
+        typecheck.check_type("foo", "foo", typing.Sequence[str])
+
+    # Python 3.5.0 only defines __parameters__
+    m = mock.Mock()
+    m.__str__ = lambda self: "typing.Sequence"
+    m.__parameters__ = (int,)
+    typecheck.check_type("foo", [10], m)
+
+
+def test_check_io():
+    typecheck.check_type("foo", io.StringIO(), typing.IO[str])
+    with pytest.raises(TypeError):
+        typecheck.check_type("foo", "foo", typing.IO[str])