diff --git a/IPython/config/loader.py b/IPython/config/loader.py index 1e1620e..3cbd132 100644 --- a/IPython/config/loader.py +++ b/IPython/config/loader.py @@ -18,13 +18,13 @@ Authors # Imports #----------------------------------------------------------------------------- -import __builtin__ +import __builtin__ as builtin_mod import re import sys from IPython.external import argparse from IPython.utils.path import filefind, get_ipython_dir -from IPython.utils import warn +from IPython.utils import py3compat, warn #----------------------------------------------------------------------------- # Exceptions @@ -131,7 +131,7 @@ class Config(dict): # that you can't have section or attribute names that are # builtins. try: - return getattr(__builtin__, key) + return getattr(builtin_mod, key) except AttributeError: pass if is_section_key(key): @@ -146,7 +146,7 @@ class Config(dict): def __setitem__(self, key, value): # Don't allow names in __builtin__ to be modified. - if hasattr(__builtin__, key): + if hasattr(builtin_mod, key): raise ConfigError('Config variable names cannot have the same name ' 'as a Python builtin: %s' % key) if self._is_section_key(key): @@ -312,7 +312,7 @@ class PyFileConfigLoader(FileConfigLoader): namespace = dict(load_subconfig=load_subconfig, get_config=get_config) fs_encoding = sys.getfilesystemencoding() or 'ascii' conf_filename = self.full_filename.encode(fs_encoding) - execfile(conf_filename, namespace) + py3compat.execfile(conf_filename, namespace) def _convert_to_config(self): if self.data is None: @@ -586,13 +586,7 @@ class ArgParseConfigLoader(CommandLineConfigLoader): def _parse_args(self, args): """self.parser->self.parsed_data""" # decode sys.argv to support unicode command-line options - uargs = [] - for a in args: - if isinstance(a, str): - # don't decode if we already got unicode - a = a.decode(sys.stdin.encoding or - sys.getdefaultencoding()) - uargs.append(a) + uargs = [py3compat.cast_unicode(a) for a in args] self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs) def _convert_to_config(self): diff --git a/IPython/config/profile/pysh/ipython_config.py b/IPython/config/profile/pysh/ipython_config.py index cf41388..94afe51 100644 --- a/IPython/config/profile/pysh/ipython_config.py +++ b/IPython/config/profile/pysh/ipython_config.py @@ -5,9 +5,9 @@ app = c.InteractiveShellApp # and merge it into the current one. load_subconfig('ipython_config.py', profile='default') -c.InteractiveShell.prompt_in1 = '\C_LightGreen\u@\h\C_LightBlue[\C_LightCyan\Y1\C_LightBlue]\C_Green|\#> ' -c.InteractiveShell.prompt_in2 = '\C_Green|\C_LightGreen\D\C_Green> ' -c.InteractiveShell.prompt_out = '<\#> ' +c.InteractiveShell.prompt_in1 = r'\C_LightGreen\u@\h\C_LightBlue[\C_LightCyan\Y1\C_LightBlue]\C_Green|\#> ' +c.InteractiveShell.prompt_in2 = r'\C_Green|\C_LightGreen\D\C_Green> ' +c.InteractiveShell.prompt_out = r'<\#> ' c.InteractiveShell.prompts_pad_left = True @@ -27,4 +27,4 @@ lines = """ if hasattr(app, 'exec_lines'): app.exec_lines.append(lines) else: - app.exec_lines = [lines] \ No newline at end of file + app.exec_lines = [lines] diff --git a/IPython/config/profile/python3/ipython_config.py b/IPython/config/profile/python3/ipython_config.py new file mode 100644 index 0000000..f710a56 --- /dev/null +++ b/IPython/config/profile/python3/ipython_config.py @@ -0,0 +1,11 @@ +c = get_config() + +# If the master config file uses syntax that's invalid in Python 3, we'll skip +# it and just use the factory defaults. +try: + load_subconfig('ipython_config.py', profile='default') +except Exception: + pass +else: + # We reset exec_lines in case they're not compatible with Python 3. + c.InteractiveShellApp.exec_lines = [] diff --git a/IPython/config/tests/test_loader.py b/IPython/config/tests/test_loader.py index 6373d37..a5bd34a 100755 --- a/IPython/config/tests/test_loader.py +++ b/IPython/config/tests/test_loader.py @@ -48,7 +48,7 @@ c = get_config() c.a=10 c.b=20 c.Foo.Bar.value=10 -c.Foo.Bam.value=range(10) +c.Foo.Bam.value=list(range(10)) # list() is just so it's the same on Python 3 c.D.C.value='hi there' """ diff --git a/IPython/core/alias.py b/IPython/core/alias.py index 7f2b2d7..4b80770 100755 --- a/IPython/core/alias.py +++ b/IPython/core/alias.py @@ -38,7 +38,7 @@ from IPython.utils.warn import warn, error #----------------------------------------------------------------------------- # This is used as the pattern for calls to split_user_input. -shell_line_split = re.compile(r'^(\s*)(\S*\s*)(.*$)') +shell_line_split = re.compile(r'^(\s*)()(\S+)(.*$)') def default_aliases(): """Return list of shell aliases to auto-define. @@ -222,7 +222,7 @@ class AliasManager(Configurable): <16> 'q:/opt/np/notepad++.exe myfile.txt' """ - pre,fn,rest = split_user_input(line) + pre,_,fn,rest = split_user_input(line) res = pre + self.expand_aliases(fn, rest) return res @@ -242,7 +242,7 @@ class AliasManager(Configurable): done = set() while 1: - pre,fn,rest = split_user_input(line, shell_line_split) + pre,_,fn,rest = split_user_input(line, shell_line_split) if fn in self.alias_table: if fn in done: warn("Cyclic alias definition, repeated '%s'" % fn) diff --git a/IPython/core/application.py b/IPython/core/application.py index 2b12ada..3384f62 100644 --- a/IPython/core/application.py +++ b/IPython/core/application.py @@ -40,6 +40,7 @@ from IPython.core import release, crashhandler from IPython.core.profiledir import ProfileDir, ProfileDirError from IPython.utils.path import get_ipython_dir, get_ipython_package_dir from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict +from IPython.utils import py3compat #----------------------------------------------------------------------------- # Classes and functions @@ -102,9 +103,12 @@ class BaseIPythonApplication(Application): def _config_file_paths_default(self): return [os.getcwdu()] - profile = Unicode(u'default', config=True, + profile = Unicode(u'', config=True, help="""The IPython profile to use.""" ) + def _profile_default(self): + return "python3" if py3compat.PY3 else "default" + def _profile_changed(self, name, old, new): self.builtin_profile_dir = os.path.join( get_ipython_package_dir(), u'config', u'profile', new @@ -222,7 +226,7 @@ class BaseIPythonApplication(Application): p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config) except ProfileDirError: # not found, maybe create it (always create default profile) - if self.auto_create or self.profile=='default': + if self.auto_create or self.profile==self._profile_default(): try: p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config) except ProfileDirError: diff --git a/IPython/core/completerlib.py b/IPython/core/completerlib.py index 98ba2ec..15e5b7e 100644 --- a/IPython/core/completerlib.py +++ b/IPython/core/completerlib.py @@ -30,6 +30,7 @@ from zipimport import zipimporter # Our own imports from IPython.core.completer import expand_user, compress_user from IPython.core.error import TryNext +from IPython.utils import py3compat # FIXME: this should be pulled in with the right call via the component system from IPython.core.ipapi import get as get_ipython @@ -66,10 +67,9 @@ def shlex_split(x): # Example: # %run "c:/python -> ['%run','"c:/python'] - # shlex.split has unicode bugs, so encode first to str - if isinstance(x, unicode): - # don't raise errors on encoding: - x = x.encode(sys.stdin.encoding or sys.getdefaultencoding(), 'replace') + # shlex.split has unicode bugs in Python 2, so encode first to str + if not py3compat.PY3: + x = py3compat.cast_bytes(x) endofline = [] while x != '': diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index cdc3649..90e66e4 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -29,6 +29,7 @@ from StringIO import StringIO from IPython.config.configurable import Configurable from IPython.lib import pretty from IPython.utils.traitlets import Bool, Dict, Int, Unicode, CUnicode, ObjectName +from IPython.utils.py3compat import unicode_to_str #----------------------------------------------------------------------------- @@ -439,7 +440,7 @@ class PlainTextFormatter(BaseFormatter): # ensure that stream does not get a mix of unicode and bytestrings, # or it will cause trouble. printer = pretty.RepresentationPrinter(stream, self.verbose, - self.max_width, self.newline.encode(), + self.max_width, unicode_to_str(self.newline), singleton_pprinters=self.singleton_printers, type_pprinters=self.type_printers, deferred_pprinters=self.deferred_printers) diff --git a/IPython/core/history.py b/IPython/core/history.py index 7666731..186310f 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -75,8 +75,10 @@ class HistoryManager(Configurable): # History saving in separate thread save_thread = Instance('IPython.core.history.HistorySavingThread') - # N.B. Event is a function returning an instance of _Event. - save_flag = Instance(threading._Event) + try: # Event is a function returning an instance of _Event... + save_flag = Instance(threading._Event) + except AttributeError: # ...until Python 3.3, when it's a class. + save_flag = Instance(threading.Event) # Private interface # Variables used to store the three last inputs from the user. On each new diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index 0ea51ae..0856a53 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -74,7 +74,9 @@ import tokenize from StringIO import StringIO # IPython modules +from IPython.core.splitinput import split_user_input, LineInfo from IPython.utils.text import make_quoted_expr +from IPython.utils.py3compat import cast_unicode #----------------------------------------------------------------------------- # Globals @@ -481,132 +483,10 @@ class InputSplitter(object): # Functions and classes for IPython-specific syntactic support #----------------------------------------------------------------------------- -# RegExp for splitting line contents into pre-char//first word-method//rest. -# For clarity, each group in on one line. - -line_split = re.compile(""" - ^(\s*) # any leading space - ([,;/%]|!!?|\?\??) # escape character or characters - \s*(%?[\w\.\*]*) # function/method, possibly with leading % - # to correctly treat things like '?%magic' - (\s+.*$|$) # rest of line - """, re.VERBOSE) - - -def split_user_input(line): - """Split user input into early whitespace, esc-char, function part and rest. - - This is currently handles lines with '=' in them in a very inconsistent - manner. - - Examples - ======== - >>> split_user_input('x=1') - ('', '', 'x=1', '') - >>> split_user_input('?') - ('', '?', '', '') - >>> split_user_input('??') - ('', '??', '', '') - >>> split_user_input(' ?') - (' ', '?', '', '') - >>> split_user_input(' ??') - (' ', '??', '', '') - >>> split_user_input('??x') - ('', '??', 'x', '') - >>> split_user_input('?x=1') - ('', '', '?x=1', '') - >>> split_user_input('!ls') - ('', '!', 'ls', '') - >>> split_user_input(' !ls') - (' ', '!', 'ls', '') - >>> split_user_input('!!ls') - ('', '!!', 'ls', '') - >>> split_user_input(' !!ls') - (' ', '!!', 'ls', '') - >>> split_user_input(',ls') - ('', ',', 'ls', '') - >>> split_user_input(';ls') - ('', ';', 'ls', '') - >>> split_user_input(' ;ls') - (' ', ';', 'ls', '') - >>> split_user_input('f.g(x)') - ('', '', 'f.g(x)', '') - >>> split_user_input('f.g (x)') - ('', '', 'f.g', '(x)') - >>> split_user_input('?%hist') - ('', '?', '%hist', '') - >>> split_user_input('?x*') - ('', '?', 'x*', '') - """ - match = line_split.match(line) - if match: - lspace, esc, fpart, rest = match.groups() - else: - # print "match failed for line '%s'" % line - try: - fpart, rest = line.split(None, 1) - except ValueError: - # print "split failed for line '%s'" % line - fpart, rest = line,'' - lspace = re.match('^(\s*)(.*)', line).groups()[0] - esc = '' - - # fpart has to be a valid python identifier, so it better be only pure - # ascii, no unicode: - try: - fpart = fpart.encode('ascii') - except UnicodeEncodeError: - lspace = unicode(lspace) - rest = fpart + u' ' + rest - fpart = u'' - - #print 'line:<%s>' % line # dbg - #print 'esc <%s> fpart <%s> rest <%s>' % (esc,fpart.strip(),rest) # dbg - return lspace, esc, fpart.strip(), rest.lstrip() - - # The escaped translators ALL receive a line where their own escape has been # stripped. Only '?' is valid at the end of the line, all others can only be # placed at the start. -class LineInfo(object): - """A single line of input and associated info. - - This is a utility class that mostly wraps the output of - :func:`split_user_input` into a convenient object to be passed around - during input transformations. - - Includes the following as properties: - - line - The original, raw line - - lspace - Any early whitespace before actual text starts. - - esc - The initial esc character (or characters, for double-char escapes like - '??' or '!!'). - - fpart - The 'function part', which is basically the maximal initial sequence - of valid python identifiers and the '.' character. This is what is - checked for alias and magic transformations, used for auto-calling, - etc. - - rest - Everything else on the line. - """ - def __init__(self, line): - self.line = line - self.lspace, self.esc, self.fpart, self.rest = \ - split_user_input(line) - - def __str__(self): - return "LineInfo [%s|%s|%s|%s]" % (self.lspace, self.esc, - self.fpart, self.rest) - - # Transformations of the special syntaxes that don't rely on an explicit escape # character but instead on patterns on the input line @@ -680,19 +560,20 @@ def _make_help_call(target, esc, lspace, next_input=None): method = 'pinfo2' if esc == '??' \ else 'psearch' if '*' in target \ else 'pinfo' - + arg = make_quoted_expr(" ".join([method, target])) + if next_input: - tpl = '%sget_ipython().magic(u"%s %s", next_input=%s)' - return tpl % (lspace, method, target, make_quoted_expr(next_input)) + tpl = '%sget_ipython().magic(%s, next_input=%s)' + return tpl % (lspace, arg, make_quoted_expr(next_input)) else: - return '%sget_ipython().magic(u"%s %s")' % (lspace, method, target) + return '%sget_ipython().magic(%s)' % (lspace, arg) _initial_space_re = re.compile(r'\s*') _help_end_re = re.compile(r"""(%? - [a-zA-Z_*][a-zA-Z0-9_*]* # Variable name - (\.[a-zA-Z_*][a-zA-Z0-9_*]*)* # .etc.etc + [a-zA-Z_*][\w*]* # Variable name + (\.[a-zA-Z_*][\w*]*)* # .etc.etc ) - (\?\??)$ # ? or ??""", + (\?\??)$ # ? or ??""", re.VERBOSE) def transform_help_end(line): """Translate lines with ?/?? at the end""" @@ -702,7 +583,6 @@ def transform_help_end(line): target = m.group(1) esc = m.group(3) lspace = _initial_space_re.match(line).group(0) - newline = _make_help_call(target, esc, lspace) # If we're mid-command, put it back on the next prompt for the user. next_input = line.rstrip('?') if line.strip() != m.group(0) else None @@ -730,14 +610,14 @@ class EscapedTransformer(object): def _tr_system(line_info): "Translate lines escaped with: !" cmd = line_info.line.lstrip().lstrip(ESC_SHELL) - return '%sget_ipython().system(%s)' % (line_info.lspace, + return '%sget_ipython().system(%s)' % (line_info.pre, make_quoted_expr(cmd)) @staticmethod def _tr_system2(line_info): "Translate lines escaped with: !!" cmd = line_info.line.lstrip()[2:] - return '%sget_ipython().getoutput(%s)' % (line_info.lspace, + return '%sget_ipython().getoutput(%s)' % (line_info.pre, make_quoted_expr(cmd)) @staticmethod @@ -747,33 +627,33 @@ class EscapedTransformer(object): if not line_info.line[1:]: return 'get_ipython().show_usage()' - return _make_help_call(line_info.fpart, line_info.esc, line_info.lspace) + return _make_help_call(line_info.ifun, line_info.esc, line_info.pre) @staticmethod def _tr_magic(line_info): "Translate lines escaped with: %" tpl = '%sget_ipython().magic(%s)' - cmd = make_quoted_expr(' '.join([line_info.fpart, - line_info.rest]).strip()) - return tpl % (line_info.lspace, cmd) + cmd = make_quoted_expr(' '.join([line_info.ifun, + line_info.the_rest]).strip()) + return tpl % (line_info.pre, cmd) @staticmethod def _tr_quote(line_info): "Translate lines escaped with: ," - return '%s%s("%s")' % (line_info.lspace, line_info.fpart, - '", "'.join(line_info.rest.split()) ) + return '%s%s("%s")' % (line_info.pre, line_info.ifun, + '", "'.join(line_info.the_rest.split()) ) @staticmethod def _tr_quote2(line_info): "Translate lines escaped with: ;" - return '%s%s("%s")' % (line_info.lspace, line_info.fpart, - line_info.rest) + return '%s%s("%s")' % (line_info.pre, line_info.ifun, + line_info.the_rest) @staticmethod def _tr_paren(line_info): "Translate lines escaped with: /" - return '%s%s(%s)' % (line_info.lspace, line_info.fpart, - ", ".join(line_info.rest.split())) + return '%s%s(%s)' % (line_info.pre, line_info.ifun, + ", ".join(line_info.the_rest.split())) def __call__(self, line): """Class to transform lines that are explicitly escaped out. @@ -837,13 +717,12 @@ class IPythonInputSplitter(InputSplitter): return super(IPythonInputSplitter, self).push(lines) # We must ensure all input is pure unicode - if type(lines)==str: - lines = lines.decode(self.encoding) + lines = cast_unicode(lines, self.encoding) lines_list = lines.splitlines() transforms = [transform_ipy_prompt, transform_classic_prompt, - transform_escaped, transform_help_end, + transform_help_end, transform_escaped, transform_assign_system, transform_assign_magic] # Transform logic diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index f61b348..58bca58 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -17,7 +17,7 @@ from __future__ import with_statement from __future__ import absolute_import -import __builtin__ +import __builtin__ as builtin_mod import __future__ import abc import ast @@ -29,7 +29,10 @@ import re import sys import tempfile import types -from contextlib import nested +try: + from contextlib import nested +except: + from IPython.utils.nested_context import nested from IPython.config.configurable import SingletonConfigurable from IPython.core import debugger, oinspect @@ -61,6 +64,7 @@ from IPython.core.profiledir import ProfileDir from IPython.external.Itpl import ItplNS from IPython.utils import PyColorize from IPython.utils import io +from IPython.utils import py3compat from IPython.utils.doctestreload import doctest_reload from IPython.utils.io import ask_yes_no, rprint from IPython.utils.ipstruct import Struct @@ -410,7 +414,10 @@ class InteractiveShell(SingletonConfigurable, Magic): # We save this here in case user code replaces raw_input, but it needs # to be after init_readline(), because PyPy's readline works by replacing # raw_input. - self.raw_input_original = raw_input + if py3compat.PY3: + self.raw_input_original = input + else: + self.raw_input_original = raw_input # init_completer must come after init_readline, because it needs to # know whether readline is present or not system-wide to configure the # completers, since the completion machinery can now operate @@ -925,7 +932,7 @@ class InteractiveShell(SingletonConfigurable, Magic): self.ns_table = {'user':user_ns, 'user_global':user_global_ns, 'internal':self.internal_ns, - 'builtin':__builtin__.__dict__ + 'builtin':builtin_mod.__dict__ } # Similarly, track all namespaces where references can be held and that @@ -979,13 +986,13 @@ class InteractiveShell(SingletonConfigurable, Magic): # Set __name__ to __main__ to better match the behavior of the # normal interpreter. user_ns = {'__name__' :'__main__', - '__builtin__' : __builtin__, - '__builtins__' : __builtin__, + py3compat.builtin_mod_name: builtin_mod, + '__builtins__' : builtin_mod, } else: user_ns.setdefault('__name__','__main__') - user_ns.setdefault('__builtin__',__builtin__) - user_ns.setdefault('__builtins__',__builtin__) + user_ns.setdefault(py3compat.builtin_mod_name,builtin_mod) + user_ns.setdefault('__builtins__',builtin_mod) if user_global_ns is None: user_global_ns = user_ns @@ -1049,7 +1056,7 @@ class InteractiveShell(SingletonConfigurable, Magic): # For more details: # http://mail.python.org/pipermail/python-dev/2001-April/014068.html - ns = dict(__builtin__ = __builtin__) + ns = dict(__builtin__ = builtin_mod) # Put 'help' in the user namespace try: @@ -1255,13 +1262,9 @@ class InteractiveShell(SingletonConfigurable, Magic): Has special code to detect magic functions. """ - #oname = oname.strip() + oname = oname.strip() #print '1- oname: <%r>' % oname # dbg - try: - oname = oname.strip().encode('ascii') - #print '2- oname: <%r>' % oname # dbg - except UnicodeEncodeError: - print 'Python identifiers can only contain ascii characters.' + if not py3compat.isidentifier(oname.lstrip(ESC_MAGIC), dotted=True): return dict(found=False) alias_ns = None @@ -1271,7 +1274,7 @@ class InteractiveShell(SingletonConfigurable, Magic): # find things in the same order that Python finds them. namespaces = [ ('Interactive', self.user_ns), ('IPython internal', self.internal_ns), - ('Python builtin', __builtin__.__dict__), + ('Python builtin', builtin_mod.__dict__), ('Alias', self.alias_manager.alias_table), ] alias_ns = self.alias_manager.alias_table @@ -1283,8 +1286,8 @@ class InteractiveShell(SingletonConfigurable, Magic): # We need to special-case 'print', which as of python2.6 registers as a # function but should only be treated as one if print_function was # loaded with a future import. In this case, just bail. - if (oname == 'print' and not (self.compile.compiler_flags & - __future__.CO_FUTURE_PRINT_FUNCTION)): + if (oname == 'print' and not py3compat.PY3 and not \ + (self.compile.compiler_flags & __future__.CO_FUTURE_PRINT_FUNCTION)): return {'found':found, 'obj':obj, 'namespace':ospace, 'ismagic':ismagic, 'isalias':isalias, 'parent':parent} @@ -1526,7 +1529,7 @@ class InteractiveShell(SingletonConfigurable, Magic): if etype is SyntaxError: # Though this won't be called by syntax errors in the input - # line, there may be SyntaxError cases whith imported code. + # line, there may be SyntaxError cases with imported code. self.showsyntaxerror(filename) elif etype is UsageError: print "UsageError:", value @@ -1679,7 +1682,9 @@ class InteractiveShell(SingletonConfigurable, Magic): # Remove some chars from the delimiters list. If we encounter # unicode chars, discard them. - delims = readline.get_completer_delims().encode("ascii", "ignore") + delims = readline.get_completer_delims() + if not py3compat.PY3: + delims = delims.encode("ascii", "ignore") for d in self.readline_remove_delims: delims = delims.replace(d, "") delims = delims.replace(ESC_MAGIC, '') @@ -1701,7 +1706,8 @@ class InteractiveShell(SingletonConfigurable, Magic): include_latest=True): if cell.strip(): # Ignore blank lines for line in cell.splitlines(): - self.readline.add_history(line.encode(stdin_encoding, 'replace')) + self.readline.add_history(py3compat.unicode_to_str(line, + stdin_encoding)) def set_next_input(self, s): """ Sets the 'default' input string for the next command line. @@ -1907,8 +1913,6 @@ class InteractiveShell(SingletonConfigurable, Magic): self.define_magic('foo',foo_impl) """ - - import new im = types.MethodType(func,self) old = getattr(self, "magic_" + magicname, None) setattr(self, "magic_" + magicname, im) @@ -2175,15 +2179,10 @@ class InteractiveShell(SingletonConfigurable, Magic): # behavior of running a script from the system command line, where # Python inserts the script's directory into sys.path dname = os.path.dirname(fname) - - if isinstance(fname, unicode): - # execfile uses default encoding instead of filesystem encoding - # so unicode filenames will fail - fname = fname.encode(sys.getfilesystemencoding() or sys.getdefaultencoding()) with prepended_to_syspath(dname): try: - execfile(fname,*where) + py3compat.execfile(fname,*where) except SystemExit, status: # If the call was made with 0 or None exit status (sys.exit(0) # or sys.exit() ), don't bother showing a traceback, as both of @@ -2455,7 +2454,7 @@ class InteractiveShell(SingletonConfigurable, Magic): # Skip our own frame in searching for locals: sys._getframe(depth+1).f_locals # locals ) - return str(res).decode(res.codec) + return py3compat.str_to_unicode(str(res), res.codec) def mktempfile(self, data=None, prefix='ipython_edit_'): """Make a new tempfile and return its filename. diff --git a/IPython/core/macro.py b/IPython/core/macro.py index e87ea78..86031eb 100644 --- a/IPython/core/macro.py +++ b/IPython/core/macro.py @@ -10,6 +10,8 @@ import re import sys +from IPython.utils import py3compat + coding_declaration = re.compile(r"#\s*coding[:=]\s*([-\w.]+)") class Macro(object): @@ -37,8 +39,7 @@ class Macro(object): self.value = code + '\n' def __str__(self): - enc = sys.stdin.encoding or sys.getdefaultencoding() - return self.value.encode(enc, "replace") + return py3compat.unicode_to_str(self.value) def __unicode__(self): return self.value diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 64cd121..8735c4d 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -15,7 +15,7 @@ # Imports #----------------------------------------------------------------------------- -import __builtin__ +import __builtin__ as builtin_mod import __future__ import bdb import inspect @@ -52,6 +52,7 @@ from IPython.core import magic_arguments, page from IPython.core.prefilter import ESC_MAGIC from IPython.lib.pylabtools import mpl_runner from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import py3compat from IPython.utils.io import file_read, nlprint from IPython.utils.path import get_py_filename, unquote_filename from IPython.utils.process import arg_split, abbrev_cwd @@ -60,7 +61,6 @@ from IPython.utils.text import LSString, SList, format_screen from IPython.utils.timing import clock, clock2 from IPython.utils.warn import warn, error from IPython.utils.ipstruct import Struct -import IPython.utils.generics #----------------------------------------------------------------------------- # Utility functions @@ -676,7 +676,7 @@ Currently the magic system has the following functions:\n""" %psearch -a _* list objects beginning with a single underscore""" try: - parameter_s = parameter_s.encode('ascii') + parameter_s.encode('ascii') except UnicodeEncodeError: print 'Python identifiers can only contain ascii characters.' return @@ -1729,7 +1729,7 @@ Currently the magic system has the following functions:\n""" # Since this seems to be done by the interpreter itself, the best # we can do is to at least restore __builtins__ for the user on # exit. - self.shell.user_ns['__builtins__'] = __builtin__ + self.shell.user_ns['__builtins__'] = builtin_mod # Ensure key global structures are restored sys.argv = save_argv @@ -2085,11 +2085,9 @@ Currently the magic system has the following functions:\n""" except (TypeError, ValueError) as e: print e.args[0] return - if isinstance(cmds, unicode): - cmds = cmds.encode("utf-8") - with open(fname,'w') as f: - f.write("# coding: utf-8\n") - f.write(cmds) + with py3compat.open(fname,'w', encoding="utf-8") as f: + f.write(u"# coding: utf-8\n") + f.write(py3compat.cast_unicode(cmds)) print 'The following commands were written to file `%s`:' % fname print cmds diff --git a/IPython/core/oinspect.py b/IPython/core/oinspect.py index 6e9951d..4597467 100644 --- a/IPython/core/oinspect.py +++ b/IPython/core/oinspect.py @@ -24,12 +24,16 @@ import os import sys import types from collections import namedtuple -from itertools import izip_longest +try: + from itertools import izip_longest +except ImportError: + from itertools import zip_longest as izip_longest # IPython's own from IPython.core import page from IPython.utils import PyColorize from IPython.utils import io +from IPython.utils import py3compat from IPython.utils.text import indent from IPython.utils.wildcard import list_namespace from IPython.utils.coloransi import * @@ -249,7 +253,7 @@ class Inspector: try: # We need a plain string here, NOT unicode! hdef = oname + inspect.formatargspec(*getargspec(obj)) - return hdef.encode('ascii') + return py3compat.unicode_to_str(hdef, 'ascii') except: return None @@ -362,7 +366,7 @@ class Inspector: except: self.noinfo('source',oname) else: - page.page(self.format(src)) + page.page(self.format(py3compat.unicode_to_str(src))) def pfile(self,obj,oname=''): """Show the whole file where an object was defined.""" @@ -455,7 +459,7 @@ class Inspector: # Source or docstring, depending on detail level and whether # source found. if detail_level > 0 and info['source'] is not None: - displayfields.append(("Source", info['source'])) + displayfields.append(("Source", self.format(py3compat.unicode_to_str(info['source'])))) elif info['docstring'] is not None: displayfields.append(("Docstring", info["docstring"])) @@ -604,12 +608,11 @@ class Inspector: source = None try: try: - src = getsource(obj,binary_file) + source = getsource(obj,binary_file) except TypeError: if hasattr(obj,'__class__'): - src = getsource(obj.__class__,binary_file) - if src is not None: - source = self.format(src) + source = getsource(obj.__class__,binary_file) + if source is not None: out['source'] = source.rstrip() except Exception: pass diff --git a/IPython/core/prefilter.py b/IPython/core/prefilter.py index d497e76..c31b34b 100755 --- a/IPython/core/prefilter.py +++ b/IPython/core/prefilter.py @@ -32,7 +32,7 @@ from IPython.core.alias import AliasManager from IPython.core.autocall import IPyAutocall from IPython.config.configurable import Configurable from IPython.core.macro import Macro -from IPython.core.splitinput import split_user_input +from IPython.core.splitinput import split_user_input, LineInfo from IPython.core import page from IPython.utils.traitlets import List, Int, Any, Unicode, CBool, Bool, Instance @@ -92,78 +92,6 @@ def is_shadowed(identifier, ip): #----------------------------------------------------------------------------- -# The LineInfo class used throughout -#----------------------------------------------------------------------------- - - -class LineInfo(object): - """A single line of input and associated info. - - Includes the following as properties: - - line - The original, raw line - - continue_prompt - Is this line a continuation in a sequence of multiline input? - - pre - The initial esc character or whitespace. - - pre_char - The escape character(s) in pre or the empty string if there isn't one. - Note that '!!' is a possible value for pre_char. Otherwise it will - always be a single character. - - pre_whitespace - The leading whitespace from pre if it exists. If there is a pre_char, - this is just ''. - - ifun - The 'function part', which is basically the maximal initial sequence - of valid python identifiers and the '.' character. This is what is - checked for alias and magic transformations, used for auto-calling, - etc. - - the_rest - Everything else on the line. - """ - def __init__(self, line, continue_prompt): - self.line = line - self.continue_prompt = continue_prompt - self.pre, self.ifun, self.the_rest = split_user_input(line) - - self.pre_char = self.pre.strip() - if self.pre_char: - self.pre_whitespace = '' # No whitespace allowd before esc chars - else: - self.pre_whitespace = self.pre - - self._oinfo = None - - def ofind(self, ip): - """Do a full, attribute-walking lookup of the ifun in the various - namespaces for the given IPython InteractiveShell instance. - - Return a dict with keys: found,obj,ospace,ismagic - - Note: can cause state changes because of calling getattr, but should - only be run if autocall is on and if the line hasn't matched any - other, less dangerous handlers. - - Does cache the results of the call, so can be called multiple times - without worrying about *further* damaging state. - """ - if not self._oinfo: - # ip.shell._ofind is actually on the Magic class! - self._oinfo = ip.shell._ofind(self.ifun) - return self._oinfo - - def __str__(self): - return "Lineinfo [%s|%s|%s]" %(self.pre, self.ifun, self.the_rest) - - -#----------------------------------------------------------------------------- # Main Prefilter manager #----------------------------------------------------------------------------- @@ -630,7 +558,7 @@ class MultiLineMagicChecker(PrefilterChecker): # both ! and !!. if line_info.continue_prompt \ and self.prefilter_manager.multi_line_specials: - if line_info.ifun.startswith(ESC_MAGIC): + if line_info.esc == ESC_MAGIC: return self.prefilter_manager.get_handler_by_name('magic') else: return None @@ -644,14 +572,16 @@ class EscCharsChecker(PrefilterChecker): """Check for escape character and return either a handler to handle it, or None if there is no escape char.""" if line_info.line[-1] == ESC_HELP \ - and line_info.pre_char != ESC_SHELL \ - and line_info.pre_char != ESC_SH_CAP: + and line_info.esc != ESC_SHELL \ + and line_info.esc != ESC_SH_CAP: # the ? can be at the end, but *not* for either kind of shell escape, # because a ? can be a vaild final char in a shell cmd return self.prefilter_manager.get_handler_by_name('help') else: + if line_info.pre: + return None # This returns None like it should if no handler exists - return self.prefilter_manager.get_handler_by_esc(line_info.pre_char) + return self.prefilter_manager.get_handler_by_esc(line_info.esc) class AssignmentChecker(PrefilterChecker): @@ -872,6 +802,7 @@ class AutoHandler(PrefilterHandler): ifun = line_info.ifun the_rest = line_info.the_rest pre = line_info.pre + esc = line_info.esc continue_prompt = line_info.continue_prompt obj = line_info.ofind(self)['obj'] #print 'pre <%s> ifun <%s> rest <%s>' % (pre,ifun,the_rest) # dbg @@ -883,13 +814,13 @@ class AutoHandler(PrefilterHandler): force_auto = isinstance(obj, IPyAutocall) auto_rewrite = getattr(obj, 'rewrite', True) - if pre == ESC_QUOTE: + if esc == ESC_QUOTE: # Auto-quote splitting on whitespace newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) ) - elif pre == ESC_QUOTE2: + elif esc == ESC_QUOTE2: # Auto-quote whole string newcmd = '%s("%s")' % (ifun,the_rest) - elif pre == ESC_PAREN: + elif esc == ESC_PAREN: newcmd = '%s(%s)' % (ifun,",".join(the_rest.split())) else: # Auto-paren. @@ -946,7 +877,7 @@ class HelpHandler(PrefilterHandler): line = line[:-1] if line: #print 'line:<%r>' % line # dbg - self.shell.magic_pinfo(line) + self.shell.magic_pinfo(line_info.ifun) else: self.shell.show_usage() return '' # Empty string is needed here! diff --git a/IPython/core/release.py b/IPython/core/release.py index e94443b..1bb912b 100644 --- a/IPython/core/release.py +++ b/IPython/core/release.py @@ -123,3 +123,18 @@ download_url = 'http://archive.ipython.org/release/%s' % version platforms = ['Linux','Mac OSX','Windows XP/2000/NT'] keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed'] + +classifiers = [ + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research' + 'License :: OSI Approved :: BSD License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.1', + 'Programming Language :: Python :: 3.2', + 'Topic :: System :: Distributed Computing', + 'Topic :: System :: Shells' + ] diff --git a/IPython/core/splitinput.py b/IPython/core/splitinput.py index 5cdbb18..bd04f6a 100755 --- a/IPython/core/splitinput.py +++ b/IPython/core/splitinput.py @@ -1,6 +1,7 @@ # encoding: utf-8 """ -Simple utility for splitting user input. +Simple utility for splitting user input. This is used by both inputsplitter and +prefilter. Authors: @@ -22,45 +23,37 @@ Authors: import re import sys +from IPython.utils import py3compat + #----------------------------------------------------------------------------- # Main function #----------------------------------------------------------------------------- - # RegExp for splitting line contents into pre-char//first word-method//rest. # For clarity, each group in on one line. -# WARNING: update the regexp if the escapes in interactiveshell are changed, as they -# are hardwired in. +# WARNING: update the regexp if the escapes in interactiveshell are changed, as +# they are hardwired in. # Although it's not solely driven by the regex, note that: # ,;/% only trigger if they are the first character on the line # ! and !! trigger if they are first char(s) *or* follow an indent # ? triggers as first or last char. -# The three parts of the regex are: -# 1) pre: pre_char *or* initial whitespace -# 2) ifun: first word/method (mix of \w and '.') -# 3) the_rest: rest of line (separated from ifun by space if non-empty) -line_split = re.compile(r'^([,;/%?]|!!?|\s*)' - r'\s*([\w\.]+)' - r'(\s+.*$|$)') - -# r'[\w\.]+' -# r'\s*=\s*%.*' +line_split = re.compile(""" + ^(\s*) # any leading space + ([,;/%]|!!?|\?\??)? # escape character or characters + \s*(%?[\w\.\*]*) # function/method, possibly with leading % + # to correctly treat things like '?%magic' + (.*?$|$) # rest of line + """, re.VERBOSE) def split_user_input(line, pattern=None): - """Split user input into pre-char/whitespace, function part and rest. - - This is currently handles lines with '=' in them in a very inconsistent - manner. + """Split user input into initial whitespace, escape character, function part + and the rest. """ # We need to ensure that the rest of this routine deals only with unicode - if type(line)==str: - codec = sys.stdin.encoding - if codec is None: - codec = 'utf-8' - line = line.decode(codec) + line = py3compat.cast_unicode(line, sys.stdin.encoding or 'utf-8') if pattern is None: pattern = line_split @@ -73,18 +66,73 @@ def split_user_input(line, pattern=None): # print "split failed for line '%s'" % line ifun, the_rest = line, u'' pre = re.match('^(\s*)(.*)',line).groups()[0] + esc = "" else: - pre,ifun,the_rest = match.groups() - - # ifun has to be a valid python identifier, so it better encode into - # ascii. We do still make it a unicode string so that we consistently - # return unicode, but it will be one that is guaranteed to be pure ascii - try: - ifun = unicode(ifun.encode('ascii')) - except UnicodeEncodeError: - the_rest = ifun + u' ' + the_rest - ifun = u'' + pre, esc, ifun, the_rest = match.groups() #print 'line:<%s>' % line # dbg #print 'pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest) # dbg - return pre, ifun.strip(), the_rest.lstrip() + return pre, esc or '', ifun.strip(), the_rest.lstrip() + +class LineInfo(object): + """A single line of input and associated info. + + Includes the following as properties: + + line + The original, raw line + + continue_prompt + Is this line a continuation in a sequence of multiline input? + + pre + Any leading whitespace. + + esc + The escape character(s) in pre or the empty string if there isn't one. + Note that '!!' and '??' are possible values for esc. Otherwise it will + always be a single character. + + ifun + The 'function part', which is basically the maximal initial sequence + of valid python identifiers and the '.' character. This is what is + checked for alias and magic transformations, used for auto-calling, + etc. In contrast to Python identifiers, it may start with "%" and contain + "*". + + the_rest + Everything else on the line. + """ + def __init__(self, line, continue_prompt=False): + self.line = line + self.continue_prompt = continue_prompt + self.pre, self.esc, self.ifun, self.the_rest = split_user_input(line) + + self.pre_char = self.pre.strip() + if self.pre_char: + self.pre_whitespace = '' # No whitespace allowd before esc chars + else: + self.pre_whitespace = self.pre + + self._oinfo = None + + def ofind(self, ip): + """Do a full, attribute-walking lookup of the ifun in the various + namespaces for the given IPython InteractiveShell instance. + + Return a dict with keys: found,obj,ospace,ismagic + + Note: can cause state changes because of calling getattr, but should + only be run if autocall is on and if the line hasn't matched any + other, less dangerous handlers. + + Does cache the results of the call, so can be called multiple times + without worrying about *further* damaging state. + """ + if not self._oinfo: + # ip.shell._ofind is actually on the Magic class! + self._oinfo = ip.shell._ofind(self.ifun) + return self._oinfo + + def __str__(self): + return "LineInfo [%s|%s|%s|%s]" %(self.pre, self.esc, self.ifun, self.the_rest) diff --git a/IPython/core/tests/tclass.py b/IPython/core/tests/tclass.py index 38b3a5a..aa8fa2d 100644 --- a/IPython/core/tests/tclass.py +++ b/IPython/core/tests/tclass.py @@ -2,6 +2,8 @@ See test_run for details.""" +from __future__ import print_function + import sys # We want to ensure that while objects remain available for immediate access, @@ -10,10 +12,11 @@ import sys class C(object): def __init__(self,name): self.name = name + self.p = print self.flush_stdout = sys.stdout.flush def __del__(self): - print 'tclass.py: deleting object:',self.name + self.p('tclass.py: deleting object:',self.name) self.flush_stdout() try: @@ -28,5 +31,5 @@ else: # This next print statement is NOT debugging, we're making the check on a # completely separate process so we verify by capturing stdout: -print 'ARGV 1-:', sys.argv[1:] +print('ARGV 1-:', sys.argv[1:]) sys.stdout.flush() diff --git a/IPython/core/tests/test_application.py b/IPython/core/tests/test_application.py index 3a59a0f..a1d0474 100644 --- a/IPython/core/tests/test_application.py +++ b/IPython/core/tests/test_application.py @@ -6,6 +6,7 @@ import tempfile from IPython.core.application import BaseIPythonApplication from IPython.testing import decorators as testdec +from IPython.utils import py3compat @testdec.onlyif_unicode_paths def test_unicode_cwd(): @@ -35,7 +36,7 @@ def test_unicode_ipdir(): old_ipdir1 = os.environ.pop("IPYTHONDIR", None) old_ipdir2 = os.environ.pop("IPYTHON_DIR", None) - os.environ["IPYTHONDIR"] = ipdir.encode("utf-8") + os.environ["IPYTHONDIR"] = py3compat.unicode_to_str(ipdir, "utf-8") try: app = BaseIPythonApplication() # The lines below are copied from Application.initialize() diff --git a/IPython/core/tests/test_compilerop.py b/IPython/core/tests/test_compilerop.py index e9989db..0854b54 100644 --- a/IPython/core/tests/test_compilerop.py +++ b/IPython/core/tests/test_compilerop.py @@ -23,6 +23,7 @@ import nose.tools as nt # Our own imports from IPython.core import compilerop +from IPython.utils import py3compat #----------------------------------------------------------------------------- # Test functions @@ -51,7 +52,7 @@ def test_cache(): def setUp(): # Check we're in a proper Python 2 environment (some imports, such # as GTK, can change the default encoding, which can hide bugs.) - nt.assert_equal(sys.getdefaultencoding(), "ascii") + nt.assert_equal(sys.getdefaultencoding(), "utf-8" if py3compat.PY3 else "ascii") def test_cache_unicode(): cp = compilerop.CachingCompiler() diff --git a/IPython/core/tests/test_handlers.py b/IPython/core/tests/test_handlers.py index 483a8d7..54166c2 100644 --- a/IPython/core/tests/test_handlers.py +++ b/IPython/core/tests/test_handlers.py @@ -10,6 +10,7 @@ import nose.tools as nt # our own packages from IPython.core import autocall from IPython.testing import decorators as dec +from IPython.testing import tools as tt from IPython.testing.globalipapp import get_ipython #----------------------------------------------------------------------------- @@ -40,15 +41,7 @@ def run(tests): """Loop through a list of (pre, post) inputs, where pre is the string handed to ipython, and post is how that string looks after it's been transformed (i.e. ipython's notion of _i)""" - for pre, post in tests: - global num_tests - num_tests += 1 - actual = ip.prefilter_manager.prefilter_lines(pre) - if actual != None: - actual = actual.rstrip('\n') - if actual != post: - failures.append('Expected %r to become %r, found %r' % ( - pre, post, actual)) + tt.check_pairs(ip.prefilter_manager.prefilter_lines, tests) def test_handlers(): diff --git a/IPython/core/tests/test_history.py b/IPython/core/tests/test_history.py index 1cbacd2..f9536d7 100644 --- a/IPython/core/tests/test_history.py +++ b/IPython/core/tests/test_history.py @@ -16,9 +16,10 @@ import nose.tools as nt # our own packages from IPython.utils.tempdir import TemporaryDirectory from IPython.core.history import HistoryManager, extract_hist_ranges +from IPython.utils import py3compat def setUp(): - nt.assert_equal(sys.getdefaultencoding(), "ascii") + nt.assert_equal(sys.getdefaultencoding(), "utf-8" if py3compat.PY3 else "ascii") def test_history(): ip = get_ipython() diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index 77cbd2a..6f211f6 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -125,7 +125,7 @@ def test_get_input_encoding(): nt.assert_true(isinstance(encoding, basestring)) # simple-minded check that at least encoding a simple string works with the # encoding we got. - nt.assert_equal('test'.encode(encoding), 'test') + nt.assert_equal(u'test'.encode(encoding), b'test') class NoInputEncodingTestCase(unittest.TestCase): @@ -390,15 +390,6 @@ def test_LineInfo(): linfo = isp.LineInfo(' %cd /home') nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]') - -def test_split_user_input(): - """Unicode test - split_user_input already has good doctests""" - line = u"Pérez Fernando" - parts = isp.split_user_input(line) - parts_expected = (u'', u'', u'', line) - nt.assert_equal(parts, parts_expected) - - # Transformer tests def transform_checker(tests, func): """Utility to loop over test inputs""" @@ -611,7 +602,8 @@ class IPythonInputTestCase(InputSplitterTestCase): isp.push(raw) out, out_raw = isp.source_raw_reset() - self.assertEqual(out.rstrip(), out_t) + self.assertEqual(out.rstrip(), out_t, + tt.pair_fail_msg.format("inputsplitter",raw, out_t, out)) self.assertEqual(out_raw.rstrip(), raw.rstrip()) def test_syntax_multiline(self): diff --git a/IPython/core/tests/test_iplib.py b/IPython/core/tests/test_iplib.py index cb91ab2..dba13a5 100644 --- a/IPython/core/tests/test_iplib.py +++ b/IPython/core/tests/test_iplib.py @@ -75,7 +75,7 @@ Traceback (most recent call last): div0() ...line 8, in div0 x/y -ZeroDivisionError: integer division or modulo by zero +ZeroDivisionError: ... """ @@ -107,7 +107,7 @@ ZeroDivisionError Traceback (most recent call last) 9 10 def sysexit(stat, mode): -ZeroDivisionError: integer division or modulo by zero +ZeroDivisionError: ... """ @@ -144,7 +144,7 @@ ZeroDivisionError Traceback (most recent call last) 9 10 def sysexit(stat, mode): -ZeroDivisionError: integer division or modulo by zero +ZeroDivisionError: ... """ diff --git a/IPython/core/tests/test_oinspect.py b/IPython/core/tests/test_oinspect.py index 5d87c0e..913551c 100644 --- a/IPython/core/tests/test_oinspect.py +++ b/IPython/core/tests/test_oinspect.py @@ -20,6 +20,7 @@ import nose.tools as nt # Our own imports from .. import oinspect +from IPython.utils import py3compat #----------------------------------------------------------------------------- # Globals and constants @@ -96,7 +97,8 @@ def test_info(): "Check that Inspector.info fills out various fields as expected." i = inspector.info(Call, oname='Call') nt.assert_equal(i['type_name'], 'type') - nt.assert_equal(i['base_class'], "") + expted_class = str(type(type)) # (Python 3) or + nt.assert_equal(i['base_class'], expted_class) nt.assert_equal(i['string_form'], "") fname = __file__ if fname.endswith(".pyc"): @@ -125,9 +127,10 @@ def test_info(): nt.assert_equal(i['call_docstring'], c.__call__.__doc__) # Test old-style classes, which for example may not have an __init__ method. - i = inspector.info(OldStyle) - nt.assert_equal(i['type_name'], 'classobj') - - i = inspector.info(OldStyle()) - nt.assert_equal(i['type_name'], 'instance') - nt.assert_equal(i['docstring'], OldStyle.__doc__) + if not py3compat.PY3: + i = inspector.info(OldStyle) + nt.assert_equal(i['type_name'], 'classobj') + + i = inspector.info(OldStyle()) + nt.assert_equal(i['type_name'], 'instance') + nt.assert_equal(i['docstring'], OldStyle.__doc__) diff --git a/IPython/core/tests/test_splitinput.py b/IPython/core/tests/test_splitinput.py new file mode 100644 index 0000000..cfc7209 --- /dev/null +++ b/IPython/core/tests/test_splitinput.py @@ -0,0 +1,32 @@ +# coding: utf-8 +from IPython.core.splitinput import split_user_input +from IPython.testing import tools as tt +from IPython.utils import py3compat + +tests = [ + ('x=1', ('', '', 'x', '=1')), + ('?', ('', '?', '', '')), + ('??', ('', '??', '', '')), + (' ?', (' ', '?', '', '')), + (' ??', (' ', '??', '', '')), + ('??x', ('', '??', 'x', '')), + ('?x=1', ('', '?', 'x', '=1')), + ('!ls', ('', '!', 'ls', '')), + (' !ls', (' ', '!', 'ls', '')), + ('!!ls', ('', '!!', 'ls', '')), + (' !!ls', (' ', '!!', 'ls', '')), + (',ls', ('', ',', 'ls', '')), + (';ls', ('', ';', 'ls', '')), + (' ;ls', (' ', ';', 'ls', '')), + ('f.g(x)', ('', '', 'f.g', '(x)')), + ('f.g (x)', ('', '', 'f.g', '(x)')), + ('?%hist', ('', '?', '%hist', '')), + ('?x*', ('', '?', 'x*', '')), + ] +if py3compat.PY3: + tests.append((u"Pérez Fernando", (u'', u'', u'Pérez', u'Fernando'))) +else: + tests.append((u"Pérez Fernando", (u'', u'', u'P', u'érez Fernando'))) + +def test_split_user_input(): + return tt.check_pairs(split_user_input, tests) diff --git a/IPython/core/ultratb.py b/IPython/core/ultratb.py index 22928da..2669c91 100644 --- a/IPython/core/ultratb.py +++ b/IPython/core/ultratb.py @@ -83,6 +83,11 @@ import tokenize import traceback import types +try: # Python 2 + generate_tokens = tokenize.generate_tokens +except AttributeError: # Python 3 + generate_tokens = tokenize.tokenize + # For purposes of monkeypatching inspect to fix a bug in it. from inspect import getsourcefile, getfile, getmodule,\ ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode @@ -94,6 +99,7 @@ from IPython.core.display_trap import DisplayTrap from IPython.core.excolors import exception_colors from IPython.utils import PyColorize from IPython.utils import io +from IPython.utils import py3compat from IPython.utils.data import uniq_stable from IPython.utils.warn import info, error @@ -278,8 +284,7 @@ def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None): # serious refactoring, so that all of the ultratb and PyColorize code # is unicode-safe. So for now this is rather an ugly hack, but # necessary to at least have readable tracebacks. Improvements welcome! - if type(line)==unicode: - line = line.encode('utf-8', 'replace') + line = py3compat.cast_bytes_py2(line, 'utf-8') new_line, err = _line_format(line, 'str', scheme) if not err: line = new_line @@ -872,7 +877,8 @@ class VerboseTB(TBTools): try: # This builds the names list in-place by capturing it from the # enclosing scope. - tokenize.tokenize(linereader, tokeneater) + for token in generate_tokens(linereader): + tokeneater(*token) except IndexError: # signals exit of tokenizer pass @@ -933,7 +939,7 @@ class VerboseTB(TBTools): # ... and format it exception = ['%s%s%s: %s' % (Colors.excName, etype_str, ColorsNormal, evalue_str)] - if type(evalue) is types.InstanceType: + if (not py3compat.PY3) and type(evalue) is types.InstanceType: try: names = [w for w in dir(evalue) if isinstance(w, basestring)] except: diff --git a/IPython/external/simplegeneric/_simplegeneric.py b/IPython/external/simplegeneric/_simplegeneric.py index 556d6f2..d417cc9 100644 --- a/IPython/external/simplegeneric/_simplegeneric.py +++ b/IPython/external/simplegeneric/_simplegeneric.py @@ -1,20 +1,24 @@ +"""This is version 0.7 of Philip J. Eby's simplegeneric module +(http://pypi.python.org/pypi/simplegeneric), patched to work with Python 3, +which doesn't support old-style classes. +""" + #Name: simplegeneric -#Version: 0.6 +#Version: 0.7 #Summary: Simple generic functions (similar to Python's own len(), pickle.dump(), etc.) -#Home-page: http://cheeseshop.python.org/pypi/simplegeneric +#Home-page: http://pypi.python.org/pypi/simplegeneric #Author: Phillip J. Eby #Author-email: peak@eby-sarna.com #License: PSF or ZPL -# This is version 0.6 of Philip J. Eby's simplegeneric module -# (http://cheeseshop.python.org/pypi/simplegeneric) patched to work -# with Python 2.3 (which doesn't support assigning to __name__) - __all__ = ["generic"] - -from types import ClassType, InstanceType -classtypes = type, ClassType +try: + from types import ClassType, InstanceType +except ImportError: + classtypes = type +else: + classtypes = type, ClassType def generic(func): """Create a simple generic function""" @@ -30,38 +34,44 @@ def generic(func): else: return func(*args, **kw) - _by_type = {object: func, InstanceType: _by_class} + _by_type = {object: func} + try: + _by_type[InstanceType] = _by_class + except NameError: # Python 3 + pass + _gbt = _by_type.get - def when_type(t): - """Decorator to add a method that will be called for type `t`""" - if not isinstance(t, classtypes): - raise TypeError( - "%r is not a type or class" % (t,) - ) - def decorate(f): - if _by_type.setdefault(t,f) is not f: + def when_type(*types): + """Decorator to add a method that will be called for the given types""" + for t in types: + if not isinstance(t, classtypes): raise TypeError( - "%r already has method for type %r" % (func, t) + "%r is not a type or class" % (t,) ) + def decorate(f): + for t in types: + if _by_type.setdefault(t,f) is not f: + raise TypeError( + "%r already has method for type %r" % (func, t) + ) return f return decorate - - _by_object = {} _gbo = _by_object.get - def when_object(o): - """Decorator to add a method that will be called for object `o`""" + def when_object(*obs): + """Decorator to add a method to be called for the given object(s)""" def decorate(f): - if _by_object.setdefault(id(o), (o,f))[1] is not f: - raise TypeError( - "%r already has method for object %r" % (func, o) - ) + for o in obs: + if _by_object.setdefault(id(o), (o,f))[1] is not f: + raise TypeError( + "%r already has method for object %r" % (func, o) + ) return f return decorate @@ -78,10 +88,7 @@ def generic(func): else: return f[1](*args, **kw) - try: - dispatch.__name__ = func.__name__ - except TypeError: - pass + dispatch.__name__ = func.__name__ dispatch.__dict__ = func.__dict__.copy() dispatch.__doc__ = func.__doc__ dispatch.__module__ = func.__module__ @@ -94,46 +101,9 @@ def generic(func): return dispatch - - def test_suite(): import doctest return doctest.DocFileSuite( 'README.txt', optionflags=doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE, ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/IPython/frontend/qt/console/rich_ipython_widget.py b/IPython/frontend/qt/console/rich_ipython_widget.py index 771c004..d137618 100644 --- a/IPython/frontend/qt/console/rich_ipython_widget.py +++ b/IPython/frontend/qt/console/rich_ipython_widget.py @@ -83,7 +83,7 @@ class RichIPythonWidget(IPythonWidget): self._append_html(self._make_out_prompt(prompt_number), True) # This helps the output to look nice. self._append_plain_text('\n', True) - self._append_png(decodestring(data['image/png']), True) + self._append_png(decodestring(data['image/png'].encode('ascii')), True) self._append_html(self.output_sep2, True) else: # Default back to the plain text representation. @@ -104,7 +104,7 @@ class RichIPythonWidget(IPythonWidget): elif data.has_key('image/png'): # PNG data is base64 encoded as it passes over the network # in a JSON structure so we decode it. - png = decodestring(data['image/png']) + png = decodestring(data['image/png'].encode('ascii')) self._append_png(png, True) else: # Default back to the plain text representation. diff --git a/IPython/frontend/terminal/embed.py b/IPython/frontend/terminal/embed.py index febec42..4d26351 100644 --- a/IPython/frontend/terminal/embed.py +++ b/IPython/frontend/terminal/embed.py @@ -26,7 +26,10 @@ from __future__ import with_statement import __main__ import sys -from contextlib import nested +try: + from contextlib import nested +except: + from IPython.utils.nested_context import nested from IPython.core import ultratb from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py index 253f3e4..abe0328 100644 --- a/IPython/frontend/terminal/interactiveshell.py +++ b/IPython/frontend/terminal/interactiveshell.py @@ -16,17 +16,22 @@ import __builtin__ import bdb -from contextlib import nested import os import re import sys +try: + from contextlib import nested +except: + from IPython.utils.nested_context import nested + from IPython.core.error import TryNext from IPython.core.usage import interactive_usage, default_banner from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC from IPython.lib.inputhook import enable_gui from IPython.lib.pylabtools import pylab_activate from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import py3compat from IPython.utils.terminal import toggle_set_term_title, set_term_title from IPython.utils.process import abbrev_cwd from IPython.utils.warn import warn @@ -332,7 +337,7 @@ class TerminalInteractiveShell(InteractiveShell): self.set_readline_completer() try: - line = self.raw_input_original(prompt).decode(self.stdin_encoding) + line = py3compat.str_to_unicode(self.raw_input_original(prompt)) except ValueError: warn("\n********\nYou or a %run:ed script called sys.stdin.close()" " or sys.stdout.close()!\nExiting IPython!") diff --git a/IPython/lib/deepreload.py b/IPython/lib/deepreload.py index 5925ee0..d9f3ca1 100644 --- a/IPython/lib/deepreload.py +++ b/IPython/lib/deepreload.py @@ -159,7 +159,10 @@ def deep_reload_hook(module): return import_module(name[i+1:], name, parent) # Save the original hooks -original_reload = __builtin__.reload +try: + original_reload = __builtin__.reload +except AttributeError: + original_reload = imp.reload # Python 3 # Replacement for reload() def reload(module, exclude=['sys', '__builtin__', '__main__']): diff --git a/IPython/lib/demo.py b/IPython/lib/demo.py index e2bc7c5..e11e339 100644 --- a/IPython/lib/demo.py +++ b/IPython/lib/demo.py @@ -169,7 +169,6 @@ print 'bye!' # #***************************************************************************** -import exceptions import os import re import shlex @@ -182,7 +181,7 @@ from IPython.utils.text import marquee __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] -class DemoError(exceptions.Exception): pass +class DemoError(Exception): pass def re_mark(mark): return re.compile(r'^\s*#\s+\s+%s\s*$' % mark,re.MULTILINE) diff --git a/IPython/lib/pretty.py b/IPython/lib/pretty.py index 2c1a556..4361f21 100644 --- a/IPython/lib/pretty.py +++ b/IPython/lib/pretty.py @@ -454,8 +454,10 @@ class GroupQueue(object): except ValueError: pass - -_baseclass_reprs = (object.__repr__, types.InstanceType.__repr__) +try: + _baseclass_reprs = (object.__repr__, types.InstanceType.__repr__) +except AttributeError: # Python 3 + _baseclass_reprs = (object.__repr__,) def _default_pprint(obj, p, cycle): @@ -648,23 +650,33 @@ _type_pprinters = { tuple: _seq_pprinter_factory('(', ')', tuple), list: _seq_pprinter_factory('[', ']', list), dict: _dict_pprinter_factory('{', '}', dict), - types.DictProxyType: _dict_pprinter_factory(''), + set: _seq_pprinter_factory('set([', '])', set), frozenset: _seq_pprinter_factory('frozenset([', '])', frozenset), super: _super_pprint, _re_pattern_type: _re_pattern_pprint, type: _type_pprint, - types.ClassType: _type_pprint, types.FunctionType: _function_pprint, types.BuiltinFunctionType: _function_pprint, types.SliceType: _repr_pprint, types.MethodType: _repr_pprint, - xrange: _repr_pprint, + datetime.datetime: _repr_pprint, datetime.timedelta: _repr_pprint, _exception_base: _exception_pprint } +try: + _type_pprinters[types.DictProxyType] = _dict_pprinter_factory('') + _type_pprinters[types.ClassType] = _type_pprint, +except AttributeError: # Python 3 + pass + +try: + _type_pprinters[xrange] = _repr_pprint +except NameError: + _type_pprinters[range] = _repr_pprint + #: printers for types specified by name _deferred_type_pprinters = { } diff --git a/IPython/lib/pylabtools.py b/IPython/lib/pylabtools.py index 48f93f3..9db8b93 100644 --- a/IPython/lib/pylabtools.py +++ b/IPython/lib/pylabtools.py @@ -19,7 +19,7 @@ Authors # Imports #----------------------------------------------------------------------------- -from cStringIO import StringIO +from io import BytesIO from IPython.utils.decorators import flag_calls @@ -99,11 +99,11 @@ def print_figure(fig, fmt='png'): fig.set_facecolor('white') fig.set_edgecolor('white') try: - string_io = StringIO() + bytes_io = BytesIO() # use 72 dpi to match QTConsole's dpi - fig.canvas.print_figure(string_io, format=fmt, dpi=72, + fig.canvas.print_figure(bytes_io, format=fmt, dpi=72, bbox_inches='tight') - data = string_io.getvalue() + data = bytes_io.getvalue() finally: fig.set_facecolor(fc) fig.set_edgecolor(ec) diff --git a/IPython/parallel/client/client.py b/IPython/parallel/client/client.py index bee991a..4f3acf7 100644 --- a/IPython/parallel/client/client.py +++ b/IPython/parallel/client/client.py @@ -370,7 +370,7 @@ class Client(HasTraits): self.session = Session(**extra_args) self._query_socket = self._context.socket(zmq.DEALER) - self._query_socket.setsockopt(zmq.IDENTITY, util.asbytes(self.session.session)) + self._query_socket.setsockopt(zmq.IDENTITY, self.session.bsession) if self._ssh: tunnel.tunnel_connection(self._query_socket, url, sshserver, **ssh_kwargs) else: @@ -496,7 +496,7 @@ class Client(HasTraits): content = msg.content self._config['registration'] = dict(content) if content.status == 'ok': - ident = util.asbytes(self.session.session) + ident = self.session.bsession if content.mux: self._mux_socket = self._context.socket(zmq.DEALER) self._mux_socket.setsockopt(zmq.IDENTITY, ident) @@ -512,7 +512,7 @@ class Client(HasTraits): self._notification_socket.setsockopt(zmq.SUBSCRIBE, b'') # if content.query: # self._query_socket = self._context.socket(zmq.DEALER) - # self._query_socket.setsockopt(zmq.IDENTITY, self.session.session) + # self._query_socket.setsockopt(zmq.IDENTITY, self.session.bsession) # connect_socket(self._query_socket, content.query) if content.control: self._control_socket = self._context.socket(zmq.DEALER) diff --git a/IPython/parallel/controller/dependency.py b/IPython/parallel/controller/dependency.py index c046556..0499762 100644 --- a/IPython/parallel/controller/dependency.py +++ b/IPython/parallel/controller/dependency.py @@ -16,6 +16,7 @@ from types import ModuleType from IPython.parallel.client.asyncresult import AsyncResult from IPython.parallel.error import UnmetDependency from IPython.parallel.util import interactive +from IPython.utils import py3compat class depend(object): """Dependency decorator, for use with tasks. @@ -65,9 +66,10 @@ class dependent(object): raise UnmetDependency() return self.f(*args, **kwargs) - @property - def __name__(self): - return self.func_name + if not py3compat.PY3: + @property + def __name__(self): + return self.func_name @interactive def _require(*names): diff --git a/IPython/parallel/controller/hub.py b/IPython/parallel/controller/hub.py index c3fe1c1..9dde8fb 100755 --- a/IPython/parallel/controller/hub.py +++ b/IPython/parallel/controller/hub.py @@ -288,7 +288,7 @@ class HubFactory(RegistrationFactory): # resubmit stream r = ZMQStream(ctx.socket(zmq.DEALER), loop) url = util.disambiguate_url(self.client_info['task'][-1]) - r.setsockopt(zmq.IDENTITY, util.asbytes(self.session.session)) + r.setsockopt(zmq.IDENTITY, self.session.bsession) r.connect(url) self.hub = Hub(loop=loop, session=self.session, monitor=sub, heartmonitor=self.heartmonitor, diff --git a/IPython/parallel/controller/scheduler.py b/IPython/parallel/controller/scheduler.py index b7f63eb..b063123 100644 --- a/IPython/parallel/controller/scheduler.py +++ b/IPython/parallel/controller/scheduler.py @@ -177,7 +177,7 @@ class TaskScheduler(SessionFactory): ident = CBytes() # ZMQ identity. This should just be self.session.session # but ensure Bytes def _ident_default(self): - return asbytes(self.session.session) + return self.session.bsession def start(self): self.engine_stream.on_recv(self.dispatch_result, copy=False) diff --git a/IPython/testing/globalipapp.py b/IPython/testing/globalipapp.py index 89b5f27..ceb6ad4 100644 --- a/IPython/testing/globalipapp.py +++ b/IPython/testing/globalipapp.py @@ -20,15 +20,15 @@ from __future__ import print_function #----------------------------------------------------------------------------- # stdlib -import __builtin__ +import __builtin__ as builtin_mod import os import sys -from types import MethodType # our own from . import tools from IPython.utils import io +from IPython.utils import py3compat from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell #----------------------------------------------------------------------------- @@ -131,7 +131,7 @@ class ipnsdict(dict): # aggressive low-level cleaning of the execution namespace, we need to # correct for that ourselves, to ensure consitency with the 'real' # ipython. - self['__builtins__'] = __builtin__ + self['__builtins__'] = builtin_mod def __delitem__(self, key): """Part of the test suite checks that we can release all @@ -204,11 +204,10 @@ def start_ipython(): # Modify the IPython system call with one that uses getoutput, so that we # can capture subcommands and print them to Python's stdout, otherwise the # doctest machinery would miss them. - shell.system = MethodType(xsys, shell, TerminalInteractiveShell) + shell.system = py3compat.MethodType(xsys, shell) - shell._showtraceback = MethodType(_showtraceback, shell, - TerminalInteractiveShell) + shell._showtraceback = py3compat.MethodType(_showtraceback, shell) # IPython is ready, now clean up some global state... @@ -223,8 +222,8 @@ def start_ipython(): # now return this without recursively calling here again. _ip = shell get_ipython = _ip.get_ipython - __builtin__._ip = _ip - __builtin__.get_ipython = get_ipython + builtin_mod._ip = _ip + builtin_mod.get_ipython = get_ipython # To avoid extra IPython messages during testing, suppress io.stdout/stderr io.stdout = StreamProxy('stdout') diff --git a/IPython/testing/nosepatch.py b/IPython/testing/nosepatch.py index 2458e39..c630fb7 100644 --- a/IPython/testing/nosepatch.py +++ b/IPython/testing/nosepatch.py @@ -17,6 +17,7 @@ Note: merely importing this module causes the monkeypatch to be applied.""" #----------------------------------------------------------------------------- import unittest +import sys import nose.loader from inspect import ismethod, isfunction @@ -64,5 +65,7 @@ def getTestCaseNames(self, testCaseClass): ########################################################################## # Apply monkeypatch here -nose.loader.TestLoader.getTestCaseNames = getTestCaseNames +# Python 3 must be running with newer version of Nose, so don't touch anything. +if sys.version_info[0] < 3: + nose.loader.TestLoader.getTestCaseNames = getTestCaseNames ########################################################################## diff --git a/IPython/testing/tools.py b/IPython/testing/tools.py index a611814..9750575 100644 --- a/IPython/testing/tools.py +++ b/IPython/testing/tools.py @@ -295,7 +295,7 @@ class TempFileMixin(object): # delete it. I have no clue why pass -pair_fail_msg = ("Testing function {0}\n\n" +pair_fail_msg = ("Testing {0}\n\n" "In:\n" " {1!r}\n" "Expected:\n" @@ -318,9 +318,10 @@ def check_pairs(func, pairs): None. Raises an AssertionError if any output does not match the expected value. """ + name = getattr(func, "func_name", getattr(func, "__name__", "")) for inp, expected in pairs: out = func(inp) - assert out == expected, pair_fail_msg.format(func.func_name, inp, expected, out) + assert out == expected, pair_fail_msg.format(name, inp, expected, out) @contextmanager def mute_warn(): @@ -342,4 +343,3 @@ def make_tempfile(name): yield finally: os.unlink(name) - diff --git a/IPython/utils/PyColorize.py b/IPython/utils/PyColorize.py index 5f3c7f6..bf3e1ba 100644 --- a/IPython/utils/PyColorize.py +++ b/IPython/utils/PyColorize.py @@ -42,6 +42,13 @@ import sys import token import tokenize +try: + generate_tokens = tokenize.generate_tokens +except AttributeError: + # Python 3. Note that we use the undocumented _tokenize because it expects + # strings, not bytes. See also Python issue #9969. + generate_tokens = tokenize._tokenize + from IPython.utils.coloransi import * ############################################################################# @@ -177,10 +184,11 @@ class Parser: error = False try: - tokenize.tokenize(text.readline, self) - except tokenize.TokenError, ex: - msg = ex[0] - line = ex[1][0] + for atoken in generate_tokens(text.readline): + self(*atoken) + except tokenize.TokenError as ex: + msg = ex.args[0] + line = ex.args[1][0] self.out.write("%s\n\n*** ERROR: %s%s%s\n" % (colors[token.ERRORTOKEN], msg, self.raw[self.lines[line]:], diff --git a/IPython/utils/_process_common.py b/IPython/utils/_process_common.py index a4a0fd8..66eb0d0 100644 --- a/IPython/utils/_process_common.py +++ b/IPython/utils/_process_common.py @@ -17,6 +17,8 @@ of subprocess utilities, and it contains tools that are common to all of them. import subprocess import sys +from IPython.utils import py3compat + #----------------------------------------------------------------------------- # Function definitions #----------------------------------------------------------------------------- @@ -116,8 +118,8 @@ def getoutput(cmd): out = process_handler(cmd, lambda p: p.communicate()[0], subprocess.STDOUT) if out is None: - out = '' - return out + return '' + return py3compat.bytes_to_str(out) def getoutputerror(cmd): @@ -138,5 +140,6 @@ def getoutputerror(cmd): out_err = process_handler(cmd, lambda p: p.communicate()) if out_err is None: - out_err = '', '' - return out_err + return '', '' + out, err = out_err + return py3compat.bytes_to_str(out), py3compat.bytes_to_str(err) diff --git a/IPython/utils/io.py b/IPython/utils/io.py index 4f55ed9..9e475ec 100644 --- a/IPython/utils/io.py +++ b/IPython/utils/io.py @@ -103,7 +103,7 @@ class Tee(object): # Inspired by: # http://mail.python.org/pipermail/python-list/2007-May/442737.html - def __init__(self, file_or_name, mode=None, channel='stdout'): + def __init__(self, file_or_name, mode="w", channel='stdout'): """Construct a new Tee object. Parameters @@ -119,7 +119,7 @@ class Tee(object): if channel not in ['stdout', 'stderr']: raise ValueError('Invalid channel spec %s' % channel) - if hasattr(file, 'write') and hasattr(file, 'seek'): + if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'): self.file = file_or_name else: self.file = open(file_or_name, mode) diff --git a/IPython/utils/jsonutil.py b/IPython/utils/jsonutil.py index 26cd389..06ccb3d 100644 --- a/IPython/utils/jsonutil.py +++ b/IPython/utils/jsonutil.py @@ -16,6 +16,9 @@ import sys import types from datetime import datetime +from IPython.utils import py3compat +next_attr_name = '__next__' if py3compat.PY3 else 'next' + #----------------------------------------------------------------------------- # Globals and constants #----------------------------------------------------------------------------- @@ -134,7 +137,7 @@ def json_clean(obj): return obj.decode(sys.getdefaultencoding(), 'replace') if isinstance(obj, container_to_list) or ( - hasattr(obj, '__iter__') and hasattr(obj, 'next')): + hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)): obj = list(obj) if isinstance(obj, list): diff --git a/IPython/utils/nested_context.py b/IPython/utils/nested_context.py new file mode 100644 index 0000000..118c4de --- /dev/null +++ b/IPython/utils/nested_context.py @@ -0,0 +1,50 @@ +"""Backwards compatibility - we use contextlib.nested to support Python 2.6, +but it's removed in Python 3.2.""" + +# TODO : Remove this once we drop support for Python 2.6, and use +# "with a, b:" instead. + +import sys + +from contextlib import contextmanager + +@contextmanager +def nested(*managers): + """Combine multiple context managers into a single nested context manager. + + This function has been deprecated in favour of the multiple manager form + of the with statement. + + The one advantage of this function over the multiple manager form of the + with statement is that argument unpacking allows it to be + used with a variable number of context managers as follows: + + with nested(*managers): + do_something() + + """ + exits = [] + vars = [] + exc = (None, None, None) + try: + for mgr in managers: + exit = mgr.__exit__ + enter = mgr.__enter__ + vars.append(enter()) + exits.append(exit) + yield vars + except: + exc = sys.exc_info() + finally: + while exits: + exit = exits.pop() + try: + if exit(*exc): + exc = (None, None, None) + except: + exc = sys.exc_info() + if exc != (None, None, None): + # Don't rely on sys.exc_info() still containing + # the right information. Another exception may + # have been raised and caught by an exit method + raise exc[0], exc[1], exc[2] diff --git a/IPython/utils/path.py b/IPython/utils/path.py index ee8d0e7..0e5b9df 100644 --- a/IPython/utils/path.py +++ b/IPython/utils/path.py @@ -23,6 +23,7 @@ import IPython from IPython.utils import warn from IPython.utils.process import system from IPython.utils.importstring import import_item +from IPython.utils import py3compat #----------------------------------------------------------------------------- # Code @@ -30,14 +31,6 @@ from IPython.utils.importstring import import_item fs_encoding = sys.getfilesystemencoding() -def _cast_unicode(s, enc=None): - """Turn 8-bit strings into unicode.""" - if isinstance(s, bytes): - enc = enc or sys.getdefaultencoding() - return s.decode(enc) - return s - - def _get_long_path_name(path): """Dummy no-op.""" return path @@ -203,7 +196,7 @@ def get_home_dir(): root=os.path.abspath(root).rstrip('\\') if _writable_dir(os.path.join(root, '_ipython')): os.environ["IPYKITROOT"] = root - return _cast_unicode(root, fs_encoding) + return py3compat.cast_unicode(root, fs_encoding) if os.name == 'posix': # Linux, Unix, AIX, OS X @@ -218,11 +211,11 @@ def get_home_dir(): homedir = Popen('echo $HOME', shell=True, stdout=PIPE).communicate()[0].strip() if homedir: - return _cast_unicode(homedir, fs_encoding) + return py3compat.cast_unicode(homedir, fs_encoding) else: raise HomeDirError('Undefined $HOME, IPython cannot proceed.') else: - return _cast_unicode(homedir, fs_encoding) + return py3compat.cast_unicode(homedir, fs_encoding) elif os.name == 'nt': # Now for win9x, XP, Vista, 7? # For some strange reason all of these return 'nt' for os.name. @@ -236,7 +229,7 @@ def get_home_dir(): pass else: if _writable_dir(homedir): - return _cast_unicode(homedir, fs_encoding) + return py3compat.cast_unicode(homedir, fs_encoding) # Now look for a local home directory try: @@ -245,7 +238,7 @@ def get_home_dir(): pass else: if _writable_dir(homedir): - return _cast_unicode(homedir, fs_encoding) + return py3compat.cast_unicode(homedir, fs_encoding) # Now the users profile directory try: @@ -254,7 +247,7 @@ def get_home_dir(): pass else: if _writable_dir(homedir): - return _cast_unicode(homedir, fs_encoding) + return py3compat.cast_unicode(homedir, fs_encoding) # Use the registry to get the 'My Documents' folder. try: @@ -269,7 +262,7 @@ def get_home_dir(): pass else: if _writable_dir(homedir): - return _cast_unicode(homedir, fs_encoding) + return py3compat.cast_unicode(homedir, fs_encoding) # A user with a lot of unix tools in win32 may have defined $HOME. # Try this as a last ditch option. @@ -279,7 +272,7 @@ def get_home_dir(): pass else: if _writable_dir(homedir): - return _cast_unicode(homedir, fs_encoding) + return py3compat.cast_unicode(homedir, fs_encoding) # If all else fails, raise HomeDirError raise HomeDirError('No valid home directory could be found') @@ -302,7 +295,7 @@ def get_xdg_dir(): # use ~/.config if not set OR empty xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config') if xdg and _writable_dir(xdg): - return _cast_unicode(xdg, fs_encoding) + return py3compat.cast_unicode(xdg, fs_encoding) return None @@ -311,7 +304,7 @@ def get_ipython_dir(): """Get the IPython directory for this platform and user. This uses the logic in `get_home_dir` to find the home directory - and the adds .ipython to the end of the path. + and then adds .ipython to the end of the path. """ env = os.environ @@ -356,13 +349,13 @@ def get_ipython_dir(): " using a temp directory."%parent) ipdir = tempfile.mkdtemp() - return _cast_unicode(ipdir, fs_encoding) + return py3compat.cast_unicode(ipdir, fs_encoding) def get_ipython_package_dir(): """Get the base directory where IPython itself is installed.""" ipdir = os.path.dirname(IPython.__file__) - return _cast_unicode(ipdir, fs_encoding) + return py3compat.cast_unicode(ipdir, fs_encoding) def get_ipython_module_path(module_str): @@ -377,7 +370,7 @@ def get_ipython_module_path(module_str): mod = import_item(module_str) the_path = mod.__file__.replace('.pyc', '.py') the_path = the_path.replace('.pyo', '.py') - return _cast_unicode(the_path, fs_encoding) + return py3compat.cast_unicode(the_path, fs_encoding) def expand_path(s): @@ -442,7 +435,7 @@ def filehash(path): """Make an MD5 hash of a file, ignoring any differences in line ending characters.""" with open(path, "rU") as f: - return md5(f.read()).hexdigest() + return md5(py3compat.str_to_bytes(f.read())).hexdigest() # If the config is unmodified from the default, we'll just delete it. # These are consistent for 0.10.x, thankfully. We're not going to worry about diff --git a/IPython/utils/pickleshare.py b/IPython/utils/pickleshare.py index 9ac7cb0..c2ca26a 100755 --- a/IPython/utils/pickleshare.py +++ b/IPython/utils/pickleshare.py @@ -35,8 +35,8 @@ License: MIT open source license. from IPython.external.path import path as Path import os,stat,time +import collections import cPickle as pickle -import UserDict import glob def gethashfile(key): @@ -44,7 +44,7 @@ def gethashfile(key): _sentinel = object() -class PickleShareDB(UserDict.DictMixin): +class PickleShareDB(collections.MutableMapping): """ The main 'connection' object for PickleShare database """ def __init__(self,root): """ Return a db object that will manage the specied directory""" @@ -187,6 +187,12 @@ class PickleShareDB(UserDict.DictMixin): else: files = [Path(p) for p in glob.glob(self.root/globpat)] return [self._normalized(p) for p in files if p.isfile()] + + def __iter__(self): + return iter(keys) + + def __len__(self): + return len(keys) def uncache(self,*items): """ Removes all, or specified items from cache diff --git a/IPython/utils/process.py b/IPython/utils/process.py index 2ed580b..f317e34 100644 --- a/IPython/utils/process.py +++ b/IPython/utils/process.py @@ -27,6 +27,7 @@ else: from ._process_posix import _find_cmd, system, getoutput from ._process_common import getoutputerror +from IPython.utils import py3compat #----------------------------------------------------------------------------- # Code @@ -65,7 +66,7 @@ def find_cmd(cmd): except OSError: raise FindCmdError('command could not be found: %s' % cmd) # which returns empty if not found - if path == '': + if path == b'': raise FindCmdError('command could not be found: %s' % cmd) return os.path.abspath(path) @@ -115,7 +116,7 @@ def arg_split(s, posix=False): # At least encoding the input when it's unicode seems to help, but there # may be more problems lurking. Apparently this is fixed in python3. is_unicode = False - if isinstance(s, unicode): + if (not py3compat.PY3) and isinstance(s, unicode): is_unicode = True s = s.encode('utf-8') lex = shlex.shlex(s, posix=posix) diff --git a/IPython/utils/py3compat.py b/IPython/utils/py3compat.py new file mode 100644 index 0000000..2e59ddc --- /dev/null +++ b/IPython/utils/py3compat.py @@ -0,0 +1,95 @@ +# coding: utf-8 +"""Compatibility tricks for Python 3. Mainly to do with unicode.""" +import sys +import types + +orig_open = open + +def no_code(x, encoding=None): + return x + +def decode(s, encoding=None): + encoding = encoding or sys.stdin.encoding or sys.getdefaultencoding() + return s.decode(encoding, "replace") + +def encode(u, encoding=None): + encoding = encoding or sys.stdin.encoding or sys.getdefaultencoding() + return u.encode(encoding, "replace") + +def cast_unicode(s, encoding=None): + if isinstance(s, bytes): + return decode(s, encoding) + return s + +def cast_bytes(s, encoding=None): + if not isinstance(s, bytes): + return encode(s, encoding) + return s + +if sys.version_info[0] >= 3: + PY3 = True + + input = input + builtin_mod_name = "builtins" + + str_to_unicode = no_code + unicode_to_str = no_code + str_to_bytes = encode + bytes_to_str = decode + cast_bytes_py2 = no_code + + def isidentifier(s, dotted=False): + if dotted: + return all(isidentifier(a) for a in s.split(".")) + return s.isidentifier() + + open = orig_open + + MethodType = types.MethodType + +else: + PY3 = False + + input = raw_input + builtin_mod_name = "__builtin__" + + str_to_unicode = decode + unicode_to_str = encode + str_to_bytes = no_code + bytes_to_str = no_code + cast_bytes_py2 = cast_bytes + + import re + _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$") + def isidentifier(s, dotted=False): + if dotted: + return all(isidentifier(a) for a in s.split(".")) + return bool(_name_re.match(s)) + + class open(object): + """Wrapper providing key part of Python 3 open() interface.""" + def __init__(self, fname, mode="r", encoding="utf-8"): + self.f = orig_open(fname, mode) + self.enc = encoding + + def write(self, s): + return self.f.write(s.encode(self.enc)) + + def read(self, size=-1): + return self.f.read(size).decode(self.enc) + + def close(self): + return self.f.close() + + def __enter__(self): + return self + + def __exit__(self, etype, value, traceback): + self.f.close() + + def MethodType(func, instance): + return types.MethodType(func, instance, type(instance)) + +def execfile(fname, glob, loc=None): + loc = loc if (loc is not None) else glob + exec compile(open(fname).read(), fname, 'exec') in glob, loc diff --git a/IPython/utils/tests/test_path.py b/IPython/utils/tests/test_path.py index 01ad730..8b9b27e 100644 --- a/IPython/utils/tests/test_path.py +++ b/IPython/utils/tests/test_path.py @@ -31,17 +31,24 @@ from IPython.testing import decorators as dec from IPython.testing.decorators import skip_if_not_win32, skip_win32 from IPython.testing.tools import make_tempfile from IPython.utils import path, io +from IPython.utils import py3compat # Platform-dependent imports try: import _winreg as wreg except ImportError: #Fake _winreg module on none windows platforms - import new - sys.modules["_winreg"] = new.module("_winreg") + import types + wr_name = "winreg" if py3compat.PY3 else "_winreg" + sys.modules[wr_name] = types.ModuleType(wr_name) import _winreg as wreg #Add entries that needs to be stubbed by the testing code (wreg.OpenKey, wreg.QueryValueEx,) = (None, None) + +try: + reload +except NameError: # Python 3 + from imp import reload #----------------------------------------------------------------------------- # Globals diff --git a/IPython/utils/tests/test_process.py b/IPython/utils/tests/test_process.py index cbde427..d40dc46 100644 --- a/IPython/utils/tests/test_process.py +++ b/IPython/utils/tests/test_process.py @@ -37,7 +37,7 @@ def test_find_cmd_python(): def test_find_cmd_ls(): """Make sure we can find the full path to ls.""" path = find_cmd('ls') - nt.assert_true(path.endswith('ls')) + nt.assert_true(path.endswith(b'ls')) def has_pywin32(): diff --git a/IPython/utils/text.py b/IPython/utils/text.py index 9b2070f..bf4a143 100644 --- a/IPython/utils/text.py +++ b/IPython/utils/text.py @@ -23,7 +23,7 @@ import textwrap from string import Formatter from IPython.external.path import path - +from IPython.utils import py3compat from IPython.utils.io import nlprint from IPython.utils.data import flatten @@ -284,7 +284,7 @@ def make_quoted_expr(s): tail = '' tailpadding = '' raw = '' - ucode = 'u' + ucode = '' if py3compat.PY3 else 'u' if "\\" in s: raw = 'r' if s.endswith('\\'): @@ -494,7 +494,7 @@ def marquee(txt='',width=78,mark='*'): """ if not txt: return (mark*width)[:width] - nmark = (width-len(txt)-2)/len(mark)/2 + nmark = (width-len(txt)-2)//len(mark)//2 if nmark < 0: nmark =0 marks = mark*nmark return '%s %s %s' % (marks,txt,marks) @@ -587,10 +587,10 @@ class EvalFormatter(Formatter): -------- In [1]: f = EvalFormatter() - In [2]: f.format('{n/4}', n=8) + In [2]: f.format('{n//4}', n=8) Out[2]: '2' - In [3]: f.format('{range(3)}') + In [3]: f.format('{list(range(3))}') Out[3]: '[0, 1, 2]' In [4]: f.format('{3*2}') diff --git a/IPython/utils/traitlets.py b/IPython/utils/traitlets.py index fedc401..4926a9f 100644 --- a/IPython/utils/traitlets.py +++ b/IPython/utils/traitlets.py @@ -52,15 +52,17 @@ import inspect import re import sys import types -from types import ( - InstanceType, ClassType, FunctionType, - ListType, TupleType -) +from types import FunctionType +try: + from types import ClassType, InstanceType + ClassTypes = (ClassType, type) +except: + ClassTypes = (type,) + from .importstring import import_item +from IPython.utils import py3compat -ClassTypes = (ClassType, type) - -SequenceTypes = (ListType, TupleType, set, frozenset) +SequenceTypes = (list, tuple, set, frozenset) #----------------------------------------------------------------------------- # Basic classes @@ -108,7 +110,7 @@ def repr_type(obj): error messages. """ the_type = type(obj) - if the_type is InstanceType: + if (not py3compat.PY3) and the_type is InstanceType: # Old-style class. the_type = obj.__class__ msg = '%r %r' % (obj, the_type) @@ -616,7 +618,7 @@ class ClassBasedTraitType(TraitType): def error(self, obj, value): kind = type(value) - if kind is InstanceType: + if (not py3compat.PY3) and kind is InstanceType: msg = 'class %s' % value.__class__.__name__ else: msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) ) @@ -880,29 +882,31 @@ class CInt(Int): except: self.error(obj, value) +if py3compat.PY3: + Long, CLong = Int, CInt +else: + class Long(TraitType): + """A long integer trait.""" -class Long(TraitType): - """A long integer trait.""" - - default_value = 0L - info_text = 'a long' + default_value = 0L + info_text = 'a long' - def validate(self, obj, value): - if isinstance(value, long): - return value - if isinstance(value, int): - return long(value) - self.error(obj, value) + def validate(self, obj, value): + if isinstance(value, long): + return value + if isinstance(value, int): + return long(value) + self.error(obj, value) -class CLong(Long): - """A casting version of the long integer trait.""" + class CLong(Long): + """A casting version of the long integer trait.""" - def validate(self, obj, value): - try: - return long(value) - except: - self.error(obj, value) + def validate(self, obj, value): + try: + return long(value) + except: + self.error(obj, value) class Float(TraitType): @@ -955,7 +959,7 @@ class CComplex(Complex): # for Python 3 conversion and for reliable unicode behaviour on Python 2. So # we don't have a Str type. class Bytes(TraitType): - """A trait for strings.""" + """A trait for byte strings.""" default_value = '' info_text = 'a string' @@ -967,7 +971,7 @@ class Bytes(TraitType): class CBytes(Bytes): - """A casting version of the string trait.""" + """A casting version of the byte string trait.""" def validate(self, obj, value): try: @@ -1006,12 +1010,12 @@ class ObjectName(TraitType): This does not check that the name exists in any scope.""" info_text = "a valid object identifier in Python" - if sys.version_info[0] < 3: + if py3compat.PY3: + # Python 3: + coerce_str = staticmethod(lambda _,s: s) + + else: # Python 2: - _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$") - def isidentifier(self, s): - return bool(self._name_re.match(s)) - def coerce_str(self, obj, value): "In Python 2, coerce ascii-only unicode to str" if isinstance(value, unicode): @@ -1021,15 +1025,10 @@ class ObjectName(TraitType): self.error(obj, value) return value - else: - # Python 3: - isidentifier = staticmethod(lambda s: s.isidentifier()) - coerce_str = staticmethod(lambda _,s: s) - def validate(self, obj, value): value = self.coerce_str(obj, value) - if isinstance(value, str) and self.isidentifier(value): + if isinstance(value, str) and py3compat.isidentifier(value): return value self.error(obj, value) @@ -1038,8 +1037,7 @@ class DottedObjectName(ObjectName): def validate(self, obj, value): value = self.coerce_str(obj, value) - if isinstance(value, str) and all(self.isidentifier(x) \ - for x in value.split('.')): + if isinstance(value, str) and py3compat.isidentifier(value, dotted=True): return value self.error(obj, value) diff --git a/IPython/zmq/displayhook.py b/IPython/zmq/displayhook.py index cc70604..2cf5ff5 100644 --- a/IPython/zmq/displayhook.py +++ b/IPython/zmq/displayhook.py @@ -30,10 +30,10 @@ class ZMQDisplayHook(object): def _encode_binary(format_dict): pngdata = format_dict.get('image/png') if pngdata is not None: - format_dict['image/png'] = encodestring(pngdata) + format_dict['image/png'] = encodestring(pngdata).decode('ascii') jpegdata = format_dict.get('image/jpeg') if jpegdata is not None: - format_dict['image/jpeg'] = encodestring(jpegdata) + format_dict['image/jpeg'] = encodestring(jpegdata).decode('ascii') class ZMQShellDisplayHook(DisplayHook): diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index 098eb65..692e324 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -33,6 +33,7 @@ from IPython.core.shellapp import ( InteractiveShellApp, shell_flags, shell_aliases ) from IPython.utils import io +from IPython.utils import py3compat from IPython.utils.jsonutil import json_clean from IPython.lib import pylabtools from IPython.utils.traitlets import ( @@ -219,7 +220,10 @@ class Kernel(Configurable): # Replace raw_input. Note that is not sufficient to replace # raw_input in the user namespace. raw_input = lambda prompt='': self._raw_input(prompt, ident, parent) - __builtin__.raw_input = raw_input + if py3compat.PY3: + __builtin__.input = raw_input + else: + __builtin__.raw_input = raw_input # Set the parent message of the display hook and out streams. shell.displayhook.set_parent(parent) diff --git a/IPython/zmq/kernelmanager.py b/IPython/zmq/kernelmanager.py index e81f723..841a1b9 100644 --- a/IPython/zmq/kernelmanager.py +++ b/IPython/zmq/kernelmanager.py @@ -188,7 +188,7 @@ class ShellSocketChannel(ZMQSocketChannel): def run(self): """The thread's main activity. Call start() instead.""" self.socket = self.context.socket(zmq.DEALER) - self.socket.setsockopt(zmq.IDENTITY, self.session.session) + self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) self.socket.connect('tcp://%s:%i' % self.address) self.iostate = POLLERR|POLLIN self.ioloop.add_handler(self.socket, self._handle_events, @@ -394,8 +394,8 @@ class SubSocketChannel(ZMQSocketChannel): def run(self): """The thread's main activity. Call start() instead.""" self.socket = self.context.socket(zmq.SUB) - self.socket.setsockopt(zmq.SUBSCRIBE,'') - self.socket.setsockopt(zmq.IDENTITY, self.session.session) + self.socket.setsockopt(zmq.SUBSCRIBE,b'') + self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) self.socket.connect('tcp://%s:%i' % self.address) self.iostate = POLLIN|POLLERR self.ioloop.add_handler(self.socket, self._handle_events, @@ -483,7 +483,7 @@ class StdInSocketChannel(ZMQSocketChannel): def run(self): """The thread's main activity. Call start() instead.""" self.socket = self.context.socket(zmq.DEALER) - self.socket.setsockopt(zmq.IDENTITY, self.session.session) + self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) self.socket.connect('tcp://%s:%i' % self.address) self.iostate = POLLERR|POLLIN self.ioloop.add_handler(self.socket, self._handle_events, @@ -562,7 +562,7 @@ class HBSocketChannel(ZMQSocketChannel): def _create_socket(self): self.socket = self.context.socket(zmq.REQ) - self.socket.setsockopt(zmq.IDENTITY, self.session.session) + self.socket.setsockopt(zmq.IDENTITY, self.session.bsession) self.socket.connect('tcp://%s:%i' % self.address) self.poller = zmq.Poller() self.poller.register(self.socket, zmq.POLLIN) diff --git a/IPython/zmq/pykernel.py b/IPython/zmq/pykernel.py index 42835da..27f96d3 100755 --- a/IPython/zmq/pykernel.py +++ b/IPython/zmq/pykernel.py @@ -25,6 +25,7 @@ import traceback import zmq # Local imports. +from IPython.utils import py3compat from IPython.utils.traitlets import HasTraits, Instance, Dict, Float from completer import KernelCompleter from entry_point import base_launch_kernel @@ -116,7 +117,10 @@ class Kernel(HasTraits): # Replace raw_input. Note that is not sufficient to replace # raw_input in the user namespace. raw_input = lambda prompt='': self._raw_input(prompt, ident, parent) - __builtin__.raw_input = raw_input + if py3compat.PY3: + __builtin__.input = raw_input + else: + __builtin__.raw_input = raw_input # Set the parent message of the display hook and out streams. sys.displayhook.set_parent(parent) diff --git a/IPython/zmq/session.py b/IPython/zmq/session.py index 4649685..4636f91 100644 --- a/IPython/zmq/session.py +++ b/IPython/zmq/session.py @@ -46,8 +46,9 @@ from zmq.eventloop.zmqstream import ZMQStream from IPython.config.configurable import Configurable, LoggingConfigurable from IPython.utils.importstring import import_item from IPython.utils.jsonutil import extract_dates, squash_dates, date_default +from IPython.utils.py3compat import str_to_bytes from IPython.utils.traitlets import (CBytes, Unicode, Bool, Any, Instance, Set, - DottedObjectName) + DottedObjectName, CUnicode) #----------------------------------------------------------------------------- # utility functions @@ -238,10 +239,18 @@ class Session(Configurable): else: self.unpack = import_item(str(new)) - session = CBytes(b'', config=True, + session = CUnicode(u'', config=True, help="""The UUID identifying this session.""") def _session_default(self): - return bytes(uuid.uuid4()) + u = unicode(uuid.uuid4()) + self.bsession = u.encode('ascii') + return u + + def _session_changed(self, name, old, new): + self.bsession = self.session.encode('ascii') + + # bsession is the session as bytes + bsession = CBytes(b'') username = Unicode(os.environ.get('USER',u'username'), config=True, help="""Username for the Session. Default is your system username.""") @@ -295,9 +304,11 @@ class Session(Configurable): pack/unpack : callables You can also set the pack/unpack callables for serialization directly. - session : bytes + session : unicode (must be ascii) the ID of this Session object. The default is to generate a new UUID. + bsession : bytes + The session as bytes username : unicode username added to message headers. The default is to ask the OS. key : bytes @@ -310,6 +321,8 @@ class Session(Configurable): super(Session, self).__init__(**kwargs) self._check_packers() self.none = self.pack({}) + # ensure self._session_default() if necessary, so bsession is defined: + self.session @property def msg_id(self): @@ -380,7 +393,7 @@ class Session(Configurable): h = self.auth.copy() for m in msg_list: h.update(m) - return h.hexdigest() + return str_to_bytes(h.hexdigest()) def serialize(self, msg, ident=None): """Serialize the message components to bytes. @@ -401,7 +414,7 @@ class Session(Configurable): [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_content, buffer1,buffer2,...]. In this list, the p_* entities are the packed or serialized versions, so if JSON is used, these - are uft8 encoded JSON strings. + are utf8 encoded JSON strings. """ content = msg.get('content', {}) if content is None: diff --git a/IPython/zmq/tests/test_session.py b/IPython/zmq/tests/test_session.py index 42ac635..b2b522c 100644 --- a/IPython/zmq/tests/test_session.py +++ b/IPython/zmq/tests/test_session.py @@ -185,4 +185,26 @@ class TestSession(SessionTestCase): content = dict(code='whoda',stuff=object()) themsg = self.session.msg('execute',content=content) pmsg = theids + + def test_session_id(self): + session = ss.Session() + # get bs before us + bs = session.bsession + us = session.session + self.assertEquals(us.encode('ascii'), bs) + session = ss.Session() + # get us before bs + us = session.session + bs = session.bsession + self.assertEquals(us.encode('ascii'), bs) + # change propagates: + session.session = 'something else' + bs = session.bsession + us = session.session + self.assertEquals(us.encode('ascii'), bs) + session = ss.Session(session='stuff') + # get us before bs + self.assertEquals(session.bsession, session.session.encode('ascii')) + self.assertEquals(b'stuff', session.bsession) + diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 index beb7c10..7b10df3 --- a/setup.py +++ b/setup.py @@ -1,270 +1,10 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -"""Setup script for IPython. - -Under Posix environments it works like a typical setup.py script. -Under Windows, the command sdist is not supported, since IPython -requires utilities which are not available under Windows.""" - -#----------------------------------------------------------------------------- -# Copyright (c) 2008-2011, IPython Development Team. -# Copyright (c) 2001-2007, Fernando Perez -# Copyright (c) 2001, Janko Hauser -# Copyright (c) 2001, Nathaniel Gray -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file COPYING.txt, distributed with this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Minimal Python version sanity check -#----------------------------------------------------------------------------- +"""This calls the setup routine for Python 2 or 3 as required.""" import sys -# This check is also made in IPython/__init__, don't forget to update both when -# changing Python version requirements. -if sys.version[0:3] < '2.6': - error = """\ -ERROR: 'IPython requires Python Version 2.6 or above.' -Exiting.""" - print >> sys.stderr, error - sys.exit(1) - -# At least we're on the python version we need, move on. - -#------------------------------------------------------------------------------- -# Imports -#------------------------------------------------------------------------------- - -# Stdlib imports -import os -import shutil - -from glob import glob - -# BEFORE importing distutils, remove MANIFEST. distutils doesn't properly -# update it when the contents of directories change. -if os.path.exists('MANIFEST'): os.remove('MANIFEST') - -from distutils.core import setup - -# Our own imports -from IPython.utils.path import target_update - -from setupbase import ( - setup_args, - find_packages, - find_package_data, - find_scripts, - find_data_files, - check_for_dependencies, - record_commit_info, -) -from setupext import setupext - -isfile = os.path.isfile -pjoin = os.path.join - -#----------------------------------------------------------------------------- -# Function definitions -#----------------------------------------------------------------------------- - -def cleanup(): - """Clean up the junk left around by the build process""" - if "develop" not in sys.argv: - try: - shutil.rmtree('ipython.egg-info') - except: - try: - os.unlink('ipython.egg-info') - except: - pass - -#------------------------------------------------------------------------------- -# Handle OS specific things -#------------------------------------------------------------------------------- - -if os.name == 'posix': - os_name = 'posix' -elif os.name in ['nt','dos']: - os_name = 'windows' +if sys.version_info[0] >= 3: + from setup3 import main else: - print 'Unsupported operating system:',os.name - sys.exit(1) - -# Under Windows, 'sdist' has not been supported. Now that the docs build with -# Sphinx it might work, but let's not turn it on until someone confirms that it -# actually works. -if os_name == 'windows' and 'sdist' in sys.argv: - print 'The sdist command is not available under Windows. Exiting.' - sys.exit(1) - -#------------------------------------------------------------------------------- -# Things related to the IPython documentation -#------------------------------------------------------------------------------- - -# update the manuals when building a source dist -if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'): - import textwrap - - # List of things to be updated. Each entry is a triplet of args for - # target_update() - to_update = [ - # FIXME - Disabled for now: we need to redo an automatic way - # of generating the magic info inside the rst. - #('docs/magic.tex', - #['IPython/Magic.py'], - #"cd doc && ./update_magic.sh" ), - - ('docs/man/ipcluster.1.gz', - ['docs/man/ipcluster.1'], - 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'), - - ('docs/man/ipcontroller.1.gz', - ['docs/man/ipcontroller.1'], - 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'), - - ('docs/man/ipengine.1.gz', - ['docs/man/ipengine.1'], - 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'), - - ('docs/man/iplogger.1.gz', - ['docs/man/iplogger.1'], - 'cd docs/man && gzip -9c iplogger.1 > iplogger.1.gz'), - - ('docs/man/ipython.1.gz', - ['docs/man/ipython.1'], - 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'), - - ('docs/man/irunner.1.gz', - ['docs/man/irunner.1'], - 'cd docs/man && gzip -9c irunner.1 > irunner.1.gz'), - - ('docs/man/pycolor.1.gz', - ['docs/man/pycolor.1'], - 'cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz'), - ] - - # Only build the docs if sphinx is present - try: - import sphinx - except ImportError: - pass - else: - # The Makefile calls the do_sphinx scripts to build html and pdf, so - # just one target is enough to cover all manual generation - - # First, compute all the dependencies that can force us to rebuild the - # docs. Start with the main release file that contains metadata - docdeps = ['IPython/core/release.py'] - # Inculde all the reST sources - pjoin = os.path.join - for dirpath,dirnames,filenames in os.walk('docs/source'): - if dirpath in ['_static','_templates']: - continue - docdeps += [ pjoin(dirpath,f) for f in filenames - if f.endswith('.txt') ] - # and the examples - for dirpath,dirnames,filenames in os.walk('docs/example'): - docdeps += [ pjoin(dirpath,f) for f in filenames - if not f.endswith('~') ] - # then, make them all dependencies for the main html docs - to_update.append( - ('docs/dist/index.html', - docdeps, - "cd docs && make dist") - ) - - [ target_update(*t) for t in to_update ] - -#--------------------------------------------------------------------------- -# Find all the packages, package data, and data_files -#--------------------------------------------------------------------------- - -packages = find_packages() -package_data = find_package_data() -data_files = find_data_files() - -#--------------------------------------------------------------------------- -# Handle scripts, dependencies, and setuptools specific things -#--------------------------------------------------------------------------- - -# For some commands, use setuptools. Note that we do NOT list install here! -# If you want a setuptools-enhanced install, just run 'setupegg.py install' -needs_setuptools = set(('develop', 'sdist', 'release', 'bdist_egg', 'bdist_rpm', - 'bdist', 'bdist_dumb', 'bdist_wininst', 'install_egg_info', - 'build_sphinx', 'egg_info', 'easy_install', 'upload', - )) -if sys.platform == 'win32': - # Depend on setuptools for install on *Windows only* - # If we get script-installation working without setuptools, - # then we can back off, but until then use it. - # See Issue #369 on GitHub for more - needs_setuptools.add('install') - -if len(needs_setuptools.intersection(sys.argv)) > 0: - import setuptools - -# This dict is used for passing extra arguments that are setuptools -# specific to setup -setuptools_extra_args = {} - -if 'setuptools' in sys.modules: - setuptools_extra_args['zip_safe'] = False - setuptools_extra_args['entry_points'] = find_scripts(True) - setup_args['extras_require'] = dict( - parallel = 'pyzmq>=2.1.4', - zmq = 'pyzmq>=2.1.4', - doc = 'Sphinx>=0.3', - test = 'nose>=0.10.1', - notebook = 'tornado>=2.0' - ) - requires = setup_args.setdefault('install_requires', []) - setupext.display_status = False - if not setupext.check_for_readline(): - if sys.platform == 'darwin': - requires.append('readline') - elif sys.platform.startswith('win') and sys.maxsize < 2**32: - # only require pyreadline on 32b Windows, due to 64b bug in pyreadline: - # https://bugs.launchpad.net/pyreadline/+bug/787574 - requires.append('pyreadline') - else: - pass - # do we want to install readline here? - - # Script to be run by the windows binary installer after the default setup - # routine, to add shortcuts and similar windows-only things. Windows - # post-install scripts MUST reside in the scripts/ dir, otherwise distutils - # doesn't find them. - if 'bdist_wininst' in sys.argv: - if len(sys.argv) > 2 and \ - ('sdist' in sys.argv or 'bdist_rpm' in sys.argv): - print >> sys.stderr, "ERROR: bdist_wininst must be run alone. Exiting." - sys.exit(1) - setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')] - setup_args['options'] = {"bdist_wininst": - {"install_script": - "ipython_win_post_install.py"}} -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) - -#--------------------------------------------------------------------------- -# Do the actual setup now -#--------------------------------------------------------------------------- - -setup_args['cmdclass'] = {'build_py': record_commit_info('IPython')} -setup_args['packages'] = packages -setup_args['package_data'] = package_data -setup_args['data_files'] = data_files -setup_args.update(setuptools_extra_args) - + from setup2 import main -if __name__ == '__main__': - setup(**setup_args) - cleanup() +main() diff --git a/setup2.py b/setup2.py new file mode 100755 index 0000000..f6b7e01 --- /dev/null +++ b/setup2.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Setup script for IPython. + +Under Posix environments it works like a typical setup.py script. +Under Windows, the command sdist is not supported, since IPython +requires utilities which are not available under Windows.""" + +#----------------------------------------------------------------------------- +# Copyright (c) 2008-2011, IPython Development Team. +# Copyright (c) 2001-2007, Fernando Perez +# Copyright (c) 2001, Janko Hauser +# Copyright (c) 2001, Nathaniel Gray +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Minimal Python version sanity check +#----------------------------------------------------------------------------- + +import sys + +# This check is also made in IPython/__init__, don't forget to update both when +# changing Python version requirements. +if sys.version[0:3] < '2.6': + error = """\ +ERROR: 'IPython requires Python Version 2.6 or above.' +Exiting.""" + print >> sys.stderr, error + sys.exit(1) + +# At least we're on the python version we need, move on. + +#------------------------------------------------------------------------------- +# Imports +#------------------------------------------------------------------------------- + +# Stdlib imports +import os +import shutil + +from glob import glob + +# BEFORE importing distutils, remove MANIFEST. distutils doesn't properly +# update it when the contents of directories change. +if os.path.exists('MANIFEST'): os.remove('MANIFEST') + +from distutils.core import setup + +# Our own imports +from IPython.utils.path import target_update + +from setupbase import ( + setup_args, + find_packages, + find_package_data, + find_scripts, + find_data_files, + check_for_dependencies, + record_commit_info, +) +from setupext import setupext + +isfile = os.path.isfile +pjoin = os.path.join + +#----------------------------------------------------------------------------- +# Function definitions +#----------------------------------------------------------------------------- + +def cleanup(): + """Clean up the junk left around by the build process""" + if "develop" not in sys.argv: + try: + shutil.rmtree('ipython.egg-info') + except: + try: + os.unlink('ipython.egg-info') + except: + pass + +#------------------------------------------------------------------------------- +# Handle OS specific things +#------------------------------------------------------------------------------- + +if os.name == 'posix': + os_name = 'posix' +elif os.name in ['nt','dos']: + os_name = 'windows' +else: + print 'Unsupported operating system:',os.name + sys.exit(1) + +# Under Windows, 'sdist' has not been supported. Now that the docs build with +# Sphinx it might work, but let's not turn it on until someone confirms that it +# actually works. +if os_name == 'windows' and 'sdist' in sys.argv: + print 'The sdist command is not available under Windows. Exiting.' + sys.exit(1) + +#------------------------------------------------------------------------------- +# Things related to the IPython documentation +#------------------------------------------------------------------------------- + +# update the manuals when building a source dist +if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'): + import textwrap + + # List of things to be updated. Each entry is a triplet of args for + # target_update() + to_update = [ + # FIXME - Disabled for now: we need to redo an automatic way + # of generating the magic info inside the rst. + #('docs/magic.tex', + #['IPython/Magic.py'], + #"cd doc && ./update_magic.sh" ), + + ('docs/man/ipcluster.1.gz', + ['docs/man/ipcluster.1'], + 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'), + + ('docs/man/ipcontroller.1.gz', + ['docs/man/ipcontroller.1'], + 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'), + + ('docs/man/ipengine.1.gz', + ['docs/man/ipengine.1'], + 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'), + + ('docs/man/iplogger.1.gz', + ['docs/man/iplogger.1'], + 'cd docs/man && gzip -9c iplogger.1 > iplogger.1.gz'), + + ('docs/man/ipython.1.gz', + ['docs/man/ipython.1'], + 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'), + + ('docs/man/irunner.1.gz', + ['docs/man/irunner.1'], + 'cd docs/man && gzip -9c irunner.1 > irunner.1.gz'), + + ('docs/man/pycolor.1.gz', + ['docs/man/pycolor.1'], + 'cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz'), + ] + + # Only build the docs if sphinx is present + try: + import sphinx + except ImportError: + pass + else: + # The Makefile calls the do_sphinx scripts to build html and pdf, so + # just one target is enough to cover all manual generation + + # First, compute all the dependencies that can force us to rebuild the + # docs. Start with the main release file that contains metadata + docdeps = ['IPython/core/release.py'] + # Inculde all the reST sources + pjoin = os.path.join + for dirpath,dirnames,filenames in os.walk('docs/source'): + if dirpath in ['_static','_templates']: + continue + docdeps += [ pjoin(dirpath,f) for f in filenames + if f.endswith('.txt') ] + # and the examples + for dirpath,dirnames,filenames in os.walk('docs/example'): + docdeps += [ pjoin(dirpath,f) for f in filenames + if not f.endswith('~') ] + # then, make them all dependencies for the main html docs + to_update.append( + ('docs/dist/index.html', + docdeps, + "cd docs && make dist") + ) + + [ target_update(*t) for t in to_update ] + +#--------------------------------------------------------------------------- +# Find all the packages, package data, and data_files +#--------------------------------------------------------------------------- + +packages = find_packages() +package_data = find_package_data() +data_files = find_data_files() + +#--------------------------------------------------------------------------- +# Handle scripts, dependencies, and setuptools specific things +#--------------------------------------------------------------------------- + +# For some commands, use setuptools. Note that we do NOT list install here! +# If you want a setuptools-enhanced install, just run 'setupegg.py install' +needs_setuptools = set(('develop', 'sdist', 'release', 'bdist_egg', 'bdist_rpm', + 'bdist', 'bdist_dumb', 'bdist_wininst', 'install_egg_info', + 'build_sphinx', 'egg_info', 'easy_install', 'upload', + )) +if sys.platform == 'win32': + # Depend on setuptools for install on *Windows only* + # If we get script-installation working without setuptools, + # then we can back off, but until then use it. + # See Issue #369 on GitHub for more + needs_setuptools.add('install') + +if len(needs_setuptools.intersection(sys.argv)) > 0: + import setuptools + +# This dict is used for passing extra arguments that are setuptools +# specific to setup +setuptools_extra_args = {} + +if 'setuptools' in sys.modules: + setuptools_extra_args['zip_safe'] = False + setuptools_extra_args['entry_points'] = find_scripts(True) + setup_args['extras_require'] = dict( + parallel = 'pyzmq>=2.1.4', + zmq = 'pyzmq>=2.1.4', + doc = 'Sphinx>=0.3', + test = 'nose>=0.10.1', + notebook = 'tornado>=2.0' + ) + requires = setup_args.setdefault('install_requires', []) + setupext.display_status = False + if not setupext.check_for_readline(): + if sys.platform == 'darwin': + requires.append('readline') + elif sys.platform.startswith('win') and sys.maxsize < 2**32: + # only require pyreadline on 32b Windows, due to 64b bug in pyreadline: + # https://bugs.launchpad.net/pyreadline/+bug/787574 + requires.append('pyreadline') + else: + pass + # do we want to install readline here? + + # Script to be run by the windows binary installer after the default setup + # routine, to add shortcuts and similar windows-only things. Windows + # post-install scripts MUST reside in the scripts/ dir, otherwise distutils + # doesn't find them. + if 'bdist_wininst' in sys.argv: + if len(sys.argv) > 2 and \ + ('sdist' in sys.argv or 'bdist_rpm' in sys.argv): + print >> sys.stderr, "ERROR: bdist_wininst must be run alone. Exiting." + sys.exit(1) + setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')] + setup_args['options'] = {"bdist_wininst": + {"install_script": + "ipython_win_post_install.py"}} +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) + +#--------------------------------------------------------------------------- +# Do the actual setup now +#--------------------------------------------------------------------------- + +setup_args['cmdclass'] = {'build_py': record_commit_info('IPython')} +setup_args['packages'] = packages +setup_args['package_data'] = package_data +setup_args['data_files'] = data_files +setup_args.update(setuptools_extra_args) + +def main(): + setup(**setup_args) + cleanup() + +if __name__ == '__main__': + main() diff --git a/setup3.py b/setup3.py new file mode 100644 index 0000000..8b5ddb6 --- /dev/null +++ b/setup3.py @@ -0,0 +1,13 @@ +import os.path +from setuptools import setup + +from setupbase import (setup_args, find_scripts, find_packages) + +setup_args['entry_points'] = find_scripts(True, suffix='3') +setup_args['packages'] = find_packages() + +def main(): + setup(use_2to3 = True, **setup_args) + +if __name__ == "__main__": + main() diff --git a/setupbase.py b/setupbase.py index 3def278..f9e6e08 100644 --- a/setupbase.py +++ b/setupbase.py @@ -23,7 +23,10 @@ from __future__ import print_function import os import sys -from ConfigParser import ConfigParser +try: + from configparser import ConfigParser +except: + from ConfigParser import ConfigParser from distutils.command.build_py import build_py from glob import glob @@ -40,6 +43,13 @@ pjoin = os.path.join def oscmd(s): print(">", s) os.system(s) + +try: + execfile +except NameError: + def execfile(fname, globs, locs=None): + locs = locs or globs + exec(compile(open(fname).read(), fname, "exec"), globs, locs) # A little utility we'll need below, since glob() does NOT allow you to do # exclusion on multiple endings! @@ -58,7 +68,7 @@ def file_doesnt_endwith(test,endings): #--------------------------------------------------------------------------- # release.py contains version, authors, license, url, keywords, etc. -execfile(pjoin('IPython','core','release.py')) +execfile(pjoin('IPython','core','release.py'), globals()) # Create a dict with the basic information # This dict is eventually passed to setup after additional keys are added. @@ -74,6 +84,7 @@ setup_args = dict( license = license, platforms = platforms, keywords = keywords, + classifiers = classifiers, cmdclass = {'install_data': install_data_ext}, ) @@ -235,28 +246,31 @@ def make_man_update_target(manpage): # Find scripts #--------------------------------------------------------------------------- -def find_scripts(entry_points=False): +def find_scripts(entry_points=False, suffix=''): """Find IPython's scripts. if entry_points is True: return setuptools entry_point-style definitions else: return file paths of plain scripts [default] + + suffix is appended to script names if entry_points is True, so that the + Python 3 scripts get named "ipython3" etc. """ if entry_points: - console_scripts = [ - 'ipython = IPython.frontend.terminal.ipapp:launch_new_instance', - 'pycolor = IPython.utils.PyColorize:main', - 'ipcontroller = IPython.parallel.apps.ipcontrollerapp:launch_new_instance', - 'ipengine = IPython.parallel.apps.ipengineapp:launch_new_instance', - 'iplogger = IPython.parallel.apps.iploggerapp:launch_new_instance', - 'ipcluster = IPython.parallel.apps.ipclusterapp:launch_new_instance', - 'iptest = IPython.testing.iptest:main', - 'irunner = IPython.lib.irunner:main' - ] - gui_scripts = [ - 'ipython-qtconsole = IPython.frontend.qt.console.qtconsoleapp:main', - ] + console_scripts = [s % suffix for s in [ + 'ipython%s = IPython.frontend.terminal.ipapp:launch_new_instance', + 'pycolor%s = IPython.utils.PyColorize:main', + 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance', + 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance', + 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance', + 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance', + 'iptest%s = IPython.testing.iptest:main', + 'irunner%s = IPython.lib.irunner:main' + ]] + gui_scripts = [s % suffix for s in [ + 'ipython%s-qtconsole = IPython.frontend.qt.console.qtconsoleapp:main', + ]] scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts) else: parallel_scripts = pjoin('IPython','parallel','scripts') diff --git a/setupext/__init__.py b/setupext/__init__.py index 5f93fe1..c8ce037 100644 --- a/setupext/__init__.py +++ b/setupext/__init__.py @@ -1,3 +1,3 @@ # load extended setup modules for distuils -from install_data_ext import install_data_ext +from .install_data_ext import install_data_ext