Using Rich Inspect to interrogate Python objects
The Rich library has a few functions that are admittedly a little out of scope for a terminal color library. One such function is inspect
which is so useful you may want to pip install rich
just for this feature.
The easiest way to describe inspect
is that it is Python's builtin help()
but easier on the eye (and with a few more features).
If you invoke it with any object, inspect
will display a nicely formatted report on that object — which makes it great for interrogating objects from the REPL. Here's an example:
>>> from rich import inspect
>>> text_file = open ( "foo.txt" , "w" )
>>> inspect ( text_file )
Here we're inspecting a file object, but it could be literally anything.
You will see the following output in the terminal:
Rich
╭─ ─────────────────── < class '_io.TextIOWrapper' > ──────────────────── ─╮
│ Character and line based layer over a BufferedIOBase object, buffer. │
│ │
│ ╭──────────────────────────────────────────────────────────────────╮ │
│ │ < _io.TextIOWrapper name = 'foo.txt' mode = 'w' encoding = 'UTF-8' > │ │
│ ╰──────────────────────────────────────────────────────────────────╯ │
│ │
│ buffer = < _io.BufferedWriter name = 'foo.txt' > │
│ closed = False │
│ encoding = 'UTF-8' │
│ errors = 'strict' │
│ line_buffering = False │
│ mode = 'w' │
│ name = 'foo.txt' │
│ newlines = None │
│ write_through = False │
╰──────────────────────────────────────────────────────────────────────╯
By default, inspect
will generate a data-oriented summary with a text representation of the object and its data attributes.
You can also add methods=True
to show all the methods in the public API.
Here's an example:
>>> inspect ( text_file , methods = True )
Rich
╭─ ────────────────────────────────────── < class '_io.TextIOWrapper' > ─────────────────────────────────────── ─╮
│ Character and line based layer over a BufferedIOBase object, buffer. │
│ │
│ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
│ │ < _io.TextIOWrapper name = 'foo.txt' mode = 'w' encoding = 'UTF-8' > │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ buffer = < _io.BufferedWriter name = 'foo.txt' > │
│ closed = False │
│ encoding = 'UTF-8' │
│ errors = 'strict' │
│ line_buffering = False │
│ mode = 'w' │
│ name = 'foo.txt' │
│ newlines = None │
│ write_through = False │
│ close = def close ( ) : Flush and close the IO object. │
│ detach = def detach ( ) : Separate the underlying buffer from the TextIOBase and return it. │
│ fileno = def fileno ( ) : Returns underlying file descriptor if one exists. │
│ flush = def flush ( ) : Flush write buffers, if applicable. │
│ isatty = def isatty ( ) : Return whether this is an 'interactive' stream. │
│ read = def read ( size = -1 , / ) : Read at most n characters from stream. │
│ readable = def readable ( ) : Return whether object was opened for reading. │
│ readline = def readline ( size = -1 , / ) : Read until newline or EOF. │
│ readlines = def readlines ( hint = -1 , / ) : Return a list of lines from the stream. │
│ reconfigure = def reconfigure ( *, encoding = None , errors = None , newline = None , line_buffering = None , │
│ write_through = None ) : Reconfigure the text stream with new parameters. │
│ seek = def seek ( cookie, whence = 0 , / ) : Change stream position. │
│ seekable = def seekable ( ) : Return whether object supports random access. │
│ tell = def tell ( ) : Return current stream position. │
│ truncate = def truncate ( pos = None , / ) : Truncate file to size bytes. │
│ writable = def writable ( ) : Return whether object was opened for writing. │
│ write = def write ( text, / ) : │
│ Write string to stream. │
│ Returns the number of characters written ( which is always equal to │
│ the length of the string ) . │
│ writelines = def writelines ( lines, / ) : Write a list of lines to stream. │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
The documentation is summarized by default to avoid generating verbose reports.
If you want to see the full unabbreviated help you can add help=True
:
>>> inspect ( text_file , methods = True , help = True )
Rich
╭─ ────────────────────────────────────── < class '_io.TextIOWrapper' > ─────────────────────────────────────── ─╮
│ Character and line based layer over a BufferedIOBase object, buffer. │
│ │
│ encoding gives the name of the encoding that the stream will be │
│ decoded or encoded with. It defaults to locale.getencoding ( ) . │
│ │
│ errors determines the strictness of encoding and decoding ( see │
│ help ( codecs.Codec ) or the documentation for codecs.register ) and │
│ defaults to "strict" . │
│ │
│ newline controls how line endings are handled. It can be None , '' , │
│ '\n' , '\r' , and '\r\n' . It works as follows: │
│ │
│ * On input, if newline is None , universal newlines mode is │
│ enabled. Lines in the input can end in '\n' , '\r' , or '\r\n' , and │
│ these are translated into '\n' before being returned to the │
│ caller. If it is '' , universal newline mode is enabled, but line │
│ endings are returned to the caller untranslated. If it has any of │
│ the other legal values, input lines are only terminated by the given │
│ string, and the line ending is returned to the caller untranslated. │
│ │
│ * On output, if newline is None , any '\n' characters written are │
│ translated to the system default line separator, os.linesep. If │
│ newline is '' or '\n' , no translation takes place. If newline is any │
│ of the other legal values, any '\n' characters written are translated │
│ to the given string. │
│ │
│ If line_buffering is True , a call to flush is implied when a call to │
│ write contains a newline character. │
│ │
│ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
│ │ < _io.TextIOWrapper name = 'foo.txt' mode = 'w' encoding = 'UTF-8' > │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ buffer = < _io.BufferedWriter name = 'foo.txt' > │
│ closed = False │
│ encoding = 'UTF-8' │
│ errors = 'strict' │
│ line_buffering = False │
│ mode = 'w' │
│ name = 'foo.txt' │
│ newlines = None │
│ write_through = False │
│ close = def close ( ) : │
│ Flush and close the IO object. │
│ │
│ This method has no effect if the file is already closed. │
│ detach = def detach ( ) : │
│ Separate the underlying buffer from the TextIOBase and return it. │
│ │
│ After the underlying buffer has been detached, the TextIO is in an │
│ unusable state. │
│ fileno = def fileno ( ) : │
│ Returns underlying file descriptor if one exists. │
│ │
│ OSError is raised if the IO object does not use a file descriptor. │
│ flush = def flush ( ) : │
│ Flush write buffers, if applicable. │
│ │
│ This is not implemented for read-only and non-blocking streams. │
│ isatty = def isatty ( ) : │
│ Return whether this is an 'interactive' stream. │
│ │
│ Return False if it can't be determined. │
│ read = def read ( size = -1 , / ) : │
│ Read at most n characters from stream. │
│ │
│ Read from underlying buffer until we have n characters or we hit EOF. │
│ If n is negative or omitted, read until EOF. │
│ readable = def readable ( ) : │
│ Return whether object was opened for reading. │
│ │
│ If False , read ( ) will raise OSError. │
│ readline = def readline ( size = -1 , / ) : │
│ Read until newline or EOF. │
│ │
│ Returns an empty string if EOF is hit immediately. │
│ readlines = def readlines ( hint = -1 , / ) : │
│ Return a list of lines from the stream. │
│ │
│ hint can be specified to control the number of lines read: no more │
│ lines will be read if the total size ( in bytes/characters ) of all │
│ lines so far exceeds hint. │
│ reconfigure = def reconfigure ( *, encoding = None , errors = None , newline = None , line_buffering = None , │
│ write_through = None ) : │
│ Reconfigure the text stream with new parameters. │
│ │
│ This also does an implicit stream flush. │
│ seek = def seek ( cookie, whence = 0 , / ) : │
│ Change stream position. │
│ │
│ Change the stream position to the given byte offset. The offset is │
│ interpreted relative to the position indicated by whence. Values │
│ for whence are: │
│ │
│ * 0 -- start of stream ( the default ) ; offset should be zero or positive │
│ * 1 -- current stream position; offset may be negative │
│ * 2 -- end of stream; offset is usually negative │
│ │
│ Return the new absolute position. │
│ seekable = def seekable ( ) : │
│ Return whether object supports random access. │
│ │
│ If False , seek ( ) , tell ( ) and truncate ( ) will raise OSError. │
│ This method may need to do a test seek ( ) . │
│ tell = def tell ( ) : Return current stream position. │
│ truncate = def truncate ( pos = None , / ) : │
│ Truncate file to size bytes. │
│ │
│ File pointer is left unchanged. Size defaults to the current IO │
│ position as reported by tell ( ) . Returns the new size. │
│ writable = def writable ( ) : │
│ Return whether object was opened for writing. │
│ │
│ If False , write ( ) will raise OSError. │
│ write = def write ( text, / ) : │
│ Write string to stream. │
│ Returns the number of characters written ( which is always equal to │
│ the length of the string ) . │
│ writelines = def writelines ( lines, / ) : │
│ Write a list of lines to stream. │
│ │
│ Line separators are not added, so it is usual for each of the │
│ lines provided to have a line separator at the end. │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
There are a few more arguments to refine the level of detail you need (private methods, dunder attributes etc).
You can see the full range of options with this delightful little incantation:
If you are interested in Rich or Textual, join our Discord server !
Addendum
Here's how to have inspect
always available without an explicit import: