diff --git a/IPython/html/base/handlers.py b/IPython/html/base/handlers.py
index 478a80dd7..f0ca19585 100644
--- a/IPython/html/base/handlers.py
+++ b/IPython/html/base/handlers.py
@@ -34,6 +34,7 @@ except ImportError:
from IPython.config import Application
from IPython.utils.path import filefind
+from IPython.utils.py3compat import string_types
# UF_HIDDEN is a stat flag not defined in the stat module.
# It is used by BSD to indicate hidden files.
@@ -307,7 +308,7 @@ class FileFindHandler(web.StaticFileHandler):
_static_paths = {}
def initialize(self, path, default_filename=None):
- if isinstance(path, basestring):
+ if isinstance(path, string_types):
path = [path]
self.root = tuple(
diff --git a/IPython/html/base/zmqhandlers.py b/IPython/html/base/zmqhandlers.py
index a552ba9c7..0d4c95af6 100644
--- a/IPython/html/base/zmqhandlers.py
+++ b/IPython/html/base/zmqhandlers.py
@@ -16,7 +16,10 @@ Authors:
# Imports
#-----------------------------------------------------------------------------
-import Cookie
+try:
+ from http.cookies import SimpleCookie # Py 3
+except ImportError:
+ from Cookie import SimpleCookie # Py 2
import logging
from tornado import web
from tornado import websocket
@@ -102,7 +105,7 @@ class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
logging.error("First ws message didn't have the form 'identity:[cookie]' - %r", msg)
try:
- self.request._cookies = Cookie.SimpleCookie(msg)
+ self.request._cookies = SimpleCookie(msg)
except:
self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True)
diff --git a/IPython/html/notebook/handlers.py b/IPython/html/notebook/handlers.py
index f9666a1c6..71b9ef9d7 100644
--- a/IPython/html/notebook/handlers.py
+++ b/IPython/html/notebook/handlers.py
@@ -16,16 +16,12 @@ Authors:
# Imports
#-----------------------------------------------------------------------------
-import os
-import json
-
from tornado import web
HTTPError = web.HTTPError
from ..base.handlers import IPythonHandler
from ..services.notebooks.handlers import _notebook_path_regex, _path_regex
-from ..utils import url_path_join, url_escape, url_unescape
-from urllib import quote
+from ..utils import url_path_join, url_escape
#-----------------------------------------------------------------------------
# Handlers
diff --git a/IPython/html/notebookapp.py b/IPython/html/notebookapp.py
index c1f1a7739..002f98406 100644
--- a/IPython/html/notebookapp.py
+++ b/IPython/html/notebookapp.py
@@ -5,6 +5,7 @@ Authors:
* Brian Granger
"""
+from __future__ import print_function
#-----------------------------------------------------------------------------
# Copyright (C) 2013 The IPython Development Team
#
@@ -628,7 +629,7 @@ class NotebookApp(BaseIPythonApplication):
time.sleep(0.1)
info = self.log.info
info('interrupted')
- print self.notebook_info()
+ print(self.notebook_info())
sys.stdout.write("Shutdown this notebook server (y/[n])? ")
sys.stdout.flush()
r,w,x = select.select([sys.stdin], [], [], 5)
@@ -639,8 +640,8 @@ class NotebookApp(BaseIPythonApplication):
ioloop.IOLoop.instance().stop()
return
else:
- print "No answer for 5s:",
- print "resuming operation..."
+ print("No answer for 5s:", end=' ')
+ print("resuming operation...")
# no answer, or answer is no:
# set it back to original SIGINT handler
# use IOLoop.add_callback because signal.signal must be called
@@ -652,7 +653,7 @@ class NotebookApp(BaseIPythonApplication):
ioloop.IOLoop.instance().stop()
def _signal_info(self, sig, frame):
- print self.notebook_info()
+ print(self.notebook_info())
def init_components(self):
"""Check the components submodule, and warn if it's unclean"""
diff --git a/IPython/html/services/notebooks/tests/test_nbmanager.py b/IPython/html/services/notebooks/tests/test_nbmanager.py
index 2aeb2769d..21358826c 100644
--- a/IPython/html/services/notebooks/tests/test_nbmanager.py
+++ b/IPython/html/services/notebooks/tests/test_nbmanager.py
@@ -1,5 +1,6 @@
# coding: utf-8
"""Tests for the notebook manager."""
+from __future__ import print_function
import os
@@ -66,7 +67,7 @@ class TestNotebookManager(TestCase):
try:
os.makedirs(os_path)
except OSError:
- print "Directory already exists."
+ print("Directory already exists.")
def test_create_notebook_model(self):
with TemporaryDirectory() as td:
diff --git a/IPython/html/services/sessions/sessionmanager.py b/IPython/html/services/sessions/sessionmanager.py
index 30d847785..efc775df8 100644
--- a/IPython/html/services/sessions/sessionmanager.py
+++ b/IPython/html/services/sessions/sessionmanager.py
@@ -22,6 +22,7 @@ import sqlite3
from tornado import web
from IPython.config.configurable import LoggingConfigurable
+from IPython.utils.py3compat import unicode_type
#-----------------------------------------------------------------------------
# Classes
@@ -66,7 +67,7 @@ class SessionManager(LoggingConfigurable):
def new_session_id(self):
"Create a uuid for a new session"
- return unicode(uuid.uuid4())
+ return unicode_type(uuid.uuid4())
def create_session(self, name=None, path=None, kernel_id=None, ws_url=None):
"""Creates a session and returns its model"""
@@ -132,7 +133,7 @@ class SessionManager(LoggingConfigurable):
query = "SELECT * FROM session WHERE %s" % (' AND '.join(conditions))
- self.cursor.execute(query, kwargs.values())
+ self.cursor.execute(query, list(kwargs.values()))
model = self.cursor.fetchone()
if model is None:
q = []
@@ -169,7 +170,7 @@ class SessionManager(LoggingConfigurable):
raise TypeError("No such column: %r" % column)
sets.append("%s=?" % column)
query = "UPDATE session SET %s WHERE session_id=?" % (', '.join(sets))
- self.cursor.execute(query, kwargs.values() + [session_id])
+ self.cursor.execute(query, list(kwargs.values()) + [session_id])
@staticmethod
def row_factory(cursor, row):
diff --git a/IPython/html/services/sessions/tests/test_sessions_api.py b/IPython/html/services/sessions/tests/test_sessions_api.py
index 363d68b7d..0d31fa766 100644
--- a/IPython/html/services/sessions/tests/test_sessions_api.py
+++ b/IPython/html/services/sessions/tests/test_sessions_api.py
@@ -1,5 +1,6 @@
"""Test the sessions web service API."""
+import errno
import io
import os
import json
@@ -51,7 +52,12 @@ class SessionAPITest(NotebookTestBase):
"""Test the sessions web service API"""
def setUp(self):
nbdir = self.notebook_dir.name
- os.mkdir(pjoin(nbdir, 'foo'))
+ try:
+ os.mkdir(pjoin(nbdir, 'foo'))
+ except OSError as e:
+ # Deleting the folder in an earlier test may have failed
+ if e.errno != errno.EEXIST:
+ raise
with io.open(pjoin(nbdir, 'foo', 'nb1.ipynb'), 'w') as f:
nb = new_notebook(name='nb1')
@@ -62,7 +68,8 @@ class SessionAPITest(NotebookTestBase):
def tearDown(self):
for session in self.sess_api.list().json():
self.sess_api.delete(session['id'])
- shutil.rmtree(pjoin(self.notebook_dir.name, 'foo'))
+ shutil.rmtree(pjoin(self.notebook_dir.name, 'foo'),
+ ignore_errors=True)
def test_create(self):
sessions = self.sess_api.list().json()
diff --git a/IPython/html/utils.py b/IPython/html/utils.py
index 7748de954..0ea9265d9 100644
--- a/IPython/html/utils.py
+++ b/IPython/html/utils.py
@@ -13,7 +13,10 @@ Authors:
#-----------------------------------------------------------------------------
import os
-from urllib import quote, unquote
+try:
+ from urllib.parse import quote, unquote
+except ImportError:
+ from urllib import quote, unquote
from IPython.utils import py3compat
diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py
index d062268ce..e94540a04 100644
--- a/IPython/testing/iptest.py
+++ b/IPython/testing/iptest.py
@@ -382,9 +382,21 @@ class StreamCapturer(Thread):
continue
ready = select(streams, [], [], 0.5)[0]
+ dead = []
with self.buffer_lock:
for fd in ready:
- self.buffer.write(os.read(fd, 1024))
+ try:
+ self.buffer.write(os.read(fd, 1024))
+ except OSError as e:
+ import errno
+ if e.errno == errno.EBADF:
+ dead.append(fd)
+ else:
+ raise
+
+ with self.streams_lock:
+ for fd in dead:
+ self.streams.remove(fd)
def add_stream(self, fd):
with self.streams_lock:
diff --git a/IPython/testing/iptestcontroller.py b/IPython/testing/iptestcontroller.py
index ce8bc8096..56e24d994 100644
--- a/IPython/testing/iptestcontroller.py
+++ b/IPython/testing/iptestcontroller.py
@@ -224,7 +224,8 @@ def prepare_controllers(options):
testgroups = options.testgroups
if testgroups:
- py_testgroups = [g for g in testgroups if g in py_test_group_names]
+ py_testgroups = [g for g in testgroups if (g in py_test_group_names) \
+ or g.startswith('IPython')]
js_testgroups = [g for g in testgroups if g in js_test_group_names]
else:
py_testgroups = py_test_group_names
@@ -236,7 +237,7 @@ def prepare_controllers(options):
c_py = [PyTestController(name) for name in py_testgroups]
configure_py_controllers(c_py, xunit=options.xunit,
- coverage=options.coverage)
+ coverage=options.coverage, extra_args=options.extra_args)
controllers = c_py + c_js
to_run = [c for c in controllers if c.will_run]
diff --git a/IPython/utils/tests/test_io.py b/IPython/utils/tests/test_io.py
index 2ece06818..4818c0740 100644
--- a/IPython/utils/tests/test_io.py
+++ b/IPython/utils/tests/test_io.py
@@ -15,14 +15,18 @@ from __future__ import print_function
import sys
-from StringIO import StringIO
from subprocess import Popen, PIPE
import unittest
import nose.tools as nt
from IPython.utils.io import Tee, capture_output
-from IPython.utils.py3compat import doctest_refactor_print
+from IPython.utils.py3compat import doctest_refactor_print, PY3
+
+if PY3:
+ from io import StringIO
+else:
+ from StringIO import StringIO
#-----------------------------------------------------------------------------
# Tests
diff --git a/setup.py b/setup.py
index 204804785..fd4b36d39 100755
--- a/setup.py
+++ b/setup.py
@@ -51,10 +51,6 @@ if os.path.exists('MANIFEST'): os.remove('MANIFEST')
from distutils.core import setup
-# On Python 3, we need distribute (new setuptools) to do the 2to3 conversion
-if PY3:
- import setuptools
-
# Our own imports
from setupbase import target_update
@@ -63,6 +59,7 @@ from setupbase import (
find_packages,
find_package_data,
find_scripts,
+ build_scripts_rename,
find_data_files,
check_for_dependencies,
git_prebuild,
@@ -266,7 +263,7 @@ if 'setuptools' in sys.modules:
setup_args['cmdclass']['develop'] = require_submodules(develop)
setuptools_extra_args['zip_safe'] = False
- setuptools_extra_args['entry_points'] = find_scripts(True)
+ setuptools_extra_args['entry_points'] = find_scripts(True, suffix = '3' if PY3 else '')
setup_args['extras_require'] = dict(
parallel = 'pyzmq>=2.1.11',
qtconsole = ['pyzmq>=2.1.11', 'pygments'],
@@ -314,29 +311,15 @@ if 'setuptools' in sys.modules:
{"install_script":
"ipython_win_post_install.py"}}
- if PY3:
- setuptools_extra_args['use_2to3'] = True
- # we try to make a 2.6, 2.7, and 3.1 to 3.3 python compatible code
- # so we explicitly disable some 2to3 fixes to be sure we aren't forgetting
- # anything.
- setuptools_extra_args['use_2to3_exclude_fixers'] = [
- 'lib2to3.fixes.fix_apply',
- 'lib2to3.fixes.fix_except',
- 'lib2to3.fixes.fix_has_key',
- 'lib2to3.fixes.fix_next',
- 'lib2to3.fixes.fix_repr',
- 'lib2to3.fixes.fix_tuple_params',
- ]
- from setuptools.command.build_py import build_py
- setup_args['cmdclass'] = {'build_py': git_prebuild('IPython', build_cmd=build_py)}
- setuptools_extra_args['entry_points'] = find_scripts(True, suffix='3')
- setuptools._dont_write_bytecode = True
else:
# If we are running without setuptools, call this function which will
# check for dependencies an inform the user what is needed. This is
# just to make life easy for users.
check_for_dependencies()
setup_args['scripts'] = find_scripts(False)
+ if PY3:
+ # Rename scripts with '3' suffix
+ setup_args['cmdclass']['build_scripts'] = build_scripts_rename
#---------------------------------------------------------------------------
# Do the actual setup now
diff --git a/setupbase.py b/setupbase.py
index 7bfdf0804..54ab2ad3e 100644
--- a/setupbase.py
+++ b/setupbase.py
@@ -28,6 +28,7 @@ try:
except:
from ConfigParser import ConfigParser
from distutils.command.build_py import build_py
+from distutils.command.build_scripts import build_scripts
from distutils.cmd import Command
from glob import glob
from subprocess import call
@@ -347,6 +348,21 @@ def find_scripts(entry_points=False, suffix=''):
]
return scripts
+class build_scripts_rename(build_scripts):
+ """Use this on Python 3 to rename scripts to ipython3 etc."""
+ _suffix = '3'
+
+ def copy_scripts(self):
+ outfiles, updated_files = super(build_scripts_rename, self).copy_scripts()
+ new_outfiles = [p + self._suffix for p in outfiles]
+ updated_files = [p + self._suffix for p in updated_files]
+ for old, new in zip(outfiles, new_outfiles):
+ if os.path.exists(new):
+ os.unlink(new)
+ self.move_file(old, new)
+ return new_outfiles, updated_files
+
+
#---------------------------------------------------------------------------
# Verify all dependencies
#---------------------------------------------------------------------------