Skip to content

Commit 41b0c47

Browse files
authoredNov 1, 2017
Merge pull request #33 from cs50/excepthook
grays out site packages in tracebacks
2 parents 0c455f6 + fca9b2f commit 41b0c47

12 files changed

+136
-7
lines changed
 

‎.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ install:
1414
before_script:
1515
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
1616
- psql -c 'create database test;' -U postgres
17-
script: python tests/sqltests.py
17+
script: python tests/sql.py
1818
jobs:
1919
include:
2020
- stage: deploy

‎setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
"Topic :: Software Development :: Libraries :: Python Modules"
1111
],
1212
description="CS50 library for Python",
13-
install_requires=["SQLAlchemy", "sqlparse"],
13+
install_requires=["SQLAlchemy", "sqlparse", "termcolor"],
1414
keywords="cs50",
1515
name="cs50",
1616
package_dir={"": "src"},
1717
packages=["cs50"],
1818
url="https://github.com/cs50/python-cs50",
19-
version="2.2.0"
19+
version="2.3.0"
2020
)

‎src/cs50/__init__.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
import imp
21
import sys
32

4-
from .cs50 import *
3+
from .cs50 import eprint, get_char, get_float, get_int, get_string
4+
try:
5+
from .cs50 import get_long
6+
except:
7+
pass
8+
9+
from . import flask
10+
511

612
class CustomImporter(object):
713
"""
@@ -11,14 +17,18 @@ class CustomImporter(object):
1117
http://xion.org.pl/2012/05/06/hacking-python-imports/
1218
http://dangerontheranger.blogspot.com/2012/07/how-to-use-sysmetapath-with-python.html
1319
"""
20+
1421
def find_module(self, fullname, path=None):
1522
if fullname == "cs50.SQL":
1623
return self
1724
return None
25+
1826
def load_module(self, name):
1927
if name in sys.modules:
2028
return sys.modules[name]
2129
from .sql import SQL
2230
sys.modules[name] = SQL
2331
return SQL
32+
33+
2434
sys.meta_path.append(CustomImporter())

‎src/cs50/cs50.py

+43-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
from __future__ import print_function
2+
23
import inspect
34
import re
45
import sys
56

7+
from distutils.sysconfig import get_python_lib
8+
from os.path import abspath, join
9+
from termcolor import colored
10+
from traceback import extract_tb, format_list, format_exception_only, format_exception
11+
12+
613
class flushfile():
714
"""
815
Disable buffering for standard output and standard error.
916
1017
http://stackoverflow.com/a/231216
1118
"""
19+
1220
def __init__(self, f):
1321
self.f = f
1422

@@ -18,9 +26,12 @@ def __getattr__(self, name):
1826
def write(self, x):
1927
self.f.write(x)
2028
self.f.flush()
29+
30+
2131
sys.stderr = flushfile(sys.stderr)
2232
sys.stdout = flushfile(sys.stdout)
2333

34+
2435
def eprint(*args, **kwargs):
2536
"""
2637
Print an error message to standard error, prefixing it with
@@ -32,6 +43,32 @@ def eprint(*args, **kwargs):
3243
print("{}:{}: ".format(filename, lineno), end="")
3344
print(*args, end=end, file=sys.stderr, sep=sep)
3445

46+
47+
def formatException(type, value, tb):
48+
"""
49+
Format traceback, darkening entries from global site-packages directories
50+
and user-specific site-packages directory.
51+
52+
https://stackoverflow.com/a/46071447/5156190
53+
"""
54+
55+
# Absolute paths to site-packages
56+
packages = tuple(join(abspath(p), "") for p in sys.path[1:])
57+
58+
# Darken lines referring to files in site-packages
59+
lines = []
60+
for line in format_exception(type, value, tb):
61+
matches = re.search(r"^ File \"([^\"]+)\", line \d+, in .+", line)
62+
if matches and matches.group(1).startswith(packages):
63+
lines += colored(line, attrs=["dark"])
64+
else:
65+
lines += line
66+
return "".join(lines).rstrip()
67+
68+
69+
sys.excepthook = lambda type, value, tb: print(formatException(type, value, tb), file=sys.stderr)
70+
71+
3572
def get_char(prompt=None):
3673
"""
3774
Read a line of text from standard input and return the equivalent char;
@@ -49,6 +86,7 @@ def get_char(prompt=None):
4986
if prompt is None:
5087
print("Retry: ", end="")
5188

