Thrift: Support for explicit Python module declaration.

Summary:
Previously, Thrift used the name of the .thrift file as the python module name.
This wasn't very flexible.  Now the python module can be explicitly declared.
Also, there was no need for t_py_generator to inherit from t_oop_generator.

Reviewed By: mcslee

Test Plan:
cd test/py/explicit_module
./runtest.sh

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665234 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 256029e..487de87 100644
--- a/compiler/cpp/src/generate/t_py_generator.cc
+++ b/compiler/cpp/src/generate/t_py_generator.cc
@@ -20,10 +20,27 @@
  */
 void t_py_generator::init_generator() {
   // Make output directory
-  mkdir(T_PY_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  string module = get_real_py_module(program_);
   package_dir_ = T_PY_DIR;
-  package_dir_ = package_dir_ + "/" + program_name_;
-  mkdir(package_dir_.c_str(), S_IREAD | S_IWRITE | S_IEXEC);
+  while (true) {
+    // TODO: Do better error checking here.
+    mkdir(package_dir_.c_str(), S_IREAD | S_IWRITE | S_IEXEC);
+    std::ofstream init_py((package_dir_+"/__init__.py").c_str());
+    init_py.close();
+    if (module.empty()) {
+      break;
+    }
+    string::size_type pos = module.find('.');
+    if (pos == string::npos) {
+      package_dir_ += "/";
+      package_dir_ += module;
+      module.clear();
+    } else {
+      package_dir_ += "/";
+      package_dir_ += module.substr(0, pos);
+      module.erase(0, pos+1);
+    }
+  }
 
   // Make output file
   string f_types_name = package_dir_+"/"+"ttypes.py";
@@ -52,7 +69,7 @@
     render_includes() << endl <<
     "from thrift.transport import TTransport" << endl <<
     "from thrift.protocol import fastbinary" << endl <<
-    "from thrift.protocol import TBinaryProtocol" << endl;
+    "from thrift.protocol import TBinaryProtocol" << endl << endl << endl;
 
   f_consts_ <<
     py_autogen_comment() << endl <<
@@ -68,7 +85,7 @@
   const vector<t_program*>& includes = program_->get_includes();
   string result = "";
   for (size_t i = 0; i < includes.size(); ++i) {
-    result += "import " + includes[i]->get_name() + ".ttypes\n";
+    result += "import " + get_real_py_module(includes[i]) + ".ttypes\n";
   }
   if (includes.size() > 0) {
     result += "\n";
@@ -590,7 +607,8 @@
 
   if (tservice->get_extends() != NULL) {
     f_service_ <<
-      "import " << tservice->get_extends()->get_program()->get_name() << "." << tservice->get_extends()->get_name() << endl;
+      "import " << get_real_py_module(tservice->get_extends()->get_program()) <<
+      "." << tservice->get_extends()->get_name() << endl;
   }
 
   f_service_ <<
@@ -1597,9 +1615,9 @@
   t_program* program = ttype->get_program();
   if (program != NULL && program != program_) {
     if (ttype->is_service()) {
-      return program->get_name() + "." + ttype->get_name();
+      return get_real_py_module(program) + "." + ttype->get_name();
     } else {
-      return program->get_name() + ".ttypes." + ttype->get_name();
+      return get_real_py_module(program) + ".ttypes." + ttype->get_name();
     }
   }
   return ttype->get_name();
diff --git a/compiler/cpp/src/generate/t_py_generator.h b/compiler/cpp/src/generate/t_py_generator.h
index b301d36..c367525 100644
--- a/compiler/cpp/src/generate/t_py_generator.h
+++ b/compiler/cpp/src/generate/t_py_generator.h
@@ -12,7 +12,7 @@
 #include <iostream>
 #include <vector>
 
-#include "t_oop_generator.h"
+#include "t_generator.h"
 
 #define T_PY_DIR "gen-py"
 
@@ -21,10 +21,10 @@
  *
  * @author Mark Slee <mcslee@facebook.com>
  */
-class t_py_generator : public t_oop_generator {
+class t_py_generator : public t_generator {
  public:
   t_py_generator(t_program* program) :
-    t_oop_generator(program) {}
+    t_generator(program) {}
 
   /**
    * Init and close methods
@@ -135,6 +135,14 @@
   std::string type_to_enum(t_type* ttype);
   std::string type_to_spec_args(t_type* ttype);
 
+  static std::string get_real_py_module(const t_program* program) {
+    std::string real_module = program->get_py_module();
+    if (real_module.empty()) {
+      return program->get_name();
+    }
+    return real_module;
+  }
+
  private:
 
   /**
diff --git a/compiler/cpp/src/parse/t_program.h b/compiler/cpp/src/parse/t_program.h
index d516617..8420847 100644
--- a/compiler/cpp/src/parse/t_program.h
+++ b/compiler/cpp/src/parse/t_program.h
@@ -154,6 +154,14 @@
     return ruby_namespace_;
   }
 
+  void set_py_module(std::string py_module) {
+    py_module_ = py_module;
+  }
+
+  const std::string& get_py_module() const {
+    return py_module_;
+  }
+
   void set_perl_package(std::string perl_package) {
     perl_package_ = perl_package;
   }
@@ -205,6 +213,9 @@
   // Ruby namespace
   std::string ruby_namespace_;
 
+  // Python namespace
+  std::string py_module_;
+
   // Perl namespace
   std::string perl_package_;
 
diff --git a/compiler/cpp/src/thriftl.ll b/compiler/cpp/src/thriftl.ll
index d047acc..fbad955 100644
--- a/compiler/cpp/src/thriftl.ll
+++ b/compiler/cpp/src/thriftl.ll
@@ -72,6 +72,7 @@
 "cpp_type"       { return tok_cpp_type;       }
 "java_package"   { return tok_java_package;   }
 "php_namespace"  { return tok_php_namespace;  }
+"py_module"      { return tok_py_module;      }
 "perl_package"   { return tok_perl_package;   }
 "ruby_namespace" { return tok_ruby_namespace; }
 "xsd_all"        { return tok_xsd_all;        }
diff --git a/compiler/cpp/src/thrifty.yy b/compiler/cpp/src/thrifty.yy
index 38ca516..8533805 100644
--- a/compiler/cpp/src/thrifty.yy
+++ b/compiler/cpp/src/thrifty.yy
@@ -75,6 +75,7 @@
 %token tok_cpp_include
 %token tok_cpp_type
 %token tok_php_namespace
+%token tok_py_module
 %token tok_perl_package
 %token tok_java_package
 %token tok_xsd_all
@@ -269,6 +270,13 @@
         g_program->set_php_namespace($2);
       }
     }
+| tok_py_module tok_identifier
+    {
+      pdebug("Header -> tok_py_module tok_identifier");
+      if (g_parse_mode == PROGRAM) {
+        g_program->set_py_module($2);
+      }
+    }
 | tok_perl_package tok_identifier
     {
       pdebug("Header -> tok_perl_namespace tok_identifier");