From b24e9a1051dcd8168009221f6c484baad207633a Mon Sep 17 00:00:00 2001
From: Syrus Akbary <me@syrusakbary.com>
Date: Wed, 8 Jun 2016 22:23:28 -0700
Subject: [PATCH] Refactored fields getter to be immutable

---
 examples/starwars/schema.py              |   8 +-
 graphene/relay/__init__.py               |   2 +
 graphene/relay/tests/test_node.py        |   7 +-
 graphene/relay/tests/test_node_custom.py |   8 +-
 graphene/types/definitions.py            | 107 +++----------
 graphene/types/enum.py                   |   3 +-
 graphene/types/field.py                  |  18 +--
 graphene/types/inputobjecttype.py        |  25 +--
 graphene/types/interface.py              |  25 +--
 graphene/types/objecttype.py             |  41 ++---
 graphene/types/options.py                |   5 -
 graphene/types/proxy.py                  |  16 +-
 graphene/types/scalars.py                |   3 -
 graphene/types/tests/test_field.py       |  19 ---
 graphene/types/tests/test_interface.py   |   2 +-
 graphene/types/tests/test_objecttype.py  | 190 ++++++++++++++---------
 graphene/types/tests/test_options.py     |   8 +-
 graphene/types/tests/test_scalars.py     |   7 +-
 graphene/types/tests/test_schema.py      |   5 +-
 graphene/types/tests/test_structures.py  |   8 +
 graphene/utils/build_field_map.py        |   0
 graphene/utils/extract_fields.py         |   8 +-
 graphene/utils/orderedtype.py            |   2 +-
 23 files changed, 242 insertions(+), 275 deletions(-)
 create mode 100644 graphene/utils/build_field_map.py

diff --git a/examples/starwars/schema.py b/examples/starwars/schema.py
index e194be0b..12f45caa 100644
--- a/examples/starwars/schema.py
+++ b/examples/starwars/schema.py
@@ -21,13 +21,17 @@ class Character(graphene.Interface):
         return [get_character(f) for f in self.friends]
 
 
-@graphene.implements(Character)
+# @graphene.implements(Character)
 class Human(graphene.ObjectType):
+    class Meta:
+        interfaces = [Character]
     home_planet = graphene.String()
 
 
-@graphene.implements(Character)
+# @graphene.implements(Character)
 class Droid(graphene.ObjectType):
+    class Meta:
+        interfaces = [Character]
     primary_function = graphene.String()
 
 
diff --git a/graphene/relay/__init__.py b/graphene/relay/__init__.py
index e69de29b..136d4eb9 100644
--- a/graphene/relay/__init__.py
+++ b/graphene/relay/__init__.py
@@ -0,0 +1,2 @@
+from .node import Node
+# from .mutation import ClientIDMutation
diff --git a/graphene/relay/tests/test_node.py b/graphene/relay/tests/test_node.py
index 67055ca3..571cff52 100644
--- a/graphene/relay/tests/test_node.py
+++ b/graphene/relay/tests/test_node.py
@@ -7,8 +7,10 @@ from ...types import ObjectType, Schema, implements
 from ...types.scalars import String
 
 
-@implements(Node)
 class MyNode(ObjectType):
+    class Meta:
+        interfaces = [Node]
+
     name = String()
 
     @staticmethod
@@ -25,8 +27,9 @@ schema = Schema(query=RootQuery, types=[MyNode])
 
 def test_node_no_get_node():
     with pytest.raises(AssertionError) as excinfo:
-        @implements(Node)
         class MyNode(ObjectType):
+            class Meta:
+                interfaces = [Node]
             pass
 
     assert "MyNode.get_node method is required by the Node interface." == str(excinfo.value)
diff --git a/graphene/relay/tests/test_node_custom.py b/graphene/relay/tests/test_node_custom.py
index 7b24855b..6b5f94ab 100644
--- a/graphene/relay/tests/test_node_custom.py
+++ b/graphene/relay/tests/test_node_custom.py
@@ -15,13 +15,17 @@ class CustomNode(Node):
             return photo_data.get(id)
 
 
-@implements(CustomNode)
+# @implements(CustomNode)
 class User(ObjectType):
+    class Meta:
+        interfaces = [CustomNode]
     name = String()
 
 
-@implements(CustomNode)
+# @implements(CustomNode)
 class Photo(ObjectType):
+    class Meta:
+        interfaces = [CustomNode]
     width = Int()
 
 
diff --git a/graphene/types/definitions.py b/graphene/types/definitions.py
index 0980f655..6da3fa75 100644
--- a/graphene/types/definitions.py
+++ b/graphene/types/definitions.py
@@ -1,11 +1,9 @@
 from collections import OrderedDict
 import inspect
-import copy
 from itertools import chain
 from functools import partial
 
 from graphql.utils.assert_valid_name import assert_valid_name
