Skip to content

Commit

Permalink
Merge pull request #33 from cs50/excepthook
Browse files Browse the repository at this point in the history
grays out site packages in tracebacks
  • Loading branch information
dmalan authored Nov 1, 2017
2 parents 0c455f6 + fca9b2f commit 41b0c47
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
14 changes: 12 additions & 2 deletions src/cs50/__init__.py
Original file line number Diff line number Diff line change
@@ -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):
"""
Expand All @@ -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())
45 changes: 43 additions & 2 deletions src/cs50/cs50.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -69,20 +107,21 @@ 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;
if text does not represent an int, user is prompted to retry. If line
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
Expand All @@ -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):
"""
Expand All @@ -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,
Expand Down
33 changes: 33 additions & 0 deletions src/cs50/flask.py
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions tests/flask/application.py
Original file line number Diff line number Diff line change
@@ -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")
2 changes: 2 additions & 0 deletions tests/flask/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cs50
Flask
10 changes: 10 additions & 0 deletions tests/flask/templates/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>

<html>
<head>
<title>error</title>
</head>
<body>
error
</body>
</html>
10 changes: 10 additions & 0 deletions tests/flask/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>

<html>
<head>
<title>flask</title>
</head>
<body>
flask
</body>
</html>
File renamed without changes.
4 changes: 4 additions & 0 deletions tests/sqlite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from cs50 import SQL

db = SQL("sqlite:///sqlite.db")
db.execute("SELECT 1")
6 changes: 6 additions & 0 deletions tests/tb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import cs50
import requests

def f():
res = requests.get("cs50.harvard.edu")
f()

0 comments on commit 41b0c47

Please sign in to comment.