diff --git a/.travis.yml b/.travis.yml
index b8504fa..5421ff5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,7 @@ install:
 before_script:
 - mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
 - psql -c 'create database test;' -U postgres
-script: python tests/sqltests.py
+script: python tests/sql.py
 jobs:
   include:
   - stage: deploy
diff --git a/setup.py b/setup.py
index 1c49faf..ea29c0f 100644
--- a/setup.py
+++ b/setup.py
@@ -10,11 +10,11 @@
         "Topic :: Software Development :: Libraries :: Python Modules"
     ],
     description="CS50 library for Python",
-    install_requires=["SQLAlchemy", "sqlparse"],
+    install_requires=["SQLAlchemy", "sqlparse", "termcolor"],
     keywords="cs50",
     name="cs50",
     package_dir={"": "src"},
     packages=["cs50"],
     url="https://github.com/cs50/python-cs50",
-    version="2.2.0"
+    version="2.3.0"
 )
diff --git a/src/cs50/__init__.py b/src/cs50/__init__.py
index 5a02d67..447dd23 100644
--- a/src/cs50/__init__.py
+++ b/src/cs50/__init__.py
@@ -1,7 +1,13 @@
-import imp
 import sys
 
-from .cs50 import *
+from .cs50 import eprint, get_char, get_float, get_int, get_string
+try:
+    from .cs50 import get_long
+except:
+    pass
+
+from . import flask
+
 
 class CustomImporter(object):
     """
@@ -11,14 +17,18 @@ class CustomImporter(object):
     http://xion.org.pl/2012/05/06/hacking-python-imports/
     http://dangerontheranger.blogspot.com/2012/07/how-to-use-sysmetapath-with-python.html
     """
+
     def find_module(self, fullname, path=None):
         if fullname == "cs50.SQL":
             return self
         return None
+
     def load_module(self, name):
         if name in sys.modules:
             return sys.modules[name]
         from .sql import SQL
         sys.modules[name] = SQL
         return SQL
+
+
 sys.meta_path.append(CustomImporter())
diff --git a/src/cs50/cs50.py b/src/cs50/cs50.py
index b03cf04..190aa96 100644
--- a/src/cs50/cs50.py
+++ b/src/cs50/cs50.py
@@ -1,14 +1,22 @@
 from __future__ import print_function
+
 import inspect
 import re
 import sys
 
+from distutils.sysconfig import get_python_lib
+from os.path import abspath, join
+from termcolor import colored
+from traceback import extract_tb, format_list, format_exception_only, format_exception
+
+
 class flushfile():
     """
     Disable buffering for standard output and standard error.
 
     http://stackoverflow.com/a/231216
     """
+
     def __init__(self, f):
         self.f = f
 
@@ -18,9 +26,12 @@ def __getattr__(self, name):
     def write(self, x):
         self.f.write(x)
         self.f.flush()
+
+
 sys.stderr = flushfile(sys.stderr)
 sys.stdout = flushfile(sys.stdout)
 
+
 def eprint(*args, **kwargs):
     """
     Print an error message to standard error, prefixing it with
@@ -32,6 +43,32 @@ def eprint(*args, **kwargs):
     print("{}:{}: ".format(filename, lineno), end="")
     print(*args, end=end, file=sys.stderr, sep=sep)
 
+
+def formatException(type, value, tb):
+    """
+    Format traceback, darkening entries from global site-packages directories
+    and user-specific site-packages directory.
+
+    https://stackoverflow.com/a/46071447/5156190
+    """
+
+    # Absolute paths to site-packages
+    packages = tuple(join(abspath(p), "") for p in sys.path[1:])
+
+    # Darken lines referring to files in site-packages
+    lines = []
+    for line in format_exception(type, value, tb):
+        matches = re.search(r"^  File \"([^\"]+)\", line \d+, in .+", line)
+        if matches and matches.group(1).startswith(packages):
+            lines += colored(line, attrs=["dark"])
+        else:
+            lines += line
+    return "".join(lines).rstrip()
+
+
+sys.excepthook = lambda type, value, tb: print(formatException(type, value, tb), file=sys.stderr)
+
+
 def get_char(prompt=None):
     """
     Read a line of text from standard input and return the equivalent char;
@@ -49,6 +86,7 @@ def get_char(prompt=None):
         if prompt is None:
             print("Retry: ", end="")
 
