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