Repository: python-attrs/attrs (python)

Issue:
attr.make_class does not work with Python>=3.7
In attr.make_class function line 2818: `type_ = type(name, bases, body)` cannot be used for dynamic generic class creation beyond python 3.7 and leads to an error `TypeError: type() doesn't support MRO entry resolution; use types.new_class()`.

The error can be recreated with:
```
from typing import TypeVar, Generic
from types import new_class
import attr

MyTypeVar = TypeVar("MyTypeVar")
MyParent = new_class("MyParent", (Generic[MyTypeVar],), {})


attr.make_class('test', {'id': attr.ib(type=str)}, (MyParent[int],))
```

As mentioned in the error it can be fixed by replacing `type_ = type(name, bases, body)` with `types.new_class(name, bases, body)`. 

This issue has been discussed here in detail: [https://bugs.python.org/issue33188](https://bugs.python.org/issue33188).

Pull request description:
Fix make_class for dynamic generic classes
Fixes #756

Does this look OK to you @avinashmnit30?

Patch:
diff --git a/src/attr/_compat.py b/src/attr/_compat.py
index b0ead6e..6939f33 100644
--- a/src/attr/_compat.py
+++ b/src/attr/_compat.py
@@ -28,6 +28,15 @@ if PY2:
     def isclass(klass):
         return isinstance(klass, (type, types.ClassType))
 
+    def new_class(name, bases, kwds, exec_body):
+        """
+        A minimal stub of types.new_class that we need for make_class.
+        """
+        ns = {}
+        exec_body(ns)
+
+        return type(name, bases, ns)
+
     # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
     TYPE = "type"
 
@@ -122,6 +131,8 @@ else:  # Python 3 and later.
     def iteritems(d):
         return d.items()
 
+    new_class = types.new_class
+
     def metadata_proxy(d):
         return types.MappingProxyType(dict(d))
 
diff --git a/src/attr/_make.py b/src/attr/_make.py
index dbbf09c..ee70160 100644
--- a/src/attr/_make.py
+++ b/src/attr/_make.py
@@ -17,6 +17,7 @@ from ._compat import (
     isclass,
     iteritems,
     metadata_proxy,
+    new_class,
     ordered_dict,
     set_closure_cell,
 )
@@ -2928,7 +2929,8 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
     if user_init is not None:
         body["__init__"] = user_init
 
-    type_ = type(name, bases, body)
+    type_ = new_class(name, bases, {}, lambda ns: ns.update(body))
+
     # For pickling to work, the __module__ variable needs to be set to the
     # frame where the class is created.  Bypass this step in environments where
     # sys._getframe is not defined (Jython for example) or sys._getframe is not


Test patch:
diff --git a/tests/test_make.py b/tests/test_make.py
index 05d0530..9486cfe 100644
--- a/tests/test_make.py
+++ b/tests/test_make.py
@@ -1118,6 +1118,22 @@ class TestMakeClass(object):
 
         assert "C(a=1, b=2)" == repr(C())
 
+    @pytest.mark.skipif(PY2, reason="Python 3-only")
+    def test_generic_dynamic_class(self):
+        """
+        make_class can create generic dynamic classes.
+
+        https://github.com/python-attrs/attrs/issues/756
+        https://bugs.python.org/issue33188
+        """
+        from types import new_class
+        from typing import Generic, TypeVar
+
+        MyTypeVar = TypeVar("MyTypeVar")
+        MyParent = new_class("MyParent", (Generic[MyTypeVar],), {})
+
+        attr.make_class("test", {"id": attr.ib(type=str)}, (MyParent[int],))
+
 
 class TestFields(object):
     """