diff --git a/.gitignore b/.gitignore index d2d6f360b..83fea0f27 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ nosetests.xml .mr.developer.cfg .project .pydevproject +.DS_Store diff --git a/HACKING.txt b/HACKING.txt index 5a16441f7..f53e1ef59 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -1,10 +1,8 @@ Development setup ================= -To create a buildout, - - $ python bootstrap.py - $ bin/buildout +Running nose tests with IPython is tricky, so there's a +run_tests.sh script for it. Release HOWTO ============= diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..fa5629966 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2014 Catherine Devlin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in index 1e7e56847..910adaed4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include README.rst include NEWS.txt +include LICENSE diff --git a/NEWS.txt b/NEWS.txt index 4a2a8954e..23a10a56d 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -134,3 +134,14 @@ result sets ------- * Avoid "connection busy" error for SQL Server (thanks Andrés Celis) + +0.3.8 +----- + +* Stop warnings for deprecated use of IPython 3 traitlets in IPython 4 (thanks graphaelli; also stonebig, aebrahim, mccahill) +* README update for keeping connection info private, from eshilts + +0.3.9 +----- + +* Fix truth value of DataFrame error (thanks michael-erasmus) diff --git a/README.rst b/README.rst index 1436d1bc9..7bf8a9a23 100644 --- a/README.rst +++ b/README.rst @@ -64,6 +64,16 @@ an existing connection by username@database ====================== Poet 733 +For secure access, you may dynamically access your credentials (e.g. from your system environment or `getpass.getpass`) to avoid storing your password in the notebook itself. Use the `$` before any variable to access it in your `%sql` command. + +.. code-block:: python + + In [11]: user = os.getenv('SOME_USER') + ....: password = os.getenv('SOME_PASSWORD') + ....: connection_string = "postgresql://{user}:{password}@localhost/some_database".format(user=user, password=password) + ....: %sql $connection_string + Out[11]: u'Connected: some_user@some_database' + You may use multiple SQL statements inside a single cell, but you will only see any query results from the last of them, so this really only makes sense for statements with no output @@ -129,19 +139,24 @@ set (usually with a `LIMIT` clause in the SQL). `displaylimit` is similar, but the entire result set is still pulled into memory (for later analysis); only the screen display is truncated. +For student use, default the limits for rows returned (100000) and rows +displayed (1000) to sizes that will not crash their web browser or pound +the SQL server. You can still reset the limits if you +want, but this raises the barrier to shooting yourself in the foot. + .. code-block:: python In [2]: %config SqlMagic SqlMagic options -------------- SqlMagic.autolimit= - Current: 0 + Current: 100000 Automatically limit the size of the returned result sets SqlMagic.autopandas= Current: False Return Pandas DataFrames instead of regular result sets SqlMagic.displaylimit= - Current: 0 + Current: 1000 Automatically limit the number of rows displayed (full result set is still stored) SqlMagic.feedback= @@ -157,6 +172,8 @@ only the screen display is truncated. In[3]: %config SqlMagic.feedback = False +Please note: if you have autopandas set to true, the displaylimit option will not apply. You can set the pandas display limit by using the pandas ``max_rows`` option as described in the `pandas documentation `_. + Pandas ------ @@ -234,6 +251,8 @@ Credits - Thomas Kluyver and Steve Holden for debugging help - Berton Earnshaw for DSN connection syntax - Andrés Celis for SQL Server bugfix +- Michael Erasmus for DataFrame truth bugfix +- Noam Finkelstein for README clarification .. _Distribute: http://pypi.python.org/pypi/distribute .. _Buildout: http://www.buildout.org/ diff --git a/run_tests.sh b/run_tests.sh index fbd434f75..99688775a 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1 +1,2 @@ ipython -c "import nose; nose.run()" +# Insert breakpoints with `from nose.tools import set_trace; set_trace()` diff --git a/setup.py b/setup.py index 48555dc04..c9f727b59 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ NEWS = open(os.path.join(here, 'NEWS.txt')).read() -version = '0.3.7.1' +version = '0.3.9' install_requires = [ 'prettytable', @@ -14,6 +14,7 @@ 'sqlalchemy>=0.6.7', 'sqlparse', 'six', + 'ipython-genutils>=0.1.0', ] @@ -33,7 +34,7 @@ keywords='database ipython postgresql mysql', author='Catherine Devlin', author_email='catherine.devlin@gmail.com', - url='pypi.python.org/pypi/ipython-sql', + url='https://pypi.python.org/pypi/ipython-sql', license='MIT', packages=find_packages('src'), package_dir = {'': 'src'}, diff --git a/src/sql/magic.py b/src/sql/magic.py index 4e30fa51f..0244fb3be 100644 --- a/src/sql/magic.py +++ b/src/sql/magic.py @@ -1,7 +1,11 @@ import re from IPython.core.magic import Magics, magics_class, cell_magic, line_magic, needs_local_scope -from IPython.config.configurable import Configurable -from IPython.utils.traitlets import Bool, Int, Unicode +try: + from traitlets.config.configurable import Configurable + from traitlets import Bool, Int, Unicode +except ImportError: + from IPython.config.configurable import Configurable + from IPython.utils.traitlets import Bool, Int, Unicode try: from pandas.core.frame import DataFrame, Series except ImportError: @@ -21,10 +25,10 @@ class SqlMagic(Magics, Configurable): Provides the %%sql magic.""" - autolimit = Int(0, config=True, help="Automatically limit the size of the returned result sets") + autolimit = Int(100000, config=True, help="Automatically limit the size of the returned result sets") style = Unicode('DEFAULT', config=True, help="Set the table printing style to any of prettytable's defined styles (currently DEFAULT, MSWORD_FRIENDLY, PLAIN_COLUMNS, RANDOM)") short_errors = Bool(True, config=True, help="Don't display the full traceback on SQL Programming Error") - displaylimit = Int(0, config=True, help="Automatically limit the number of rows displayed (full result set is still stored)") + displaylimit = Int(1000, config=True, help="Automatically limit the number of rows displayed (full result set is still stored)") autopandas = Bool(False, config=True, help="Return Pandas DataFrames instead of regular result sets") column_local_vars = Bool(False, config=True, help="Return data into local variables from column names") feedback = Bool(True, config=True, help="Print number of rows affected by DML") @@ -82,7 +86,7 @@ def execute(self, line, cell='', local_ns={}): try: result = sql.run.run(conn, parsed['sql'], self, user_ns) - if result and ~isinstance(result, str) and self.column_local_vars: + if result is not None and ~isinstance(result, str) and self.column_local_vars: #Instead of returning values, set variables directly in the #users namespace. Variable names given by column names diff --git a/src/tests/test_magic.py b/src/tests/test_magic.py index 527b6f417..0d553d720 100644 --- a/src/tests/test_magic.py +++ b/src/tests/test_magic.py @@ -125,3 +125,10 @@ def test_bind_vars(): ip.user_global_ns['x'] = 22 result = ip.run_line_magic('sql', "sqlite:// SELECT :x") assert result[0][0] == 22 + +@with_setup(_setup, _teardown) +def test_autopandas(): + ip.run_line_magic('config', "SqlMagic.autopandas = True") + dframe = ip.run_cell("%sql SELECT * FROM test;") + assert dframe.success + assert dframe.result.name[0] == 'foo' diff --git a/src/tests/test_parse.py b/src/tests/test_parse.py index 84f65f50e..143c02be2 100644 --- a/src/tests/test_parse.py +++ b/src/tests/test_parse.py @@ -1,6 +1,9 @@ from sql.parse import parse from six.moves import configparser -from IPython.config.configurable import Configurable +try: + from traitlets.config.configurable import Configurable +except ImportError: + from IPython.config.configurable import Configurable empty_config = Configurable()