Review comments
- Update flag description to be more precise
- No implict enum generation (gen enum flag required)
- Use latest thrift test IDL for uuid coverage
- rebase on latest main
diff --git a/compiler/cpp/src/thrift/generate/t_py_generator.cc b/compiler/cpp/src/thrift/generate/t_py_generator.cc
index ddca9fe..90bfd05 100644
--- a/compiler/cpp/src/thrift/generate/t_py_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_py_generator.cc
@@ -17,21 +17,21 @@
  * under the License.
  */
 
+#include <string>
 #include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <limits>
-#include <string>
 #include <vector>
 
-#include "thrift/generate/t_generator.h"
-#include "thrift/platform.h"
-#include "thrift/version.h"
-#include <algorithm>
-#include <sstream>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sstream>
+#include <algorithm>
+#include "thrift/platform.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_generator.h"
 
 using std::map;
 using std::ostream;
@@ -49,7 +49,7 @@
   t_py_generator(t_program* program,
                  const std::map<std::string, std::string>& parsed_options,
                  const std::string& option_string)
-    : t_generator(program) {
+    : t_generator (program) {
     update_keywords_for_validation();
 
     std::map<std::string, std::string>::const_iterator iter;
@@ -71,70 +71,66 @@
     gen_dynbaseclass_frozen_ = "";
     import_dynbase_ = "";
     package_prefix_ = "";
-    for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
-      if (iter->first.compare("enum") == 0) {
+    for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+      if( iter->first.compare("enum") == 0) {
         gen_enum_ = true;
-      } else if (iter->first.compare("new_style") == 0) {
-        pwarning(
-            0,
-            "new_style is enabled by default, so the option will be removed in the near future.\n");
-      } else if (iter->first.compare("old_style") == 0) {
+      } else if( iter->first.compare("new_style") == 0) {
+        pwarning(0, "new_style is enabled by default, so the option will be removed in the near future.\n");
+      } else if( iter->first.compare("old_style") == 0) {
         gen_newstyle_ = false;
         pwarning(0, "old_style is deprecated and may be removed in the future.\n");
-      } else if (iter->first.compare("utf8strings") == 0) {
-        pwarning(0,
-                 "utf8strings is enabled by default, so the option will be removed in the near "
-                 "future.\n");
-      } else if (iter->first.compare("no_utf8strings") == 0) {
+      } else if( iter->first.compare("utf8strings") == 0) {
+        pwarning(0, "utf8strings is enabled by default, so the option will be removed in the near future.\n");
+      } else if( iter->first.compare("no_utf8strings") == 0) {
         gen_utf8strings_ = false;
-      } else if (iter->first.compare("slots") == 0) {
+      } else if( iter->first.compare("slots") == 0) {
         gen_slots_ = true;
-      } else if (iter->first.compare("package_prefix") == 0) {
+      } else if( iter->first.compare("package_prefix") == 0) {
         package_prefix_ = iter->second;
-      } else if (iter->first.compare("dynamic") == 0) {
+      } else if( iter->first.compare("dynamic") == 0) {
         gen_dynamic_ = true;
         gen_newstyle_ = false; // dynamic is newstyle
-        if (gen_dynbaseclass_.empty()) {
+        if( gen_dynbaseclass_.empty()) {
           gen_dynbaseclass_ = "TBase";
         }
-        if (gen_dynbaseclass_frozen_.empty()) {
+        if( gen_dynbaseclass_frozen_.empty()) {
           gen_dynbaseclass_frozen_ = "TFrozenBase";
         }
-        if (gen_dynbaseclass_exc_.empty()) {
+        if( gen_dynbaseclass_exc_.empty()) {
           gen_dynbaseclass_exc_ = "TExceptionBase";
         }
-        if (gen_dynbaseclass_frozen_exc_.empty()) {
+        if( gen_dynbaseclass_frozen_exc_.empty()) {
           gen_dynbaseclass_frozen_exc_ = "TFrozenExceptionBase";
         }
-        if (import_dynbase_.empty()) {
-          import_dynbase_
-              = "from thrift.protocol.TBase import TBase, TFrozenBase, TExceptionBase, "
-                "TFrozenExceptionBase, TTransport\n";
+        if( import_dynbase_.empty()) {
+          import_dynbase_ = "from thrift.protocol.TBase import TBase, TFrozenBase, TExceptionBase, TFrozenExceptionBase, TTransport\n";
         }
-      } else if (iter->first.compare("dynbase") == 0) {
+      } else if( iter->first.compare("dynbase") == 0) {
         gen_dynbase_ = true;
         gen_dynbaseclass_ = (iter->second);
-      } else if (iter->first.compare("dynfrozen") == 0) {
+      } else if( iter->first.compare("dynfrozen") == 0) {
         gen_dynbaseclass_frozen_ = (iter->second);
-      } else if (iter->first.compare("dynexc") == 0) {
+      } else if( iter->first.compare("dynexc") == 0) {
         gen_dynbaseclass_exc_ = (iter->second);
-      } else if (iter->first.compare("dynfrozenexc") == 0) {
+      } else if( iter->first.compare("dynfrozenexc") == 0) {
         gen_dynbaseclass_frozen_exc_ = (iter->second);
-      } else if (iter->first.compare("dynimport") == 0) {
+      } else if( iter->first.compare("dynimport") == 0) {
         gen_dynbase_ = true;
         import_dynbase_ = (iter->second);
-      } else if (iter->first.compare("zope.interface") == 0) {
+      } else if( iter->first.compare("zope.interface") == 0) {
         gen_zope_interface_ = true;
-      } else if (iter->first.compare("twisted") == 0) {
+      } else if( iter->first.compare("twisted") == 0) {
         gen_twisted_ = true;
         gen_zope_interface_ = true;
-      } else if (iter->first.compare("tornado") == 0) {
+      } else if( iter->first.compare("tornado") == 0) {
         gen_tornado_ = true;
-      } else if (iter->first.compare("coding") == 0) {
+      } else if( iter->first.compare("coding") == 0) {
         coding_ = iter->second;
       } else if (iter->first.compare("type_hints") == 0) {
+        if (!gen_enum_) {
+          throw "the type_hints py option requires the enum py option";
+        }
         gen_type_hints_ = true;
-        gen_enum_ = true;
       } else {
         throw "unknown option py:" + iter->first;
       }
@@ -155,7 +151,9 @@
     }
   }
 
-  std::string indent_str() const override { return "    "; }
+  std::string indent_str() const override {
+    return "    ";
+  }
 
   /**
    * Init and close methods
@@ -208,7 +206,9 @@
    * Serialization constructs
    */
 
-  void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+  void generate_deserialize_field(std::ostream& out,
+                                  t_field* tfield,
+                                  std::string prefix = "");
 
   void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
 
@@ -218,7 +218,9 @@
 
   void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
 
-  void generate_deserialize_list_element(std::ostream& out, t_list* tlist, std::string prefix = "");
+  void generate_deserialize_list_element(std::ostream& out,
+                                         t_list* tlist,
+                                         std::string prefix = "");
 
   void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
 
@@ -256,9 +258,7 @@
   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,
-                                 bool interface = false,
-                                 bool send_part = false);
+  std::string function_signature(t_function* tfunction, bool interface = false, bool send_part = false);
   std::string argument_list(t_struct* tstruct,
                             std::vector<std::string>* pre = nullptr,
                             std::vector<std::string>* post = nullptr);
@@ -273,9 +273,7 @@
     return sub_namespace == "twisted";
   }
 
-  static std::string get_real_py_module(const t_program* program,
-                                        bool gen_twisted,
-                                        std::string package_dir = "") {
+  static std::string get_real_py_module(const t_program* program, bool gen_twisted, std::string package_dir="") {
     if (gen_twisted) {
       std::string twisted_module = program->get_namespace("py.twisted");
       if (!twisted_module.empty()) {
@@ -291,8 +289,7 @@
   }
 
   static bool is_immutable(t_type* ttype) {
-    std::map<std::string, std::vector<std::string>>::iterator it
-        = ttype->annotations_.find("python.immutable");
+    std::map<std::string, std::vector<std::string>>::iterator it = ttype->annotations_.find("python.immutable");
 
     if (it == ttype->annotations_.end()) {
       // Exceptions are immutable by default.
@@ -305,6 +302,7 @@
   }
 
 private:
+
   /**
    * True if we should generate new-style classes.
    */
@@ -312,8 +310,8 @@
   bool gen_enum_;
 
   /**
-   * True if we should generate dynamic style classes.
-   */
+  * True if we should generate dynamic style classes.
+  */
   bool gen_dynamic_;
 
   bool gen_dynbase_;
@@ -327,8 +325,8 @@
   bool gen_slots_;
 
   /**
-   * True if we should generate classes type hints and type checks in write methods.
-   */
+  * True if we should generate classes type hints and type checks in write methods.
+  */
   bool gen_type_hints_;
 
   std::string copy_options_;
@@ -374,13 +372,11 @@
 
 protected:
   std::set<std::string> lang_keywords_for_validation() const override {
-    std::string keywords[]
-        = {"False", "None",     "True",   "and",      "as",     "assert", "break",
-           "class", "continue", "def",    "del",      "elif",   "else",   "except",
-           "exec",  "finally",  "for",    "from",     "global", "if",     "import",
-           "in",    "is",       "lambda", "nonlocal", "not",    "or",     "pass",
-           "print", "raise",    "return", "try",      "while",  "with",   "yield"};
-    return std::set<std::string>(keywords, keywords + sizeof(keywords) / sizeof(keywords[0]));
+    std::string keywords[] = { "False", "None", "True", "and", "as", "assert", "break", "class",
+          "continue", "def", "del", "elif", "else", "except", "exec", "finally", "for", "from",
+          "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "print",
+          "raise", "return", "try", "while", "with", "yield" };
+    return std::set<std::string>(keywords, keywords + sizeof(keywords)/sizeof(keywords[0]) );
   }
 };
 
@@ -443,9 +439,10 @@
 
   f_types_ << "all_structs = []" << '\n';
 
-  f_consts_ << py_autogen_comment() << '\n'
-            << py_imports() << '\n'
-            << "from .ttypes import *" << '\n';
+  f_consts_ <<
+    py_autogen_comment() << '\n' <<
+    py_imports() << '\n' <<
+    "from .ttypes import *" << '\n';
 }
 
 /**
@@ -466,11 +463,11 @@
 string t_py_generator::py_autogen_comment() {
   string coding;
   if (!coding_.empty()) {
-    coding = "# -*- coding: " + coding_ + " -*-\n";
+      coding = "# -*- coding: " + coding_ + " -*-\n";
   }
-  return coding + std::string("#\n") + "# Autogenerated by Thrift Compiler (" + THRIFT_VERSION
-         + ")\n" + "#\n" + "# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
-         + "#\n" + "#  options string: " + copy_options_ + "\n" + "#\n";
+  return coding + std::string("#\n") + "# Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+         + "#\n" + "# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "#\n"
+         + "#  options string: " + copy_options_ + "\n" + "#\n";
 }
 
 /**
@@ -485,9 +482,12 @@
   ss << "from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, "
         "TApplicationException"
      << '\n'
-     << "from thrift.protocol.TProtocol import TProtocolException" << '\n'
-     << "from thrift.TRecursive import fix_spec" << '\n'
-     << "from uuid import UUID" << '\n';
+     << "from thrift.protocol.TProtocol import TProtocolException"
+     << '\n'
+     << "from thrift.TRecursive import fix_spec"
+     << '\n'
+     << "from uuid import UUID"
+     << '\n';
   if (gen_enum_) {
     ss << "from enum import IntEnum" << '\n';
   }
@@ -540,8 +540,10 @@
 
   f_types_ << '\n'
            << '\n'
-           << "class " << tenum->get_name() << (base_class.empty() ? "" : "(" + base_class + ")")
-           << ":" << '\n';
+           << "class " << tenum->get_name()
+           << (base_class.empty() ? "" : "(" + base_class + ")")
+           << ":"
+           << '\n';
   indent_up();
   generate_python_docstring(f_types_, tenum);
 
@@ -649,7 +651,7 @@
         throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
       }
       indent(out) << render_const_value(g_type_string, v_iter->first) << ": "
-                  << render_const_value(field_type, v_iter->second) << "," << '\n';
+          << render_const_value(field_type, v_iter->second) << "," << '\n';
     }
     indent_down();
     indent(out) << "})";
@@ -665,7 +667,7 @@
     map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
     for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
       indent(out) << render_const_value(ktype, v_iter->first) << ": "
-                  << render_const_value(vtype, v_iter->second) << "," << '\n';
+          << render_const_value(vtype, v_iter->second) << "," << '\n';
     }
     indent_down();
     indent(out) << "}";
@@ -719,7 +721,7 @@
  * definitions are grouped at the end of the file to enable co-recursive structs.
  */
 void t_py_generator::generate_forward_declaration(t_struct* tstruct) {
-  generate_py_struct(tstruct, tstruct->is_xception());
+    generate_py_struct(tstruct, tstruct->is_xception());
 }
 
 /**
@@ -746,6 +748,7 @@
   generate_py_struct_definition(f_types_, tstruct, is_exception);
 }
 
+
 /**
  * Generate the thrift_spec for a struct
  * For example,
@@ -819,7 +822,7 @@
   } else if (gen_dynamic_) {
     if (is_immutable(tstruct)) {
       out << "(" << gen_dynbaseclass_frozen_ << ")";
-    } else {
+    } else  {
       out << "(" << gen_dynbaseclass_ << ")";
     }
   } else if (gen_newstyle_) {
@@ -892,9 +895,9 @@
       if (is_immutable(tstruct)) {
         if (gen_enum_ && type->is_enum()) {
           indent(out) << "super(" << tstruct->get_name() << ", self).__setattr__('"
-                      << (*m_iter)->get_name() << "', " << (*m_iter)->get_name() << " if hasattr("
-                      << (*m_iter)->get_name() << ", 'value') else " << type_name(type)
-                      << ".__members__.get(" << (*m_iter)->get_name() << "))" << '\n';
+                      << (*m_iter)->get_name() << "', " << (*m_iter)->get_name()
+                      << " if hasattr("  << (*m_iter)->get_name() << ", 'value') else "
+                      << type_name(type) << ".__members__.get(" << (*m_iter)->get_name() << "))" << '\n';
         } else if (gen_newstyle_ || gen_dynamic_) {
           indent(out) << "super(" << tstruct->get_name() << ", self).__setattr__('"
                       << (*m_iter)->get_name() << "', " << (*m_iter)->get_name() << ")" << '\n';
@@ -923,10 +926,11 @@
     // trivial because we know which fields are user-provided, without slots we need to build a
     // way to know which fields are user-provided.
     if (gen_slots_ && !gen_dynamic_) {
-      out << indent() << "if args[0] not in self.__slots__:" << '\n';
-      indent_up();
-      out << indent() << "super().__setattr__(*args)" << '\n' << indent() << "return" << '\n';
-      indent_down();
+        out << indent() << "if args[0] not in self.__slots__:" << '\n';
+        indent_up();
+        out << indent() << "super().__setattr__(*args)" << '\n'
+            << indent() << "return" << '\n';
+        indent_down();
     }
     out << indent() << "raise TypeError(\"can't modify immutable instance\")" << '\n';
     indent_down();
@@ -940,10 +944,11 @@
     // trivial because we know which fields are user-provided, without slots we need to build a
     // way to know which fields are user-provided.
     if (gen_slots_ && !gen_dynamic_) {
-      out << indent() << "if args[0] not in self.__slots__:" << '\n';
-      indent_up();
-      out << indent() << "super().__delattr__(*args)" << '\n' << indent() << "return" << '\n';
-      indent_down();
+        out << indent() << "if args[0] not in self.__slots__:" << '\n';
+        indent_up();
+        out << indent() << "super().__delattr__(*args)" << '\n'
+            << indent() << "return" << '\n';
+        indent_down();
     }
     out << indent() << "raise TypeError(\"can't modify immutable instance\")" << '\n';
     indent_down();
@@ -977,8 +982,7 @@
         t_type* type = (*m_iter)->get_type();
         if (type->is_enum()) {
           out << indent() << "if name == \"" << (*m_iter)->get_name() << "\":" << '\n'
-              << indent() << indent_str()
-              << "super().__setattr__(name, value if hasattr(value, 'value') else "
+              << indent() << indent_str() << "super().__setattr__(name, value if hasattr(value, 'value') else "
               << type_name(type) << ".__members__.get(value))" << '\n'
               << indent() << indent_str() << "return" << '\n';
         }
@@ -1018,10 +1022,8 @@
     // Equality and inequality methods that compare by value
     out << indent() << "def __eq__(self, other):" << '\n';
     indent_up();
-    out << indent()
-        << "return isinstance(other, self.__class__) and "
-           "self.__dict__ == other.__dict__"
-        << '\n';
+    out << indent() << "return isinstance(other, self.__class__) and "
+                       "self.__dict__ == other.__dict__" << '\n';
     indent_down();
     out << '\n';
 
@@ -1105,7 +1107,7 @@
         result << "None";
       }
       indent(out) << result.str() << '\n';
-    }
+     }
   }
 
   // Loop over reading in fields
@@ -1181,8 +1183,9 @@
   indent(out) << "if oprot._fast_encode is not None and self.thrift_spec is not None:" << '\n';
   indent_up();
 
-  indent(out) << "oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))"
-              << '\n';
+  indent(out)
+      << "oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))"
+      << '\n';
   indent(out) << "return" << '\n';
   indent_down();
 
@@ -1206,8 +1209,8 @@
   }
 
   // Write the struct map
-  out << indent() << "oprot.writeFieldStop()" << '\n'
-      << indent() << "oprot.writeStructEnd()" << '\n';
+  out << indent() << "oprot.writeFieldStop()" << '\n' << indent() << "oprot.writeStructEnd()"
+      << '\n';
 
   out << '\n';
 
@@ -1251,9 +1254,8 @@
 
   if (tservice->get_extends() != nullptr) {
     f_service_ << "import "
-               << get_real_py_module(tservice->get_extends()->get_program(), gen_twisted_,
-                                     package_prefix_)
-               << "." << tservice->get_extends()->get_name() << '\n';
+               << get_real_py_module(tservice->get_extends()->get_program(), gen_twisted_, package_prefix_) << "."
+               << tservice->get_extends()->get_name() << '\n';
   }
 
   f_service_ << "import logging" << '\n'
@@ -1283,7 +1285,8 @@
   generate_service_remote(tservice);
 
   // Close service file
-  f_service_ << "fix_spec(all_structs)" << '\n' << "del all_structs" << '\n';
+  f_service_ << "fix_spec(all_structs)" << '\n'
+             << "del all_structs" << '\n';
   f_service_.close();
 }
 
@@ -1442,8 +1445,8 @@
     }
   } else {
     if (gen_twisted_) {
-      f_service_ << indent() << extends << ".Client.__init__(self, transport, oprot_factory)"
-                 << '\n';
+      f_service_ << indent() << extends
+                 << ".Client.__init__(self, transport, oprot_factory)" << '\n';
     } else if (gen_tornado_) {
       f_service_ << indent() << extends
                  << ".Client.__init__(self, transport, iprot_factory, oprot_factory)" << '\n';
@@ -1454,9 +1457,9 @@
   indent_down();
 
   if (gen_tornado_ && extends.empty()) {
-    f_service_ << '\n'
-               << indent() << "@gen.engine" << '\n'
-               << indent() << "def _start_receiving(self):" << '\n';
+    f_service_ << '\n' <<
+      indent() << "@gen.engine" << '\n' <<
+      indent() << "def _start_receiving(self):" << '\n';
     indent_up();
     indent(f_service_) << "while True:" << '\n';
     indent_up();
@@ -1553,10 +1556,9 @@
       indent(f_service_) << "d.addCallbacks(" << '\n';
 
       indent_up();
-      f_service_ << indent() << "callback=self.cb_send_" << funname << "," << '\n'
-                 << indent() << "callbackArgs=(seqid,)," << '\n'
-                 << indent() << "errback=self.eb_send_" << funname << "," << '\n'
-                 << indent() << "errbackArgs=(seqid,))" << '\n';
+      f_service_ << indent() << "callback=self.cb_send_" << funname << "," << '\n' << indent()
+                 << "callbackArgs=(seqid,)," << '\n' << indent() << "errback=self.eb_send_"
+                 << funname << "," << '\n' << indent() << "errbackArgs=(seqid,))" << '\n';
       indent_down();
 
       indent(f_service_) << "return d" << '\n';
@@ -1567,9 +1569,8 @@
       indent_up();
       if ((*f_iter)->is_oneway()) {
         // if one-way, fire the deferred & remove it from _reqs
-        f_service_ << indent() << "d = self._reqs.pop(seqid)" << '\n'
-                   << indent() << "d.callback(None)" << '\n'
-                   << indent() << "return d" << '\n';
+        f_service_ << indent() << "d = self._reqs.pop(seqid)" << '\n' << indent()
+                   << "d.callback(None)" << '\n' << indent() << "return d" << '\n';
       } else {
         f_service_ << indent() << "return self._reqs[seqid]" << '\n';
       }
@@ -1579,9 +1580,8 @@
       // add an errback to fail the request if the call to send_<> raised an exception
       indent(f_service_) << "def eb_send_" << funname << "(self, f, seqid):" << '\n';
       indent_up();
-      f_service_ << indent() << "d = self._reqs.pop(seqid)" << '\n'
-                 << indent() << "d.errback(f)" << '\n'
-                 << indent() << "return d" << '\n';
+      f_service_ << indent() << "d = self._reqs.pop(seqid)" << '\n' << indent() << "d.errback(f)"
+                 << '\n' << indent() << "return d" << '\n';
       indent_down();
     }
 
@@ -1611,13 +1611,12 @@
 
     // Write to the stream
     if (gen_twisted_ || gen_tornado_) {
-      f_service_ << indent() << "args.write(oprot)" << '\n'
-                 << indent() << "oprot.writeMessageEnd()" << '\n'
-                 << indent() << "oprot.trans.flush()" << '\n';
+      f_service_ << indent() << "args.write(oprot)" << '\n' << indent() << "oprot.writeMessageEnd()"
+                 << '\n' << indent() << "oprot.trans.flush()" << '\n';
     } else {
-      f_service_ << indent() << "args.write(self._oprot)" << '\n'
-                 << indent() << "self._oprot.writeMessageEnd()" << '\n'
-                 << indent() << "self._oprot.trans.flush()" << '\n';
+      f_service_ << indent() << "args.write(self._oprot)" << '\n' << indent()
+                 << "self._oprot.writeMessageEnd()" << '\n' << indent()
+                 << "self._oprot.trans.flush()" << '\n';
     }
 
     indent_down();
@@ -1632,7 +1631,8 @@
       } else {
         t_struct noargs(program_);
         t_function recv_function((*f_iter)->get_returntype(),
-                                 string("recv_") + (*f_iter)->get_name(), &noargs);
+                                 string("recv_") + (*f_iter)->get_name(),
+                                 &noargs);
         f_service_ << indent() << "def " << function_signature(&recv_function) << ":" << '\n';
       }
       indent_up();
@@ -1643,27 +1643,23 @@
         f_service_ << indent() << "d = self._reqs.pop(rseqid)" << '\n';
       } else if (gen_tornado_) {
       } else {
-        f_service_ << indent() << "iprot = self._iprot" << '\n'
-                   << indent() << "(fname, mtype, rseqid) = iprot.readMessageBegin()" << '\n';
+        f_service_ << indent() << "iprot = self._iprot" << '\n' << indent()
+                   << "(fname, mtype, rseqid) = iprot.readMessageBegin()" << '\n';
       }
 
       f_service_ << indent() << "if mtype == TMessageType.EXCEPTION:" << '\n'
                  << indent() << indent_str() << "x = TApplicationException()" << '\n';
 
       if (gen_twisted_) {
-        f_service_ << indent() << indent_str() << "x.read(iprot)" << '\n'
-                   << indent() << indent_str() << "iprot.readMessageEnd()" << '\n'
-                   << indent() << indent_str() << "return d.errback(x)" << '\n'
-                   << indent() << "result = " << resultname << "()" << '\n'
-                   << indent() << "result.read(iprot)" << '\n'
-                   << indent() << "iprot.readMessageEnd()" << '\n';
+        f_service_ << indent() << indent_str() << "x.read(iprot)" << '\n' << indent()
+                   << indent_str() << "iprot.readMessageEnd()" << '\n' << indent() << indent_str() << "return d.errback(x)"
+                   << '\n' << indent() << "result = " << resultname << "()" << '\n' << indent()
+                   << "result.read(iprot)" << '\n' << indent() << "iprot.readMessageEnd()" << '\n';
       } else {
-        f_service_ << indent() << indent_str() << "x.read(iprot)" << '\n'
-                   << indent() << indent_str() << "iprot.readMessageEnd()" << '\n'
-                   << indent() << indent_str() << "raise x" << '\n'
-                   << indent() << "result = " << resultname << "()" << '\n'
-                   << indent() << "result.read(iprot)" << '\n'
-                   << indent() << "iprot.readMessageEnd()" << '\n';
+        f_service_ << indent() << indent_str() << "x.read(iprot)" << '\n' << indent()
+                   << indent_str() << "iprot.readMessageEnd()" << '\n' << indent() << indent_str() << "raise x" << '\n'
+                   << indent() << "result = " << resultname << "()" << '\n' << indent()
+                   << "result.read(iprot)" << '\n' << indent() << "iprot.readMessageEnd()" << '\n';
       }
 
       // Careful, only return _result if not a void function
@@ -1738,31 +1734,28 @@
   ofstream_with_content_based_conditional_update f_remote;
   f_remote.open(f_remote_name.c_str());
 
-  f_remote << "#!/usr/bin/env python" << '\n'
-           << py_autogen_comment() << '\n'
-           << "import sys" << '\n'
-           << "import pprint" << '\n'
-           << "if sys.version_info[0] > 2:" << '\n'
-           << indent_str() << "from urllib.parse import urlparse" << '\n'
-           << "else:" << '\n'
-           << indent_str() << "from urlparse import urlparse" << '\n'
-           << "from thrift.transport import TTransport, TSocket, TSSLSocket, THttpClient" << '\n'
-           << "from thrift.protocol.TBinaryProtocol import TBinaryProtocol" << '\n'
-           << '\n';
+  f_remote <<
+    "#!/usr/bin/env python" << '\n' <<
+    py_autogen_comment() << '\n' <<
+    "import sys" << '\n' <<
+    "import pprint" << '\n' <<
+    "if sys.version_info[0] > 2:" << '\n' <<
+    indent_str() << "from urllib.parse import urlparse" << '\n' <<
+    "else:" << '\n' <<
+    indent_str() << "from urlparse import urlparse" << '\n' <<
+    "from thrift.transport import TTransport, TSocket, TSSLSocket, THttpClient" << '\n' <<
+    "from thrift.protocol.TBinaryProtocol import TBinaryProtocol" << '\n' << '\n';
 
-  f_remote << "from " << module_ << " import " << service_name_ << '\n'
-           << "from " << module_ << ".ttypes import *" << '\n'
-           << '\n';
+  f_remote <<
+    "from " << module_ << " import " << service_name_ << '\n' <<
+    "from " << module_ << ".ttypes import *" << '\n' << '\n';
 
-  f_remote << "if len(sys.argv) <= 1 or sys.argv[1] == '--help':" << '\n'
-           << indent_str() << "print('')" << '\n'
-           << indent_str()
-           << "print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] "
-              "[-novalidate] [-ca_certs certs] [-keyfile keyfile] [-certfile certfile] function "
-              "[arg1 [arg2...]]')"
-           << '\n'
-           << indent_str() << "print('')" << '\n'
-           << indent_str() << "print('Functions:')" << '\n';
+  f_remote <<
+    "if len(sys.argv) <= 1 or sys.argv[1] == '--help':" << '\n' <<
+    indent_str() << "print('')" << '\n' <<
+    indent_str() << "print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] [-novalidate] [-ca_certs certs] [-keyfile keyfile] [-certfile certfile] function [arg1 [arg2...]]')" << '\n' <<
+    indent_str() << "print('')" << '\n' <<
+    indent_str() << "print('Functions:')" << '\n';
   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
     f_remote << indent_str() << "print('  " << (*f_iter)->get_returntype()->get_name() << " "
              << (*f_iter)->get_name() << "(";
@@ -1847,18 +1840,15 @@
            << indent_str() << "transport = THttpClient.THttpClient(host, port, uri)" << '\n'
            << "else:" << '\n'
            << indent_str() << "if ssl:" << '\n'
-           << indent_str() << indent_str()
-           << "socket = TSSLSocket.TSSLSocket(host, port, "
+           << indent_str() << indent_str() << "socket = TSSLSocket.TSSLSocket(host, port, "
               "validate=validate, ca_certs=ca_certs, keyfile=keyfile, certfile=certfile)"
            << '\n'
            << indent_str() << "else:" << '\n'
            << indent_str() << indent_str() << "socket = TSocket.TSocket(host, port)" << '\n'
            << indent_str() << "if framed:" << '\n'
-           << indent_str() << indent_str() << "transport = TTransport.TFramedTransport(socket)"
-           << '\n'
+           << indent_str() << indent_str() << "transport = TTransport.TFramedTransport(socket)" << '\n'
            << indent_str() << "else:" << '\n'
-           << indent_str() << indent_str() << "transport = TTransport.TBufferedTransport(socket)"
-           << '\n'
+           << indent_str() << indent_str() << "transport = TTransport.TBufferedTransport(socket)" << '\n'
            << "protocol = TBinaryProtocol(transport)" << '\n'
            << "client = " << service_name_ << ".Client(protocol)" << '\n'
            << "transport.open()" << '\n'
@@ -1881,8 +1871,8 @@
     f_remote << "if cmd == '" << (*f_iter)->get_name() << "':" << '\n';
     indent_up();
     f_remote << indent() << "if len(args) != " << num_args << ":" << '\n'
-             << indent() << indent_str() << "print('" << (*f_iter)->get_name() << " requires "
-             << num_args << " args')" << '\n'
+             << indent() << indent_str() << "print('" << (*f_iter)->get_name() << " requires " << num_args
+             << " args')" << '\n'
              << indent() << indent_str() << "sys.exit(1)" << '\n'
              << indent() << "pp.pprint(client." << (*f_iter)->get_name() << "(";
     indent_down();
@@ -1918,11 +1908,12 @@
 #ifndef _MSC_VER
 
   // Make file executable, love that bitwise OR action
-  chmod(f_remote_name.c_str(), S_IRUSR | S_IWUSR | S_IXUSR
+  chmod(f_remote_name.c_str(),
+        S_IRUSR | S_IWUSR | S_IXUSR
 #ifndef _WIN32
-                                   | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
+        | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
 #endif
-  );
+        );
 
 #endif // _MSC_VER
 }
@@ -1983,7 +1974,7 @@
 
   f_service_ << indent() << "def on_message_begin(self, func):" << '\n';
   indent_up();
-  f_service_ << indent() << "self._on_message_begin = func" << '\n';
+    f_service_ << indent() << "self._on_message_begin = func" << '\n';
   indent_down();
   f_service_ << '\n';
 
@@ -1994,7 +1985,7 @@
   f_service_ << indent() << "(name, type, seqid) = iprot.readMessageBegin()" << '\n';
   f_service_ << indent() << "if self._on_message_begin:" << '\n';
   indent_up();
-  f_service_ << indent() << "self._on_message_begin(name, type, seqid)" << '\n';
+    f_service_ << indent() << "self._on_message_begin(name, type, seqid)" << '\n';
   indent_down();
 
   // TODO(mcslee): validate message
@@ -2053,9 +2044,8 @@
   (void)tservice;
   // Open function
   if (gen_tornado_) {
-    f_service_ << indent() << "@gen.coroutine" << '\n'
-               << indent() << "def process_" << tfunction->get_name()
-               << "(self, seqid, iprot, oprot):" << '\n';
+    f_service_ << indent() << "@gen.coroutine" << '\n' << indent() << "def process_"
+               << tfunction->get_name() << "(self, seqid, iprot, oprot):" << '\n';
   } else {
     f_service_ << indent() << "def process_" << tfunction->get_name()
                << "(self, seqid, iprot, oprot):" << '\n';
@@ -2066,9 +2056,8 @@
   string argsname = tfunction->get_name() + "_args";
   string resultname = tfunction->get_name() + "_result";
 
-  f_service_ << indent() << "args = " << argsname << "()" << '\n'
-             << indent() << "args.read(iprot)" << '\n'
-             << indent() << "iprot.readMessageEnd()" << '\n';
+  f_service_ << indent() << "args = " << argsname << "()" << '\n' << indent() << "args.read(iprot)"
+             << '\n' << indent() << "iprot.readMessageEnd()" << '\n';
 
   t_struct* xs = tfunction->get_xceptions();
   const std::vector<t_field*>& xceptions = xs->get_members();
@@ -2173,8 +2162,8 @@
                  << indent() << "oprot.trans.flush()" << '\n';
     } else {
       f_service_ << indent() << "except Exception:" << '\n'
-                 << indent() << indent_str() << "logging.exception('Exception in oneway handler')"
-                 << '\n';
+                 << indent() << indent_str()
+                 << "logging.exception('Exception in oneway handler')" << '\n';
     }
     indent_down();
 
@@ -2232,8 +2221,8 @@
                  << '\n';
     } else {
       f_service_ << indent() << "except Exception:" << '\n'
-                 << indent() << indent_str() << "logging.exception('Exception in oneway handler')"
-                 << '\n';
+                 << indent() << indent_str()
+                 << "logging.exception('Exception in oneway handler')" << '\n';
     }
 
     if (!tfunction->is_oneway()) {
@@ -2278,7 +2267,8 @@
     }
 
     indent_down();
-    f_service_ << indent() << "except TTransport.TTransportException:" << '\n'
+    f_service_ << indent()
+               << "except TTransport.TTransportException:" << '\n'
                << indent() << indent_str() << "raise" << '\n';
 
     if (!tfunction->is_oneway()) {
@@ -2312,8 +2302,7 @@
                  << indent() << "oprot.trans.flush()" << '\n';
     } else {
       f_service_ << indent() << "except Exception:" << '\n'
-                 << indent() << indent_str() << "logging.exception('Exception in oneway handler')"
-                 << '\n';
+                 << indent() << indent_str() << "logging.exception('Exception in oneway handler')" << '\n';
     }
 
     // Close function
@@ -2324,7 +2313,9 @@
 /**
  * Deserializes a field of any type.
  */
-void t_py_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) {
+void t_py_generator::generate_deserialize_field(ostream& out,
+                                                t_field* tfield,
+                                                string prefix) {
   t_type* type = get_true_type(tfield->get_type());
 
   if (type->is_void()) {
@@ -2348,11 +2339,10 @@
       case t_base_type::TYPE_STRING:
         if (type->is_binary()) {
           out << "readBinary()";
-        } else if (!gen_utf8strings_) {
+        } else if(!gen_utf8strings_) {
           out << "readString()";
         } else {
-          out << "readString().decode('utf-8', errors='replace') if sys.version_info[0] == 2 else "
-                 "iprot.readString()";
+          out << "readString().decode('utf-8', errors='replace') if sys.version_info[0] == 2 else iprot.readString()";
         }
         break;
       case t_base_type::TYPE_BOOL:
@@ -2389,7 +2379,8 @@
     }
     out << '\n';
   } else {
-    printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(),
+    printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+           tfield->get_name().c_str(),
            type->get_name().c_str());
   }
 }
@@ -2423,20 +2414,20 @@
 
   // Declare variables, read header
   if (ttype->is_map()) {
-    out << indent() << prefix << " = {}" << '\n'
-        << indent() << "(" << ktype << ", " << vtype << ", " << size << ") = iprot.readMapBegin()"
-        << '\n';
+    out << indent() << prefix << " = {}" << '\n' << indent() << "(" << ktype << ", " << vtype
+        << ", " << size << ") = iprot.readMapBegin()" << '\n';
   } else if (ttype->is_set()) {
-    out << indent() << prefix << " = set()" << '\n'
-        << indent() << "(" << etype << ", " << size << ") = iprot.readSetBegin()" << '\n';
+    out << indent() << prefix << " = set()" << '\n' << indent() << "(" << etype << ", " << size
+        << ") = iprot.readSetBegin()" << '\n';
   } else if (ttype->is_list()) {
-    out << indent() << prefix << " = []" << '\n'
-        << indent() << "(" << etype << ", " << size << ") = iprot.readListBegin()" << '\n';
+    out << indent() << prefix << " = []" << '\n' << indent() << "(" << etype << ", " << size
+        << ") = iprot.readListBegin()" << '\n';
   }
 
   // For loop iterates over elements
   string i = tmp("_i");
-  indent(out) << "for " << i << " in range(" << size << "):" << '\n';
+  indent(out) <<
+    "for " << i << " in range(" << size << "):" << '\n';
 
   indent_up();
 
@@ -2499,7 +2490,9 @@
 /**
  * Write a list element
  */
-void t_py_generator::generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix) {
+void t_py_generator::generate_deserialize_list_element(ostream& out,
+                                                       t_list* tlist,
+                                                       string prefix) {
   string elem = tmp("_elem");
   t_field felem(tlist->get_elem_type(), elem);
 
@@ -2544,8 +2537,7 @@
         } else if (!gen_utf8strings_) {
           out << "writeString(" << name << ")";
         } else {
-          out << "writeString(" << name << ".encode('utf-8') if sys.version_info[0] == 2 else "
-              << name << ")";
+          out << "writeString(" << name << ".encode('utf-8') if sys.version_info[0] == 2 else " << name << ")";
         }
         break;
       case t_base_type::TYPE_BOOL:
@@ -2573,7 +2565,7 @@
         throw "compiler error: no Python name for base type " + t_base_type::t_base_name(tbase);
       }
     } else if (type->is_enum()) {
-      if (gen_enum_) {
+      if (gen_enum_){
         out << "writeI32(" << name << ".value)";
       } else {
         out << "writeI32(" << name << ")";
@@ -2581,8 +2573,10 @@
     }
     out << '\n';
   } else {
-    printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", prefix.c_str(),
-           tfield->get_name().c_str(), type->get_name().c_str());
+    printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+           prefix.c_str(),
+           tfield->get_name().c_str(),
+           type->get_name().c_str());
   }
 }
 
