Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

grays out site packages in tracebacks #33

Merged
merged 13 commits into from
Nov 1, 2017
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -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):
"""
@@ -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

@@ -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,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
@@ -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,
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()