text: """
  Extend pytest to allow flake checking of notebooks.
  
  A notebooks is first converted to a python script, then checked with
  pyflakes.
  
  IPython magics are supposed to be handled in a useful way, being
  converted to python or skipped as appropriate (depending on their
  interaction with python code). See the magics module for more info.
  """
  
  import sys
  import warnings
  import io
  import os
  import re
  
  import pytest
  import nbformat
  import nbconvert
  
  from . import _pyflakes
  from . import magics
  
  # list of regexes
  flakes_to_ignore = []
  
  class NBLintError(Exception):
      pass
  
  class LintNb(pytest.Item):
  
      def repr_failure(self, excinfo):
          if excinfo.errisinstance(NBLintError):
              return excinfo.value.args[0]
          else:
              return super(LintNb, self).repr_failure(excinfo)
  
      def runtest(self):
          with io.open(self.name,encoding='utf8') as nbfile:
              nb = nbformat.read(nbfile, as_version=4)
  
              magics_processor = magics.Processor(
                  extra_line_blacklist = _get_list_from_conf('nbsmoke_flakes_line_magics_blacklist', self.parent.parent.config),
                  extra_cell_blacklist = _get_list_from_conf('nbsmoke_flakes_cell_magics_blacklist', self.parent.parent.config),
                  extra_magic_handlers = self.parent.parent.config.getini('nbsmoke_magic_handlers'))
              magics_processor.insert_get_ipython(nb)
  
              ipy, _ = nbconvert.PythonExporter().from_notebook_node(nb)
  
              debug=self.config.option.nbsmoke_lint_debug
              filenames = []
  
              self._write_debug_file(debug, ipy, self.name, "pre", filenames)
  
              py = magics_processor.ipython_to_python_for_flake_checks(ipy)
  
              self._write_debug_file(debug, py, self.name, "post", filenames)
  
              flake_result = _pyflakes.flake_check(
                  # notebooks will start with "coding: utf-8", but py already unicode
                  py.encode('utf8') if sys.version_info[0]==2 else py,
                  # flake_check uses the builtin compile function which takes a filename
                  # as input. From Python 3.9 we've seen that this filename isn't just used
                  # as metadata indicating where the code source origin, it's also used
                  # to report the content of the line that errored. This doesn't work for
                  # us, our source code is a python string and the filename points to
                  # a notebook. So the trick is just to pass the repr of the filename.
                  filename="%r" % self.name)
  
              ### remove flakes by regex ###
              _user_flakes_to_ignore = self.parent.parent.config.getini('nbsmoke_flakes_to_ignore')
              if _user_flakes_to_ignore != '':
                  _user_flakes_to_ignore = _user_flakes_to_ignore.splitlines()
              for pattern in set(flakes_to_ignore) | set(_user_flakes_to_ignore):
                  flake_result['messages'] = [msg for msg in flake_result['messages'] if not re.search(pattern, msg)]
              ##############################
  
              if flake_result['messages']:
                  msg = "%s\n** "%self.name
                  if self.config.option.nbsmoke_lint_onlywarn and 'message_for_onlywarn' in flake_result:
                      msg += flake_result['message_for_onlywarn']
                  else:
                      msg += "\n** ".join(flake_result['messages'])
                  msg += "\n"+"To see python source that was checked by pyflakes: %s"%filenames[1]
                  msg += "\n"+"To see python source before magics were handled by nbsmoke: %s"%filenames[0]
  
                  if self.config.option.nbsmoke_lint_onlywarn:
                      warnings.warn("Flakes (or error) detected:\n"+msg+"\n")
                  else:
                      raise NBLintError(msg)
  
      @staticmethod
      def _write_debug_file(debug, py, name, message, filenames):
          # hack to allow debugging of ipynb linting
          if debug:
              # should I use a temp file instead?
              filename = os.path.splitext(name)[0]+".nbsmoke-debug-%smagicprocess.py"%message
              with io.open(filename,'w',encoding='utf8') as df:
                  df.write(py)
              filenames.append(filename)
          else:
              filenames.append("pass --nbsmoke-lint-debug")
  
  def _get_list_from_conf(name,conf):
      # does this not exist in pytest/elsewhere?
      option_raw = conf.getini(name)
      return option_raw.splitlines()
  