@@ -2749,7 +2743,6 @@
   } else {
     result << "None";
   }
-
   return result.str();
 }
 
@@ -2842,8 +2835,7 @@
     return get_real_py_module(program, gen_twisted_, package_prefix_) + "." + ttype->get_name();
   }
   if (program != nullptr && program != program_) {
-    return get_real_py_module(program, gen_twisted_, package_prefix_) + ".ttypes."
-           + ttype->get_name();
+    return get_real_py_module(program, gen_twisted_, package_prefix_) + ".ttypes." + ttype->get_name();
   }
   return ttype->get_name();
 }
@@ -2970,12 +2962,12 @@
   }
 
   if (ttype->is_binary()) {
-    return "'BINARY'";
+    return  "'BINARY'";
   } else if (gen_utf8strings_ && ttype->is_base_type()
              && reinterpret_cast<t_base_type*>(ttype)->is_string()) {
     return "'UTF8'";
   } else if (ttype->is_base_type() || ttype->is_enum()) {
-    return "None";
+    return  "None";
   } else if (ttype->is_struct() || ttype->is_xception()) {
     return "[" + type_name(ttype) + ", None]";
   } else if (ttype->is_map()) {
@@ -3009,22 +3001,19 @@
     "    zope.interface:  Generate code for use with zope.interface.\n"
     "    twisted:         Generate Twisted-friendly RPC services.\n"
     "    tornado:         Generate code for use with Tornado.\n"
-    "    no_utf8strings:  Do not Encode/decode strings using utf8 in the generated code. Basically "
-    "no effect for Python 3.\n"
+    "    no_utf8strings:  Do not Encode/decode strings using utf8 in the generated code. Basically no effect for Python 3.\n"
     "    coding=CODING:   Add file encoding declare in generated file.\n"
     "    slots:           Generate code using slots for instance members.\n"
     "    dynamic:         Generate dynamic code, less code generated but slower.\n"
     "    dynbase=CLS      Derive generated classes from class CLS instead of TBase.\n"
-    "    dynfrozen=CLS    Derive generated immutable classes from class CLS instead of "
-    "TFrozenBase.\n"
+    "    dynfrozen=CLS    Derive generated immutable classes from class CLS instead of TFrozenBase.\n"
     "    dynexc=CLS       Derive generated exceptions from CLS instead of TExceptionBase.\n"
-    "    dynfrozenexc=CLS Derive generated immutable exceptions from CLS instead of "
-    "TFrozenExceptionBase.\n"
+    "    dynfrozenexc=CLS Derive generated immutable exceptions from CLS instead of TFrozenExceptionBase.\n"
     "    dynimport='from foo.bar import CLS'\n"
     "                     Add an import line to generated code to find the dynbase class.\n"
     "    package_prefix='top.package.'\n"
     "                     Package prefix for generated files.\n"
     "    old_style:       Deprecated. Generate old-style classes.\n"
-    "    enum:            Generates Python's IntEnum, connects thrift to python enums. Python 3.4 "
-    "and higher.\n"
-    "    type_hints:      Generate type hints in write method, including IntEnum generation.\n")
+    "    enum:            Generates Python's IntEnum, connects thrift to python enums. Python 3.4 and higher.\n"
+    "    type_hints:      Generate type hints and type checks in write method, including IntEnum generation.\n"
+)
diff --git a/test/py/Makefile.am b/test/py/Makefile.am
index b4be953..7e403d1 100644
--- a/test/py/Makefile.am
+++ b/test/py/Makefile.am
@@ -125,9 +125,9 @@
 
 gen-py-type_hints/%/__init__.py: ../%.thrift $(THRIFT)
 	test -d gen-py-type_hints || $(MKDIR_P) gen-py-type_hints
