Extend attribute and item accessor interface using object_api
diff --git a/tests/constructor_stats.h b/tests/constructor_stats.h
index 69e385e..5dd215f 100644
--- a/tests/constructor_stats.h
+++ b/tests/constructor_stats.h
@@ -103,7 +103,7 @@
 
     int alive() {
         // Force garbage collection to ensure any pending destructors are invoked:
-        py::module::import("gc").attr("collect").operator py::object()();
+        py::module::import("gc").attr("collect")();
         int total = 0;
         for (const auto &p : _instances) if (p.second > 0) total += p.second;
         return total;
diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp
index 9dafe77..4ab90e6 100644
--- a/tests/test_python_types.cpp
+++ b/tests/test_python_types.cpp
@@ -203,7 +203,7 @@
         py::print("no new line here", "end"_a=" -- ");
         py::print("next print");
 
-        auto py_stderr = py::module::import("sys").attr("stderr").cast<py::object>();
+        auto py_stderr = py::module::import("sys").attr("stderr");
         py::print("this goes to stderr", "file"_a=py_stderr);
 
         py::print("flush", "flush"_a=true);
@@ -222,4 +222,39 @@
         auto d2 = py::dict("z"_a=3, **d1);
         return d2;
     });
+
+    m.def("test_accessor_api", [](py::object o) {
+        auto d = py::dict();
+
+        d["basic_attr"] = o.attr("basic_attr");
+
+        auto l = py::list();
+        for (const auto &item : o.attr("begin_end")) {
+            l.append(item);
+        }
+        d["begin_end"] = l;
+
+        d["operator[object]"] = o.attr("d")["operator[object]"_s];
+        d["operator[char *]"] = o.attr("d")["operator[char *]"];
+
+        d["attr(object)"] = o.attr("sub").attr("attr_obj");
+        d["attr(char *)"] = o.attr("sub").attr("attr_char");
+        try {
+            o.attr("sub").attr("missing").ptr();
+        } catch (const py::error_already_set &) {
+            d["missing_attr_ptr"] = "raised"_s;
+        }
+        try {
+            o.attr("missing").attr("doesn't matter");
+        } catch (const py::error_already_set &) {
+            d["missing_attr_chain"] = "raised"_s;
+        }
+
+        d["is_none"] = py::cast(o.attr("basic_attr").is_none());
+
+        d["operator()"] = o.attr("func")(1);
+        d["operator*"] = o.attr("func")(*o.attr("begin_end"));
+
+        return d;
+    });
 });
diff --git a/tests/test_python_types.py b/tests/test_python_types.py
index fe58f93..4f2cdb2 100644
--- a/tests/test_python_types.py
+++ b/tests/test_python_types.py
@@ -248,3 +248,33 @@
     from pybind11_tests import test_dict_keyword_constructor
 
     assert test_dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3}
+
+
+def test_accessors():
+    from pybind11_tests import test_accessor_api
+
+    class SubTestObject:
+        attr_obj = 1
+        attr_char = 2
+
+    class TestObject:
+        basic_attr = 1
+        begin_end = [1, 2, 3]
+        d = {"operator[object]": 1, "operator[char *]": 2}
+        sub = SubTestObject()
+
+        def func(self, x, *args):
+            return self.basic_attr + x + sum(args)
+
+    d = test_accessor_api(TestObject())
+    assert d["basic_attr"] == 1
+    assert d["begin_end"] == [1, 2, 3]
+    assert d["operator[object]"] == 1
+    assert d["operator[char *]"] == 2
+    assert d["attr(object)"] == 1
+    assert d["attr(char *)"] == 2
+    assert d["missing_attr_ptr"] == "raised"
+    assert d["missing_attr_chain"] == "raised"
+    assert d["is_none"] is False
+    assert d["operator()"] == 2
+    assert d["operator*"] == 7