class_docstr: Visit cell to look for get_ipython calls.
  
  Note that the source of the abstract syntax tree
  will already have been processed by IPython's
  TransformerManager().transform_cell.
  
  For example,
  
      %matplotlib inline
  
  would have been transformed to
  
      get_ipython().run_line_magic('matplotlib', 'inline')
  
  and we look for instances of the latter (and likewise for other
  types of magics).
class_docstr_tok: ["Visit","cell","to","look","for","get_ipython","calls",".","Note","that","the","source","of","the","abstract","syntax","tree","will","already","have","been","processed","by","IPython's","TransformerManager","(",")",".transform_cell",".","For","example",",","%","matplotlib","inline","would","have","been","transformed","to","get_ipython","(",")",".run_line_magic","(","'matplotlib","'",",","'inline","'",")","and","we","look","for","instances","of","the","latter","(","and","likewise","for","other","types","of","magics",")","."]
docstr: Look for magics in body of cell.
  
  For examples,
  
      !ls
      !!ls
      ?ls
      ??ls
  
  would (respectively) get transformed to
  
      get_ipython().system('ls')
      get_ipython().getoutput('ls')
      get_ipython().run_line_magic('pinfo', 'ls')
      get_ipython().run_line_magic('pinfo2', 'ls')
  
  and we look for instances of any of the latter.
docstr_tok: ["Look","for","magics","in","body","of","cell",".","For","examples",",","!","ls","!","!","ls","?","ls","?","?","ls","would","(","respectively",")","get","transformed","to","get_ipython","(",")",".system","(","'ls","'",")","get_ipython","(",")",".getoutput","(","'ls","'",")","get_ipython","(",")",".run_line_magic","(","'pinfo","'",",","'ls","'",")","get_ipython","(",")",".run_line_magic","(","'pinfo2","'",",","'ls","'",")","and","we","look","for","instances","of","any","of","the","latter","."]
code: def visit_Expr(self, node: ast.Expr) -> None:
      """Look for magics in body of cell.
  
      For examples,
  
          !ls
          !!ls
          ?ls
          ??ls
  
      would (respectively) get transformed to
  
          get_ipython().system('ls')
          get_ipython().getoutput('ls')
          get_ipython().run_line_magic('pinfo', 'ls')
          get_ipython().run_line_magic('pinfo2', 'ls')
  
      and we look for instances of any of the latter.
      """
      if isinstance(node.value, ast.Call) and _is_ipython_magic(
          node.value.func
      ):
          args = _get_str_args(node.value.args)
          if node.value.func.attr == "run_line_magic":
              if args[0] == "pinfo":
                  src = f"?{args[1]}"
              elif args[0] == "pinfo2":
                  src = f"??{args[1]}"
              else:
                  src = f"%{args[0]}"
                  if args[1]:
                      src += f" {args[1]}"
          elif node.value.func.attr == "system":
              src = f"!{args[0]}"
          elif node.value.func.attr == "getoutput":
              src = f"!!{args[0]}"
          else:
              raise NothingChanged  # unsupported magic.
          self.magics[node.value.lineno].append(
              OffsetAndMagic(node.value.col_offset, src)
          )
      self.generic_visit(node)
  
imports: import ast
  import collections
  import dataclasses
  import secrets
  import sys
  from functools import lru_cache
  from importlib.util import find_spec
  from typing import Dict, List, Optional, Tuple
  from black.output import out
  from black.report import NothingChanged
label_desc: Use image node_id 3 for calling the MagicFinder obj's underlying member method code with example usage: obj.visit_Expr(node) without return types