89+
5290
def get_float(prompt=None):
5391
"""
5492
Read a line of text from standard input and return the equivalent float
@@ -69,20 +107,21 @@ def get_float(prompt=None):
69107
if prompt is None:
70108
print("Retry: ", end="")
71109

110+
72111
def get_int(prompt=None):
73112
"""
74113
Read a line of text from standard input and return the equivalent int;
75114
if text does not represent an int, user is prompted to retry. If line
76115
can't be read, return None.
77116
"""
78117
while True:
79-
s = get_string(prompt);
118+
s = get_string(prompt)
80119
if s is None:
81120
return None
82121
if re.search(r"^[+-]?\d+$", s):
83122
try:
84123
i = int(s, 10)
85-
if type(i) is int: # could become long in Python 2
124+
if type(i) is int: # could become long in Python 2
86125
return i
87126
except ValueError:
88127
pass
@@ -91,6 +130,7 @@ def get_int(prompt=None):
91130
if prompt is None:
92131
print("Retry: ", end="")
93132

133+
94134
if sys.version_info.major != 3:
95135
def get_long(prompt=None):
96136
"""
@@ -112,6 +152,7 @@ def get_long(prompt=None):
112152
if prompt is None:
113153
print("Retry: ", end="")
114154

155+
115156
def get_string(prompt=None):
116157
"""
117158
Read a line of text from standard input and return it as a string,

‎src/cs50/flask.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from distutils.version import StrictVersion
2+
from pkg_resources import get_distribution
3+
4+
from .cs50 import formatException
5+
6+
# Try to monkey-patch Flask, if installed
7+
try:
8+
9+
# Only patch 0.12 (in case logging changes in 0.13)
10+
version = StrictVersion(get_distribution("flask").version)
11+
assert version >= StrictVersion("0.10") and version < StrictVersion("0.13")
12+
13+
# Get default logger
14+
import flask.logging
15+
f = flask.logging.create_logger
16+
17+
def create_logger(app):
18+
"""Wrap default logger"""
19+
20+
# Create default logger
21+
logger = f(app)
22+
23+
# Reformat default logger's exceptions
24+
# https://docs.python.org/3/library/logging.html#logging.Formatter.formatException
25+
for handler in logger.handlers:
26+
handler.formatter.formatException = lambda exc_info: formatException(*exc_info)
27+
return logger
28+
29+
# Replace default logger
30+
flask.logging.create_logger = create_logger
31+
32+
except:
33+
pass

‎tests/flask/application.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import requests
2+
from flask import Flask, render_template
3+
4+
import cs50
5+
6+
app = Flask(__name__)
7+
8+
@app.route("/")
9+
def index():
10+
def f():
11+
res = requests.get("cs50.harvard.edu")
12+
f()
13+
return render_template("index.html")

‎tests/flask/requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
cs50
2+
Flask

‎tests/flask/templates/error.html

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
3+
<html>
4+
<head>
5+
<title>error</title>
6+
</head>
7+
<body>
8+
error
9+
</body>
10+
</html>

‎tests/flask/templates/index.html

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
3+
<html>
4+
<head>
5+
<title>flask</title>
6+
</head>
7+
<body>
8+
flask
9+
</body>
10+
</html>

‎tests/sqltests.py ‎tests/sql.py

File renamed without changes.

‎tests/sqlite.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from cs50 import SQL
2+
3+
db = SQL("sqlite:///sqlite.db")
4+
db.execute("SELECT 1")

‎tests/tb.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import cs50
2+
import requests
3+
4+
def f():
5+
res = requests.get("cs50.harvard.edu")
6+
f()

0 commit comments

Comments
 (0)
Please sign in to comment.