-	test ../v0.16/$(notdir $<) \
-	&& $(THRIFT) --gen py:type_hints -out gen-py-type_hints ../v0.16/$(notdir $<) \
-	|| $(THRIFT) --gen py:type_hints -out gen-py-type_hints $<
+	test ../$(notdir $<) \
+	&& $(THRIFT) --gen py:type_hints,enum -out gen-py-type_hints ../$(notdir $<) \
+	|| $(THRIFT) --gen py:type_hints,enum -out gen-py-type_hints $<
 
 clean-local:
 	$(RM) -r build
diff --git a/test/py/generate.cmake b/test/py/generate.cmake
index a439c44..80e7f51 100644
--- a/test/py/generate.cmake
+++ b/test/py/generate.cmake
@@ -14,7 +14,7 @@
 generate(${MY_PROJECT_DIR}/test/v0.16/ThriftTest.thrift py:dynamic gen-py-dynamic)
 generate(${MY_PROJECT_DIR}/test/v0.16/ThriftTest.thrift py:dynamic,slots gen-py-dynamicslots)
 generate(${MY_PROJECT_DIR}/test/v0.16/ThriftTest.thrift py:enum gen-py-enum)
-generate(${MY_PROJECT_DIR}/test/v0.16/ThriftTest.thrift py:type_hints gen-py-type_hints)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:type_hints,enum gen-py-type_hints)
 
 generate(${MY_PROJECT_DIR}/test/v0.16/DebugProtoTest.thrift py gen-py-default)
 generate(${MY_PROJECT_DIR}/test/v0.16/DebugProtoTest.thrift py:slots gen-py-slots)
