##// END OF EJS Templates
Merge pull request #6184 from takluyver/rm-py3compat-open...
Min RK -
r17304:ecdfa090 merge
parent child Browse files
Show More
@@ -0,0 +1,2 b''
1 * ``IPython.utils.py3compat.open`` was removed: :func:`io.open` provides all
2 the same functionality.
@@ -1,187 +1,188 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Tests for the IPython tab-completion machinery.
2 """Tests for the IPython tab-completion machinery.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Module imports
5 # Module imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 # stdlib
8 # stdlib
9 import io
9 import os
10 import os
10 import sys
11 import sys
11 import tempfile
12 import tempfile
12 from datetime import datetime
13 from datetime import datetime
13
14
14 # third party
15 # third party
15 import nose.tools as nt
16 import nose.tools as nt
16
17
17 # our own packages
18 # our own packages
18 from IPython.config.loader import Config
19 from IPython.config.loader import Config
19 from IPython.utils.tempdir import TemporaryDirectory
20 from IPython.utils.tempdir import TemporaryDirectory
20 from IPython.core.history import HistoryManager, extract_hist_ranges
21 from IPython.core.history import HistoryManager, extract_hist_ranges
21 from IPython.utils import py3compat
22 from IPython.utils import py3compat
22
23
23 def setUp():
24 def setUp():
24 nt.assert_equal(sys.getdefaultencoding(), "utf-8" if py3compat.PY3 else "ascii")
25 nt.assert_equal(sys.getdefaultencoding(), "utf-8" if py3compat.PY3 else "ascii")
25
26
26 def test_history():
27 def test_history():
27 ip = get_ipython()
28 ip = get_ipython()
28 with TemporaryDirectory() as tmpdir:
29 with TemporaryDirectory() as tmpdir:
29 hist_manager_ori = ip.history_manager
30 hist_manager_ori = ip.history_manager
30 hist_file = os.path.join(tmpdir, 'history.sqlite')
31 hist_file = os.path.join(tmpdir, 'history.sqlite')
31 try:
32 try:
32 ip.history_manager = HistoryManager(shell=ip, hist_file=hist_file)
33 ip.history_manager = HistoryManager(shell=ip, hist_file=hist_file)
33 hist = [u'a=1', u'def f():\n test = 1\n return test', u"b='β‚¬Γ†ΒΎΓ·ΓŸ'"]
34 hist = [u'a=1', u'def f():\n test = 1\n return test', u"b='β‚¬Γ†ΒΎΓ·ΓŸ'"]
34 for i, h in enumerate(hist, start=1):
35 for i, h in enumerate(hist, start=1):
35 ip.history_manager.store_inputs(i, h)
36 ip.history_manager.store_inputs(i, h)
36
37
37 ip.history_manager.db_log_output = True
38 ip.history_manager.db_log_output = True
38 # Doesn't match the input, but we'll just check it's stored.
39 # Doesn't match the input, but we'll just check it's stored.
39 ip.history_manager.output_hist_reprs[3] = "spam"
40 ip.history_manager.output_hist_reprs[3] = "spam"
40 ip.history_manager.store_output(3)
41 ip.history_manager.store_output(3)
41
42
42 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
43 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
43
44
44 # Detailed tests for _get_range_session
45 # Detailed tests for _get_range_session
45 grs = ip.history_manager._get_range_session
46 grs = ip.history_manager._get_range_session
46 nt.assert_equal(list(grs(start=2,stop=-1)), list(zip([0], [2], hist[1:-1])))
47 nt.assert_equal(list(grs(start=2,stop=-1)), list(zip([0], [2], hist[1:-1])))
47 nt.assert_equal(list(grs(start=-2)), list(zip([0,0], [2,3], hist[-2:])))
48 nt.assert_equal(list(grs(start=-2)), list(zip([0,0], [2,3], hist[-2:])))
48 nt.assert_equal(list(grs(output=True)), list(zip([0,0,0], [1,2,3], zip(hist, [None,None,'spam']))))
49 nt.assert_equal(list(grs(output=True)), list(zip([0,0,0], [1,2,3], zip(hist, [None,None,'spam']))))
49
50
50 # Check whether specifying a range beyond the end of the current
51 # Check whether specifying a range beyond the end of the current
51 # session results in an error (gh-804)
52 # session results in an error (gh-804)
52 ip.magic('%hist 2-500')
53 ip.magic('%hist 2-500')
53
54
54 # Check that we can write non-ascii characters to a file
55 # Check that we can write non-ascii characters to a file
55 ip.magic("%%hist -f %s" % os.path.join(tmpdir, "test1"))
56 ip.magic("%%hist -f %s" % os.path.join(tmpdir, "test1"))
56 ip.magic("%%hist -pf %s" % os.path.join(tmpdir, "test2"))
57 ip.magic("%%hist -pf %s" % os.path.join(tmpdir, "test2"))
57 ip.magic("%%hist -nf %s" % os.path.join(tmpdir, "test3"))
58 ip.magic("%%hist -nf %s" % os.path.join(tmpdir, "test3"))
58 ip.magic("%%save %s 1-10" % os.path.join(tmpdir, "test4"))
59 ip.magic("%%save %s 1-10" % os.path.join(tmpdir, "test4"))
59
60
60 # New session
61 # New session
61 ip.history_manager.reset()
62 ip.history_manager.reset()
62 newcmds = [u"z=5",
63 newcmds = [u"z=5",
63 u"class X(object):\n pass",
64 u"class X(object):\n pass",
64 u"k='p'",
65 u"k='p'",
65 u"z=5"]
66 u"z=5"]
66 for i, cmd in enumerate(newcmds, start=1):
67 for i, cmd in enumerate(newcmds, start=1):
67 ip.history_manager.store_inputs(i, cmd)
68 ip.history_manager.store_inputs(i, cmd)
68 gothist = ip.history_manager.get_range(start=1, stop=4)
69 gothist = ip.history_manager.get_range(start=1, stop=4)
69 nt.assert_equal(list(gothist), list(zip([0,0,0],[1,2,3], newcmds)))
70 nt.assert_equal(list(gothist), list(zip([0,0,0],[1,2,3], newcmds)))
70 # Previous session:
71 # Previous session:
71 gothist = ip.history_manager.get_range(-1, 1, 4)
72 gothist = ip.history_manager.get_range(-1, 1, 4)
72 nt.assert_equal(list(gothist), list(zip([1,1,1],[1,2,3], hist)))
73 nt.assert_equal(list(gothist), list(zip([1,1,1],[1,2,3], hist)))
73
74
74 newhist = [(2, i, c) for (i, c) in enumerate(newcmds, 1)]
75 newhist = [(2, i, c) for (i, c) in enumerate(newcmds, 1)]
75
76
76 # Check get_hist_tail
77 # Check get_hist_tail
77 gothist = ip.history_manager.get_tail(5, output=True,
78 gothist = ip.history_manager.get_tail(5, output=True,
78 include_latest=True)
79 include_latest=True)
79 expected = [(1, 3, (hist[-1], "spam"))] \
80 expected = [(1, 3, (hist[-1], "spam"))] \
80 + [(s, n, (c, None)) for (s, n, c) in newhist]
81 + [(s, n, (c, None)) for (s, n, c) in newhist]
81 nt.assert_equal(list(gothist), expected)
82 nt.assert_equal(list(gothist), expected)
82
83
83 gothist = ip.history_manager.get_tail(2)
84 gothist = ip.history_manager.get_tail(2)
84 expected = newhist[-3:-1]
85 expected = newhist[-3:-1]
85 nt.assert_equal(list(gothist), expected)
86 nt.assert_equal(list(gothist), expected)
86
87
87 # Check get_hist_search
88 # Check get_hist_search
88 gothist = ip.history_manager.search("*test*")
89 gothist = ip.history_manager.search("*test*")
89 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
90 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
90
91
91 gothist = ip.history_manager.search("*=*")
92 gothist = ip.history_manager.search("*=*")
92 nt.assert_equal(list(gothist),
93 nt.assert_equal(list(gothist),
93 [(1, 1, hist[0]),
94 [(1, 1, hist[0]),
94 (1, 2, hist[1]),
95 (1, 2, hist[1]),
95 (1, 3, hist[2]),
96 (1, 3, hist[2]),
96 newhist[0],
97 newhist[0],
97 newhist[2],
98 newhist[2],
98 newhist[3]])
99 newhist[3]])
99
100
100 gothist = ip.history_manager.search("*=*", n=4)
101 gothist = ip.history_manager.search("*=*", n=4)
101 nt.assert_equal(list(gothist),
102 nt.assert_equal(list(gothist),
102 [(1, 3, hist[2]),
103 [(1, 3, hist[2]),
103 newhist[0],
104 newhist[0],
104 newhist[2],
105 newhist[2],
105 newhist[3]])
106 newhist[3]])
106
107
107 gothist = ip.history_manager.search("*=*", unique=True)
108 gothist = ip.history_manager.search("*=*", unique=True)
108 nt.assert_equal(list(gothist),
109 nt.assert_equal(list(gothist),
109 [(1, 1, hist[0]),
110 [(1, 1, hist[0]),
110 (1, 2, hist[1]),
111 (1, 2, hist[1]),
111 (1, 3, hist[2]),
112 (1, 3, hist[2]),
112 newhist[2],
113 newhist[2],
113 newhist[3]])
114 newhist[3]])
114
115
115 gothist = ip.history_manager.search("*=*", unique=True, n=3)
116 gothist = ip.history_manager.search("*=*", unique=True, n=3)
116 nt.assert_equal(list(gothist),
117 nt.assert_equal(list(gothist),
117 [(1, 3, hist[2]),
118 [(1, 3, hist[2]),
118 newhist[2],
119 newhist[2],
119 newhist[3]])
120 newhist[3]])
120
121
121 gothist = ip.history_manager.search("b*", output=True)
122 gothist = ip.history_manager.search("b*", output=True)
122 nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] )
123 nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] )
123
124
124 # Cross testing: check that magic %save can get previous session.
125 # Cross testing: check that magic %save can get previous session.
125 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
126 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
126 ip.magic("save " + testfilename + " ~1/1-3")
127 ip.magic("save " + testfilename + " ~1/1-3")
127 with py3compat.open(testfilename, encoding='utf-8') as testfile:
128 with io.open(testfilename, encoding='utf-8') as testfile:
128 nt.assert_equal(testfile.read(),
129 nt.assert_equal(testfile.read(),
129 u"# coding: utf-8\n" + u"\n".join(hist)+u"\n")
130 u"# coding: utf-8\n" + u"\n".join(hist)+u"\n")
130
131
131 # Duplicate line numbers - check that it doesn't crash, and
132 # Duplicate line numbers - check that it doesn't crash, and
132 # gets a new session
133 # gets a new session
133 ip.history_manager.store_inputs(1, "rogue")
134 ip.history_manager.store_inputs(1, "rogue")
134 ip.history_manager.writeout_cache()
135 ip.history_manager.writeout_cache()
135 nt.assert_equal(ip.history_manager.session_number, 3)
136 nt.assert_equal(ip.history_manager.session_number, 3)
136 finally:
137 finally:
137 # Ensure saving thread is shut down before we try to clean up the files
138 # Ensure saving thread is shut down before we try to clean up the files
138 ip.history_manager.save_thread.stop()
139 ip.history_manager.save_thread.stop()
139 # Forcibly close database rather than relying on garbage collection
140 # Forcibly close database rather than relying on garbage collection
140 ip.history_manager.db.close()
141 ip.history_manager.db.close()
141 # Restore history manager
142 # Restore history manager
142 ip.history_manager = hist_manager_ori
143 ip.history_manager = hist_manager_ori
143
144
144
145
145 def test_extract_hist_ranges():
146 def test_extract_hist_ranges():
146 instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5 ~10/"
147 instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5 ~10/"
147 expected = [(0, 1, 2), # 0 == current session
148 expected = [(0, 1, 2), # 0 == current session
148 (2, 3, 4),
149 (2, 3, 4),
149 (-4, 5, 7),
150 (-4, 5, 7),
150 (-4, 7, 10),
151 (-4, 7, 10),
151 (-9, 2, None), # None == to end
152 (-9, 2, None), # None == to end
152 (-8, 1, None),
153 (-8, 1, None),
153 (-7, 1, 6),
154 (-7, 1, 6),
154 (-10, 1, None)]
155 (-10, 1, None)]
155 actual = list(extract_hist_ranges(instr))
156 actual = list(extract_hist_ranges(instr))
156 nt.assert_equal(actual, expected)
157 nt.assert_equal(actual, expected)
157
158
158 def test_magic_rerun():
159 def test_magic_rerun():
159 """Simple test for %rerun (no args -> rerun last line)"""
160 """Simple test for %rerun (no args -> rerun last line)"""
160 ip = get_ipython()
161 ip = get_ipython()
161 ip.run_cell("a = 10", store_history=True)
162 ip.run_cell("a = 10", store_history=True)
162 ip.run_cell("a += 1", store_history=True)
163 ip.run_cell("a += 1", store_history=True)
163 nt.assert_equal(ip.user_ns["a"], 11)
164 nt.assert_equal(ip.user_ns["a"], 11)
164 ip.run_cell("%rerun", store_history=True)
165 ip.run_cell("%rerun", store_history=True)
165 nt.assert_equal(ip.user_ns["a"], 12)
166 nt.assert_equal(ip.user_ns["a"], 12)
166
167
167 def test_timestamp_type():
168 def test_timestamp_type():
168 ip = get_ipython()
169 ip = get_ipython()
169 info = ip.history_manager.get_session_info()
170 info = ip.history_manager.get_session_info()
170 nt.assert_true(isinstance(info[1], datetime))
171 nt.assert_true(isinstance(info[1], datetime))
171
172
172 def test_hist_file_config():
173 def test_hist_file_config():
173 cfg = Config()
174 cfg = Config()
174 tfile = tempfile.NamedTemporaryFile(delete=False)
175 tfile = tempfile.NamedTemporaryFile(delete=False)
175 cfg.HistoryManager.hist_file = tfile.name
176 cfg.HistoryManager.hist_file = tfile.name
176 try:
177 try:
177 hm = HistoryManager(shell=get_ipython(), config=cfg)
178 hm = HistoryManager(shell=get_ipython(), config=cfg)
178 nt.assert_equal(hm.hist_file, cfg.HistoryManager.hist_file)
179 nt.assert_equal(hm.hist_file, cfg.HistoryManager.hist_file)
179 finally:
180 finally:
180 try:
181 try:
181 os.remove(tfile.name)
182 os.remove(tfile.name)
182 except OSError:
183 except OSError:
183 # same catch as in testing.tools.TempFileMixin
184 # same catch as in testing.tools.TempFileMixin
184 # On Windows, even though we close the file, we still can't
185 # On Windows, even though we close the file, we still can't
185 # delete it. I have no clue why
186 # delete it. I have no clue why
186 pass
187 pass
187
188
@@ -1,271 +1,247 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Compatibility tricks for Python 3. Mainly to do with unicode."""
2 """Compatibility tricks for Python 3. Mainly to do with unicode."""
3 import functools
3 import functools
4 import os
4 import os
5 import sys
5 import sys
6 import re
6 import re
7 import types
7 import types
8
8
9 from .encoding import DEFAULT_ENCODING
9 from .encoding import DEFAULT_ENCODING
10
10
11 orig_open = open
12
13 def no_code(x, encoding=None):
11 def no_code(x, encoding=None):
14 return x
12 return x
15
13
16 def decode(s, encoding=None):
14 def decode(s, encoding=None):
17 encoding = encoding or DEFAULT_ENCODING
15 encoding = encoding or DEFAULT_ENCODING
18 return s.decode(encoding, "replace")
16 return s.decode(encoding, "replace")
19
17
20 def encode(u, encoding=None):
18 def encode(u, encoding=None):
21 encoding = encoding or DEFAULT_ENCODING
19 encoding = encoding or DEFAULT_ENCODING
22 return u.encode(encoding, "replace")
20 return u.encode(encoding, "replace")
23
21
24
22
25 def cast_unicode(s, encoding=None):
23 def cast_unicode(s, encoding=None):
26 if isinstance(s, bytes):
24 if isinstance(s, bytes):
27 return decode(s, encoding)
25 return decode(s, encoding)
28 return s
26 return s
29
27
30 def cast_bytes(s, encoding=None):
28 def cast_bytes(s, encoding=None):
31 if not isinstance(s, bytes):
29 if not isinstance(s, bytes):
32 return encode(s, encoding)
30 return encode(s, encoding)
33 return s
31 return s
34
32
35 def _modify_str_or_docstring(str_change_func):
33 def _modify_str_or_docstring(str_change_func):
36 @functools.wraps(str_change_func)
34 @functools.wraps(str_change_func)
37 def wrapper(func_or_str):
35 def wrapper(func_or_str):
38 if isinstance(func_or_str, string_types):
36 if isinstance(func_or_str, string_types):
39 func = None
37 func = None
40 doc = func_or_str
38 doc = func_or_str
41 else:
39 else:
42 func = func_or_str
40 func = func_or_str
43 doc = func.__doc__
41 doc = func.__doc__
44
42
45 doc = str_change_func(doc)
43 doc = str_change_func(doc)
46
44
47 if func:
45 if func:
48 func.__doc__ = doc
46 func.__doc__ = doc
49 return func
47 return func
50 return doc
48 return doc
51 return wrapper
49 return wrapper
52
50
53 def safe_unicode(e):
51 def safe_unicode(e):
54 """unicode(e) with various fallbacks. Used for exceptions, which may not be
52 """unicode(e) with various fallbacks. Used for exceptions, which may not be
55 safe to call unicode() on.
53 safe to call unicode() on.
56 """
54 """
57 try:
55 try:
58 return unicode_type(e)
56 return unicode_type(e)
59 except UnicodeError:
57 except UnicodeError:
60 pass
58 pass
61
59
62 try:
60 try:
63 return str_to_unicode(str(e))
61 return str_to_unicode(str(e))
64 except UnicodeError:
62 except UnicodeError:
65 pass
63 pass
66
64
67 try:
65 try:
68 return str_to_unicode(repr(e))
66 return str_to_unicode(repr(e))
69 except UnicodeError:
67 except UnicodeError:
70 pass
68 pass
71
69
72 return u'Unrecoverably corrupt evalue'
70 return u'Unrecoverably corrupt evalue'
73
71
74 if sys.version_info[0] >= 3:
72 if sys.version_info[0] >= 3:
75 PY3 = True
73 PY3 = True
76
74
77 # keep reference to builtin_mod because the kernel overrides that value
75 # keep reference to builtin_mod because the kernel overrides that value
78 # to forward requests to a frontend.
76 # to forward requests to a frontend.
79 def input(prompt=''):
77 def input(prompt=''):
80 return builtin_mod.input(prompt)
78 return builtin_mod.input(prompt)
81
79
82 builtin_mod_name = "builtins"
80 builtin_mod_name = "builtins"
83 import builtins as builtin_mod
81 import builtins as builtin_mod
84
82
85 str_to_unicode = no_code
83 str_to_unicode = no_code
86 unicode_to_str = no_code
84 unicode_to_str = no_code
87 str_to_bytes = encode
85 str_to_bytes = encode
88 bytes_to_str = decode
86 bytes_to_str = decode
89 cast_bytes_py2 = no_code
87 cast_bytes_py2 = no_code
90 cast_unicode_py2 = no_code
88 cast_unicode_py2 = no_code
91
89
92 string_types = (str,)
90 string_types = (str,)
93 unicode_type = str
91 unicode_type = str
94
92
95 def isidentifier(s, dotted=False):
93 def isidentifier(s, dotted=False):
96 if dotted:
94 if dotted:
97 return all(isidentifier(a) for a in s.split("."))
95 return all(isidentifier(a) for a in s.split("."))
98 return s.isidentifier()
96 return s.isidentifier()
99
97
100 open = orig_open
101 xrange = range
98 xrange = range
102 def iteritems(d): return iter(d.items())
99 def iteritems(d): return iter(d.items())
103 def itervalues(d): return iter(d.values())
100 def itervalues(d): return iter(d.values())
104 getcwd = os.getcwd
101 getcwd = os.getcwd
105
102
106 MethodType = types.MethodType
103 MethodType = types.MethodType
107
104
108 def execfile(fname, glob, loc=None):
105 def execfile(fname, glob, loc=None):
109 loc = loc if (loc is not None) else glob
106 loc = loc if (loc is not None) else glob
110 with open(fname, 'rb') as f:
107 with open(fname, 'rb') as f:
111 exec(compile(f.read(), fname, 'exec'), glob, loc)
108 exec(compile(f.read(), fname, 'exec'), glob, loc)
112
109
113 # Refactor print statements in doctests.
110 # Refactor print statements in doctests.
114 _print_statement_re = re.compile(r"\bprint (?P<expr>.*)$", re.MULTILINE)
111 _print_statement_re = re.compile(r"\bprint (?P<expr>.*)$", re.MULTILINE)
115 def _print_statement_sub(match):
112 def _print_statement_sub(match):
116 expr = match.groups('expr')
113 expr = match.groups('expr')
117 return "print(%s)" % expr
114 return "print(%s)" % expr
118
115
119 @_modify_str_or_docstring
116 @_modify_str_or_docstring
120 def doctest_refactor_print(doc):
117 def doctest_refactor_print(doc):
121 """Refactor 'print x' statements in a doctest to print(x) style. 2to3
118 """Refactor 'print x' statements in a doctest to print(x) style. 2to3
122 unfortunately doesn't pick up on our doctests.
119 unfortunately doesn't pick up on our doctests.
123
120
124 Can accept a string or a function, so it can be used as a decorator."""
121 Can accept a string or a function, so it can be used as a decorator."""
125 return _print_statement_re.sub(_print_statement_sub, doc)
122 return _print_statement_re.sub(_print_statement_sub, doc)
126
123
127 # Abstract u'abc' syntax:
124 # Abstract u'abc' syntax:
128 @_modify_str_or_docstring
125 @_modify_str_or_docstring
129 def u_format(s):
126 def u_format(s):
130 """"{u}'abc'" --> "'abc'" (Python 3)
127 """"{u}'abc'" --> "'abc'" (Python 3)
131
128
132 Accepts a string or a function, so it can be used as a decorator."""
129 Accepts a string or a function, so it can be used as a decorator."""
133 return s.format(u='')
130 return s.format(u='')
134
131
135 def get_closure(f):
132 def get_closure(f):
136 """Get a function's closure attribute"""
133 """Get a function's closure attribute"""
137 return f.__closure__
134 return f.__closure__
138
135
139 else:
136 else:
140 PY3 = False
137 PY3 = False
141
138
142 # keep reference to builtin_mod because the kernel overrides that value
139 # keep reference to builtin_mod because the kernel overrides that value
143 # to forward requests to a frontend.
140 # to forward requests to a frontend.
144 def input(prompt=''):
141 def input(prompt=''):
145 return builtin_mod.raw_input(prompt)
142 return builtin_mod.raw_input(prompt)
146
143
147 builtin_mod_name = "__builtin__"
144 builtin_mod_name = "__builtin__"
148 import __builtin__ as builtin_mod
145 import __builtin__ as builtin_mod
149
146
150 str_to_unicode = decode
147 str_to_unicode = decode
151 unicode_to_str = encode
148 unicode_to_str = encode
152 str_to_bytes = no_code
149 str_to_bytes = no_code
153 bytes_to_str = no_code
150 bytes_to_str = no_code
154 cast_bytes_py2 = cast_bytes
151 cast_bytes_py2 = cast_bytes
155 cast_unicode_py2 = cast_unicode
152 cast_unicode_py2 = cast_unicode
156
153
157 string_types = (str, unicode)
154 string_types = (str, unicode)
158 unicode_type = unicode
155 unicode_type = unicode
159
156
160 import re
157 import re
161 _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
158 _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
162 def isidentifier(s, dotted=False):
159 def isidentifier(s, dotted=False):
163 if dotted:
160 if dotted:
164 return all(isidentifier(a) for a in s.split("."))
161 return all(isidentifier(a) for a in s.split("."))
165 return bool(_name_re.match(s))
162 return bool(_name_re.match(s))
166
163
167 class open(object):
168 """Wrapper providing key part of Python 3 open() interface."""
169 def __init__(self, fname, mode="r", encoding="utf-8"):
170 self.f = orig_open(fname, mode)
171 self.enc = encoding
172
173 def write(self, s):
174 return self.f.write(s.encode(self.enc))
175
176 def read(self, size=-1):
177 return self.f.read(size).decode(self.enc)
178
179 def close(self):
180 return self.f.close()
181
182 def __enter__(self):
183 return self
184
185 def __exit__(self, etype, value, traceback):
186 self.f.close()
187
188 xrange = xrange
164 xrange = xrange
189 def iteritems(d): return d.iteritems()
165 def iteritems(d): return d.iteritems()
190 def itervalues(d): return d.itervalues()
166 def itervalues(d): return d.itervalues()
191 getcwd = os.getcwdu
167 getcwd = os.getcwdu
192
168
193 def MethodType(func, instance):
169 def MethodType(func, instance):
194 return types.MethodType(func, instance, type(instance))
170 return types.MethodType(func, instance, type(instance))
195
171
196 def doctest_refactor_print(func_or_str):
172 def doctest_refactor_print(func_or_str):
197 return func_or_str
173 return func_or_str
198
174
199 def get_closure(f):
175 def get_closure(f):
200 """Get a function's closure attribute"""
176 """Get a function's closure attribute"""
201 return f.func_closure
177 return f.func_closure
202
178
203 # Abstract u'abc' syntax:
179 # Abstract u'abc' syntax:
204 @_modify_str_or_docstring
180 @_modify_str_or_docstring
205 def u_format(s):
181 def u_format(s):
206 """"{u}'abc'" --> "u'abc'" (Python 2)
182 """"{u}'abc'" --> "u'abc'" (Python 2)
207
183
208 Accepts a string or a function, so it can be used as a decorator."""
184 Accepts a string or a function, so it can be used as a decorator."""
209 return s.format(u='u')
185 return s.format(u='u')
210
186
211 if sys.platform == 'win32':
187 if sys.platform == 'win32':
212 def execfile(fname, glob=None, loc=None):
188 def execfile(fname, glob=None, loc=None):
213 loc = loc if (loc is not None) else glob
189 loc = loc if (loc is not None) else glob
214 # The rstrip() is necessary b/c trailing whitespace in files will
190 # The rstrip() is necessary b/c trailing whitespace in files will
215 # cause an IndentationError in Python 2.6 (this was fixed in 2.7,
191 # cause an IndentationError in Python 2.6 (this was fixed in 2.7,
216 # but we still support 2.6). See issue 1027.
192 # but we still support 2.6). See issue 1027.
217 scripttext = builtin_mod.open(fname).read().rstrip() + '\n'
193 scripttext = builtin_mod.open(fname).read().rstrip() + '\n'
218 # compile converts unicode filename to str assuming
194 # compile converts unicode filename to str assuming
219 # ascii. Let's do the conversion before calling compile
195 # ascii. Let's do the conversion before calling compile
220 if isinstance(fname, unicode):
196 if isinstance(fname, unicode):
221 filename = unicode_to_str(fname)
197 filename = unicode_to_str(fname)
222 else:
198 else:
223 filename = fname
199 filename = fname
224 exec(compile(scripttext, filename, 'exec'), glob, loc)
200 exec(compile(scripttext, filename, 'exec'), glob, loc)
225 else:
201 else:
226 def execfile(fname, *where):
202 def execfile(fname, *where):
227 if isinstance(fname, unicode):
203 if isinstance(fname, unicode):
228 filename = fname.encode(sys.getfilesystemencoding())
204 filename = fname.encode(sys.getfilesystemencoding())
229 else:
205 else:
230 filename = fname
206 filename = fname
231 builtin_mod.execfile(filename, *where)
207 builtin_mod.execfile(filename, *where)
232
208
233
209
234 def annotate(**kwargs):
210 def annotate(**kwargs):
235 """Python 3 compatible function annotation for Python 2."""
211 """Python 3 compatible function annotation for Python 2."""
236 if not kwargs:
212 if not kwargs:
237 raise ValueError('annotations must be provided as keyword arguments')
213 raise ValueError('annotations must be provided as keyword arguments')
238 def dec(f):
214 def dec(f):
239 if hasattr(f, '__annotations__'):
215 if hasattr(f, '__annotations__'):
240 for k, v in kwargs.items():
216 for k, v in kwargs.items():
241 f.__annotations__[k] = v
217 f.__annotations__[k] = v
242 else:
218 else:
243 f.__annotations__ = kwargs
219 f.__annotations__ = kwargs
244 return f
220 return f
245 return dec
221 return dec
246
222
247
223
248 # Parts below taken from six:
224 # Parts below taken from six:
249 # Copyright (c) 2010-2013 Benjamin Peterson
225 # Copyright (c) 2010-2013 Benjamin Peterson
250 #
226 #
251 # Permission is hereby granted, free of charge, to any person obtaining a copy
227 # Permission is hereby granted, free of charge, to any person obtaining a copy
252 # of this software and associated documentation files (the "Software"), to deal
228 # of this software and associated documentation files (the "Software"), to deal
253 # in the Software without restriction, including without limitation the rights
229 # in the Software without restriction, including without limitation the rights
254 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
230 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
255 # copies of the Software, and to permit persons to whom the Software is
231 # copies of the Software, and to permit persons to whom the Software is
256 # furnished to do so, subject to the following conditions:
232 # furnished to do so, subject to the following conditions:
257 #
233 #
258 # The above copyright notice and this permission notice shall be included in all
234 # The above copyright notice and this permission notice shall be included in all
259 # copies or substantial portions of the Software.
235 # copies or substantial portions of the Software.
260 #
236 #
261 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
237 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
262 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
238 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
263 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
239 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
264 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
240 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
265 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
241 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
266 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
242 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
267 # SOFTWARE.
243 # SOFTWARE.
268
244
269 def with_metaclass(meta, *bases):
245 def with_metaclass(meta, *bases):
270 """Create a base class with a metaclass."""
246 """Create a base class with a metaclass."""
271 return meta("_NewBase", bases, {})
247 return meta("_NewBase", bases, {})
@@ -1,64 +1,66 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Script to auto-generate our API docs.
2 """Script to auto-generate our API docs.
3 """
3 """
4 # stdlib imports
4 # stdlib imports
5 import os
5 import os
6 import sys
6 import sys
7
7
8 # local imports
8 # local imports
9 sys.path.append(os.path.abspath('sphinxext'))
9 sys.path.append(os.path.abspath('sphinxext'))
10 from apigen import ApiDocWriter
10 from apigen import ApiDocWriter
11
11
12 #*****************************************************************************
12 #*****************************************************************************
13 if __name__ == '__main__':
13 if __name__ == '__main__':
14 pjoin = os.path.join
14 pjoin = os.path.join
15 package = 'IPython'
15 package = 'IPython'
16 outdir = pjoin('source','api','generated')
16 outdir = pjoin('source','api','generated')
17 docwriter = ApiDocWriter(package,rst_extension='.rst')
17 docwriter = ApiDocWriter(package,rst_extension='.rst')
18 # You have to escape the . here because . is a special char for regexps.
18 # You have to escape the . here because . is a special char for regexps.
19 # You must do make clean if you change this!
19 # You must do make clean if you change this!
20 docwriter.package_skip_patterns += [r'\.external$',
20 docwriter.package_skip_patterns += [r'\.external$',
21 # Extensions are documented elsewhere.
21 # Extensions are documented elsewhere.
22 r'\.extensions',
22 r'\.extensions',
23 r'\.config\.profile',
23 r'\.config\.profile',
24 # These should be accessed via nbformat.current
24 # These should be accessed via nbformat.current
25 r'\.nbformat\.v\d+',
25 r'\.nbformat\.v\d+',
26 ]
26 ]
27
27
28 # The inputhook* modules often cause problems on import, such as trying to
28 # The inputhook* modules often cause problems on import, such as trying to
29 # load incompatible Qt bindings. It's easiest to leave them all out. The
29 # load incompatible Qt bindings. It's easiest to leave them all out. The
30 # main API is in the inputhook module, which is documented.
30 # main API is in the inputhook module, which is documented.
31 docwriter.module_skip_patterns += [ r'\.lib\.inputhook.+',
31 docwriter.module_skip_patterns += [ r'\.lib\.inputhook.+',
32 r'\.ipdoctest',
32 r'\.ipdoctest',
33 r'\.testing\.plugin',
33 r'\.testing\.plugin',
34 # This just prints a deprecation msg:
34 # This just prints a deprecation msg:
35 r'\.frontend$',
35 r'\.frontend$',
36 # Deprecated:
36 # Deprecated:
37 r'\.core\.magics\.deprecated'
37 r'\.core\.magics\.deprecated',
38 # We document this manually.
38 # We document this manually.
39 r'\.utils\.py3compat',
39 r'\.utils\.py3compat',
40 # These are exposed by nbformat.current
40 # These are exposed by nbformat.current
41 r'\.nbformat\.convert',
41 r'\.nbformat\.convert',
42 r'\.nbformat\.validator',
42 r'\.nbformat\.validator',
43 # These are exposed in display
43 # These are exposed in display
44 r'\.core\.display',
44 r'\.core\.display',
45 r'\.lib\.display',
45 r'\.lib\.display',
46 # This isn't actually a module
47 r'\.html\.fabfile',
46 ]
48 ]
47
49
48 # These modules import functions and classes from other places to expose
50 # These modules import functions and classes from other places to expose
49 # them as part of the public API. They must have __all__ defined. The
51 # them as part of the public API. They must have __all__ defined. The
50 # non-API modules they import from should be excluded by the skip patterns
52 # non-API modules they import from should be excluded by the skip patterns
51 # above.
53 # above.
52 docwriter.names_from__all__.update({
54 docwriter.names_from__all__.update({
53 'IPython.nbformat.current',
55 'IPython.nbformat.current',
54 'IPython.display',
56 'IPython.display',
55 })
57 })
56
58
57 # Now, generate the outputs
59 # Now, generate the outputs
58 docwriter.write_api_docs(outdir)
60 docwriter.write_api_docs(outdir)
59 # Write index with .txt extension - we can include it, but Sphinx won't try
61 # Write index with .txt extension - we can include it, but Sphinx won't try
60 # to compile it
62 # to compile it
61 docwriter.write_index(outdir, 'gen.txt',
63 docwriter.write_index(outdir, 'gen.txt',
62 relative_to = pjoin('source','api')
64 relative_to = pjoin('source','api')
63 )
65 )
64 print ('%d files written' % len(docwriter.written_modules))
66 print ('%d files written' % len(docwriter.written_modules))
General Comments 0
You need to be logged in to leave comments. Login now