@@ -1353,6 +1353,60 @@ std::unique_ptr<GuardManager> make_guard_manager(
1353
1353
return std::make_unique<GuardManager>(root);
1354
1354
}
1355
1355
1356
+ /* *
1357
+ * Represents __getattr__ acccessor.
1358
+ */
1359
+ class GetAttrGuardAccessor : public GuardAccessor {
1360
+ public:
1361
+ GetAttrGuardAccessor (
1362
+ RootGuardManager* root,
1363
+ py::str name,
1364
+ py::handle example_value)
1365
+ : GuardAccessor(root, name, example_value), _attr_name(name.ptr()) {}
1366
+
1367
+ // NB: Intentional duplication between check_nopybind and
1368
+ // check_verbose_nopybind.
1369
+ bool check_nopybind (PyObject* obj) override { // borrowed ref
1370
+ PyObject* x = PyObject_GetAttr (obj, _attr_name); // new ref
1371
+ if (x == nullptr ) {
1372
+ // Attribute absent, clear the exception and return false.
1373
+ PyErr_Clear ();
1374
+ return false ;
1375
+ }
1376
+ bool result = _guard_manager->check_nopybind (x);
1377
+ Py_DECREF (x);
1378
+ return result;
1379
+ }
1380
+
1381
+ GuardDebugInfo check_verbose_nopybind (
1382
+ PyObject* obj) override { // borrowed ref
1383
+ PyObject* x = PyObject_GetAttr (obj, _attr_name); // new ref
1384
+ if (x == nullptr ) {
1385
+ // Attribute absent, clear the exception and return false.
1386
+ PyErr_Clear ();
1387
+ return GuardDebugInfo (
1388
+ false ,
1389
+ std::string (" get attr failed for attr name " ) +
1390
+ py::str (_attr_name).cast <std::string>(),
1391
+ 0 );
1392
+ }
1393
+ GuardDebugInfo result = _guard_manager->check_verbose_nopybind (x);
1394
+ Py_DECREF (x);
1395
+ return result;
1396
+ }
1397
+
1398
+ std::string repr () const override {
1399
+ // Helpful when priting GuardManager tree structure.
1400
+ return " GetAttrGuardAccessor(" + py::str (_attr_name).cast <std::string>() +
1401
+ " )" ;
1402
+ }
1403
+
1404
+ private:
1405
+ // no need of py::object here because the attr_name is already passed on to
1406
+ // the base class as accessor_key which is a py::object.
1407
+ PyObject* _attr_name;
1408
+ };
1409
+
1356
1410
} // namespace
1357
1411
1358
1412
static void * _torchinductor_pyobject_tensor_data_ptr (PyObject* obj) {
@@ -1458,6 +1512,10 @@ PyObject* torch_c_dynamo_guards_init() {
1458
1512
py::class_<GuardAccessor, std::unique_ptr<GuardAccessor>>(
1459
1513
py_m, " GuardAccessor" )
1460
1514
.def (" repr" , &GuardAccessor::repr);
1515
+ py::class_<
1516
+ GetAttrGuardAccessor,
1517
+ GuardAccessor,
1518
+ std::unique_ptr<GetAttrGuardAccessor>>(py_m, " GetAttrGuardAccessor" );
1461
1519
1462
1520
// Guard Manager - No constructor in python, python should use
1463
1521
// RootGuardManager.
@@ -1510,7 +1568,13 @@ PyObject* torch_c_dynamo_guards_init() {
1510
1568
py::object verbose_code_parts) -> void {
1511
1569
self.add_leaf_guard (
1512
1570
std::make_shared<EQUALS_MATCH>(value, verbose_code_parts));
1513
- });
1571
+ })
1572
+ // return by reference because C++ GuardManager has the ownership of
1573
+ // accessors and guard managers
1574
+ .def (
1575
+ " getattr_manager" ,
1576
+ &GuardManager::get_child_manager<GetAttrGuardAccessor>,
1577
+ py::return_value_policy::reference);
1514
1578
1515
1579
// Root Guard Manager
1516
1580
py::class_<RootGuardManager, GuardManager, std::unique_ptr<RootGuardManager>>(
0 commit comments