-from graphql.type.definition import GraphQLObjectType
 
 from .options import Options
 
@@ -28,34 +26,22 @@ class ClassTypeMeta(type):
         else:
             meta = attr_meta
 
-        new_class.add_to_class('_meta', new_class.get_options(meta))
+        new_class._meta = new_class.get_options(meta)
+        new_class._meta.parent = new_class
+        new_class._meta.validate_attrs()
+
         if new_class._meta.name:
             assert_valid_name(new_class._meta.name)
-        new_class.construct_graphql_type(bases)
 
         return mcs.construct(new_class, bases, attrs)
 
     def get_options(cls, meta):
         raise NotImplementedError("get_options is not implemented")
 
-    def construct_graphql_type(cls, bases):
-        raise NotImplementedError("construct_graphql_type is not implemented")
-
-    def add_to_class(cls, name, value):
-        # We should call the contribute_to_class method only if it's bound
-        if not inspect.isclass(value) and hasattr(
-                value, 'contribute_to_class'):
-            value.contribute_to_class(cls, name)
-        else:
-            setattr(cls, name, value)
-
     def construct(cls, bases, attrs):
         # Add all attributes to the class.
-        for obj_name, obj in attrs.items():
-            cls.add_to_class(obj_name, obj)
-
-        # if not cls._meta.abstract:
-        #     from ..types import List, NonNull
+        for name, value in attrs.items():
+            setattr(cls, name, value)
 
         return cls
 
@@ -64,19 +50,29 @@ class FieldsMeta(type):
 
     def _build_field_map(cls, bases, local_fields):
         from ..utils.extract_fields import get_base_fields
-        extended_fields = get_base_fields(cls, bases)
-        fields = chain(extended_fields, local_fields)
+        extended_fields = list(get_base_fields(cls, bases))
+
+        fields = []
+        field_names = set(f.name for f in local_fields)
+        for extended_field in extended_fields:
+            if extended_field.name in field_names:
+                continue
+            fields.append(extended_field)
+            field_names.add(extended_field.name)
+
+        fields.extend(local_fields)
+
         return OrderedDict((f.name, f) for f in fields)
 
-    def _fields(cls, bases, attrs):
-        from ..utils.is_graphene_type import is_graphene_type
+    def _extract_local_fields(cls, attrs):
         from ..utils.extract_fields import extract_fields
+        return extract_fields(cls, attrs)
 
-        inherited_types = [
+    def _fields(cls, bases, attrs, local_fields, extra_types=()):
+        from ..utils.is_graphene_type import is_graphene_type
+        inherited_types = tuple(
             base._meta.graphql_type for base in bases if is_graphene_type(base) and not base._meta.abstract
-        ]
-
-        local_fields = extract_fields(cls, attrs)
+        ) + extra_types
         return partial(cls._build_field_map, inherited_types, local_fields)
 
 
@@ -84,58 +80,3 @@ class GrapheneGraphQLType(object):
     def __init__(self, *args, **kwargs):
         self.graphene_type = kwargs.pop('graphene_type')
         super(GrapheneGraphQLType, self).__init__(*args, **kwargs)
-
-
-class GrapheneFieldsType(GrapheneGraphQLType):
-    def __init__(self, *args, **kwargs):
-        self._fields = None
-        self._field_map = None
-        super(GrapheneFieldsType, self).__init__(*args, **kwargs)
-
-    def add_field(self, field):
-        # We clear the cached fields
-        self._field_map = None
-        self._fields.add(field)
-
-
-class FieldMap(object):
-    def __init__(self, parent, bases=None, fields=None):
-        self.parent = parent
-        self.fields = fields or []
-        self.bases = bases or []
-
-    def add(self, field):
-        self.fields.append(field)
-
-    def __call__(self):
-        # It's in a call function for assuring that if a field is added
-        # in runtime then it will be reflected in the Class type fields
-        # If we add the field in the class type creation, then we
-        # would not be able to change it later.
-        from .field import Field
-        prev_fields = []
-        graphql_type = self.parent._meta.graphql_type
-
-        # We collect the fields from the interfaces
-        if isinstance(graphql_type, GraphQLObjectType):
-            interfaces = graphql_type.get_interfaces()
-            for interface in interfaces:
-                prev_fields += interface.get_fields().items()
-
-        # We collect the fields from the bases
-        for base in self.bases:
-            prev_fields += base.get_fields().items()
-
-        fields = prev_fields + [
-            (field.name, field) for field in sorted(self.fields)
-        ]
-
-        # Then we copy all the fields and assign the parent
-        new_fields = []
-        for field_name, field in fields:
-            field = copy.copy(field)
-            if isinstance(field, Field):
-                field.parent = self.parent
-            new_fields.append((field_name, field))
-
-        return OrderedDict(new_fields)
diff --git a/graphene/types/enum.py b/graphene/types/enum.py
index 69eb197f..586dcaaf 100644
--- a/graphene/types/enum.py
+++ b/graphene/types/enum.py
@@ -42,7 +42,7 @@ class EnumTypeMeta(ClassTypeMeta):
             abstract=False
         )
 
