text: """
  Tasks for maintaining the project.
  
  Execute 'invoke --list' for guidance on using Invoke
  """
  import shutil
  import pprint
  
  from invoke import task
  import webbrowser
  from pathlib import Path
  
  Path().expanduser()
  
  ROOT_DIR = Path(__file__).parent
  SETUP_FILE = ROOT_DIR.joinpath("setup.py")
  TEST_DIR = ROOT_DIR.joinpath("tests")
  SOURCE_DIR = ROOT_DIR.joinpath("lsde2021")
  TOX_DIR = ROOT_DIR.joinpath(".tox")
  COVERAGE_FILE = ROOT_DIR.joinpath(".coverage")
  COVERAGE_DIR = ROOT_DIR.joinpath("htmlcov")
  COVERAGE_REPORT = COVERAGE_DIR.joinpath("index.html")
  PYTHON_DIRS = [str(d) for d in [SOURCE_DIR, TEST_DIR]]
  
  
  def _delete_file(file):
      try:
          file.unlink(missing_ok=True)
      except TypeError:
          # missing_ok argument added in 3.8
          try:
              file.unlink()
          except FileNotFoundError:
              pass
  
  
  @task(help={"check": "Checks if source is formatted without applying changes"})
  def format(c, check=False):
      """Format code"""
      python_dirs_string = " ".join(PYTHON_DIRS)
      black_options = "--diff" if check else ""
      c.run("pipenv run black {} {}".format(black_options, python_dirs_string))
      isort_options = "{}".format("--check-only" if check else "")
      c.run("pipenv run isort {} {}".format(isort_options, python_dirs_string))
  
  
  @task
  def lint(c):
      """Lint code"""
      c.run("pipenv run flake8 {}".format(SOURCE_DIR))
  
  
  @task
  def test(c, min_coverage=None, parallel=True, verbose=True):
      """Run tests"""
      cov_options = "--cov-fail-under={}".format(min_coverage) if min_coverage else ""
      parallel_options = "-n auto" if parallel else ""
      verbose_options = "-vv" if verbose else ""
      c.run(
          "pipenv run pytest --cov={} {} {} {}".format(
              SOURCE_DIR, parallel_options, verbose_options, cov_options
          )
      )
  
  
  @task
  def type_check(c):
      """Check types"""
      c.run("pipenv run mypy")
  
  
  @task
  def install_hooks(c):
      """Install pre-commit hooks"""
      c.run("pipenv run pre-commit install -t pre-commit")
      c.run("pipenv run pre-commit install -t pre-push")
  
  
  @task
  def pre_commit(c):
      """Run all pre-commit checks"""
      c.run("pipenv run pre-commit run --all-files")
  
  
  @task
  def clean_build(c):
      """Clean up files from package building"""
      c.run("rm -fr build/")
      c.run("rm -fr dist/")
      c.run("rm -fr .eggs/")
      c.run("find . -name '*.egg-info' -exec rm -fr {} +")
      c.run("find . -name '*.egg' -exec rm -f {} +")
  
  
  @task
  def clean_python(c):
      """Clean up python file artifacts"""
      c.run("find . -name '*.pyc' -exec rm -f {} +")
      c.run("find . -name '*.pyo' -exec rm -f {} +")
      c.run("find . -name '*~' -exec rm -f {} +")
      c.run("find . -name '__pycache__' -exec rm -fr {} +")
  
  
  @task
  def clean_tests(c):
      """Clean up files from testing"""
      _delete_file(COVERAGE_FILE)
      shutil.rmtree(TOX_DIR, ignore_errors=True)
      shutil.rmtree(COVERAGE_DIR, ignore_errors=True)
  
  
  @task(pre=[clean_build, clean_python, clean_tests])
  def clean(c):
      """Runs all clean sub-tasks"""
      pass
  
  
  @task(clean)
  def dist(c):
      """Build source and wheel packages"""
      c.run("python setup.py sdist")
      c.run("python setup.py bdist_wheel")
  
  
  @task()
  def requirements(c):
      """Lock and write out the requirements"""
      c.run("pipenv lock -r > requirements.txt")
  