Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cf34af7

Browse files
committedNov 22, 2020
reverting to 5.0.5
1 parent e7c0df8 commit cf34af7

File tree

7 files changed

+41
-226
lines changed

7 files changed

+41
-226
lines changed
 

‎.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ deploy:
2020
\"target_commitish\": \"$TRAVIS_COMMIT\", \"name\": \"v$(python setup.py --version)\"
2121
}" --user bot50:$GITHUB_TOKEN https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases'
2222
on:
23-
branch: master
23+
branch: fixing-lists
2424
- provider: pypi
2525
user: "$PYPI_USERNAME"
2626
password: "$PYPI_PASSWORD"
27-
on: master
27+
on: fixing-lists
2828
notifications:
2929
slack:
3030
secure: lJklhcBVjDT6KzUNa3RFHXdXSeH7ytuuGrkZ5ZcR72CXMoTf2pMJTzPwRLWOp6lCSdDC9Y8MWLrcg/e33dJga4Jlp9alOmWqeqesaFjfee4st8vAsgNbv8/RajPH1gD2bnkt8oIwUzdHItdb5AucKFYjbH2g0d8ndoqYqUeBLrnsT1AP5G/Vi9OHC9OWNpR0FKaZIJE0Wt52vkPMH3sV2mFeIskByPB+56U5y547mualKxn61IVR/dhYBEtZQJuSvnwKHPOn9Pkk7cCa+SSSeTJ4w5LboY8T17otaYNauXo46i1bKIoGiBcCcrJyQHHiPQmcq/YU540MC5Wzt9YXUycmJzRi347oyQeDee27wV3XJlWMXuuhbtJiKCFny7BTQ160VATlj/dbwIzN99Ra6/BtTumv/6LyTdKIuVjdAkcN8dtdDW1nlrQ29zuPNCcXXzJ7zX7kQaOCUV1c2OrsbiH/0fE9nknUORn97txqhlYVi0QMS7764wFo6kg0vpmFQRkkQySsJl+TmgcZ01AlsJc2EMMWVuaj9Af9JU4/4yalqDiXIh1fOYYUZnLfOfWS+MsnI+/oLfqJFyMbrsQQTIjs+kTzbiEdhd2R4EZgusU/xRFWokS2NAvahexrRhRQ6tpAI+LezPrkNOR3aHiykBf+P9BkUa0wPp6V2Ayc6q0=