-    def construct_graphql_type(cls, bases):
+    def construct(cls, bases, attrs):
         if not cls._meta.graphql_type and not cls._meta.abstract:
             cls._meta.graphql_type = GrapheneEnumType(
                 graphene_type=cls,
@@ -50,7 +50,6 @@ class EnumTypeMeta(ClassTypeMeta):
                 description=cls._meta.description or cls.__doc__,
             )
 
-    def construct(cls, bases, attrs):
         if not cls._meta.enum:
             cls._meta.enum = type(cls.__name__, (PyEnum,), attrs)
 
diff --git a/graphene/types/field.py b/graphene/types/field.py
index 8c34c66c..adb63a71 100644
--- a/graphene/types/field.py
+++ b/graphene/types/field.py
@@ -61,16 +61,13 @@ class Field(AbstractField, GraphQLField, OrderedType):
             where.__name__
         )
 
-    def contribute_to_class(self, cls, attname):
+    def mount(self, parent, attname=None):
         from .objecttype import ObjectType
         from .interface import Interface
+        assert issubclass(parent, (ObjectType, Interface)), self.mount_error_message(parent)
 
-        assert issubclass(cls, (ObjectType, Interface)), self.mount_error_message(cls)
         self.attname = attname
-        self.parent = cls
-        add_field = getattr(cls._meta.graphql_type, "add_field", None)
-        assert add_field, self.mount_error_message(cls)
-        add_field(self)
+        self.parent = parent
 
     @property
     def resolver(self):
@@ -149,15 +146,12 @@ class InputField(AbstractField, GraphQLInputObjectField, OrderedType):
             where.__name__
         )
 
-    def contribute_to_class(self, cls, attname):
+    def mount(self, parent, attname):
         from .inputobjecttype import InputObjectType
 
-        assert issubclass(cls, (InputObjectType)), self.mount_error_message(cls)
+        assert issubclass(parent, (InputObjectType)), self.mount_error_message(parent)
         self.attname = attname
-        self.parent = cls
-        add_field = getattr(cls._meta.graphql_type, "add_field", None)
-        assert add_field, self.mount_error_message(cls)
-        add_field(self)
+        self.parent = parent
 
     def __copy__(self):
         return InputField(
diff --git a/graphene/types/inputobjecttype.py b/graphene/types/inputobjecttype.py
index 671611a5..8651df37 100644
--- a/graphene/types/inputobjecttype.py
+++ b/graphene/types/inputobjecttype.py
@@ -2,11 +2,11 @@ import six
 
 from graphql import GraphQLInputObjectType
 
-from .definitions import FieldsMeta, ClassTypeMeta, GrapheneFieldsType
+from .definitions import FieldsMeta, ClassTypeMeta, GrapheneGraphQLType
 from .proxy import TypeProxy
 
 
-class GrapheneInputObjectType(GrapheneFieldsType, GraphQLInputObjectType):
+class GrapheneInputObjectType(GrapheneGraphQLType, GraphQLInputObjectType):
     pass
 
 
@@ -21,17 +21,18 @@ class InputObjectTypeMeta(FieldsMeta, ClassTypeMeta):
             abstract=False
         )
 
-    def construct_graphql_type(cls, bases):
-        pass
-
     def construct(cls, bases, attrs):
-        if not cls._meta.graphql_type and not cls._meta.abstract:
-            cls._meta.graphql_type = GrapheneInputObjectType(
-                graphene_type=cls,
-                name=cls._meta.name or cls.__name__,
-                description=cls._meta.description or cls.__doc__,
-                fields=cls._fields(bases, attrs),
-            )
+        if not cls._meta.abstract:
+            local_fields = cls._extract_local_fields(attrs)
+            if not cls._meta.graphql_type:
+                cls._meta.graphql_type = GrapheneInputObjectType(
+                    graphene_type=cls,
+                    name=cls._meta.name or cls.__name__,
+                    description=cls._meta.description or cls.__doc__,
+                    fields=cls._fields(bases, attrs, local_fields),
+                )
+            else:
+                assert not local_fields, "Can't mount Fields in an InputObjectType with a defined graphql_type"
         return super(InputObjectTypeMeta, cls).construct(bases, attrs)
 
 
diff --git a/graphene/types/interface.py b/graphene/types/interface.py
index 5400dc06..9843f9dc 100644
--- a/graphene/types/interface.py
+++ b/graphene/types/interface.py
@@ -1,10 +1,10 @@
 import six
 
 from graphql import GraphQLInterfaceType
