Drop L specifier; reimplement tuple printing in C++
When you call repr() on a long in Python 2, it prints a long suffix.
This is annoying for tests which assert on the exact output. Use str()
instead.
But then there is a problem with Python 2's default tuple str() implementation,
where it calls repr() on its arguments rather than str(). This means that
if you have a tuple of longs, it will render as "(1L, 2L)" in Python 2.
To solve this problem, we just reimplement tuple printing in C++.
This is not a very robust fix (nested tuples, dictionaries, all these situations
will fail) but in practice it hits the cases that matter.
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
diff --git a/torch/csrc/jit/ir.cpp b/torch/csrc/jit/ir.cpp
index ac2b3e6..6d35397 100644
--- a/torch/csrc/jit/ir.cpp
+++ b/torch/csrc/jit/ir.cpp
@@ -4,6 +4,8 @@
#include "torch/csrc/utils/python_strings.h"
#include "torch/csrc/autograd/function.h"
+#include "pybind11/pybind11.h"
+
#include <iostream>
#include <unordered_map>
#include <unordered_set>
@@ -11,6 +13,8 @@
#include <stack>
#include <sstream>
+namespace py = pybind11;
+
namespace torch { namespace jit {
std::string getPythonName(const PyObject* obj, bool is_legacy) {
@@ -40,8 +44,42 @@
}
static std::ostream& operator<<(std::ostream & out, THPObjectPtr& obj) {
- THPObjectPtr repr { PyObject_Repr(obj.get()) };
- return out << THPUtils_unpackString(repr.get());
+ auto pyobj = py::handle(obj.get());
+ if (py::isinstance<py::tuple>(pyobj)) {
+ // This special-case for printing tuples handles a problem where
+ // str((2L, 3L)) outputs "(2L, 3L)" in Python 2 but "(2, 3)"
+ // in Python 3. In order to suppress the L-suffix, we must
+ // manually print the string ourselves, calling str() on the
+ // sub-elements.
+ //
+ // This is a fairly fragile fix (What if you have nested tuples
+ // in tuples? What if you have dictionaries?) but it seems to hit
+ // the cases that are triggered in practice in onnx-pytorch. Revisit
+ // this code if this is not the case.
+ //
+ // By the way, one non-solution for this problem is to monkeypatch
+ // tuple.__str__; this doesn't work because Python doesn't allow
+ // monkeypatching methods of built-in types.
+ auto pytuple = pyobj.cast<py::tuple>();
+ out << "(";
+ size_t i = 0;
+ for (auto& o : pytuple) {
+ if (i > 0) {
+ out << ", ";
+ }
+ THPObjectPtr str(py::str(o).release().ptr());
+ out << THPUtils_unpackString(str.get());
+ i++;
+ }
+ if (i == 1) {
+ out << ",";
+ }
+ out << ")";
+ return out;
+ } else {
+ THPObjectPtr str { PyObject_Str(obj.get()) };
+ return out << THPUtils_unpackString(str.get());
+ }
}
std::string PythonOp::name() {