File: framelocals_mapping.cpp

package info (click to toggle)
pytorch 2.6.0%2Bdfsg-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 161,672 kB
  • sloc: python: 1,278,832; cpp: 900,322; ansic: 82,710; asm: 7,754; java: 3,363; sh: 2,811; javascript: 2,443; makefile: 597; ruby: 195; xml: 84; objc: 68
file content (128 lines) | stat: -rw-r--r-- 3,918 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <torch/csrc/dynamo/framelocals_mapping.h>

#include <torch/csrc/dynamo/cpython_defs.h>
#include <torch/csrc/dynamo/cpython_includes.h>
#include <torch/csrc/dynamo/debug_macros.h>
#include <torch/csrc/utils/pybind.h>

#include <internal/pycore_code.h>

#if IS_PYTHON_3_11_PLUS

// Our own version of PyFrame_GetLocals.
// Also combines functionality from frame_init_get_vars and frame_get_var.
// PyFrame_GetLocals:
// https://github.com/python/cpython/blob/0325a8a8cdba6c091bcbbb3c995f3bf1d1217012/Objects/frameobject.c#L1213
// frame_init_get_vars:
// https://github.com/python/cpython/blob/0325a8a8cdba6c091bcbbb3c995f3bf1d1217012/Objects/frameobject.c#L1136
// frame_get_var:
// https://github.com/python/cpython/blob/0325a8a8cdba6c091bcbbb3c995f3bf1d1217012/Objects/frameobject.c#L1162
// PyFrame_GetLocals returns the frame locals dict.
// frame_init_get_vars initializes free variables from the closure.
// frame_get_var fetches the variable value from the frame given the index
// NOTE: hidden variables are not included.
// Returns a new reference.
PyObject* get_framelocals_mapping(_PyInterpreterFrame* frame) {
  if (!frame->stacktop) {
    return py::dict().release().ptr();
  }

  PyCodeObject* co = F_CODE(frame);
  py::dict mapping;

  auto update_mapping = [&](int i, PyObject* value) {
    _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);

    if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) {
      return;
    }

#if IS_PYTHON_3_12_PLUS
    if (kind & CO_FAST_HIDDEN) {
      return;
    }
#endif

    if (kind & CO_FAST_FREE) {
      CHECK(value != nullptr && PyCell_Check(value));
      value = PyCell_GET(value);
    }

    if (value != nullptr) {
      py::str name =
          py::cast<py::str>(PyTuple_GET_ITEM(co->co_localsplusnames, i));
      mapping[name] = py::cast<py::object>(value);
    }
  };

  int offset = co->co_nlocalsplus - co->co_nfreevars;
  for (int i = 0; i < offset; i++) {
    update_mapping(i, frame->localsplus[i]);
  }
  // Get references to closure variables
  PyObject* closure = ((PyFunctionObject*)FUNC(frame))->func_closure;
  for (int i = 0; i < co->co_nfreevars; ++i) {
    update_mapping(offset + i, PyTuple_GET_ITEM(closure, i));
  }

  // NOTE no need to move the instruction pointer to after COPY_FREE_VARS
  // since we don't actually copy free vars from the closure to the frame
  // localsplus.

  return mapping.release().ptr();
}

#else

// Based on
// https://github.com/python/cpython/blob/5f24da9d75bb0150781b17ee4706e93e6bb364ea/Objects/frameobject.c#L1016
PyObject* get_framelocals_mapping(PyFrameObject* frame) {
  PyCodeObject* co = F_CODE(frame);
  py::dict mapping;

  auto update_mapping =
      [&](PyObject* names, int i, PyObject* value, bool deref) {
        py::str name = py::cast<py::str>(PyTuple_GET_ITEM(names, i));
        if (deref) {
          CHECK(value != nullptr && PyCell_Check(value));
          value = PyCell_GET(value);
        }
        if (value == nullptr) {
          mapping.attr("pop")(name, py::none());
        } else {
          mapping[name] = py::cast<py::object>(value);
        }
      };

  // locals
  int nlocals = PyTuple_GET_SIZE(co->co_varnames);
  if (nlocals > co->co_nlocals) {
    nlocals = co->co_nlocals;
  }
  for (int i = 0; i < nlocals; i++) {
    update_mapping(co->co_varnames, i, frame->f_localsplus[i], false);
  }

  // cellvars
  int ncells = PyTuple_GET_SIZE(co->co_cellvars);
  for (int i = 0; i < ncells; i++) {
    update_mapping(
        co->co_cellvars, i, frame->f_localsplus[co->co_nlocals + i], true);
  }

  // freevars
  if (co->co_flags & CO_OPTIMIZED) {
    int nfree = PyTuple_GET_SIZE(co->co_freevars);
    for (int i = 0; i < nfree; i++) {
      update_mapping(
          co->co_freevars,
          i,
          frame->f_localsplus[co->co_nlocals + ncells + i],
          true);
    }
  }

  return mapping.release().ptr();
}

#endif