@@ -23,7 +23,7 @@
 generate(${MY_PROJECT_DIR}/test/v0.16/DebugProtoTest.thrift py:dynamic gen-py-dynamic)
 generate(${MY_PROJECT_DIR}/test/v0.16/DebugProtoTest.thrift py:dynamic,slots gen-py-dynamicslots)
 generate(${MY_PROJECT_DIR}/test/v0.16/DebugProtoTest.thrift py:enum gen-py-enum)
-generate(${MY_PROJECT_DIR}/test/v0.16/DebugProtoTest.thrift py:type_hints gen-py-type_hints)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:type_hints,enum gen-py-type_hints)
 
 generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py gen-py-default)
 generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:slots gen-py-slots)
@@ -32,7 +32,7 @@
 generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:dynamic gen-py-dynamic)
 generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:dynamic,slots gen-py-dynamicslots)
 generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:enum gen-py-enum)
-generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:type_hints gen-py-type_hints)
+generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:type_hints,enum gen-py-type_hints)
 
 generate(${MY_PROJECT_DIR}/test/Recursive.thrift py gen-py-default)
 generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:slots gen-py-slots)
@@ -41,4 +41,4 @@
 generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:dynamic gen-py-dynamic)
 generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:dynamic,slots gen-py-dynamicslots)
 generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:enum gen-py-enum)
-generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:type_hints gen-py-type_hints)
+generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:type_hints,enum gen-py-type_hints)