-from .definitions import FieldsMeta, ClassTypeMeta, GrapheneFieldsType
+from .definitions import FieldsMeta, ClassTypeMeta, GrapheneGraphQLType
 
 
-class GrapheneInterfaceType(GrapheneFieldsType, GraphQLInterfaceType):
+class GrapheneInterfaceType(GrapheneGraphQLType, GraphQLInterfaceType):
     pass
 
 
@@ -19,18 +19,19 @@ class InterfaceTypeMeta(FieldsMeta, ClassTypeMeta):
             abstract=False
         )
 
-    def construct_graphql_type(cls, bases):
-        pass
-
     def construct(cls, bases, attrs):
-        if not cls._meta.graphql_type and not cls._meta.abstract:
+        if not cls._meta.abstract:
+            local_fields = cls._extract_local_fields(attrs)
+            if not cls._meta.graphql_type:
+                cls._meta.graphql_type = GrapheneInterfaceType(
+                    graphene_type=cls,
+                    name=cls._meta.name or cls.__name__,
+                    description=cls._meta.description or cls.__doc__,
+                    fields=cls._fields(bases, attrs, local_fields),
+                )
+            else:
+                assert not local_fields, "Can't mount Fields in an Interface with a defined graphql_type"
 
-            cls._meta.graphql_type = GrapheneInterfaceType(
-                graphene_type=cls,
-                name=cls._meta.name or cls.__name__,
-                description=cls._meta.description or cls.__doc__,
-                fields=cls._fields(bases, attrs),
-            )
         return super(InterfaceTypeMeta, cls).construct(bases, attrs)
 
 
diff --git a/graphene/types/objecttype.py b/graphene/types/objecttype.py
index 5cdfa761..01bc6e5c 100644
--- a/graphene/types/objecttype.py
+++ b/graphene/types/objecttype.py
@@ -3,11 +3,11 @@ import six
 
 from graphql import GraphQLObjectType
 
-from .definitions import ClassTypeMeta, GrapheneFieldsType, FieldMap
+from .definitions import FieldsMeta, ClassTypeMeta, GrapheneGraphQLType
 from .interface import GrapheneInterfaceType
 
 
-class GrapheneObjectType(GrapheneFieldsType, GraphQLObjectType):
+class GrapheneObjectType(GrapheneGraphQLType, GraphQLObjectType):
 
     def __init__(self, *args, **kwargs):
         super(GrapheneObjectType, self).__init__(*args, **kwargs)
@@ -43,7 +43,7 @@ def get_interfaces(cls, interfaces):
         yield graphql_type
 
 
-class ObjectTypeMeta(ClassTypeMeta):
+class ObjectTypeMeta(FieldsMeta, ClassTypeMeta):
 
     def get_options(cls, meta):
         return cls.options_class(
@@ -55,23 +55,24 @@ class ObjectTypeMeta(ClassTypeMeta):
             abstract=False
         )
 
-    def get_interfaces(cls):
-        return get_interfaces(cls, cls._meta.interfaces)
+    def construct(cls, bases, attrs):
+        if not cls._meta.abstract:
+            interfaces = tuple(get_interfaces(cls, cls._meta.interfaces))
+            local_fields = cls._extract_local_fields(attrs)
+            if not cls._meta.graphql_type:
+                cls = super(ObjectTypeMeta, cls).construct(bases, attrs)
+                cls._meta.graphql_type = GrapheneObjectType(
+                    graphene_type=cls,
+                    name=cls._meta.name or cls.__name__,
+                    description=cls._meta.description or cls.__doc__,
+                    fields=cls._fields(bases, attrs, local_fields, interfaces),
+                    interfaces=interfaces,
+                )
+                return cls
+            else:
+                assert not local_fields, "Can't mount Fields in an ObjectType with a defined graphql_type"
 
-    def construct_graphql_type(cls, bases):
-        if not cls._meta.graphql_type and not cls._meta.abstract:
-            from ..utils.is_graphene_type import is_graphene_type
-            inherited_types = [
-                base._meta.graphql_type for base in bases if is_graphene_type(base)
-            ]
-
-            cls._meta.graphql_type = GrapheneObjectType(
-                graphene_type=cls,
-                name=cls._meta.name or cls.__name__,
-                description=cls._meta.description or cls.__doc__,
-                fields=FieldMap(cls, bases=filter(None, inherited_types)),
-                interfaces=tuple(cls.get_interfaces()),
-            )
+        return super(ObjectTypeMeta, cls).construct(bases, attrs)
 
 
 def implements(*interfaces):
@@ -80,8 +81,10 @@ def implements(*interfaces):
     def wrap_class(cls):
         interface_types = get_interfaces(cls, interfaces)
         graphql_type = cls._meta.graphql_type
+        # fields = cls._build_field_map(interface_types, graphql_type.get_fields().values())
         new_type = copy.copy(graphql_type)
         new_type._provided_interfaces = tuple(graphql_type._provided_interfaces) + tuple(interface_types)
