diff --git a/Makefile b/Makefile
index ded7c8f..dcce1e7 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ DESCRIPTION = CS50 Library for Python
 MAINTAINER = CS50 <sysadmins@cs50.harvard.edu>
 NAME = python-cs50
 OLD_NAME = lib50-python
-VERSION = 1.2.4
+VERSION = 1.3.0
 
 .PHONY: bash
 bash:
diff --git a/README.md b/README.md
index f4be06d..6f943ca 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,6 @@ Requires [Docker Engine](https://docs.docker.com/engine/installation/).
 ## TODO
 
 * Add install target to Makefile.
-* Add comments.
 * Conditionally install for Python 2 and/or Python 3.
 * Add targets for `pacman`, `rpm`.
 * Add tests.
diff --git a/after-install.sh b/after-install.sh
index cb3373b..9c78f1c 100644
--- a/after-install.sh
+++ b/after-install.sh
@@ -1,5 +1,6 @@
 #!/bin/bash
 
+pip2 install SQLAlchemy
 pip3 install SQLAlchemy
 
 chmod -R a+rX /usr/lib/python2.7/dist-packages/cs50
diff --git a/src/__init__.py b/src/__init__.py
index 8435dae..5a02d67 100644
--- a/src/__init__.py
+++ b/src/__init__.py
@@ -1,2 +1,24 @@
+import imp
+import sys
+
 from .cs50 import *
-from .sql import *
+
+class CustomImporter(object):
+    """
+    Import cs50.SQL lazily so that rest of library can be used without SQLAlchemy installed.
+
+    https://docs.python.org/3/library/imp.html
+    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())
diff --git a/src/sql.py b/src/sql.py
index db974e3..2064133 100644
--- a/src/sql.py
+++ b/src/sql.py
@@ -1,19 +1,28 @@
 import sqlalchemy
 
 class SQL(object):
-    """TODO"""
+    """Wrap SQLAlchemy to provide a simple SQL API."""
 
     def __init__(self, url):
-        """TODO"""
+        """
+        Create instance of sqlalchemy.engine.Engine.
+
+        URL should be a string that indicates database dialect and connection arguments.
+
+        http://docs.sqlalchemy.org/en/latest/core/engines.html#sqlalchemy.create_engine
+        """
         try:
             self.engine = sqlalchemy.create_engine(url)
         except Exception as e:
             raise RuntimeError(e)
 
     def execute(self, text, *multiparams, **params):
-        """TODO"""
+        """
+        Execute a SQL statement.
+        """
         try:
 
+            # bind parameters before statement reaches database, so that bound parameters appear in exceptions
             # 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
@@ -21,23 +30,23 @@ def execute(self, text, *multiparams, **params):
             statement = sqlalchemy.text(text).bindparams(*multiparams, **params)
             result = self.engine.execute(str(statement.compile(compile_kwargs={"literal_binds": True})))
 
-            # SELECT
+            # if SELECT (or INSERT with RETURNING), return result set as list of dict objects
             if result.returns_rows:
                 rows = result.fetchall()
                 return [dict(row) for row in rows]
 
-            # INSERT
+            # if INSERT, return primary key value for a newly inserted row
             elif result.lastrowid is not None:
                 return result.lastrowid
 
-            # DELETE, UPDATE
+            # if DELETE or UPDATE (or INSERT without RETURNING), return number of rows matched
             else:
                 return result.rowcount
 
+        # if constraint violated, return None
         except sqlalchemy.exc.IntegrityError:
             return None
 
+        # else raise error
         except Exception as e:
             raise RuntimeError(e)
-
-
diff --git a/test/python2.py b/test/python2.py
index 69de822..10f19e4 100644
--- a/test/python2.py
+++ b/test/python2.py
@@ -1,4 +1,6 @@
 import cs50
 
+from cs50 import SQL
+
 l = cs50.get_long()
 print(l)
diff --git a/test/python3.py b/test/python3.py
index 69de822..efef779 100644
--- a/test/python3.py
+++ b/test/python3.py
@@ -1,4 +1,6 @@
 import cs50
+from cs50 import SQL
+
+i = cs50.get_int()
+print(i)
 
-l = cs50.get_long()
-print(l)