• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/python/lib/core/py_seq_tensor.h"
17 
18 #include "tensorflow/core/framework/tensor.h"
19 #include "tensorflow/core/framework/tensor_shape.h"
20 #include "tensorflow/core/framework/types.h"
21 #include "tensorflow/core/lib/core/errors.h"
22 #include "tensorflow/core/lib/core/stringpiece.h"
23 #include "tensorflow/core/lib/strings/str_util.h"
24 #include "tensorflow/core/platform/types.h"
25 #include "tensorflow/python/lib/core/numpy.h"
26 #include "tensorflow/python/lib/core/py_util.h"
27 #include "tensorflow/python/lib/core/safe_ptr.h"
28 
29 namespace tensorflow {
30 namespace {
31 
PyIsInstance(PyObject * obj,PyTypeObject * t)32 inline bool PyIsInstance(PyObject* obj, PyTypeObject* t) {
33   return PyObject_IsInstance(obj, reinterpret_cast<PyObject*>(t));
34 }
35 
PyType(PyObject * obj)36 inline PyObject* PyType(PyObject* obj) {
37   return reinterpret_cast<PyObject*>(obj->ob_type);
38 }
39 
IsPyString(PyObject * obj)40 bool IsPyString(PyObject* obj) {
41   return PyBytes_Check(obj) || PyUnicode_Check(obj);
42 }
43 
IsPyInt(PyObject * obj)44 bool IsPyInt(PyObject* obj) {
45 #if PY_MAJOR_VERSION >= 3
46   return PyLong_Check(obj) ||
47          PyIsInstance(obj, &PyIntegerArrType_Type);  // NumPy integers
48 #else
49   return PyInt_Check(obj) || PyLong_Check(obj) ||
50          PyIsInstance(obj, &PyIntegerArrType_Type);  // NumPy integers
51 #endif
52 }
53 
IsPyDouble(PyObject * obj)54 bool IsPyDouble(PyObject* obj) {
55   return PyIsInstance(obj, &PyDoubleArrType_Type);  // NumPy double type.
56 }
57 
IsNumpyHalf(PyObject * obj)58 bool IsNumpyHalf(PyObject* obj) {
59   return PyIsInstance(obj, &PyHalfArrType_Type);
60 }
61 
IsPyFloat(PyObject * obj)62 bool IsPyFloat(PyObject* obj) {
63   return PyFloat_Check(obj) ||
64          PyIsInstance(obj, &PyFloatingArrType_Type);  // NumPy float types
65 }
66 
67 // If the input is a zero dimensional PyArray return it converted to a scalar.
68 // Otherwise return the input and increment its reference count.
69 // Users must Py_DECREF the output of this method.
ZeroDimArrayToScalar(PyObject * obj)70 PyObject* ZeroDimArrayToScalar(PyObject* obj) {
71   if (PyArray_IsZeroDim(obj) && !PyArray_IsScalar(obj, Generic)) {
72     auto pyarray_obj = reinterpret_cast<PyArrayObject*>(obj);
73     obj = PyArray_ToScalar(PyArray_DATA(pyarray_obj), pyarray_obj);
74   } else {
75     Py_INCREF(obj);
76   }
77   return obj;
78 }
79 
80 // Converts Python object `c` that should hold a Python string into a
81 // C++ string in *out.  Returns nullptr on success, or a message on error.
82 // Defined below, but forward declared here for use in PyRepr.
83 const char* ConvertOneString(PyObject* v, string* out);
84 
PyRepr(PyObject * obj)85 string PyRepr(PyObject* obj) {
86   if (obj == nullptr) {
87     return "<null>";
88   }
89   Safe_PyObjectPtr repr_obj = make_safe(PyObject_Repr(obj));
90   if (repr_obj) {
91     string repr_str;
92     if (ConvertOneString(repr_obj.get(), &repr_str) == nullptr) {
93       return repr_str;
94     }
95   }
96   return "<error computing repr()>";
97 }
98 
IsPyDimension(PyObject * obj)99 bool IsPyDimension(PyObject* obj) {
100   const char* tp_name = obj->ob_type->tp_name;
101   if (strcmp(tp_name, "Dimension") != 0) return false;
102   bool ret = str_util::EndsWith(
103       PyRepr(PyType(obj)),
104       "tensorflow.python.framework.tensor_shape.Dimension'>");
105   return ret;
106 }
107 
108 // Sets *elem to a NEW reference to an element in seq on success.
109 // REQUIRES: PySequence_Check(seq) && PySequence_Length(seq) > 0.
SampleElementFromSequence(PyObject * seq,PyObject ** elem)110 Status SampleElementFromSequence(PyObject* seq, PyObject** elem) {
111   *elem = PySequence_GetItem(seq, 0);
112   if (*elem != nullptr) return Status::OK();
113   // seq may implement the sequence protocol (i.e., implement __getitem__)
114   // but may legitimately not have a 0-th element (__getitem__(self, 0)
115   // raises a KeyError). For example:
116   // seq = pandas.Series([0, 1, 2], index=[2, 4, 6])
117   //
118   // We don't actually care for the element at key 0, any element will do
119   // for inferring the element types. All elements are expected to
120   // have the same type, and this will be validated when converting
121   // to an EagerTensor.
122   PyErr_Clear();
123   Safe_PyObjectPtr iter(PyObject_GetIter(seq));
124   if (PyErr_Occurred()) {
125     return errors::InvalidArgument("Cannot infer dtype of a ",
126                                    Py_TYPE(seq)->tp_name,
127                                    " object: ", PyExceptionFetch());
128   }
129   *elem = PyIter_Next(iter.get());
130   if (PyErr_Occurred()) {
131     return errors::InvalidArgument(
132         "Cannot infer dtype of a ", Py_TYPE(seq)->tp_name,
133         " object, as iter(<object>).next() failed: ", PyExceptionFetch());
134   }
135   if (*elem == nullptr) {
136     return errors::InvalidArgument("Cannot infer dtype of a ",
137                                    Py_TYPE(seq)->tp_name,
138                                    " object since it is an empty sequence");
139   }
140   return Status::OK();
141 }
142 
InferShapeAndType(PyObject * obj,TensorShape * shape,DataType * dtype)143 Status InferShapeAndType(PyObject* obj, TensorShape* shape, DataType* dtype) {
144   std::vector<Safe_PyObjectPtr> refs_to_clean;
145   while (true) {
146     // Convert any zero dimensional numpy arrays to scalars first of all.
147     // We also have to make sure a reference to the safe_obj is kept.
148     obj = ZeroDimArrayToScalar(obj);
149     refs_to_clean.push_back(make_safe(obj));
150     // We test strings first, in case a string is considered a sequence.
151     if (IsPyString(obj)) {
152       *dtype = DT_STRING;
153     } else if (PySequence_Check(obj)) {
154       auto length = PySequence_Length(obj);
155       if (length > 0) {
156         shape->AddDim(length);
157         PyObject* elem = nullptr;
158         TF_RETURN_IF_ERROR(SampleElementFromSequence(obj, &elem));
159         obj = elem;
160         refs_to_clean.push_back(make_safe(obj));
161         continue;
162       } else if (length == 0) {
163         shape->AddDim(length);
164         *dtype = DT_INVALID;  // Invalid dtype for empty tensors.
165       } else {
166         // The sequence does not have a valid length (PySequence_Length < 0).
167         if (PyErr_Occurred()) {
168           // PySequence_Length failed and set an exception. Fetch the message
169           // and convert it to a failed status.
170           return errors::InvalidArgument(PyExceptionFetch());
171         } else {
172           // This is almost certainly dead code: PySequence_Length failed but
173           // did not set an exception.
174           return errors::InvalidArgument(
175               "Attempted to convert an invalid sequence to a Tensor.");
176         }
177       }
178     } else if (IsPyDouble(obj)) {
179       *dtype = DT_DOUBLE;
180     } else if (IsNumpyHalf(obj)) {
181       *dtype = DT_HALF;
182     } else if (IsPyFloat(obj)) {
183       *dtype = DT_FLOAT;
184     } else if (PyBool_Check(obj) || PyIsInstance(obj, &PyBoolArrType_Type)) {
185       // Have to test for bool before int, since IsInt(True/False) == true.
186       *dtype = DT_BOOL;
187     } else if (IsPyInt(obj)) {
188       *dtype = DT_INT64;
189     } else if (IsPyDimension(obj)) {
190       *dtype = DT_INT64;
191     } else if (PyComplex_Check(obj) ||
192                PyIsInstance(obj, &PyComplexFloatingArrType_Type)) {  // NumPy
193       *dtype = DT_COMPLEX128;
194     } else {
195       return errors::InvalidArgument("Attempt to convert a value (",
196                                      PyRepr(obj),
197                                      ") with an unsupported type (",
198                                      PyRepr(PyType(obj)), ") to a Tensor.");
199     }
200     return Status::OK();
201   }
202 }
203 
204 // Error messages
205 
206 const char ErrorConverting[] =
207     "Error while converting Python sequence to Tensor.";
208 const char ErrorRectangular[] =
209     "Can't convert non-rectangular Python sequence to Tensor.";
210 const char ErrorMixedTypes[] =
211     "Can't convert Python sequence with mixed types to Tensor.";
212 const char ErrorOutOfRange[] =
213     "Can't convert Python sequence with out-of-range integer to Tensor.";
214 const char ErrorOutOfRangeDouble[] =
215     "Can't convert Python sequence with a value out of range for a "
216     "double-precision float.";
217 const char ErrorConvertingUnicodeString[] =
218     "Error converting unicode string while converting Python sequence to "
219     "Tensor.";
220 const char ErrorFoundInt64[] =
221     "Can't convert Python sequence with out-of-range integer to int32 Tensor.";
222 const char ErrorFoundFloat[] =
223     "Can't convert Python sequence with floating point values to integer "
224     "Tensor.";
225 
226 // Template for defining a function for recursively convering obj into
227 // an array of TYPE using the conversion function CONVERT.
228 // Note that these helper functions require shape.dims() >= 1.
229 
230 #define DEFINE_HELPER(FUNCTION, TYPE, TYPE_ENUM, CONVERT)                 \
231   const char* FUNCTION##Helper(PyObject* obj, const TensorShape& shape,   \
232                                TYPE** buf) {                              \
233     if (TF_PREDICT_FALSE(obj == nullptr)) {                               \
234       return ErrorConverting;                                             \
235     }                                                                     \
236     if (shape.dims() > 1) {                                               \
237       /* Iterate over outer dim, and recursively convert each element. */ \
238       const int64 s = shape.dim_size(0);                                  \
239       Safe_PyObjectPtr seq = make_safe(PySequence_Fast(obj, ""));         \
240       if (TF_PREDICT_FALSE(seq == nullptr)) return ErrorRectangular;      \
241       if (TF_PREDICT_FALSE(s != PySequence_Fast_GET_SIZE(seq.get()))) {   \
242         return ErrorRectangular;                                          \
243       }                                                                   \
244       TensorShape rest = shape;                                           \
245       rest.RemoveDim(0);                                                  \
246       for (int64 i = 0; i < s; ++i) {                                     \
247         const char* error = FUNCTION##Helper(                             \
248             PySequence_Fast_GET_ITEM(seq.get(), i), rest, buf);           \
249         if (TF_PREDICT_FALSE(error != nullptr)) return error;             \
250       }                                                                   \
251     } else {                                                              \
252       Safe_PyObjectPtr seq = make_safe(PySequence_Fast(obj, ""));         \
253       if (TF_PREDICT_FALSE(seq == nullptr)) return ErrorRectangular;      \
254       const int64 s = shape.dim_size(0);                                  \
255       if (TF_PREDICT_FALSE(s != PySequence_Fast_GET_SIZE(seq.get()))) {   \
256         return ErrorRectangular;                                          \
257       }                                                                   \
258       PyObject** l = PySequence_Fast_ITEMS(seq.get());                    \
259       for (int64 i = 0; i < s; ++i) {                                     \
260         auto scalar = ZeroDimArrayToScalar(l[i]);                         \
261         const char* error = CONVERT(scalar, *buf);                        \
262         Py_DECREF(scalar);                                                \
263         if (TF_PREDICT_FALSE(error != nullptr)) return error;             \
264         ++*buf;                                                           \
265       }                                                                   \
266     }                                                                     \
267     return nullptr;                                                       \
268   }                                                                       \
269   const char* FUNCTION(PyObject* obj, const TensorShape& shape,           \
270                        Tensor* dest) {                                    \
271     /* TODO(josh11b): Allocator & attributes? */                            \
272     Tensor result(TYPE_ENUM, shape);                                      \
273     if (shape.dims() == 0) { /* Scalar case */                            \
274       TYPE value;                                                         \
275       auto scalar = ZeroDimArrayToScalar(obj);                            \
276       const char* error = CONVERT(scalar, &value);                        \
277       Py_DECREF(scalar);                                                  \
278       if (error != nullptr) return error;                                 \
279       result.scalar<TYPE>()() = value;                                    \
280     } else {                                                              \
281       TYPE* buf = result.flat<TYPE>().data();                             \
282       const char* error = FUNCTION##Helper(obj, shape, &buf);             \
283       if (error != nullptr) return error;                                 \
284     }                                                                     \
285     *dest = result;                                                       \
286     return nullptr;                                                       \
287   }
288 
289 // Int support
290 
ConvertOneInt64(PyObject * v,int64 * out)291 const char* ConvertOneInt64(PyObject* v, int64* out) {
292 #if PY_MAJOR_VERSION < 3
293   if (TF_PREDICT_TRUE(PyInt_Check(v))) {
294     *out = PyInt_AS_LONG(v);
295     return nullptr;
296   }
297 #endif
298   if (TF_PREDICT_TRUE(PyLong_Check(v) || IsPyDimension(v))) {
299     int overflow = 0;
300     // Have to use LongLong for 64 bits, since long is 32 bits on Windows.
301     *out = PyLong_AsLongLongAndOverflow(v, &overflow);
302     if (TF_PREDICT_FALSE(overflow)) return ErrorOutOfRange;
303     return nullptr;
304   }
305   if (PyIsInstance(v, &PyIntegerArrType_Type)) {  // NumPy integers
306 #if PY_MAJOR_VERSION < 3
307     Safe_PyObjectPtr as_int = make_safe(PyNumber_Int(v));
308 #else
309     Safe_PyObjectPtr as_int = make_safe(PyNumber_Long(v));
310 #endif
311     return ConvertOneInt64(as_int.get(), out);
312   }
313   if (IsPyFloat(v)) return ErrorFoundFloat;
314   return ErrorMixedTypes;
315 }
316 
317 DEFINE_HELPER(ConvertInt64, int64, DT_INT64, ConvertOneInt64);
318 
ConvertOneInt32(PyObject * v,int32 * out)319 const char* ConvertOneInt32(PyObject* v, int32* out) {
320   int64 i;
321 #if PY_MAJOR_VERSION < 3
322   if (TF_PREDICT_TRUE(PyInt_Check(v))) {
323     i = PyInt_AS_LONG(v);
324   } else
325 #endif
326       if (PyLong_Check(v) || IsPyDimension(v)) {
327     int overflow = 0;
328     // Have to use LongLong for 64 bits, since long is 32 bits on Windows.
329     i = PyLong_AsLongLongAndOverflow(v, &overflow);
330     if (TF_PREDICT_FALSE(overflow)) return ErrorOutOfRange;
331   } else if (PyIsInstance(v, &PyIntegerArrType_Type)) {  // NumPy integers
332 #if PY_MAJOR_VERSION < 3
333     Safe_PyObjectPtr as_int = make_safe(PyNumber_Int(v));
334 #else
335     Safe_PyObjectPtr as_int = make_safe(PyNumber_Long(v));
336 #endif
337     return ConvertOneInt32(as_int.get(), out);
338   } else if (IsPyFloat(v)) {
339     return ErrorFoundFloat;
340   } else {
341     return ErrorMixedTypes;
342   }
343   *out = static_cast<uint32>(static_cast<uint64>(i));
344   // Check for 32-bit overflow.
345   if (TF_PREDICT_FALSE(i != *out)) return ErrorFoundInt64;
346   return nullptr;
347 }
348 
349 DEFINE_HELPER(ConvertInt32, int32, DT_INT32, ConvertOneInt32);
350 
351 // Floating-point support
352 
353 template <class T>
ConvertOneFloat(PyObject * v,T * out)354 const char* ConvertOneFloat(PyObject* v, T* out) {
355   if (PyErr_Occurred()) {
356     return nullptr;
357   }
358   if (TF_PREDICT_TRUE(PyFloat_Check(v))) {
359     double as_double = PyFloat_AsDouble(v);
360     // Handle infinity.
361     if (as_double == std::numeric_limits<double>::infinity()) {
362       *out = std::numeric_limits<T>::infinity();
363       return nullptr;
364     } else if (as_double == -1 * std::numeric_limits<double>::infinity()) {
365       *out = -1 * std::numeric_limits<T>::infinity();
366       return nullptr;
367     }
368     // Check for overflow.
369     if (as_double > std::numeric_limits<T>::max() ||
370         as_double < std::numeric_limits<T>::lowest()) {
371       return ErrorOutOfRangeDouble;
372     }
373     *out = static_cast<T>(as_double);
374     return nullptr;
375   }
376 #if PY_MAJOR_VERSION < 3
377   if (PyInt_Check(v)) {
378     *out = PyInt_AS_LONG(v);
379     return nullptr;
380   }
381 #endif
382   if (PyLong_Check(v)) {
383     *out = PyLong_AsDouble(v);
384     if (PyErr_Occurred()) return ErrorOutOfRangeDouble;
385     return nullptr;
386   }
387   if (PyIsInstance(v, &PyFloatingArrType_Type)) {  // NumPy float types
388     Safe_PyObjectPtr as_float = make_safe(PyNumber_Float(v));
389     if (PyErr_Occurred()) {
390       return nullptr;
391     }
392     return ConvertOneFloat<T>(as_float.get(), out);
393   }
394   if (PyIsInstance(v, &PyIntegerArrType_Type)) {  // NumPy integers
395 #if PY_MAJOR_VERSION < 3
396     Safe_PyObjectPtr as_int = make_safe(PyNumber_Int(v));
397 #else
398     Safe_PyObjectPtr as_int = make_safe(PyNumber_Long(v));
399 #endif
400     if (PyErr_Occurred()) {
401       return nullptr;
402     }
403     return ConvertOneFloat<T>(as_int.get(), out);
404   }
405   return ErrorMixedTypes;
406 }
407 
408 DEFINE_HELPER(ConvertDouble, double, DT_DOUBLE, ConvertOneFloat<double>);
409 DEFINE_HELPER(ConvertFloat, float, DT_FLOAT, ConvertOneFloat<float>);
410 
ConvertOneNumpyHalf(PyObject * v,Eigen::half * out)411 const char* ConvertOneNumpyHalf(PyObject* v, Eigen::half* out) {
412   // NOTE(nareshmodi): Is there a way to convert to C double without the
413   // intermediate Python double? This will help with ConvertOneFloat as well.
414   Safe_PyObjectPtr as_float = make_safe(PyNumber_Float(v));
415   double v_double = PyFloat_AS_DOUBLE(as_float.get());
416   *out = Eigen::half(v_double);
417 
418   return nullptr;
419 }
420 DEFINE_HELPER(ConvertNumpyHalf, Eigen::half, DT_HALF, ConvertOneNumpyHalf);
421 
422 // String support
423 
ConvertOneString(PyObject * v,string * out)424 const char* ConvertOneString(PyObject* v, string* out) {
425   if (PyBytes_Check(v)) {
426     out->assign(PyBytes_AS_STRING(v), PyBytes_GET_SIZE(v));
427     return nullptr;
428   }
429   if (PyUnicode_Check(v)) {
430 #if PY_MAJOR_VERSION >= 3
431     Py_ssize_t size;
432     const char* str = PyUnicode_AsUTF8AndSize(v, &size);
433     if (str == nullptr) return ErrorConvertingUnicodeString;
434     out->assign(str, size);
435     return nullptr;
436 #else
437     PyObject* py_str = PyUnicode_AsUTF8String(v);
438     if (py_str == nullptr) return ErrorConvertingUnicodeString;
439     out->assign(PyBytes_AS_STRING(py_str), PyBytes_GET_SIZE(py_str));
440     Py_DECREF(py_str);
441     return nullptr;
442 #endif
443   }
444   return ErrorMixedTypes;
445 }
446 
447 DEFINE_HELPER(ConvertString, string, DT_STRING, ConvertOneString);
448 
449 // Complex support
450 
ConvertOneComplex(PyObject * v,complex128 * out)451 const char* ConvertOneComplex(PyObject* v, complex128* out) {
452   if (PyComplex_Check(v)) {
453     *out = complex128(PyComplex_RealAsDouble(v), PyComplex_ImagAsDouble(v));
454     return nullptr;
455   } else if (PyIsInstance(v, &PyComplexFloatingArrType_Type)) {  // NumPy
456     auto as_complex = PyComplex_AsCComplex(v);
457     *out = complex128(as_complex.real, as_complex.imag);
458     return nullptr;
459   }
460   return ErrorMixedTypes;
461 }
462 
463 DEFINE_HELPER(ConvertComplex, complex128, DT_COMPLEX128, ConvertOneComplex);
464 
465 // Bool support
466 
ConvertOneBool(PyObject * v,bool * out)467 const char* ConvertOneBool(PyObject* v, bool* out) {
468   if (v == Py_True) {
469     *out = true;
470   } else if (v == Py_False) {
471     *out = false;
472   } else if (PyIsInstance(v, &PyBoolArrType_Type)) {  // NumPy
473     *out = PyObject_IsTrue(v);
474   } else {
475     return ErrorMixedTypes;
476   }
477   return nullptr;
478 }
479 
480 DEFINE_HELPER(ConvertBool, bool, DT_BOOL, ConvertOneBool);
481 
482 #undef DEFINE_HELPER
483 
484 }  // namespace
485 
486 #define RETURN_STRING_AS_STATUS(...)                             \
487   do {                                                           \
488     const char* _error = (__VA_ARGS__);                          \
489     if (TF_PREDICT_TRUE(_error == nullptr)) return Status::OK(); \
490     return errors::InvalidArgument(_error);                      \
491   } while (0)
492 
PySeqToTensor(PyObject * obj,PyObject * dtype,Tensor * ret)493 Status PySeqToTensor(PyObject* obj, PyObject* dtype, Tensor* ret) {
494   DataType infer_dtype;
495   TensorShape shape;
496   TF_RETURN_IF_ERROR(InferShapeAndType(obj, &shape, &infer_dtype));
497   DataType requested_dtype = DT_INVALID;
498   if (dtype != Py_None) {
499     int32 dtype_as_int = -1;
500     if (ConvertOneInt32(dtype, &dtype_as_int) == nullptr) {
501       requested_dtype = static_cast<DataType>(dtype_as_int);
502     }
503   }
504   // NOTE(josh11b): If don't successfully convert to the requested type,
505   // we just try instead to create a tensor of the inferred type and
506   // let the caller convert it to the requested type using a cast
507   // operation.
508   switch (requested_dtype) {
509     case DT_FLOAT:
510       if (ConvertFloat(obj, shape, ret) == nullptr) return Status::OK();
511       break;
512 
513     case DT_DOUBLE:
514       if (ConvertDouble(obj, shape, ret) == nullptr) return Status::OK();
515       break;
516 
517     case DT_HALF:
518       RETURN_STRING_AS_STATUS(ConvertNumpyHalf(obj, shape, ret));
519 
520     case DT_INT64:
521       if (ConvertInt64(obj, shape, ret) == nullptr) return Status::OK();
522       break;
523 
524     case DT_INT32:
525       if (ConvertInt32(obj, shape, ret) == nullptr) return Status::OK();
526       break;
527 
528     case DT_COMPLEX128:
529       if (ConvertComplex(obj, shape, ret) == nullptr) return Status::OK();
530       break;
531 
532     case DT_STRING:
533       if (ConvertString(obj, shape, ret) == nullptr) return Status::OK();
534       break;
535 
536     case DT_BOOL:
537       if (ConvertBool(obj, shape, ret) == nullptr) return Status::OK();
538       break;
539 
540     default:
541       break;
542   }
543   switch (infer_dtype) {
544     case DT_FLOAT:
545       // TODO(josh11b): Handle mixed floats and complex numbers?
546       if (requested_dtype == DT_INVALID) {
547         // TensorFlow uses float32s to represent floating point numbers
548         // by default (for space and speed over using doubles).
549         RETURN_STRING_AS_STATUS(ConvertFloat(obj, shape, ret));
550       } else {
551         // We are going to do a cast to the user's requested dtype
552         // after this.  We use doubles for this intermediate result so
553         // we don't lose precision that might be representable in the
554         // final type.
555         RETURN_STRING_AS_STATUS(ConvertDouble(obj, shape, ret));
556       }
557 
558     case DT_DOUBLE:
559       RETURN_STRING_AS_STATUS(ConvertDouble(obj, shape, ret));
560 
561     case DT_HALF:
562       RETURN_STRING_AS_STATUS(ConvertNumpyHalf(obj, shape, ret));
563 
564     case DT_INT64:
565       if (requested_dtype == DT_INVALID) {
566         const char* error = ConvertInt32(obj, shape, ret);
567         if (error == ErrorFoundInt64) {
568           error = ConvertInt64(obj, shape, ret);
569         }
570         if (error == ErrorFoundFloat) {
571           error = ConvertFloat(obj, shape, ret);
572         }
573         // TODO(josh11b): May also want to fall back to using doubles if
574         // error == ErrorOutOfRange?
575         RETURN_STRING_AS_STATUS(error);
576       } else {
577         const char* error = ConvertInt64(obj, shape, ret);
578         if (error == ErrorFoundFloat) {
579           error = ConvertDouble(obj, shape, ret);
580         }
581         RETURN_STRING_AS_STATUS(error);
582       }
583 
584     case DT_STRING:
585       RETURN_STRING_AS_STATUS(ConvertString(obj, shape, ret));
586 
587     case DT_COMPLEX128:
588       RETURN_STRING_AS_STATUS(ConvertComplex(obj, shape, ret));
589 
590     case DT_BOOL:
591       RETURN_STRING_AS_STATUS(ConvertBool(obj, shape, ret));
592 
593     case DT_INVALID:  // Only occurs for empty tensors.
594       *ret = Tensor(requested_dtype == DT_INVALID ? DT_FLOAT : requested_dtype,
595                     shape);
596       return Status::OK();
597 
598     default:
599       return errors::Unimplemented("Missing Python -> Tensor conversion for ",
600                                    DataTypeString(infer_dtype));
601   }
602 
603   return Status::OK();
604 }
605 
606 }  // namespace tensorflow
607