feat(python): function to inspect a single-chunk Array (#436)

diff --git a/python/src/nanoarrow/_repr_utils.py b/python/src/nanoarrow/_repr_utils.py
index 3209a34..bd090af 100644
--- a/python/src/nanoarrow/_repr_utils.py
+++ b/python/src/nanoarrow/_repr_utils.py
@@ -248,3 +248,46 @@
     device_type = f"- device_type: {device.device_type.name} <{device.device_type_id}>"
     device_id = f"- device_id: {device.device_id}"
     return "\n".join([title_line, device_type, device_id])
+
+
+def array_inspect(array, indent=0, max_char_width=80):
+    array_view = array.view()
+
+    if max_char_width < 20:
+        max_char_width = 20
+
+    indent_str = " " * indent
+    class_label = "ArrowArray"
+    if array._addr() == 0:
+        return f"<{class_label} <NULL>>"
+    elif not array.is_valid():
+        return f"<{class_label} <released>>"
+
+    schema_string = array.schema._to_string(
+        max_chars=max_char_width - indent - 23, recursive=True
+    )
+    lines = [f"<{class_label} {schema_string}>"]
+    for attr in ("length", "offset", "null_count"):
+        attr_repr = repr(getattr(array, attr))
+        lines.append(f"{indent_str}- {attr}: {attr_repr}")
+
+    lines.append(f"{indent_str}- buffers[{array_view.n_buffers}]:")
+    for i, buffer in enumerate(array_view.buffers):
+        buffer_type = array_view.buffer_type(i)
+        lines.append(
+            f"{indent_str}  - {buffer_type} "
+            f"<{buffer_view_repr(buffer, max_char_width - indent - 4 - len(buffer))}>"
+        )
+
+    if array.dictionary:
+        dictionary_repr = array_inspect(array.dictionary, indent=indent + 2)
+        lines.append(f"{indent_str}- dictionary: {dictionary_repr}")
+    else:
+        lines.append(f"{indent_str}- dictionary: NULL")
+
+    lines.append(f"{indent_str}- children[{array.n_children}]:")
+    for child in array.children:
+        child_repr = array_inspect(child, indent=indent + 4)
+        lines.append(f"{indent_str}  {repr(child.schema.name)}: {child_repr}")
+
+    return "\n".join(lines)
diff --git a/python/src/nanoarrow/array.py b/python/src/nanoarrow/array.py
index af2e3cd..e38dc9c 100644
--- a/python/src/nanoarrow/array.py
+++ b/python/src/nanoarrow/array.py
@@ -490,3 +490,11 @@
 
     def __repr__(self) -> str:
         return self.to_string()
+
+    def inspect(self):
+        """
+        Print the details of the array (type, length, offset, buffers,
+        and children arrays).
+        """
+        self._assert_one_chunk("inspect")
+        print(_repr_utils.array_inspect(c_array(self)))
diff --git a/python/tests/test_array.py b/python/tests/test_array.py
index ee88d20..553a635 100644
--- a/python/tests/test_array.py
+++ b/python/tests/test_array.py
@@ -280,3 +280,22 @@
     assert len(repr_lines) == 2
     assert repr_lines[1].endswith("...")
     assert len(repr_lines[1]) == 80
+
+
+def test_array_inspect(capsys):
+    array = na.Array(range(10), na.int32())
+    array.inspect()
+    captured = capsys.readouterr()
+    assert captured.out.startswith("<ArrowArray int32>")
+
+    # with children
+    c_array = na.c_array_from_buffers(
+        na.struct({f"col{i}": na.int32() for i in range(100)}),
+        length=1,
+        buffers=[None],
+        children=[na.c_array([123456], na.int32())] * 100,
+    )
+    array = na.Array(c_array)
+    array.inspect()
+    captured = capsys.readouterr()
+    assert captured.out.startswith("<ArrowArray struct<col0: int32")