pytorch/torch/csrc
Edward Yang 4d72538f80 Give Tensor a trivial (for now) metaclass _TensorMeta (#56147)
Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/56147

This is support of #55686, you can see the broader context of the metaclass in
a more complete PR #56017.  The short story is that in the future I want to
give Tensor a non-trivial metaclass, so to derisk the change first I give it a
trivial metaclass to shake out any bugs that might be caused by it.  The
metaclass shouldn't have any performance impact on Tensor as it only gets
invoked upon subclass creation.

By the way, it was totally not documented how to create metaclasses in the Python
C API, and it took a good bit of trial error to figure it out (and the answer is
now immortalized in https://stackoverflow.com/q/67077317/23845 -- the things
that I got wrong in earlier versions of the PR included setting tp_basicsize
incorrectly, incorrectly setting Py_TPFLAGS_HAVE_GC on the metaclass--you want
to leave it unset so that it inherits, and determining that tp_init is what
actually gets called when you construct a class, not tp_call as another
not-to-be-named StackOverflow question suggests).

Aside: Ordinarily, adding a metaclass to a class is a user visible change, as
it means that it is no longer valid to mixin another class with a different
metaclass. However, because _C._TensorBase is a C extension object, it will
typically conflict with most other metaclasses, so this is not BC breaking.

Signed-off-by: Edward Z. Yang <ezyang@fb.com>

Test Plan: Imported from OSS

Reviewed By: H-Huang

Differential Revision: D28028747

Pulled By: ezyang

fbshipit-source-id: c1e35a986aeb3db540c73d188f53dce951eeed33
2021-04-28 09:24:21 -07:00
..
api Add torch.linalg.svdvals (#56684) 2021-04-25 03:42:24 -07:00
autograd Give Tensor a trivial (for now) metaclass _TensorMeta (#56147) 2021-04-28 09:24:21 -07:00
cuda Remove C++17 only optional include (#56782) 2021-04-27 07:35:15 -07:00
deploy torch: Add cpython as a dependency for torch_python_obj (#56740) 2021-04-22 16:52:29 -07:00
distributed change ddpLoggingData struct to map or dict (#56641) 2021-04-28 06:43:25 -07:00
fx [FX] Adds C-level monkeypatching of torch.randn so that we can capture it during tracing. (#54060) 2021-04-01 07:34:31 -07:00
generic
jit [static runtime] binding for aten::norm_out (#56636) 2021-04-28 08:44:10 -07:00
multiprocessing
onnx
tensor Replace all direct cdata access with THPVariable_Unpack (#55799) 2021-04-15 08:57:04 -07:00
utils Fix: sparse_csr_tensor segfaults when crow_indices or col_indices are non-tensors (#56723) 2021-04-27 14:47:12 -07:00
copy_utils.h
CudaIPCTypes.cpp Fix SIGSEGV in CudaIPCTypes.cpp. (#53080) 2021-03-16 16:39:40 -07:00
CudaIPCTypes.h Fix SIGSEGV in CudaIPCTypes.cpp. (#53080) 2021-03-16 16:39:40 -07:00
DataLoader.cpp
DataLoader.h
Device.cpp
Device.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
dl.c
Dtype.cpp
Dtype.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
DynamicTypes.cpp Restore storage on meta tensors; increase meta coverage (#53973) 2021-03-29 08:37:46 -07:00
DynamicTypes.h
empty.c
Exceptions.cpp
Exceptions.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
Generator.cpp Replace all direct cdata access with THPVariable_Unpack (#55799) 2021-04-15 08:57:04 -07:00
Generator.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
Layout.cpp
Layout.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
MemoryFormat.cpp
MemoryFormat.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
Module.cpp Write mini dump on pybind exceptions (#55652) 2021-04-19 14:53:43 -07:00
Module.h
python_dimname.cpp Lint trailing newlines (#54737) 2021-03-30 13:09:52 -07:00
python_dimname.h Lint trailing newlines (#54737) 2021-03-30 13:09:52 -07:00
python_headers.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
PythonTypes.h
QScheme.cpp
QScheme.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
README.md
serialization.cpp
serialization.h
Size.cpp Replace all direct cdata access with THPVariable_Unpack (#55799) 2021-04-15 08:57:04 -07:00
Size.h
Storage.cpp
Storage.h
StorageDefs.h
Stream.cpp
Stream.h [BE] Make torch/csrc/jit/tensorexpr/ clang-tidy clean (#55628) 2021-04-08 19:44:14 -07:00
stub.c
THP_export.h
THP.h
TypeInfo.cpp
TypeInfo.h
Types.h
utils.cpp Revert D27448156: irange for size_t 2021-04-03 19:14:00 -07:00
utils.h
WindowsTorchApiMacro.h

csrc

The csrc directory contains all of the code concerned with integration with Python. This is in contrast to lib, which contains the Torch libraries that are Python agnostic. csrc depends on lib, but not vice versa.

There are a number of utilities for easing integration with Python which are worth knowing about, which we briefly describe here. But the most important gotchas:

  • DO NOT forget to take out the GIL with pybind11::gil_scoped_acquire before calling Python API or bringing a THPObjectPtr into scope.

  • Make sure you include Python.h first in your header files, before any system headers; otherwise, you will get error: "_XOPEN_SOURCE" redefined error. If you pay attention to warnings, you will see where you need to do this.

Notes

Note [Storage is not nullptr]

Historically, Torch supported nullptr storage, as a minor optimization to avoid having to allocate a storage object when it would be empty. However, this is actually a confusing special case to deal with, so by-in-large, PyTorch assumes that, in fact, storage is never nullptr.

One important case where this assumption is important is when tracking the CUDA device a tensor is stored in: this information is stored solely in the storage, so if a storage is nullptr, we lose this information.

Although storage is never nullptr, the data field of THStorage may be nullptr. This mostly occurs when we want to pre-allocate an output tensor struct, but then have it be resized and filled with data by some operator: there's no point in allocating data for it in this case!

Files

Exceptions.h

Frequently when working with the Python API, you may call a function which returns an error. In this case, we want to return directly to the Python interpreter, so that this exception can be propagated accordingly; however, because the Python API is C-based, what actually will happen is it will return control to whatever C++ code called it. Similarly, if we raise a C++ exception, prior to returning to the Python interpreter, we must set the Python error flags, so it turns into a C++ exception.

Moreover, when using the following macros, the generated warnings will be converted into python warnings that can be caught by the user.

Exceptions define helpers for two main cases:

  • For code where you write the python binding by hand, HANDLE_TH_ERRORS, END_HANDLE_TH_ERRORS and an exception class python_error. You call them like this:
// Entry point from Python interpreter
PyObject* run(PyObject* arg) {
  HANDLE_TH_ERRORS
  ...
  if (!x) throw python_error();
  // From c10/Exception.h
  TORCH_CHECK(cond, "cond was false here");
  TORCH_WARN("Warning message");
  ...
  END_HANDLE_TH_ERRORS
}

The HANDLE_TH_ERRORS macro will catch all exceptions and convert them into an appropriate Python signal. python_error is a special exception which doesn't contain any info, instead it says, "An error occurred in the Python API; if you return to the interpreter, Python will raise that exception, nothing else needs to be done."

  • For code that you bind using pybind, HANDLE_TH_ERRORS and END_HANDLE_TH_ERRORS_PYBIND can be used. They will work jointly with pybind error handling to raise pytorch errors and warnings natively and let pybind handle other errors. It can be used as:
// Function given to the pybind binding
at::Tensor foo(at::Tensor x) {
  HANDLE_TH_ERRORS
  ...
  if (!x) throw python_error();
  // pybind native error
  if (!x) throw py::value_error();
  // From c10/Exception.h
  TORCH_CHECK(cond, "cond was false here");
  TORCH_WARN("Warning message");
  ...
  END_HANDLE_TH_ERRORS_PYBIND
}

GIL

Whenever you make any calls to the Python API, you must have taken out the Python GIL, as none of these calls are thread safe. pybind11::gil_scoped_acquire is a RAII struct which handles taking and releasing the GIL. Use it like this:

void iWantToUsePython() {
  pybind11::gil_scoped_acquire gil;
  ...
}

In general, the compiler will NOT warn you if you use Python functionality without taking out the GIL, so DO NOT FORGET this call.

utils/object_ptr.h

THPPointer is a smart pointer class analogous to std::shared_ptr, but which is overloaded to handle reference counting scheme of various objects which are not based on shared_ptr. The most important overloads are:

  • PyObject (so important we've aliased it as THPObjectPtr), which hooks into Python reference counting. (By the way, that means you MUST take out the GIL before bringing one of these into scope!)

  • The various TH tensor and storage types (e.g., THTensor), which hook into TH's reference counting. (TH's reference counting IS thread safe, no locks necessary.)