+
 def get_float(prompt=None):
     """
     Read a line of text from standard input and return the equivalent float
@@ -69,6 +107,7 @@ def get_float(prompt=None):
         if prompt is None:
             print("Retry: ", end="")
 
+
 def get_int(prompt=None):
     """
     Read a line of text from standard input and return the equivalent int;
@@ -76,13 +115,13 @@ def get_int(prompt=None):
     can't be read, return None.
     """
     while True:
-        s = get_string(prompt);
+        s = get_string(prompt)
         if s is None:
             return None
         if re.search(r"^[+-]?\d+$", s):
             try:
                 i = int(s, 10)
-                if type(i) is int: # could become long in Python 2
+                if type(i) is int:  # could become long in Python 2
                     return i
             except ValueError:
                 pass
@@ -91,6 +130,7 @@ def get_int(prompt=None):
         if prompt is None:
             print("Retry: ", end="")
 
+
 if sys.version_info.major != 3:
     def get_long(prompt=None):
         """
@@ -112,6 +152,7 @@ def get_long(prompt=None):
             if prompt is None:
                 print("Retry: ", end="")
 
+
 def get_string(prompt=None):
     """
     Read a line of text from standard input and return it as a string,
diff --git a/src/cs50/flask.py b/src/cs50/flask.py
new file mode 100644
index 0000000..e55bf22
--- /dev/null
+++ b/src/cs50/flask.py
@@ -0,0 +1,33 @@
+from distutils.version import StrictVersion
+from pkg_resources import get_distribution
+
+from .cs50 import formatException
+
+# Try to monkey-patch Flask, if installed
+try:
+
+    # Only patch 0.12 (in case logging changes in 0.13)
+    version = StrictVersion(get_distribution("flask").version)
+    assert version >= StrictVersion("0.10") and version < StrictVersion("0.13")
+
+    # Get default logger
+    import flask.logging
+    f = flask.logging.create_logger
+
+    def create_logger(app):
+        """Wrap default logger"""
+
+        # Create default logger
+        logger = f(app)
+
+        # Reformat default logger's exceptions
+        # https://docs.python.org/3/library/logging.html#logging.Formatter.formatException
+        for handler in logger.handlers:
+            handler.formatter.formatException = lambda exc_info: formatException(*exc_info)
+        return logger
+
+    # Replace default logger
+    flask.logging.create_logger = create_logger
+
+except:
+    pass
diff --git a/tests/flask/application.py b/tests/flask/application.py
new file mode 100644
index 0000000..643dada
--- /dev/null
+++ b/tests/flask/application.py
@@ -0,0 +1,13 @@
+import requests
+from flask import Flask, render_template
+
+import cs50
+
+app = Flask(__name__)
+
+@app.route("/")
+def index():
+    def f():
+        res = requests.get("cs50.harvard.edu")
+    f()
+    return render_template("index.html")
diff --git a/tests/flask/requirements.txt b/tests/flask/requirements.txt
new file mode 100644
index 0000000..7d0c101
--- /dev/null
+++ b/tests/flask/requirements.txt
@@ -0,0 +1,2 @@
+cs50
+Flask
diff --git a/tests/flask/templates/error.html b/tests/flask/templates/error.html
new file mode 100644
index 0000000..3302040
--- /dev/null
+++ b/tests/flask/templates/error.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+
+<html>
+    <head>
+        <title>error</title>
+    </head>
+    <body>
+        error
+    </body>
+</html>
diff --git a/tests/flask/templates/index.html b/tests/flask/templates/index.html
new file mode 100644
index 0000000..2f6a145
--- /dev/null
+++ b/tests/flask/templates/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+
+<html>
+    <head>
+        <title>flask</title>
+    </head>
+    <body>
+        flask
+    </body>
+</html>
diff --git a/tests/sqltests.py b/tests/sql.py
similarity index 100%
rename from tests/sqltests.py
rename to tests/sql.py
diff --git a/tests/sqlite.py b/tests/sqlite.py
new file mode 100644
index 0000000..e60ef78
--- /dev/null
+++ b/tests/sqlite.py
@@ -0,0 +1,4 @@
+from cs50 import SQL
+
+db = SQL("sqlite:///sqlite.db")
+db.execute("SELECT 1")
diff --git a/tests/tb.py b/tests/tb.py
new file mode 100644
index 0000000..0b2b24e
--- /dev/null
+++ b/tests/tb.py
@@ -0,0 +1,6 @@
+import cs50
+import requests
+
+def f():
+    res = requests.get("cs50.harvard.edu")
+f()