+        new_type._fields = graphql_type._fields
         cls._meta.graphql_type = new_type
         cls._meta.graphql_type.check_interfaces()
         return cls
diff --git a/graphene/types/options.py b/graphene/types/options.py
index 59b30f8c..7e311bef 100644
--- a/graphene/types/options.py
+++ b/graphene/types/options.py
@@ -8,11 +8,6 @@ class Options(object):
             setattr(self, name, value)
         self.valid_attrs = defaults.keys()
 
-    def contribute_to_class(self, cls, name):
-        cls._meta = self
-        self.parent = cls
-        self.validate_attrs()
-
     def validate_attrs(self):
         # Store the original user-defined values for each option,
         # for use when serializing the model definition
diff --git a/graphene/types/proxy.py b/graphene/types/proxy.py
index b56158fc..44b55897 100644
--- a/graphene/types/proxy.py
+++ b/graphene/types/proxy.py
@@ -3,6 +3,8 @@ from .argument import Argument
 from ..utils.orderedtype import OrderedType
 
 
+# UnmountedType ?
+
 class TypeProxy(OrderedType):
     '''
     This class acts a proxy for a Graphene Type, so it can be mounted
@@ -51,20 +53,6 @@ class TypeProxy(OrderedType):
             **self.kwargs
         )
 
-    def contribute_to_class(self, cls, attname):
-        from .inputobjecttype import InputObjectType
-        from .objecttype import ObjectType
-        from .interface import Interface
-
-        if issubclass(cls, (ObjectType, Interface)):
-            inner = self.as_field()
-        elif issubclass(cls, (InputObjectType)):
-            inner = self.as_inputfield()
-        else:
-            raise Exception('TypedProxy "{}" cannot be mounted in {}'.format(self.get_type(), cls))
-
-        return inner.contribute_to_class(cls, attname)
-
     def as_mounted(self, cls):
         from .inputobjecttype import InputObjectType
         from .objecttype import ObjectType
diff --git a/graphene/types/scalars.py b/graphene/types/scalars.py
index e023fe89..40b3e719 100644
--- a/graphene/types/scalars.py
+++ b/graphene/types/scalars.py
@@ -20,9 +20,6 @@ class ScalarTypeMeta(ClassTypeMeta):
             abstract=False
         )
 
-    def construct_graphql_type(cls, bases):
-        pass
-
     def construct(cls, *args, **kwargs):
         constructed = super(ScalarTypeMeta, cls).construct(*args, **kwargs)
         if not cls._meta.graphql_type and not cls._meta.abstract:
diff --git a/graphene/types/tests/test_field.py b/graphene/types/tests/test_field.py
index ec9168f7..daaa253f 100644
--- a/graphene/types/tests/test_field.py
+++ b/graphene/types/tests/test_field.py
@@ -38,25 +38,6 @@ def test_not_source_and_resolver():
     assert "You cannot have a source and a resolver at the same time" == str(excinfo.value)
 
 
-def test_contributed_field_objecttype():
-    class MyObject(ObjectType):
-        pass
-
-    field = Field(GraphQLString)
-    field.contribute_to_class(MyObject, 'field_name')
-
-    assert field.name == 'fieldName'
-
-
-def test_contributed_field_non_objecttype():
-    class MyObject(object):
-        pass
-
-    field = Field(GraphQLString)
-    with pytest.raises(AssertionError):
-        field.contribute_to_class(MyObject, 'field_name')
-
-
 def test_copy_field_works():
     field = Field(GraphQLString)
     copy.copy(field)
diff --git a/graphene/types/tests/test_interface.py b/graphene/types/tests/test_interface.py
index 620d3998..7c48b3a9 100644
--- a/graphene/types/tests/test_interface.py
+++ b/graphene/types/tests/test_interface.py
@@ -81,4 +81,4 @@ def test_interface_add_fields_in_reused_graphql_type():
             class Meta:
                 graphql_type = MyGraphQLType
 
-    assert """Field "MyGraphQLType.field" can only be mounted in ObjectType or Interface, received GrapheneInterface.""" == str(excinfo.value)
+    assert """Can't mount Fields in an Interface with a defined graphql_type""" == str(excinfo.value)
diff --git a/graphene/types/tests/test_objecttype.py b/graphene/types/tests/test_objecttype.py
index 7ebc3523..e7ae9d64 100644
--- a/graphene/types/tests/test_objecttype.py
+++ b/graphene/types/tests/test_objecttype.py
@@ -10,8 +10,8 @@ from ..field import Field
 
 
 class Container(ObjectType):
-    field1 = Field(GraphQLString)
-    field2 = Field(GraphQLString)
+    field1 = Field(GraphQLString, name='field1')
+    field2 = Field(GraphQLString, name='field2')
 
 
 def test_generate_objecttype():
@@ -53,116 +53,152 @@ def test_generate_objecttype_with_fields():
     assert 'field' in fields
 
 
+def test_ordered_fields_in_objecttype():
+    class MyObjectType(ObjectType):
+        b = Field(GraphQLString)
+        a = Field(GraphQLString)
+        field = Field(GraphQLString)
+        asa = Field(GraphQLString)
+
+    graphql_type = MyObjectType._meta.graphql_type
+    fields = graphql_type.get_fields()
+    assert fields.keys() == ['b', 'a', 'field', 'asa']
+
+
 def test_objecttype_inheritance():
     class MyInheritedObjectType(ObjectType):
         inherited = Field(GraphQLString)
 
     class MyObjectType(MyInheritedObjectType):
-        field = Field(GraphQLString)
+        field1 = Field(GraphQLString)
+        field2 = Field(GraphQLString)
 
     graphql_type = MyObjectType._meta.graphql_type
     fields = graphql_type.get_fields()
-    assert 'field' in fields
+    assert 'field1' in fields
+    assert 'field2' in fields
     assert 'inherited' in fields
-    assert fields['field'] > fields['inherited']
+    assert fields['field1'] > fields['inherited']
+    assert fields['field2'] > fields['field1']
 
 
-def test_objecttype_as_container_only_args():
-    container = Container("1", "2")
-    assert container.field1 == "1"
-    assert container.field2 == "2"
+def test_objecttype_as_container_get_fields():
+
+    class Container(ObjectType):
+        field1 = Field(GraphQLString)
+        field2 = Field(GraphQLString)
+
+    assert Container._meta.graphql_type.get_fields().keys() == ['field1', 'field2']
 
 
-def test_objecttype_as_container_args_kwargs():
-    container = Container("1", field2="2")
-    assert container.field1 == "1"
-    assert container.field2 == "2"
+def test_parent_container_get_fields():
+    fields = Container._meta.graphql_type.get_fields()
+    print [(f.creation_counter, f.name) for f in fields.values()]
+    assert fields.keys() == ['field1', 'field2']
 
 
-def test_objecttype_as_container_few_kwargs():
-    container = Container(field2="2")
-    assert container.field2 == "2"
+# def test_objecttype_as_container_only_args():
+#     container = Container("1", "2")
+#     assert container.field1 == "1"
+#     assert container.field2 == "2"
 
 
-def test_objecttype_as_container_all_kwargs():
-    container = Container(field1="1", field2="2")
-    assert container.field1 == "1"
-    assert container.field2 == "2"
+# def test_objecttype_as_container_args_kwargs():
+#     container = Container("1", field2="2")
+#     assert container.field1 == "1"
+#     assert container.field2 == "2"
 
 
-def test_objecttype_as_container_extra_args():
-    with pytest.raises(IndexError) as excinfo:
-        Container("1", "2", "3")
-
-    assert "Number of args exceeds number of fields" == str(excinfo.value)
+# def test_objecttype_as_container_few_kwargs():
+#     container = Container(field2="2")
+#     assert container.field2 == "2"
 
 
-def test_objecttype_as_container_invalid_kwargs():
-    with pytest.raises(TypeError) as excinfo:
-        Container(unexisting_field="3")
-
-    assert "'unexisting_field' is an invalid keyword argument for this function" == str(excinfo.value)
+# def test_objecttype_as_container_all_kwargs():
+#     container = Container(field1="1", field2="2")
+#     assert container.field1 == "1"
+#     assert container.field2 == "2"
 
 
-def test_objecttype_reuse_graphql_type():
-    MyGraphQLType = GraphQLObjectType('MyGraphQLType', fields={
-        'field': GraphQLField(GraphQLString)
-    })
+# def test_objecttype_as_container_extra_args():
+#     with pytest.raises(IndexError) as excinfo:
+#         Container("1", "2", "3")
 
-    class GrapheneObjectType(ObjectType):
-        class Meta:
-            graphql_type = MyGraphQLType
-
-    graphql_type = GrapheneObjectType._meta.graphql_type
-    assert graphql_type == MyGraphQLType
-    instance = GrapheneObjectType(field="A")
-    assert instance.field == "A"
+#     assert "Number of args exceeds number of fields" == str(excinfo.value)
 
 
-def test_objecttype_add_fields_in_reused_graphql_type():
-    MyGraphQLType = GraphQLObjectType('MyGraphQLType', fields={
-        'field': GraphQLField(GraphQLString)
-    })
+# def test_objecttype_as_container_invalid_kwargs():
+#     with pytest.raises(TypeError) as excinfo:
+#         Container(unexisting_field="3")
 
-    with pytest.raises(AssertionError) as excinfo:
-        class GrapheneObjectType(ObjectType):
-            field = Field(GraphQLString)
-
-            class Meta:
-                graphql_type = MyGraphQLType
-
-    assert """Field "MyGraphQLType.field" can only be mounted in ObjectType or Interface, received GrapheneObjectType.""" == str(excinfo.value)
+#     assert "'unexisting_field' is an invalid keyword argument for this function" == str(excinfo.value)
 
 
-def test_objecttype_graphql_interface():
-    MyInterface = GraphQLInterfaceType('MyInterface', fields={
-        'field': GraphQLField(GraphQLString)
-    })
+# def test_objecttype_reuse_graphql_type():
+#     MyGraphQLType = GraphQLObjectType('MyGraphQLType', fields={
+#         'field': GraphQLField(GraphQLString)
+#     })
 
-    class GrapheneObjectType(ObjectType):
-        class Meta:
-            interfaces = [MyInterface]
+#     class GrapheneObjectType(ObjectType):
+#         class Meta:
+#             graphql_type = MyGraphQLType
 
-    graphql_type = GrapheneObjectType._meta.graphql_type
-    assert graphql_type.get_interfaces() == (MyInterface, )
-    # assert graphql_type.is_type_of(MyInterface, None, None)
-    fields = graphql_type.get_fields()
-    assert 'field' in fields
+#     graphql_type = GrapheneObjectType._meta.graphql_type
+#     assert graphql_type == MyGraphQLType
+#     instance = GrapheneObjectType(field="A")
+#     assert instance.field == "A"
 
 
-def test_objecttype_graphene_interface():
-    class GrapheneInterface(Interface):
-        field = Field(GraphQLString)
+# def test_objecttype_add_fields_in_reused_graphql_type():
+#     MyGraphQLType = GraphQLObjectType('MyGraphQLType', fields={
+#         'field': GraphQLField(GraphQLString)
+#     })
 
-    class GrapheneObjectType(ObjectType):
-        class Meta:
-            interfaces = [GrapheneInterface]
+#     with pytest.raises(AssertionError) as excinfo:
+#         class GrapheneObjectType(ObjectType):
+#             field = Field(GraphQLString)
 
-    graphql_type = GrapheneObjectType._meta.graphql_type
-    assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, )
-    assert graphql_type.is_type_of(GrapheneObjectType(), None, None)
-    fields = graphql_type.get_fields()
-    assert 'field' in fields
+#             class Meta:
+#                 graphql_type = MyGraphQLType
+
+#     assert """Field "MyGraphQLType.field" can only be mounted in ObjectType or Interface, received GrapheneObjectType.""" == str(excinfo.value)
+
+
+# def test_objecttype_graphql_interface():
+#     MyInterface = GraphQLInterfaceType('MyInterface', fields={
+#         'field': GraphQLField(GraphQLString)
+#     })
+
+#     class GrapheneObjectType(ObjectType):
+#         class Meta:
+#             interfaces = [MyInterface]
+
+#     graphql_type = GrapheneObjectType._meta.graphql_type
+#     assert graphql_type.get_interfaces() == (MyInterface, )
+#     # assert graphql_type.is_type_of(MyInterface, None, None)
+#     fields = graphql_type.get_fields()
+#     assert 'field' in fields
+
+
+# def test_objecttype_graphene_interface():
+#     class GrapheneInterface(Interface):
+#         name = Field(GraphQLString)
+#         extended = Field(GraphQLString)
+
+#     class GrapheneObjectType(ObjectType):
+#         class Meta:
+#             interfaces = [GrapheneInterface]
+
+#         field = Field(GraphQLString)
+
+#     graphql_type = GrapheneObjectType._meta.graphql_type
+#     assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, )
+#     assert graphql_type.is_type_of(GrapheneObjectType(), None, None)
+#     fields = graphql_type.get_fields()
+#     assert 'field' in fields
+#     assert 'extended' in fields
+#     assert 'name' in fields
+#     assert fields['field'] > fields['extended'] > fields['name']
 
 
 # def test_objecttype_graphene_interface_extended():
diff --git a/graphene/types/tests/test_options.py b/graphene/types/tests/test_options.py
index 5d4c334d..8ff5bbc6 100644
--- a/graphene/types/tests/test_options.py
+++ b/graphene/types/tests/test_options.py
@@ -21,16 +21,11 @@ def test_options_contribute_to_class():
         overwritten = True
         accepted = True
 
-    class MyObject(object):
-        pass
 
     options = Options(Meta, attr=True, overwritten=False)
     options.valid_attrs = ['accepted', 'overwritten']
     assert options.attr
     assert not options.overwritten
-    options.contribute_to_class(MyObject, '_meta')
-    assert MyObject._meta == options
-    assert options.parent == MyObject
 
 
 def test_options_invalid_attrs():
@@ -41,9 +36,10 @@ def test_options_invalid_attrs():
         pass
 
     options = Options(Meta, valid=True)
+    options.parent = MyObject
     options.valid_attrs = ['valid']
     assert options.valid
     with pytest.raises(TypeError) as excinfo:
-        options.contribute_to_class(MyObject, '_meta')
+        options.validate_attrs()
 
     assert "MyObject.Meta got invalid attributes: invalid" == str(excinfo.value)
diff --git a/graphene/types/tests/test_scalars.py b/graphene/types/tests/test_scalars.py
index ad7da2c1..ad9ab102 100644
--- a/graphene/types/tests/test_scalars.py
+++ b/graphene/types/tests/test_scalars.py
@@ -23,22 +23,27 @@ scalar_classes = {
 
 @pytest.mark.parametrize("scalar_class,expected_graphql_type", scalar_classes.items())
 def test_scalar_as_field(scalar_class, expected_graphql_type):
+    field_before = Field(None)
     scalar = scalar_class()
     field = scalar.as_field()
     graphql_type = get_graphql_type(scalar_class)
+    field_after = Field(None)
     assert isinstance(field, Field)
     assert field.type == graphql_type
     assert graphql_type == expected_graphql_type
+    assert field_before < field < field_after
 
 
 @pytest.mark.parametrize("scalar_class,graphql_type", scalar_classes.items())
 def test_scalar_in_objecttype(scalar_class, graphql_type):
     class MyObjectType(ObjectType):
+        before = Field(scalar_class)
         field = scalar_class()
+        after = Field(scalar_class)
 
     graphql_type = get_graphql_type(MyObjectType)
     fields = graphql_type.get_fields()
-    assert 'field' in fields
+    assert fields.keys() == ['before', 'field', 'after']
     assert isinstance(fields['field'], Field)
 
 
diff --git a/graphene/types/tests/test_schema.py b/graphene/types/tests/test_schema.py
index ca44421a..c3fd8ead 100644
--- a/graphene/types/tests/test_schema.py
+++ b/graphene/types/tests/test_schema.py
@@ -16,8 +16,11 @@ class Pet(ObjectType):
     type = String()
 
 
-@implements(Character)
+# @implements(Character)
 class Human(ObjectType):
+    class Meta:
+        interfaces = [Character]
+
     pet = Field(Pet)
 
     def resolve_pet(self, *args):
diff --git a/graphene/types/tests/test_structures.py b/graphene/types/tests/test_structures.py
index 056791a0..48b39568 100644
--- a/graphene/types/tests/test_structures.py
+++ b/graphene/types/tests/test_structures.py
@@ -4,6 +4,7 @@ from graphql import GraphQLString, GraphQLList, GraphQLNonNull
 
 from ..structures import List, NonNull
 from ..scalars import String
+from ..field import Field
 
 
 def test_list():
@@ -42,3 +43,10 @@ def test_nonnull_list():
     assert isinstance(list_instance, GraphQLNonNull)
     assert isinstance(list_instance.of_type, GraphQLList)
     assert list_instance.of_type.of_type == GraphQLString
+
+
+def test_preserve_order():
+    field1 = List(lambda: None)
+    field2 = Field(lambda: None)
+
+    assert field1 < field2
diff --git a/graphene/utils/build_field_map.py b/graphene/utils/build_field_map.py
new file mode 100644
index 00000000..e69de29b
diff --git a/graphene/utils/extract_fields.py b/graphene/utils/extract_fields.py
index 014c156e..5b6727c7 100644
--- a/graphene/utils/extract_fields.py
+++ b/graphene/utils/extract_fields.py
@@ -15,6 +15,7 @@ def extract_fields(cls, attrs):
             continue
         field = value.as_mounted(cls) if is_field_proxy else copy.copy(value)
         field.attname = attname
+        field.parent = cls
         fields.add(attname)
         del attrs[attname]
         _fields.append(field)
@@ -24,10 +25,15 @@ def extract_fields(cls, attrs):
 
 def get_base_fields(cls, bases):
     fields = set()
+    _fields = list()
     for _class in bases:
         for attname, field in get_graphql_type(_class).get_fields().items():
             if attname in fields:
                 continue
             field = copy.copy(field)
+            if isinstance(field, Field):
+                field.parent = cls
             fields.add(attname)
-            yield field
+            _fields.append(field)
+
+    return sorted(_fields)
diff --git a/graphene/utils/orderedtype.py b/graphene/utils/orderedtype.py
index 4e88183d..a58f4d08 100644
--- a/graphene/utils/orderedtype.py
+++ b/graphene/utils/orderedtype.py
@@ -3,7 +3,7 @@ from functools import total_ordering
 
 @total_ordering
 class OrderedType(object):
-    creation_counter = 0
+    creation_counter = 1
 
     def __init__(self, _creation_counter=None):
         self.creation_counter = _creation_counter or self.gen_counter()