‎README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import cs50
1515
1616
...
1717
18-
f = cs50.get_float()
19-
i = cs50.get_int()
20-
s = cs50.get_string()
18+
f = cs50.get_float();
19+
i = cs50.get_int();
20+
s = cs50.get_string();
2121
```

‎setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
package_dir={"": "src"},
1717
packages=["cs50"],
1818
url="https://github.com/cs50/python-cs50",
19-
version="5.1.0"
19+
version="5.0.5"
2020
)

‎src/cs50/sql.py

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def __init__(self, url, **kwargs):
4343
import os
4444
import re
4545
import sqlalchemy
46-
import sqlalchemy.orm
4746
import sqlite3
4847

4948
# Get logger
@@ -60,11 +59,6 @@ def __init__(self, url, **kwargs):
6059
# Create engine, disabling SQLAlchemy's own autocommit mode, raising exception if back end's module not installed
6160
self._engine = sqlalchemy.create_engine(url, **kwargs).execution_options(autocommit=False)
6261

63-
# Create a variable to hold the session. If None, autocommit is on.
64-
self._Session = sqlalchemy.orm.session.sessionmaker(bind=self._engine)
65-
self._session = None
66-
self._in_transaction = False
67-
6862
# Listener for connections
6963
def connect(dbapi_connection, connection_record):
7064

@@ -96,8 +90,9 @@ def connect(dbapi_connection, connection_record):
9690
self._logger.disabled = disabled
9791

9892
def __del__(self):
99-
"""Close database session and connection."""
100-
self._close_session()
93+
"""Close database connection."""
94+
if hasattr(self, "_connection"):
95+
self._connection.close()
10196

10297
@_enable_logging
10398
def execute(self, sql, *args, **kwargs):
@@ -130,12 +125,6 @@ def execute(self, sql, *args, **kwargs):
130125
if token.ttype in [sqlparse.tokens.Keyword.DDL, sqlparse.tokens.Keyword.DML]:
131126
command = token.value.upper()
132127
break
133-
134-
# Begin a new session, if transaction opened by caller (not using autocommit)
135-
elif token.value.upper() in ["BEGIN", "START"]:
136-
if self._in_transaction:
137-
raise RuntimeError("transaction already open")
138-
self._in_transaction = True
139128
else:
140129
command = None
141130

@@ -283,10 +272,6 @@ def execute(self, sql, *args, **kwargs):
283272
statement = "".join([str(token) for token in tokens])
284273

285274
# Connect to database (for transactions' sake)
286-
if self._session is None:
287-
self._session = self._Session()
288-
289-
# Set up a Flask app teardown function to close session at teardown
290275
try:
291276

292277
# Infer whether Flask is installed
@@ -295,17 +280,29 @@ def execute(self, sql, *args, **kwargs):
295280
# Infer whether app is defined
296281
assert flask.current_app
297282

298-
# Disconnect later - but only once
299-
if not hasattr(self, "_teardown_appcontext_added"):
300-
self._teardown_appcontext_added = True
283+
# If no connection for app's current request yet
284+
if not hasattr(flask.g, "_connection"):
301285

286+
# Connect now
287+
flask.g._connection = self._engine.connect()
288+
289+
# Disconnect later
302290
@flask.current_app.teardown_appcontext
303291
def shutdown_session(exception=None):
304-
"""Close any existing session on app context teardown."""
305-
self._close_session()
292+
if hasattr(flask.g, "_connection"):
293+
flask.g._connection.close()
294+
295+
# Use this connection
296+
connection = flask.g._connection
306297

307298
except (ModuleNotFoundError, AssertionError):
308-
pass
299+
300+
# If no connection yet
301+
if not hasattr(self, "_connection"):
302+
self._connection = self._engine.connect()
303+
304+
# Use this connection
305+
connection = self._connection
309306

310307
# Catch SQLAlchemy warnings
311308
with warnings.catch_warnings():
@@ -319,14 +316,8 @@ def shutdown_session(exception=None):
319316
# Join tokens into statement, abbreviating binary data as <class 'bytes'>
320317
_statement = "".join([str(bytes) if token.ttype == sqlparse.tokens.Other else str(token) for token in tokens])
321318

322-
# If COMMIT or ROLLBACK, turn on autocommit mode
323-
if command in ["COMMIT", "ROLLBACK"] and "TO" not in (token.value for token in tokens):
324-
if not self._in_transaction:
325-
raise RuntimeError("transactions must be opened with BEGIN or START TRANSACTION")
326-
self._in_transaction = False
327-
328319
# Execute statement
329-
result = self._session.execute(sqlalchemy.text(statement))
320+
result = connection.execute(sqlalchemy.text(statement))
330321

331322
# Return value
332323
ret = True
@@ -355,7 +346,7 @@ def shutdown_session(exception=None):
355346
elif command == "INSERT":
356347
if self._engine.url.get_backend_name() in ["postgres", "postgresql"]:
357348
try:
358-
result = self._session.execute("SELECT LASTVAL()")
349+
result = connection.execute("SELECT LASTVAL()")
359350
ret = result.first()[0]
360351
except sqlalchemy.exc.OperationalError: # If lastval is not yet defined in this session
361352
ret = None
@@ -366,10 +357,6 @@ def shutdown_session(exception=None):
366357
elif command in ["DELETE", "UPDATE"]:
367358
ret = result.rowcount
368359

369-
# If autocommit is on, commit
370-
if not self._in_transaction:
371-
self._session.commit()
372-
373360
# If constraint violated, return None
374361
except sqlalchemy.exc.IntegrityError as e:
375362
self._logger.debug(termcolor.colored(statement, "yellow"))
@@ -389,13 +376,6 @@ def shutdown_session(exception=None):
389376
self._logger.debug(termcolor.colored(_statement, "green"))
390377
return ret
391378

392-
def _close_session(self):
393-
"""Closes any existing session and resets instance variables."""
394-
if self._session is not None:
395-
self._session.close()
396-
self._session = None
397-
self._in_transaction = False
398-
399379
def _escape(self, value):
400380
"""
401381
Escapes value using engine's conversion function.
@@ -475,7 +455,7 @@ def __escape(value):
475455

476456
# Escape value(s), separating with commas as needed
477457
if type(value) in [list, tuple]:
478-
return sqlparse.sql.TokenList([__escape(v) for v in value])
458+
return sqlparse.sql.TokenList(sqlparse.parse(", ".join([str(__escape(v)) for v in value])))
479459
else:
480460
return __escape(value)
481461

‎tests/flask/application.py

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,22 @@
1-
import logging
2-
import os
31
import requests
42
import sys
3+
from flask import Flask, render_template
54

65
sys.path.insert(0, "../../src")
76

87
import cs50
98
import cs50.flask
109

11-
from flask import Flask, render_template
12-
1310
app = Flask(__name__)
1411

15-
logging.disable(logging.CRITICAL)
16-
os.environ["WERKZEUG_RUN_MAIN"] = "true"
17-
18-
db_url = "sqlite:///../test.db"
19-
db = cs50.SQL(db_url)
12+
db = cs50.SQL("sqlite:///../sqlite.db")
2013

2114
@app.route("/")
2215
def index():
16+
db.execute("SELECT 1")
2317
"""
2418
def f():
2519
res = requests.get("cs50.harvard.edu")
2620
f()
2721
"""
2822
return render_template("index.html")
29-
30-
@app.route("/autocommit")
31-
def autocommit():
32-
db.execute("INSERT INTO test (val) VALUES (?)", "def")
33-
db2 = cs50.SQL(db_url)
34-
ret = db2.execute("SELECT val FROM test WHERE val=?", "def")
35-
return str(ret == [{"val": "def"}])
36-
37-
@app.route("/create")
38-
def create():
39-
ret = db.execute("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, val VARCHAR(16))")
40-
return str(ret)
41-
42-
@app.route("/delete")
43-
def delete():
44-
ret = db.execute("DELETE FROM test")
45-
return str(ret > 0)
46-
47-
@app.route("/drop")
48-
def drop():
49-
ret = db.execute("DROP TABLE test")
50-
return str(ret)
51-
52-
@app.route("/insert")
53-
def insert():
54-
ret = db.execute("INSERT INTO test (val) VALUES (?)", "abc")
55-
return str(ret > 0)
56-
57-
@app.route("/multiple_connections")
58-
def multiple_connections():
59-
ctx = len(app.teardown_appcontext_funcs)
60-
db1 = cs50.SQL(db_url)
61-
td1 = (len(app.teardown_appcontext_funcs) == ctx + 1)
62-
db2 = cs50.SQL(db_url)
63-
td2 = (len(app.teardown_appcontext_funcs) == ctx + 2)
64-
return str(td1 and td2)
65-
66-
@app.route("/select")
67-
def select():
68-
ret = db.execute("SELECT val FROM test")
69-
return str(ret == [{"val": "abc"}])
70-
71-
@app.route("/single_teardown")
72-
def single_teardown():
73-
db.execute("SELECT * FROM test")
74-
ctx = len(app.teardown_appcontext_funcs)
75-
db.execute("SELECT COUNT(id) FROM test")
76-
return str(ctx == len(app.teardown_appcontext_funcs))

‎tests/flask/test.py

Lines changed: 0 additions & 49 deletions
This file was deleted.

‎tests/sql.py

Lines changed: 8 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -115,36 +115,11 @@ def test_blob(self):
115115
self.db.execute("INSERT INTO cs50(bin) VALUES(:bin)", bin=row["bin"])
116116
self.assertEqual(self.db.execute("SELECT id, bin FROM cs50"), rows)
117117

118-
def test_autocommit(self):
119-
self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('foo')"), 1)
120-
self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('bar')"), 2)
121-
122-
# Load a new database instance to confirm the INSERTs were committed
123-
db2 = SQL(self.db_url)
124-
self.assertEqual(db2.execute("DELETE FROM cs50 WHERE id < 3"), 2)
125-
126-
def test_commit_no_transaction(self):
127-
with self.assertRaises(RuntimeError):
128-
self.db.execute("COMMIT")
129-
with self.assertRaises(RuntimeError):
130-
self.db.execute("ROLLBACK")
131-
132118
def test_commit(self):
133119
self.db.execute("BEGIN")
134120
self.db.execute("INSERT INTO cs50 (val) VALUES('foo')")
135121
self.db.execute("COMMIT")
136-
137-
# Load a new database instance to confirm the INSERT was committed
138-
db2 = SQL(self.db_url)
139-
self.assertEqual(db2.execute("SELECT val FROM cs50"), [{"val": "foo"}])
140-
141-
"""
142-
def test_double_begin(self):
143-
self.db.execute("BEGIN")
144-
with self.assertRaises(RuntimeError):
145-
self.db.execute("BEGIN")
146-
self.db.execute("ROLLBACK")
147-
"""
122+
self.assertEqual(self.db.execute("SELECT val FROM cs50"), [{"val": "foo"}])
148123

149124
def test_rollback(self):
150125
self.db.execute("BEGIN")
@@ -153,17 +128,6 @@ def test_rollback(self):
153128
self.db.execute("ROLLBACK")
154129
self.assertEqual(self.db.execute("SELECT val FROM cs50"), [])
155130

156-
def test_savepoint(self):
157-
self.db.execute("BEGIN")
158-
self.db.execute("INSERT INTO cs50 (val) VALUES('foo')")
159-
self.db.execute("SAVEPOINT sp1")
160-
self.db.execute("INSERT INTO cs50 (val) VALUES('bar')")
161-
self.assertEqual(self.db.execute("SELECT val FROM cs50"), [{"val": "foo"}, {"val": "bar"}])
162-
self.db.execute("ROLLBACK TO sp1")
163-
self.assertEqual(self.db.execute("SELECT val FROM cs50"), [{"val": "foo"}])
164-
self.db.execute("ROLLBACK")
165-
self.assertEqual(self.db.execute("SELECT val FROM cs50"), [])
166-
167131
def tearDown(self):
168132
self.db.execute("DROP TABLE cs50")
169133
self.db.execute("DROP TABLE IF EXISTS foo")
@@ -178,40 +142,30 @@ def tearDownClass(self):
178142
if not str(e).startswith("(1051"):
179143
raise e
180144

181-
182145
class MySQLTests(SQLTests):
183146
@classmethod
184147
def setUpClass(self):
185-
self.db_url = "mysql://root@localhost/test"
186-
self.db = SQL(self.db_url)
187-
print("\nMySQL tests")
148+
self.db = SQL("mysql://root@localhost/test")
188149

189150
def setUp(self):
190151
self.db.execute("CREATE TABLE cs50 (id INTEGER NOT NULL AUTO_INCREMENT, val VARCHAR(16), bin BLOB, PRIMARY KEY (id))")
191152

192-
193153
class PostgresTests(SQLTests):
194154
@classmethod
195155
def setUpClass(self):
196-
self.db_url = "postgresql://postgres@localhost/test"
197-
self.db = SQL(self.db_url)
198-
print("\nPOSTGRES tests")
156+
self.db = SQL("postgresql://postgres@localhost/test")
199157

200158
def setUp(self):
201159
self.db.execute("CREATE TABLE cs50 (id SERIAL PRIMARY KEY, val VARCHAR(16), bin BYTEA)")
202160

203161
def test_cte(self):
204162
self.assertEqual(self.db.execute("WITH foo AS ( SELECT 1 AS bar ) SELECT bar FROM foo"), [{"bar": 1}])
205163

206-
207164
class SQLiteTests(SQLTests):
208-
209165
@classmethod
210166
def setUpClass(self):
211167
open("test.db", "w").close()
212-
self.db_url = "sqlite:///test.db"
213-
self.db = SQL(self.db_url)
214-
print("\nSQLite tests")
168+
self.db = SQL("sqlite:///test.db")
215169

216170
def setUp(self):
217171
self.db.execute("CREATE TABLE cs50(id INTEGER PRIMARY KEY, val TEXT, bin BLOB)")
@@ -257,39 +211,23 @@ def test_qmark(self):
257211
self.assertEqual(self.db.execute("SELECT * FROM foo"), [{"firstname": "bar", "lastname": "baz"}])
258212
self.db.execute("DELETE FROM foo")
259213

260-
self.db.execute("INSERT INTO foo VALUES (?)", ("bar", "baz"))
261-
self.assertEqual(self.db.execute("SELECT * FROM foo"), [{"firstname": "bar", "lastname": "baz"}])
262-
self.db.execute("DELETE FROM foo")
263-
264-
self.db.execute("INSERT INTO foo VALUES (?)", ["bar", "baz"])
265-
self.assertEqual(self.db.execute("SELECT * FROM foo"), [{"firstname": "bar", "lastname": "baz"}])
266-
self.db.execute("DELETE FROM foo")
267-
268214
self.db.execute("INSERT INTO foo VALUES (?, ?)", ["bar", "baz"])
269215
self.assertEqual(self.db.execute("SELECT * FROM foo"), [{"firstname": "bar", "lastname": "baz"}])
270216
self.db.execute("DELETE FROM foo")
271217

272-
self.db.execute("INSERT INTO foo VALUES (?, ?)", "bar", "baz")
218+
219+
self.db.execute("INSERT INTO foo VALUES (?,?)", "bar", "baz")
273220
self.assertEqual(self.db.execute("SELECT * FROM foo"), [{"firstname": "bar", "lastname": "baz"}])
274221
self.db.execute("DELETE FROM foo")
275222

276223
self.db.execute("CREATE TABLE bar (firstname STRING)")
277-
278-
self.db.execute("INSERT INTO bar VALUES (?)", "baz")
279-
self.db.execute("INSERT INTO bar VALUES (?)", "qux")
280-
self.assertEqual(self.db.execute("SELECT * FROM bar WHERE firstname IN (?)", ("baz", "qux")), [{"firstname": "baz"}, {"firstname": "qux"}])
281-
self.db.execute("DELETE FROM bar")
282-
283-
self.db.execute("INSERT INTO bar VALUES (?)", "baz")
284-
self.db.execute("INSERT INTO bar VALUES (?)", "qux")
285-
self.assertEqual(self.db.execute("SELECT * FROM bar WHERE firstname IN (?)", ["baz", "qux"]), [{"firstname": "baz"}, {"firstname": "qux"}])
286-
self.db.execute("DELETE FROM bar")
287-
288224
self.db.execute("INSERT INTO bar VALUES (?)", "baz")
289225
self.assertEqual(self.db.execute("SELECT * FROM bar"), [{"firstname": "baz"}])
290226

291227
self.assertRaises(RuntimeError, self.db.execute, "INSERT INTO foo VALUES (?)")
292228
self.assertRaises(RuntimeError, self.db.execute, "INSERT INTO foo VALUES (?, ?)")
229+
# self.assertRaises(RuntimeError, self.db.execute, "INSERT INTO foo VALUES (?)", ('bar', 'baz'))
230+
# self.assertRaises(RuntimeError, self.db.execute, "INSERT INTO foo VALUES (?)", ['bar', 'baz'])
293231
self.assertRaises(RuntimeError, self.db.execute, "INSERT INTO foo VALUES (?, ?)", 'bar', 'baz', 'qux')
294232
self.assertRaises(RuntimeError, self.db.execute, "INSERT INTO foo VALUES (?, ?)", ('bar', 'baz', 'qux'))
295233
self.assertRaises(RuntimeError, self.db.execute, "INSERT INTO foo VALUES (?, ?)", ['bar', 'baz', 'qux'])

0 commit comments

Comments
 (0)
Please sign in to comment.