Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: cs50/python-cs50
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v9.0.0
Choose a base ref
...
head repository: cs50/python-cs50
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Loading
Showing with 295 additions and 131 deletions.
  1. +27 −4 .github/workflows/main.yml
  2. +1 −0 .gitignore
  3. +0 −2 README.md
  4. +2 −2 docker-compose.yml
  5. +4 −2 setup.py
  6. +1 −0 src/cs50/__init__.py
  7. +34 −18 src/cs50/cs50.py
  8. +13 −4 src/cs50/flask.py
  9. +197 −92 src/cs50/sql.py
  10. +9 −6 tests/foo.py
  11. +7 −1 tests/sql.py
31 changes: 27 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -20,26 +20,49 @@ jobs:
ports:
- 5432:5432
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.6'
python-version: '3.7'
check-latest: true
- name: Setup databases
run: |
pip install .
pip install mysqlclient psycopg2-binary
pip install mysqlclient psycopg2-binary SQLAlchemy
- name: Run tests
run: python tests/sql.py
env:
MYSQL_HOST: 127.0.0.1
POSTGRESQL_HOST: 127.0.0.1

- name: Install pypa/build
run: python -m pip install build --user

- name: Build a binary wheel and a source tarball
run: python -m build --sdist --wheel --outdir dist/ .

- name: Deploy to PyPI
if: ${{ github.ref == 'refs/heads/main' }}
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}

- name: Get Version
id: py_version
run: |
echo ::set-output name=version::$(python3 setup.py --version)
- name: Create Release
if: ${{ github.ref == 'refs/heads/main' }}
uses: actions/github-script@v7
with:
github-token: ${{ github.token }}
script: |
github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: "v${{ steps.py_version.outputs.version }}",
tag_commitish: "${{ github.sha }}"
})
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.*
!/.github/
!.gitignore
build/
*.db
*.egg-info/
*.pyc
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# CS50 Library for Python

[![Build Status](https://travis-ci.com/cs50/python-cs50.svg?branch=master)](https://travis-ci.org/cs50/python-cs50)

## Installation

```
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ services:
- postgres
environment:
MYSQL_HOST: mysql
POSTGRESQL_HOST: postgresql
POSTGRESQL_HOST: postgres
links:
- mysql
- postgres
@@ -20,7 +20,7 @@ services:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
healthcheck:
test: ["CMD", "mysqladmin", "-uroot", "ping"]
image: cs50/mysql:8
image: cs50/mysql
ports:
- 3306:3306
postgres:
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -10,11 +10,13 @@
"Topic :: Software Development :: Libraries :: Python Modules"
],
description="CS50 library for Python",
install_requires=["Flask>=1.0", "SQLAlchemy", "sqlparse", "termcolor", "wheel"],
install_requires=["Flask>=1.0", "packaging", "SQLAlchemy>=2,<3", "sqlparse", "termcolor", "wheel"],
keywords="cs50",
license="GPLv3",
long_description_content_type="text/markdown",
name="cs50",
package_dir={"": "src"},
packages=["cs50"],
url="https://github.com/cs50/python-cs50",
version="9.0.0"
version="9.4.0"
)
1 change: 1 addition & 0 deletions src/cs50/__init__.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@

# Import cs50_*
from .cs50 import get_char, get_float, get_int, get_string

try:
from .cs50 import get_long
except ImportError:
52 changes: 34 additions & 18 deletions src/cs50/cs50.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@
import re
import sys

from distutils.sysconfig import get_python_lib
from os.path import abspath, join
from termcolor import colored
from traceback import format_exception
@@ -18,7 +17,9 @@

try:
# Patch formatException
logging.root.handlers[0].formatter.formatException = lambda exc_info: _formatException(*exc_info)
logging.root.handlers[
0
].formatter.formatException = lambda exc_info: _formatException(*exc_info)
except IndexError:
pass

@@ -38,26 +39,31 @@
_logger.addHandler(handler)


class _flushfile():
class _Unbuffered:
"""
Disable buffering for standard output and standard error.
http://stackoverflow.com/a/231216
https://stackoverflow.com/a/107717
https://docs.python.org/3/library/io.html
"""

def __init__(self, f):
self.f = f
def __init__(self, stream):
self.stream = stream

def __getattr__(self, name):
return object.__getattribute__(self.f, name)
def __getattr__(self, attr):
return getattr(self.stream, attr)

def write(self, x):
self.f.write(x)
self.f.flush()
def write(self, b):
self.stream.write(b)
self.stream.flush()

def writelines(self, lines):
self.stream.writelines(lines)
self.stream.flush()

sys.stderr = _flushfile(sys.stderr)
sys.stdout = _flushfile(sys.stdout)

sys.stderr = _Unbuffered(sys.stderr)
sys.stdout = _Unbuffered(sys.stdout)


def _formatException(type, value, tb):
@@ -79,19 +85,29 @@ def _formatException(type, value, tb):
lines += line
else:
matches = re.search(r"^(\s*)(.*?)(\s*)$", line, re.DOTALL)
lines.append(matches.group(1) + colored(matches.group(2), "yellow") + matches.group(3))
lines.append(
matches.group(1)
+ colored(matches.group(2), "yellow")
+ matches.group(3)
)
return "".join(lines).rstrip()


sys.excepthook = lambda type, value, tb: print(_formatException(type, value, tb), file=sys.stderr)
sys.excepthook = lambda type, value, tb: print(
_formatException(type, value, tb), file=sys.stderr
)


def eprint(*args, **kwargs):
raise RuntimeError("The CS50 Library for Python no longer supports eprint, but you can use print instead!")
raise RuntimeError(
"The CS50 Library for Python no longer supports eprint, but you can use print instead!"
)


def get_char(prompt):
raise RuntimeError("The CS50 Library for Python no longer supports get_char, but you can use get_string instead!")
raise RuntimeError(
"The CS50 Library for Python no longer supports get_char, but you can use get_string instead!"
)


def get_float(prompt):
@@ -135,7 +151,7 @@ def get_string(prompt):
as line endings. If user inputs only a line ending, returns "", not None.
Returns None upon error or no input whatsoever (i.e., just EOF).
"""
if type(prompt) is not str:
if not isinstance(prompt, str):
raise TypeError("prompt must be of type str")
try:
return input(prompt)
17 changes: 13 additions & 4 deletions src/cs50/flask.py
Original file line number Diff line number Diff line change
@@ -2,22 +2,31 @@
import pkgutil
import sys


def _wrap_flask(f):
if f is None:
return

from distutils.version import StrictVersion
from packaging.version import Version, InvalidVersion
from .cs50 import _formatException

if f.__version__ < StrictVersion("1.0"):
try:
if Version(f.__version__) < Version("1.0"):
return
except InvalidVersion:
return

if os.getenv("CS50_IDE_TYPE") == "online":
from werkzeug.middleware.proxy_fix import ProxyFix

_flask_init_before = f.Flask.__init__

def _flask_init_after(self, *args, **kwargs):
_flask_init_before(self, *args, **kwargs)
self.wsgi_app = ProxyFix(self.wsgi_app, x_proto=1) # For HTTPS-to-HTTP proxy
self.wsgi_app = ProxyFix(
self.wsgi_app, x_proto=1
) # For HTTPS-to-HTTP proxy

f.Flask.__init__ = _flask_init_after


@@ -27,7 +36,7 @@ def _flask_init_after(self, *args, **kwargs):

# If Flask wasn't imported
else:
flask_loader = pkgutil.get_loader('flask')
flask_loader = pkgutil.get_loader("flask")
if flask_loader:
_exec_module_before = flask_loader.exec_module

Loading