THRIFT-242. python: Used named arguments in __init__ instead of a dict

This is a wire-compatible but non-source-compatible change.
When initializing structures, you must use

Foo(bar=1, baz="qux")
Foo(**{"bar": 1, "baz": "qux"})

instead of

Foo({"bar": 1, "baz": "qux"})


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@734536 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_py_generator.cc b/compiler/cpp/src/generate/t_py_generator.cc
index 8c9747a..0218394 100644
--- a/compiler/cpp/src/generate/t_py_generator.cc
+++ b/compiler/cpp/src/generate/t_py_generator.cc
@@ -143,7 +143,8 @@
   std::string py_imports();
   std::string render_includes();
   std::string render_fastbinary_includes();
-  std::string declare_field(t_field* tfield);
+  std::string declare_argument(t_field* tfield);
+  std::string render_field_default_value(t_field* tfield);
   std::string type_name(t_type* ttype);
   std::string function_signature(t_function* tfunction, std::string prefix="");
   std::string argument_list(t_struct* tstruct);
@@ -387,7 +388,7 @@
   } else if (type->is_enum()) {
     indent(out) << value->get_integer();
   } else if (type->is_struct() || type->is_xception()) {
-    out << type->get_name() << "({" << endl;
+    out << type->get_name() << "(**{" << endl;
     indent_up();
     const vector<t_field*>& fields = ((t_struct*)type)->get_members();
     vector<t_field*>::const_iterator f_iter;
@@ -563,7 +564,7 @@
             << type_to_enum((*m_iter)->get_type()) << ", "
             << "'" << (*m_iter)->get_name() << "'" << ", "
             << type_to_spec_args((*m_iter)->get_type()) << ", "
-            << "None" << ", "
+            << render_field_default_value(*m_iter) << ", "
             << "),"
             << " # " << sorted_keys_pos
             << endl;
@@ -578,36 +579,38 @@
   }
 
 
-  out <<
-    indent() << "def __init__(self, d=None):" << endl;
-  indent_up();
+  if (members.size() > 0) {
+    out <<
+      indent() << "def __init__(self,";
 
-  if (members.size() == 0) {
-    indent(out) <<
-      "pass" <<endl;
-  } else {
     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
       // This fills in default values, as opposed to nulls
-      indent(out) <<
-        declare_field(*m_iter) << endl;
+      out << " " << declare_argument(*m_iter) << ",";
     }
 
-    indent(out) <<
-      "if isinstance(d, dict):" << endl;
+    out << "):" << endl;
+
     indent_up();
+
     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
-      out <<
-        indent() << "if '" << (*m_iter)->get_name() << "' in d:" << endl <<
-        indent() << "  self." << (*m_iter)->get_name() << " = d['" << (*m_iter)->get_name() << "']" << endl;
+      // Initialize fields
+      t_type* type = (*m_iter)->get_type();
+      if (!type->is_base_type() && !type->is_enum() && (*m_iter)->get_value() != NULL) {
+        indent(out) <<
+          "if " << (*m_iter)->get_name() << " is " << "self.thrift_spec[" <<
+            (*m_iter)->get_key() << "][4]:" << endl;
+        indent(out) << "  " << (*m_iter)->get_name() << " = " <<
+          render_field_default_value(*m_iter) << endl;
+      }
+      indent(out) <<
+        "self." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << endl;
     }
+
     indent_down();
+
+    out << endl;
   }
 
-  indent_down();
-
-  out << endl;
-
-
   generate_py_struct_reader(out, tstruct);
   generate_py_struct_writer(out, tstruct);
 
@@ -1756,19 +1759,34 @@
 }
 
 /**
- * Declares a field, which may include initialization as necessary.
+ * Declares an argument, which may include initialization as necessary.
  *
- * @param ttype The type
+ * @param tfield The field
  */
-string t_py_generator::declare_field(t_field* tfield) {
-  string result = "self." + tfield->get_name();
+string t_py_generator::declare_argument(t_field* tfield) {
+  std::ostringstream result;
+  result << tfield->get_name() << "=";
+  if (tfield->get_value() != NULL) {
+    result << "thrift_spec[" <<
+      tfield->get_key() << "][4]";
+  } else {
+    result << "None";
+  }
+  return result.str();
+}
+
+/**
+ * Renders a field default value, returns None otherwise.
+ *
+ * @param tfield The field
+ */
+string t_py_generator::render_field_default_value(t_field* tfield) {
   t_type* type = get_true_type(tfield->get_type());
   if (tfield->get_value() != NULL) {
-    result += " = " + render_const_value(type, tfield->get_value());
+    return render_const_value(type, tfield->get_value());
   } else {
-    result += " = None";
+    return "None";
   }
-  return result;
 }
 
 /**
diff --git a/test/py/SerializationTest.py b/test/py/SerializationTest.py
index 3b34661..4be8b8c 100755
--- a/test/py/SerializationTest.py
+++ b/test/py/SerializationTest.py
@@ -14,25 +14,25 @@
 class AbstractTest(unittest.TestCase):
 
   def setUp(self):
-      self.v1obj = VersioningTestV1(d=dict(
+      self.v1obj = VersioningTestV1(
           begin_in_both=12345,
           end_in_both=54321,
-          ))
+          )
 
-      self.v2obj = VersioningTestV2(d=dict(
+      self.v2obj = VersioningTestV2(
           begin_in_both=12345,
           newint=1,
           newbyte=2,
           newshort=3,
           newlong=4,
           newdouble=5.0,
-          newstruct=Bonk(d=dict(message="Hello!", type=123)),
+          newstruct=Bonk(message="Hello!", type=123),
           newlist=[7,8,9],
           newset=[42,1,8],
           newmap={1:2,2:3},
           newstring="Hola!",
           end_in_both=54321,
-          ))
+          )
 
   def _serialize(self, obj):
       trans = TTransport.TMemoryBuffer()