From c1c61a7dd4fc2c7169c108a42ef607dff27ec46c Mon Sep 17 00:00:00 2001
From: "David J. Malan" <malan@harvard.edu>
Date: Mon, 23 Oct 2017 12:12:17 -0400
Subject: [PATCH 1/2] removed get_char, tidied comments

---
 src/cs50/cs50.py      | 28 +++++----------
 test/cs50/__init__.py | 21 +++++++++++
 test/cs50/cs50.py     | 81 +++++++++++++++++++++++++++++++++++++++++++
 test/cs50/sql.py      | 43 +++++++++++++++++++++++
 4 files changed, 154 insertions(+), 19 deletions(-)
 create mode 100644 test/cs50/__init__.py
 create mode 100644 test/cs50/cs50.py
 create mode 100644 test/cs50/sql.py

diff --git a/src/cs50/cs50.py b/src/cs50/cs50.py
index b03cf04..03007a4 100644
--- a/src/cs50/cs50.py
+++ b/src/cs50/cs50.py
@@ -3,6 +3,7 @@
 import re
 import sys
 
+
 class flushfile():
     """
     Disable buffering for standard output and standard error.
@@ -18,9 +19,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,22 +36,6 @@ def eprint(*args, **kwargs):
     print("{}:{}: ".format(filename, lineno), end="")
     print(*args, end=end, file=sys.stderr, sep=sep)
 
-def get_char(prompt=None):
-    """
-    Read a line of text from standard input and return the equivalent char;
-    if text is not a single char, user is prompted to retry. If line can't
-    be read, return None.
-    """
-    while True:
-        s = get_string(prompt)
-        if s is None:
-            return None
-        if len(s) == 1:
-            return s[0]
-
-        # temporarily here for backwards compatibility
-        if prompt is None:
-            print("Retry: ", end="")
 
 def get_float(prompt=None):
     """
@@ -69,6 +57,7 @@ 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;
@@ -82,12 +71,12 @@ def get_int(prompt=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
 
-        # temporarily here for backwards compatibility
+        # Temporarily here for backwards compatibility
         if prompt is None:
             print("Retry: ", end="")
 
@@ -108,10 +97,11 @@ def get_long(prompt=None):
                 except ValueError:
                     pass
 
-            # temporarily here for backwards compatibility
+            # Temporarily here for backwards compatibility
             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,
diff --git a/test/cs50/__init__.py b/test/cs50/__init__.py
new file mode 100644
index 0000000..2ec0ec0
--- /dev/null
+++ b/test/cs50/__init__.py
@@ -0,0 +1,21 @@
+from .cs50 import *
+
+class CustomImporter(object):
+    """
+    Import cs50.SQL lazily.
+
+    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, fullname):
+        print("1")
+        if fullname != "cs50.SQL":
+            raise ImportError(fullname)
+        print("2")
+        from .sql import SQL
+        print("3")
+        return SQL
+sys.meta_path.append(CustomImporter())
diff --git a/test/cs50/cs50.py b/test/cs50/cs50.py
new file mode 100644
index 0000000..657f405
--- /dev/null
+++ b/test/cs50/cs50.py
@@ -0,0 +1,81 @@
+from __future__ import print_function
+import re
+import sys
+
+class flushfile():
+    """
+    Disable buffering for standard output and standard error.
+
+    http://stackoverflow.com/a/231216
+    """
+    def __init__(self, f):
+        self.f = f
+
+    def __getattr__(self, name):
+        return object.__getattribute__(self.f, name)
+
+    def write(self, x):
+        self.f.write(x)
+        self.f.flush()
+sys.stderr = flushfile(sys.stderr)
+sys.stdout = flushfile(sys.stdout)
+
+def get_char():
+    """Read a line of text from standard input and return the equivalent char."""
+    while True:
+        s = get_string()
+        if s is None:
+            return None
+        if len(s) == 1:
+            return s[0]
+        print("Retry: ", end="")
+
+def get_float():
+    """Read a line of text from standard input and return the equivalent float."""
+    while True:
+        s = get_string()
+        if s is None:
+            return None
+        if len(s) > 0 and re.search(r"^[+-]?\d*(?:\.\d*)?$", s):
+            try:
+                return float(s)
+            except ValueError:
+                pass
+        print("Retry: ", end="")
+
+def get_int():
+    """Read a line of text from standard input and return the equivalent int."""
+    while True:
+        s = get_string();
+        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
+                    return i
+            except ValueError:
+                pass
+        print("Retry: ", end="")
+
+if sys.version_info.major != 3:
+    def get_long():
+        """Read a line of text from standard input and return the equivalent long."""
+        while True:
+            s = get_string();
+            if s is None:
+                return None
+            if re.search(r"^[+-]?\d+$", s):
+                try:
+                    return long(s, 10)
+                except ValueError:
+                    pass
+            print("Retry: ", end="")
+
+def get_string():
+    """Read a line of text from standard input and return it as a string."""
+    try:
+        s = sys.stdin.readline()
+        return re.sub(r"(?:\r|\r\n|\n)$", "", s)
+    except ValueError:
+        return None
diff --git a/test/cs50/sql.py b/test/cs50/sql.py
new file mode 100644
index 0000000..db974e3
--- /dev/null
+++ b/test/cs50/sql.py
@@ -0,0 +1,43 @@
+import sqlalchemy
+
+class SQL(object):
+    """TODO"""
+
+    def __init__(self, url):
+        """TODO"""
+        try:
+            self.engine = sqlalchemy.create_engine(url)
+        except Exception as e:
+            raise RuntimeError(e)
+
+    def execute(self, text, *multiparams, **params):
+        """TODO"""
+        try:
+
+            # http://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.text
+            # https://groups.google.com/forum/#!topic/sqlalchemy/FfLwKT1yQlg
+            # http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.Engine.execute
+            # http://docs.sqlalchemy.org/en/latest/faq/sqlexpressions.html#how-do-i-render-sql-expressions-as-strings-possibly-with-bound-parameters-inlined
+            statement = sqlalchemy.text(text).bindparams(*multiparams, **params)
+            result = self.engine.execute(str(statement.compile(compile_kwargs={"literal_binds": True})))
+
+            # SELECT
+            if result.returns_rows:
+                rows = result.fetchall()
+                return [dict(row) for row in rows]
+
+            # INSERT
+            elif result.lastrowid is not None:
+                return result.lastrowid
+
+            # DELETE, UPDATE
+            else:
+                return result.rowcount
+
+        except sqlalchemy.exc.IntegrityError:
+            return None
+
+        except Exception as e:
+            raise RuntimeError(e)
+
+

From c99f84177b3e17f891d9e03684fb72da6d246129 Mon Sep 17 00:00:00 2001
From: "David J. Malan" <malan@harvard.edu>
Date: Mon, 23 Oct 2017 12:12:44 -0400
Subject: [PATCH 2/2] upped version

---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index 1c49faf..a5eaed9 100644
--- a/setup.py
+++ b/setup.py
@@ -16,5 +16,5 @@
     package_dir={"": "src"},
     packages=["cs50"],
     url="https://github.com/cs50/python-cs50",
-    version="2.2.0"
+    version="2.3.0"
 )