Python3 type hints
args not optional type hint
UUID generator support
Remove runtime type check
diff --git a/compiler/cpp/src/thrift/generate/t_py_generator.cc b/compiler/cpp/src/thrift/generate/t_py_generator.cc
index d3f23b6..ddca9fe 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;
@@ -63,6 +63,7 @@
gen_twisted_ = false;
gen_dynamic_ = false;
gen_enum_ = false;
+ gen_type_hints_ = false;
coding_ = "";
gen_dynbaseclass_ = "";
gen_dynbaseclass_exc_ = "";
@@ -70,61 +71,70 @@
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) {
+ gen_type_hints_ = true;
+ gen_enum_ = true;
} else {
throw "unknown option py:" + iter->first;
}
@@ -145,9 +155,7 @@
}
}
- std::string indent_str() const override {
- return " ";
- }
+ std::string indent_str() const override { return " "; }
/**
* Init and close methods
@@ -200,9 +208,7 @@
* 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 = "");
@@ -212,9 +218,7 @@
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 = "");
@@ -252,18 +256,26 @@
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);
+ 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);
std::string type_to_enum(t_type* ttype);
std::string type_to_spec_args(t_type* ttype);
+ std::string type_to_py_type(t_type* type);
+ std::string member_hint(t_type* type, t_field::e_req req);
+ std::string arg_hint(t_type* type);
+ std::string func_hint(t_type* type);
static bool is_valid_namespace(const std::string& sub_namespace) {
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()) {
@@ -279,7 +291,8 @@
}
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.
@@ -292,7 +305,6 @@
}
private:
-
/**
* True if we should generate new-style classes.
*/
@@ -300,8 +312,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_;
@@ -314,6 +326,11 @@
bool gen_slots_;
+ /**
+ * True if we should generate classes type hints and type checks in write methods.
+ */
+ bool gen_type_hints_;
+
std::string copy_options_;
/**
@@ -357,11 +374,13 @@
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]));
}
};
@@ -424,10 +443,9 @@
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';
}
/**
@@ -448,11 +466,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";
}
/**
@@ -460,13 +478,16 @@
*/
string t_py_generator::py_imports() {
ostringstream ss;
+ if (gen_type_hints_) {
+ ss << "from __future__ import annotations" << '\n' << "import typing" << '\n';
+ }
+
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 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';
}
@@ -519,10 +540,8 @@
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);
@@ -597,6 +616,9 @@
out << emit_double_as_string(value->get_double());
}
break;
+ case t_base_type::TYPE_UUID:
+ out << "UUID(\"" << get_escaped_string(value) << "\")";
+ break;
default:
throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
}
@@ -627,7 +649,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) << "})";
@@ -643,7 +665,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) << "}";
@@ -697,7 +719,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());
}
/**
@@ -724,7 +746,6 @@
generate_py_struct_definition(f_types_, tstruct, is_exception);
}
-
/**
* Generate the thrift_spec for a struct
* For example,
@@ -798,7 +819,7 @@
} else if (gen_dynamic_) {
if (is_immutable(tstruct)) {
out << "(" << gen_dynbaseclass_frozen_ << ")";
- } else {
+ } else {
out << "(" << gen_dynbaseclass_ << ")";
}
} else if (gen_newstyle_) {
@@ -871,9 +892,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';
@@ -882,7 +903,9 @@
<< "'] = " << (*m_iter)->get_name() << '\n';
}
} else {
- indent(out) << "self." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << '\n';
+ indent(out) << "self." << (*m_iter)->get_name()
+ << member_hint((*m_iter)->get_type(), (*m_iter)->get_req()) << " = "
+ << (*m_iter)->get_name() << '\n';
}
}
@@ -900,11 +923,10 @@
// 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();
@@ -918,11 +940,10 @@
// 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();
@@ -956,7 +977,8 @@
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';
}
@@ -996,8 +1018,10 @@
// 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';
@@ -1081,7 +1105,7 @@
result << "None";
}
indent(out) << result.str() << '\n';
- }
+ }
}
// Loop over reading in fields
@@ -1153,13 +1177,12 @@
indent(out) << "def write(self, oprot):" << '\n';
indent_up();
-
+ indent(out) << "self.validate()" << '\n';
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();
@@ -1183,8 +1206,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';
@@ -1228,8 +1251,9 @@
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'
@@ -1259,8 +1283,7 @@
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();
}
@@ -1419,8 +1442,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';
@@ -1431,9 +1454,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();
@@ -1530,9 +1553,10 @@
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';
@@ -1543,8 +1567,9 @@
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';
}
@@ -1554,13 +1579,14 @@
// 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();
}
f_service_ << '\n';
- indent(f_service_) << "def send_" << function_signature(*f_iter, false) << ":" << '\n';
+ indent(f_service_) << "def send_" << function_signature(*f_iter, false, true) << ":" << '\n';
indent_up();
std::string argsname = (*f_iter)->get_name() + "_args";
@@ -1585,12 +1611,13 @@
// 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();
@@ -1605,8 +1632,7 @@
} 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();
@@ -1617,23 +1643,27 @@
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
@@ -1708,28 +1738,31 @@
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() << "(";
@@ -1814,15 +1847,18 @@
<< 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'
@@ -1845,8 +1881,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();
@@ -1882,12 +1918,11 @@
#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
}
@@ -1948,7 +1983,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';
@@ -1959,7 +1994,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
@@ -2018,8 +2053,9 @@
(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';
@@ -2030,8 +2066,9 @@
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();
@@ -2136,8 +2173,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();
@@ -2195,8 +2232,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()) {
@@ -2241,8 +2278,7 @@
}
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()) {
@@ -2276,7 +2312,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';
}
// Close function
@@ -2287,9 +2324,7 @@
/**
* 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()) {
@@ -2313,10 +2348,11 @@
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:
@@ -2337,6 +2373,9 @@
case t_base_type::TYPE_DOUBLE:
out << "readDouble()";
break;
+ case t_base_type::TYPE_UUID:
+ out << "readUuid()";
+ break;
default:
throw "compiler error: no Python name for base type " + t_base_type::t_base_name(tbase);
}
@@ -2350,8 +2389,7 @@
}
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());
}
}
@@ -2385,20 +2423,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();
@@ -2461,9 +2499,7 @@
/**
* 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);
@@ -2508,7 +2544,8 @@
} 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:
@@ -2529,11 +2566,14 @@
case t_base_type::TYPE_DOUBLE:
out << "writeDouble(" << name << ")";
break;
+ case t_base_type::TYPE_UUID:
+ out << "writeUuid(" << name << ")";
+ break;
default:
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 << ")";
@@ -2541,10 +2581,8 @@
}
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());
}
}
@@ -2702,12 +2740,16 @@
*/
string t_py_generator::declare_argument(t_field* tfield) {
std::ostringstream result;
- result << tfield->get_name() << "=";
+ t_field::e_req req = tfield->get_req();
+ result << tfield->get_name() << member_hint(tfield->get_type(), req);
+
+ result << " = ";
if (tfield->get_value() != nullptr) {
result << render_field_default_value(tfield);
} else {
result << "None";
}
+
return result.str();
}
@@ -2731,7 +2773,7 @@
* @param tfunction Function definition
* @return String of rendered function definition
*/
-string t_py_generator::function_signature(t_function* tfunction, bool interface) {
+string t_py_generator::function_signature(t_function* tfunction, bool interface, bool send_part) {
vector<string> pre;
vector<string> post;
string signature = tfunction->get_name() + "(";
@@ -2741,6 +2783,10 @@
}
signature += argument_list(tfunction->get_arglist(), &pre, &post) + ")";
+ if (!send_part) {
+ signature += func_hint(tfunction->get_returntype());
+ }
+
return signature;
}
@@ -2771,6 +2817,7 @@
result += ", ";
}
result += (*f_iter)->get_name();
+ result += arg_hint((*f_iter)->get_type());
}
if (post) {
for (s_iter = post->begin(); s_iter != post->end(); ++s_iter) {
@@ -2795,13 +2842,84 @@
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();
}
+string t_py_generator::arg_hint(t_type* type) {
+ if (gen_type_hints_) {
+ return ": " + type_to_py_type(type);
+ }
+
+ return "";
+}
+
+string t_py_generator::member_hint(t_type* type, t_field::e_req req) {
+ if (gen_type_hints_) {
+ if (req != t_field::T_REQUIRED) {
+ return ": typing.Optional[" + type_to_py_type(type) + "]";
+ } else {
+ return ": " + type_to_py_type(type);
+ }
+ }
+
+ return "";
+}
+
+string t_py_generator::func_hint(t_type* type) {
+ if (gen_type_hints_) {
+ return " -> " + type_to_py_type(type);
+ }
+
+ return "";
+}
+
/**
- * Converts the parse type to a Python tyoe
+ * Converts the parse type to a Python type
+ */
+string t_py_generator::type_to_py_type(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_binary()) {
+ return "bytes";
+ }
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "None";
+ case t_base_type::TYPE_STRING:
+ return "str";
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ return "int";
+ case t_base_type::TYPE_DOUBLE:
+ return "float";
+ case t_base_type::TYPE_UUID:
+ return "UUID";
+ }
+ } else if (type->is_enum() || type->is_struct() || type->is_xception()) {
+ return type_name(type);
+ } else if (type->is_map()) {
+ return "dict[" + type_to_py_type(((t_map*)type)->get_key_type()) + ", "
+ + type_to_py_type(((t_map*)type)->get_val_type()) + "]";
+ } else if (type->is_set()) {
+ return "set[" + type_to_py_type(((t_set*)type)->get_elem_type()) + "]";
+ } else if (type->is_list()) {
+ return "list[" + type_to_py_type(((t_list*)type)->get_elem_type()) + "]";
+ }
+
+ throw "INVALID TYPE IN type_to_py_type: " + type->get_name();
+}
+
+/**
+ * Converts the parse type to a Python type enum
*/
string t_py_generator::type_to_enum(t_type* type) {
type = get_true_type(type);
@@ -2825,6 +2943,8 @@
return "TType.I64";
case t_base_type::TYPE_DOUBLE:
return "TType.DOUBLE";
+ case t_base_type::TYPE_UUID:
+ return "TType.UUID";
default:
throw "compiler error: unhandled type";
}
@@ -2850,12 +2970,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()) {
@@ -2883,25 +3003,28 @@
return "Python";
}
-
THRIFT_REGISTER_GENERATOR(
py,
"Python",
" 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"
-)
+ " 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")