Show More
@@ -0,0 +1,41 b'' | |||||
|
1 | """Minimal script to reproduce our nasty reference counting bug. | |||
|
2 | ||||
|
3 | The problem is related to https://bugs.launchpad.net/ipython/+bug/269966 | |||
|
4 | ||||
|
5 | The original fix for that appeared to work, but John D. Hunter found a | |||
|
6 | matplotlib example which, when run twice in a row, would break. The problem | |||
|
7 | were references held by open figures to internals of Tkinter. | |||
|
8 | ||||
|
9 | This code reproduces the problem that John saw, without matplotlib. | |||
|
10 | ||||
|
11 | This script is meant to be called by other parts of the test suite that call it | |||
|
12 | via %run as if it were executed interactively by the user. As of 2009-04-13, | |||
|
13 | test_magic.py calls it. | |||
|
14 | """ | |||
|
15 | ||||
|
16 | #----------------------------------------------------------------------------- | |||
|
17 | # Module imports | |||
|
18 | #----------------------------------------------------------------------------- | |||
|
19 | import sys | |||
|
20 | ||||
|
21 | from IPython import ipapi | |||
|
22 | ||||
|
23 | #----------------------------------------------------------------------------- | |||
|
24 | # Globals | |||
|
25 | #----------------------------------------------------------------------------- | |||
|
26 | ip = ipapi.get() | |||
|
27 | ||||
|
28 | if not '_refbug_cache' in ip.user_ns: | |||
|
29 | ip.user_ns['_refbug_cache'] = [] | |||
|
30 | ||||
|
31 | ||||
|
32 | aglobal = 'Hello' | |||
|
33 | def f(): | |||
|
34 | return aglobal | |||
|
35 | ||||
|
36 | cache = ip.user_ns['_refbug_cache'] | |||
|
37 | cache.append(f) | |||
|
38 | ||||
|
39 | def call_f(): | |||
|
40 | for func in cache: | |||
|
41 | print 'lowercased:',func().lower() |
@@ -0,0 +1,17 b'' | |||||
|
1 | """Tests for the FakeModule objects. | |||
|
2 | """ | |||
|
3 | ||||
|
4 | import nose.tools as nt | |||
|
5 | ||||
|
6 | from IPython.FakeModule import FakeModule, init_fakemod_dict | |||
|
7 | ||||
|
8 | # Make a fakemod and check a few properties | |||
|
9 | def test_mk_fakemod(): | |||
|
10 | fm = FakeModule() | |||
|
11 | yield nt.assert_true,fm | |||
|
12 | yield nt.assert_true,lambda : hasattr(fm,'__file__') | |||
|
13 | ||||
|
14 | def test_mk_fakemod_fromdict(): | |||
|
15 | """Test making a FakeModule object with initial data""" | |||
|
16 | fm = FakeModule(dict(hello=True)) | |||
|
17 | nt.assert_true(fm.hello) |
@@ -15,6 +15,37 b' sessions.' | |||||
15 |
|
15 | |||
16 | import types |
|
16 | import types | |
17 |
|
17 | |||
|
18 | def init_fakemod_dict(fm,adict=None): | |||
|
19 | """Initialize a FakeModule instance __dict__. | |||
|
20 | ||||
|
21 | Kept as a standalone function and not a method so the FakeModule API can | |||
|
22 | remain basically empty. | |||
|
23 | ||||
|
24 | This should be considered for private IPython use, used in managing | |||
|
25 | namespaces for %run. | |||
|
26 | ||||
|
27 | Parameters | |||
|
28 | ---------- | |||
|
29 | ||||
|
30 | fm : FakeModule instance | |||
|
31 | ||||
|
32 | adict : dict, optional | |||
|
33 | """ | |||
|
34 | ||||
|
35 | dct = {} | |||
|
36 | # It seems pydoc (and perhaps others) needs any module instance to | |||
|
37 | # implement a __nonzero__ method, so we add it if missing: | |||
|
38 | dct.setdefault('__nonzero__',lambda : True) | |||
|
39 | dct.setdefault('__file__',__file__) | |||
|
40 | ||||
|
41 | if adict is not None: | |||
|
42 | dct.update(adict) | |||
|
43 | ||||
|
44 | # Hard assignment of the object's __dict__. This is nasty but deliberate. | |||
|
45 | fm.__dict__.clear() | |||
|
46 | fm.__dict__.update(dct) | |||
|
47 | ||||
|
48 | ||||
18 | class FakeModule(types.ModuleType): |
|
49 | class FakeModule(types.ModuleType): | |
19 | """Simple class with attribute access to fake a module. |
|
50 | """Simple class with attribute access to fake a module. | |
20 |
|
51 | |||
@@ -29,14 +60,7 b' class FakeModule(types.ModuleType):' | |||||
29 |
|
60 | |||
30 | # tmp to force __dict__ instance creation, else self.__dict__ fails |
|
61 | # tmp to force __dict__ instance creation, else self.__dict__ fails | |
31 | self.__iptmp = None |
|
62 | self.__iptmp = None | |
32 |
|
||||
33 | # It seems pydoc (and perhaps others) needs any module instance to |
|
|||
34 | # implement a __nonzero__ method, so we add it if missing: |
|
|||
35 | self.__dict__.setdefault('__nonzero__',lambda : True) |
|
|||
36 | self.__dict__.setdefault('__file__',__file__) |
|
|||
37 |
|
||||
38 | # cleanup our temp trick |
|
63 | # cleanup our temp trick | |
39 | del self.__iptmp |
|
64 | del self.__iptmp | |
40 |
|
65 | # Now, initialize the actual data in the instance dict. | ||
41 | if adict is not None: |
|
66 | init_fakemod_dict(self,adict) | |
42 | self.__dict__.update(adict) |
|
@@ -1584,24 +1584,18 b' Currently the magic system has the following functions:\\n"""' | |||||
1584 | prog_ns = self.shell.user_ns |
|
1584 | prog_ns = self.shell.user_ns | |
1585 | __name__save = self.shell.user_ns['__name__'] |
|
1585 | __name__save = self.shell.user_ns['__name__'] | |
1586 | prog_ns['__name__'] = '__main__' |
|
1586 | prog_ns['__name__'] = '__main__' | |
1587 |
main_mod = |
|
1587 | main_mod = self.shell.new_main_mod(prog_ns) | |
1588 | else: |
|
1588 | else: | |
1589 | # Run in a fresh, empty namespace |
|
1589 | # Run in a fresh, empty namespace | |
1590 | if opts.has_key('n'): |
|
1590 | if opts.has_key('n'): | |
1591 | name = os.path.splitext(os.path.basename(filename))[0] |
|
1591 | name = os.path.splitext(os.path.basename(filename))[0] | |
1592 | else: |
|
1592 | else: | |
1593 | name = '__main__' |
|
1593 | name = '__main__' | |
1594 | main_mod = FakeModule() |
|
1594 | ||
|
1595 | main_mod = self.shell.new_main_mod() | |||
1595 | prog_ns = main_mod.__dict__ |
|
1596 | prog_ns = main_mod.__dict__ | |
1596 | prog_ns['__name__'] = name |
|
1597 | prog_ns['__name__'] = name | |
1597 |
|
1598 | |||
1598 | # The shell MUST hold a reference to main_mod so after %run exits, |
|
|||
1599 | # the python deletion mechanism doesn't zero it out (leaving |
|
|||
1600 | # dangling references). However, we should drop old versions of |
|
|||
1601 | # main_mod. There is now a proper API to manage this caching in |
|
|||
1602 | # the main shell object, we use that. |
|
|||
1603 | self.shell.cache_main_mod(main_mod) |
|
|||
1604 |
|
||||
1605 | # Since '%run foo' emulates 'python foo.py' at the cmd line, we must |
|
1599 | # Since '%run foo' emulates 'python foo.py' at the cmd line, we must | |
1606 | # set the __file__ global in the script's namespace |
|
1600 | # set the __file__ global in the script's namespace | |
1607 | prog_ns['__file__'] = filename |
|
1601 | prog_ns['__file__'] = filename | |
@@ -1703,9 +1697,14 b' Currently the magic system has the following functions:\\n"""' | |||||
1703 | else: |
|
1697 | else: | |
1704 | # regular execution |
|
1698 | # regular execution | |
1705 | runner(filename,prog_ns,prog_ns,exit_ignore=exit_ignore) |
|
1699 | runner(filename,prog_ns,prog_ns,exit_ignore=exit_ignore) | |
|
1700 | ||||
1706 | if opts.has_key('i'): |
|
1701 | if opts.has_key('i'): | |
1707 | self.shell.user_ns['__name__'] = __name__save |
|
1702 | self.shell.user_ns['__name__'] = __name__save | |
1708 | else: |
|
1703 | else: | |
|
1704 | # The shell MUST hold a reference to prog_ns so after %run | |||
|
1705 | # exits, the python deletion mechanism doesn't zero it out | |||
|
1706 | # (leaving dangling references). | |||
|
1707 | self.shell.cache_main_mod(prog_ns,filename) | |||
1709 | # update IPython interactive namespace |
|
1708 | # update IPython interactive namespace | |
1710 | del prog_ns['__name__'] |
|
1709 | del prog_ns['__name__'] | |
1711 | self.shell.user_ns.update(prog_ns) |
|
1710 | self.shell.user_ns.update(prog_ns) | |
@@ -1719,6 +1718,7 b' Currently the magic system has the following functions:\\n"""' | |||||
1719 | # added. Otherwise it will trap references to objects |
|
1718 | # added. Otherwise it will trap references to objects | |
1720 | # contained therein. |
|
1719 | # contained therein. | |
1721 | del sys.modules[main_mod_name] |
|
1720 | del sys.modules[main_mod_name] | |
|
1721 | ||||
1722 | self.shell.reloadhist() |
|
1722 | self.shell.reloadhist() | |
1723 |
|
1723 | |||
1724 | return stats |
|
1724 | return stats | |
@@ -1800,7 +1800,28 b' Currently the magic system has the following functions:\\n"""' | |||||
1800 | import timeit |
|
1800 | import timeit | |
1801 | import math |
|
1801 | import math | |
1802 |
|
1802 | |||
1803 | units = [u"s", u"ms", u"\xb5s", u"ns"] |
|
1803 | # XXX: Unfortunately the unicode 'micro' symbol can cause problems in | |
|
1804 | # certain terminals. Until we figure out a robust way of | |||
|
1805 | # auto-detecting if the terminal can deal with it, use plain 'us' for | |||
|
1806 | # microseconds. I am really NOT happy about disabling the proper | |||
|
1807 | # 'micro' prefix, but crashing is worse... If anyone knows what the | |||
|
1808 | # right solution for this is, I'm all ears... | |||
|
1809 | # | |||
|
1810 | # Note: using | |||
|
1811 | # | |||
|
1812 | # s = u'\xb5' | |||
|
1813 | # s.encode(sys.getdefaultencoding()) | |||
|
1814 | # | |||
|
1815 | # is not sufficient, as I've seen terminals where that fails but | |||
|
1816 | # print s | |||
|
1817 | # | |||
|
1818 | # succeeds | |||
|
1819 | # | |||
|
1820 | # See bug: https://bugs.launchpad.net/ipython/+bug/348466 | |||
|
1821 | ||||
|
1822 | #units = [u"s", u"ms",u'\xb5',"ns"] | |||
|
1823 | units = [u"s", u"ms",u'us',"ns"] | |||
|
1824 | ||||
1804 | scaling = [1, 1e3, 1e6, 1e9] |
|
1825 | scaling = [1, 1e3, 1e6, 1e9] | |
1805 |
|
1826 | |||
1806 | opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', |
|
1827 | opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', | |
@@ -1839,9 +1860,9 b' Currently the magic system has the following functions:\\n"""' | |||||
1839 | # determine number so that 0.2 <= total time < 2.0 |
|
1860 | # determine number so that 0.2 <= total time < 2.0 | |
1840 | number = 1 |
|
1861 | number = 1 | |
1841 | for i in range(1, 10): |
|
1862 | for i in range(1, 10): | |
1842 | number *= 10 |
|
|||
1843 | if timer.timeit(number) >= 0.2: |
|
1863 | if timer.timeit(number) >= 0.2: | |
1844 | break |
|
1864 | break | |
|
1865 | number *= 10 | |||
1845 |
|
1866 | |||
1846 | best = min(timer.repeat(repeat, number)) / number |
|
1867 | best = min(timer.repeat(repeat, number)) / number | |
1847 |
|
1868 |
@@ -128,7 +128,7 b' prompt_specials_color = {' | |||||
128 | r'\N': '${self.cache.prompt_count}', |
|
128 | r'\N': '${self.cache.prompt_count}', | |
129 | # Prompt/history count, with the actual digits replaced by dots. Used |
|
129 | # Prompt/history count, with the actual digits replaced by dots. Used | |
130 | # mainly in continuation prompts (prompt_in2) |
|
130 | # mainly in continuation prompts (prompt_in2) | |
131 | r'\D': '${"."*len(str(self.cache.prompt_count))}', |
|
131 | r'\D': '${"."*__builtins__.len(__builtins__.str(self.cache.prompt_count))}', | |
132 | # Current working directory |
|
132 | # Current working directory | |
133 | r'\w': '${os.getcwd()}', |
|
133 | r'\w': '${os.getcwd()}', | |
134 | # Current time |
|
134 | # Current time |
@@ -20,10 +20,10 b" name = 'ipython'" | |||||
20 | # because bdist_rpm does not accept dashes (an RPM) convention, and |
|
20 | # because bdist_rpm does not accept dashes (an RPM) convention, and | |
21 | # bdist_deb does not accept underscores (a Debian convention). |
|
21 | # bdist_deb does not accept underscores (a Debian convention). | |
22 |
|
22 | |||
23 |
development = |
|
23 | development = True # change this to False to do a release | |
24 |
version_base = '0. |
|
24 | version_base = '0.10' | |
25 | branch = 'ipython' |
|
25 | branch = 'ipython' | |
26 |
revision = '11 |
|
26 | revision = '1195' | |
27 |
|
27 | |||
28 | if development: |
|
28 | if development: | |
29 | if branch == 'ipython': |
|
29 | if branch == 'ipython': |
@@ -54,7 +54,7 b' from pprint import pprint, pformat' | |||||
54 | from IPython import Debugger,OInspect,PyColorize,ultraTB |
|
54 | from IPython import Debugger,OInspect,PyColorize,ultraTB | |
55 | from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names |
|
55 | from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names | |
56 | from IPython.Extensions import pickleshare |
|
56 | from IPython.Extensions import pickleshare | |
57 | from IPython.FakeModule import FakeModule |
|
57 | from IPython.FakeModule import FakeModule, init_fakemod_dict | |
58 | from IPython.Itpl import Itpl,itpl,printpl,ItplNS,itplns |
|
58 | from IPython.Itpl import Itpl,itpl,printpl,ItplNS,itplns | |
59 | from IPython.Logger import Logger |
|
59 | from IPython.Logger import Logger | |
60 | from IPython.Magic import Magic |
|
60 | from IPython.Magic import Magic | |
@@ -108,6 +108,197 b' def softspace(file, newvalue):' | |||||
108 | return oldvalue |
|
108 | return oldvalue | |
109 |
|
109 | |||
110 |
|
110 | |||
|
111 | def user_setup(ipythondir,rc_suffix,mode='install',interactive=True): | |||
|
112 | """Install or upgrade the user configuration directory. | |||
|
113 | ||||
|
114 | Can be called when running for the first time or to upgrade the user's | |||
|
115 | .ipython/ directory. | |||
|
116 | ||||
|
117 | Parameters | |||
|
118 | ---------- | |||
|
119 | ipythondir : path | |||
|
120 | The directory to be used for installation/upgrade. In 'install' mode, | |||
|
121 | if this path already exists, the function exits immediately. | |||
|
122 | ||||
|
123 | rc_suffix : str | |||
|
124 | Extension for the config files. On *nix platforms it is typically the | |||
|
125 | empty string, while Windows normally uses '.ini'. | |||
|
126 | ||||
|
127 | mode : str, optional | |||
|
128 | Valid modes are 'install' and 'upgrade'. | |||
|
129 | ||||
|
130 | interactive : bool, optional | |||
|
131 | If False, do not wait for user input on any errors. Normally after | |||
|
132 | printing its status information, this function waits for the user to | |||
|
133 | hit Return before proceeding. This is because the default use case is | |||
|
134 | when first installing the IPython configuration, so we want the user to | |||
|
135 | acknowledge the initial message, which contains some useful | |||
|
136 | information. | |||
|
137 | """ | |||
|
138 | ||||
|
139 | # For automatic use, deactivate all i/o | |||
|
140 | if interactive: | |||
|
141 | def wait(): | |||
|
142 | try: | |||
|
143 | raw_input("Please press <RETURN> to start IPython.") | |||
|
144 | except EOFError: | |||
|
145 | print >> Term.cout | |||
|
146 | print '*'*70 | |||
|
147 | ||||
|
148 | def printf(s): | |||
|
149 | print s | |||
|
150 | else: | |||
|
151 | wait = lambda : None | |||
|
152 | printf = lambda s : None | |||
|
153 | ||||
|
154 | # Install mode should be re-entrant: if the install dir already exists, | |||
|
155 | # bail out cleanly | |||
|
156 | if mode == 'install' and os.path.isdir(ipythondir): | |||
|
157 | return | |||
|
158 | ||||
|
159 | cwd = os.getcwd() # remember where we started | |||
|
160 | glb = glob.glob | |||
|
161 | ||||
|
162 | printf('*'*70) | |||
|
163 | if mode == 'install': | |||
|
164 | printf( | |||
|
165 | """Welcome to IPython. I will try to create a personal configuration directory | |||
|
166 | where you can customize many aspects of IPython's functionality in:\n""") | |||
|
167 | else: | |||
|
168 | printf('I am going to upgrade your configuration in:') | |||
|
169 | ||||
|
170 | printf(ipythondir) | |||
|
171 | ||||
|
172 | rcdirend = os.path.join('IPython','UserConfig') | |||
|
173 | cfg = lambda d: os.path.join(d,rcdirend) | |||
|
174 | try: | |||
|
175 | rcdir = filter(os.path.isdir,map(cfg,sys.path))[0] | |||
|
176 | printf("Initializing from configuration: %s" % rcdir) | |||
|
177 | except IndexError: | |||
|
178 | warning = """ | |||
|
179 | Installation error. IPython's directory was not found. | |||
|
180 | ||||
|
181 | Check the following: | |||
|
182 | ||||
|
183 | The ipython/IPython directory should be in a directory belonging to your | |||
|
184 | PYTHONPATH environment variable (that is, it should be in a directory | |||
|
185 | belonging to sys.path). You can copy it explicitly there or just link to it. | |||
|
186 | ||||
|
187 | IPython will create a minimal default configuration for you. | |||
|
188 | ||||
|
189 | """ | |||
|
190 | warn(warning) | |||
|
191 | wait() | |||
|
192 | ||||
|
193 | if sys.platform =='win32': | |||
|
194 | inif = 'ipythonrc.ini' | |||
|
195 | else: | |||
|
196 | inif = 'ipythonrc' | |||
|
197 | minimal_setup = {'ipy_user_conf.py' : 'import ipy_defaults', | |||
|
198 | inif : '# intentionally left blank' } | |||
|
199 | os.makedirs(ipythondir, mode = 0777) | |||
|
200 | for f, cont in minimal_setup.items(): | |||
|
201 | # In 2.5, this can be more cleanly done using 'with' | |||
|
202 | fobj = file(ipythondir + '/' + f,'w') | |||
|
203 | fobj.write(cont) | |||
|
204 | fobj.close() | |||
|
205 | ||||
|
206 | return | |||
|
207 | ||||
|
208 | if mode == 'install': | |||
|
209 | try: | |||
|
210 | shutil.copytree(rcdir,ipythondir) | |||
|
211 | os.chdir(ipythondir) | |||
|
212 | rc_files = glb("ipythonrc*") | |||
|
213 | for rc_file in rc_files: | |||
|
214 | os.rename(rc_file,rc_file+rc_suffix) | |||
|
215 | except: | |||
|
216 | warning = """ | |||
|
217 | ||||
|
218 | There was a problem with the installation: | |||
|
219 | %s | |||
|
220 | Try to correct it or contact the developers if you think it's a bug. | |||
|
221 | IPython will proceed with builtin defaults.""" % sys.exc_info()[1] | |||
|
222 | warn(warning) | |||
|
223 | wait() | |||
|
224 | return | |||
|
225 | ||||
|
226 | elif mode == 'upgrade': | |||
|
227 | try: | |||
|
228 | os.chdir(ipythondir) | |||
|
229 | except: | |||
|
230 | printf(""" | |||
|
231 | Can not upgrade: changing to directory %s failed. Details: | |||
|
232 | %s | |||
|
233 | """ % (ipythondir,sys.exc_info()[1]) ) | |||
|
234 | wait() | |||
|
235 | return | |||
|
236 | else: | |||
|
237 | sources = glb(os.path.join(rcdir,'[A-Za-z]*')) | |||
|
238 | for new_full_path in sources: | |||
|
239 | new_filename = os.path.basename(new_full_path) | |||
|
240 | if new_filename.startswith('ipythonrc'): | |||
|
241 | new_filename = new_filename + rc_suffix | |||
|
242 | # The config directory should only contain files, skip any | |||
|
243 | # directories which may be there (like CVS) | |||
|
244 | if os.path.isdir(new_full_path): | |||
|
245 | continue | |||
|
246 | if os.path.exists(new_filename): | |||
|
247 | old_file = new_filename+'.old' | |||
|
248 | if os.path.exists(old_file): | |||
|
249 | os.remove(old_file) | |||
|
250 | os.rename(new_filename,old_file) | |||
|
251 | shutil.copy(new_full_path,new_filename) | |||
|
252 | else: | |||
|
253 | raise ValueError('unrecognized mode for install: %r' % mode) | |||
|
254 | ||||
|
255 | # Fix line-endings to those native to each platform in the config | |||
|
256 | # directory. | |||
|
257 | try: | |||
|
258 | os.chdir(ipythondir) | |||
|
259 | except: | |||
|
260 | printf(""" | |||
|
261 | Problem: changing to directory %s failed. | |||
|
262 | Details: | |||
|
263 | %s | |||
|
264 | ||||
|
265 | Some configuration files may have incorrect line endings. This should not | |||
|
266 | cause any problems during execution. """ % (ipythondir,sys.exc_info()[1]) ) | |||
|
267 | wait() | |||
|
268 | else: | |||
|
269 | for fname in glb('ipythonrc*'): | |||
|
270 | try: | |||
|
271 | native_line_ends(fname,backup=0) | |||
|
272 | except IOError: | |||
|
273 | pass | |||
|
274 | ||||
|
275 | if mode == 'install': | |||
|
276 | printf(""" | |||
|
277 | Successful installation! | |||
|
278 | ||||
|
279 | Please read the sections 'Initial Configuration' and 'Quick Tips' in the | |||
|
280 | IPython manual (there are both HTML and PDF versions supplied with the | |||
|
281 | distribution) to make sure that your system environment is properly configured | |||
|
282 | to take advantage of IPython's features. | |||
|
283 | ||||
|
284 | Important note: the configuration system has changed! The old system is | |||
|
285 | still in place, but its setting may be partly overridden by the settings in | |||
|
286 | "~/.ipython/ipy_user_conf.py" config file. Please take a look at the file | |||
|
287 | if some of the new settings bother you. | |||
|
288 | ||||
|
289 | """) | |||
|
290 | else: | |||
|
291 | printf(""" | |||
|
292 | Successful upgrade! | |||
|
293 | ||||
|
294 | All files in your directory: | |||
|
295 | %(ipythondir)s | |||
|
296 | which would have been overwritten by the upgrade were backed up with a .old | |||
|
297 | extension. If you had made particular customizations in those files you may | |||
|
298 | want to merge them back into the new files.""" % locals() ) | |||
|
299 | wait() | |||
|
300 | os.chdir(cwd) | |||
|
301 | ||||
111 | #**************************************************************************** |
|
302 | #**************************************************************************** | |
112 | # Local use exceptions |
|
303 | # Local use exceptions | |
113 | class SpaceInInput(exceptions.Exception): pass |
|
304 | class SpaceInInput(exceptions.Exception): pass | |
@@ -308,13 +499,24 b' class InteractiveShell(object,Magic):' | |||||
308 | # calling functions defined in the script that use other things from |
|
499 | # calling functions defined in the script that use other things from | |
309 | # the script will fail, because the function's closure had references |
|
500 | # the script will fail, because the function's closure had references | |
310 | # to the original objects, which are now all None. So we must protect |
|
501 | # to the original objects, which are now all None. So we must protect | |
311 |
# these modules from deletion by keeping a cache. |
|
502 | # these modules from deletion by keeping a cache. | |
312 | # stale modules around (we only need the one from the last run), we use |
|
503 | # | |
313 | # a dict keyed with the full path to the script, so only the last |
|
504 | # To avoid keeping stale modules around (we only need the one from the | |
314 | # version of the module is held in the cache. The %reset command will |
|
505 | # last run), we use a dict keyed with the full path to the script, so | |
315 | # flush this cache. See the cache_main_mod() and clear_main_mod_cache() |
|
506 | # only the last version of the module is held in the cache. Note, | |
316 | # methods for details on use. |
|
507 | # however, that we must cache the module *namespace contents* (their | |
317 | self._user_main_modules = {} |
|
508 | # __dict__). Because if we try to cache the actual modules, old ones | |
|
509 | # (uncached) could be destroyed while still holding references (such as | |||
|
510 | # those held by GUI objects that tend to be long-lived)> | |||
|
511 | # | |||
|
512 | # The %reset command will flush this cache. See the cache_main_mod() | |||
|
513 | # and clear_main_mod_cache() methods for details on use. | |||
|
514 | ||||
|
515 | # This is the cache used for 'main' namespaces | |||
|
516 | self._main_ns_cache = {} | |||
|
517 | # And this is the single instance of FakeModule whose __dict__ we keep | |||
|
518 | # copying and clearing for reuse on each %run | |||
|
519 | self._user_main_module = FakeModule() | |||
318 |
|
520 | |||
319 | # A table holding all the namespaces IPython deals with, so that |
|
521 | # A table holding all the namespaces IPython deals with, so that | |
320 | # introspection facilities can search easily. |
|
522 | # introspection facilities can search easily. | |
@@ -330,7 +532,7 b' class InteractiveShell(object,Magic):' | |||||
330 | # a simple list. |
|
532 | # a simple list. | |
331 | self.ns_refs_table = [ user_ns, user_global_ns, self.user_config_ns, |
|
533 | self.ns_refs_table = [ user_ns, user_global_ns, self.user_config_ns, | |
332 | self.alias_table, self.internal_ns, |
|
534 | self.alias_table, self.internal_ns, | |
333 |
self. |
|
535 | self._main_ns_cache ] | |
334 |
|
536 | |||
335 | # We need to insert into sys.modules something that looks like a |
|
537 | # We need to insert into sys.modules something that looks like a | |
336 | # module but which accesses the IPython namespace, for shelve and |
|
538 | # module but which accesses the IPython namespace, for shelve and | |
@@ -1114,156 +1316,11 b' class InteractiveShell(object,Magic):' | |||||
1114 | def user_setup(self,ipythondir,rc_suffix,mode='install'): |
|
1316 | def user_setup(self,ipythondir,rc_suffix,mode='install'): | |
1115 | """Install the user configuration directory. |
|
1317 | """Install the user configuration directory. | |
1116 |
|
1318 | |||
1117 | Can be called when running for the first time or to upgrade the user's |
|
1319 | Note | |
1118 | .ipython/ directory with the mode parameter. Valid modes are 'install' |
|
1320 | ---- | |
1119 | and 'upgrade'.""" |
|
1321 | DEPRECATED: use the top-level user_setup() function instead. | |
1120 |
|
||||
1121 | def wait(): |
|
|||
1122 | try: |
|
|||
1123 | raw_input("Please press <RETURN> to start IPython.") |
|
|||
1124 | except EOFError: |
|
|||
1125 | print >> Term.cout |
|
|||
1126 | print '*'*70 |
|
|||
1127 |
|
||||
1128 | cwd = os.getcwd() # remember where we started |
|
|||
1129 | glb = glob.glob |
|
|||
1130 | print '*'*70 |
|
|||
1131 | if mode == 'install': |
|
|||
1132 | print \ |
|
|||
1133 | """Welcome to IPython. I will try to create a personal configuration directory |
|
|||
1134 | where you can customize many aspects of IPython's functionality in:\n""" |
|
|||
1135 | else: |
|
|||
1136 | print 'I am going to upgrade your configuration in:' |
|
|||
1137 |
|
||||
1138 | print ipythondir |
|
|||
1139 |
|
||||
1140 | rcdirend = os.path.join('IPython','UserConfig') |
|
|||
1141 | cfg = lambda d: os.path.join(d,rcdirend) |
|
|||
1142 | try: |
|
|||
1143 | rcdir = filter(os.path.isdir,map(cfg,sys.path))[0] |
|
|||
1144 | print "Initializing from configuration",rcdir |
|
|||
1145 | except IndexError: |
|
|||
1146 | warning = """ |
|
|||
1147 | Installation error. IPython's directory was not found. |
|
|||
1148 |
|
||||
1149 | Check the following: |
|
|||
1150 |
|
||||
1151 | The ipython/IPython directory should be in a directory belonging to your |
|
|||
1152 | PYTHONPATH environment variable (that is, it should be in a directory |
|
|||
1153 | belonging to sys.path). You can copy it explicitly there or just link to it. |
|
|||
1154 |
|
||||
1155 | IPython will create a minimal default configuration for you. |
|
|||
1156 |
|
||||
1157 | """ |
|
|||
1158 | warn(warning) |
|
|||
1159 | wait() |
|
|||
1160 |
|
||||
1161 | if sys.platform =='win32': |
|
|||
1162 | inif = 'ipythonrc.ini' |
|
|||
1163 | else: |
|
|||
1164 | inif = 'ipythonrc' |
|
|||
1165 | minimal_setup = {'ipy_user_conf.py' : 'import ipy_defaults', |
|
|||
1166 | inif : '# intentionally left blank' } |
|
|||
1167 | os.makedirs(ipythondir, mode = 0777) |
|
|||
1168 | for f, cont in minimal_setup.items(): |
|
|||
1169 | open(ipythondir + '/' + f,'w').write(cont) |
|
|||
1170 |
|
||||
1171 | return |
|
|||
1172 |
|
||||
1173 | if mode == 'install': |
|
|||
1174 | try: |
|
|||
1175 | shutil.copytree(rcdir,ipythondir) |
|
|||
1176 | os.chdir(ipythondir) |
|
|||
1177 | rc_files = glb("ipythonrc*") |
|
|||
1178 | for rc_file in rc_files: |
|
|||
1179 | os.rename(rc_file,rc_file+rc_suffix) |
|
|||
1180 | except: |
|
|||
1181 | warning = """ |
|
|||
1182 |
|
||||
1183 | There was a problem with the installation: |
|
|||
1184 | %s |
|
|||
1185 | Try to correct it or contact the developers if you think it's a bug. |
|
|||
1186 | IPython will proceed with builtin defaults.""" % sys.exc_info()[1] |
|
|||
1187 | warn(warning) |
|
|||
1188 | wait() |
|
|||
1189 | return |
|
|||
1190 |
|
||||
1191 | elif mode == 'upgrade': |
|
|||
1192 | try: |
|
|||
1193 | os.chdir(ipythondir) |
|
|||
1194 | except: |
|
|||
1195 | print """ |
|
|||
1196 | Can not upgrade: changing to directory %s failed. Details: |
|
|||
1197 | %s |
|
|||
1198 | """ % (ipythondir,sys.exc_info()[1]) |
|
|||
1199 | wait() |
|
|||
1200 | return |
|
|||
1201 | else: |
|
|||
1202 | sources = glb(os.path.join(rcdir,'[A-Za-z]*')) |
|
|||
1203 | for new_full_path in sources: |
|
|||
1204 | new_filename = os.path.basename(new_full_path) |
|
|||
1205 | if new_filename.startswith('ipythonrc'): |
|
|||
1206 | new_filename = new_filename + rc_suffix |
|
|||
1207 | # The config directory should only contain files, skip any |
|
|||
1208 | # directories which may be there (like CVS) |
|
|||
1209 | if os.path.isdir(new_full_path): |
|
|||
1210 | continue |
|
|||
1211 | if os.path.exists(new_filename): |
|
|||
1212 | old_file = new_filename+'.old' |
|
|||
1213 | if os.path.exists(old_file): |
|
|||
1214 | os.remove(old_file) |
|
|||
1215 | os.rename(new_filename,old_file) |
|
|||
1216 | shutil.copy(new_full_path,new_filename) |
|
|||
1217 | else: |
|
|||
1218 | raise ValueError,'unrecognized mode for install:',`mode` |
|
|||
1219 |
|
||||
1220 | # Fix line-endings to those native to each platform in the config |
|
|||
1221 | # directory. |
|
|||
1222 | try: |
|
|||
1223 | os.chdir(ipythondir) |
|
|||
1224 | except: |
|
|||
1225 | print """ |
|
|||
1226 | Problem: changing to directory %s failed. |
|
|||
1227 | Details: |
|
|||
1228 | %s |
|
|||
1229 |
|
||||
1230 | Some configuration files may have incorrect line endings. This should not |
|
|||
1231 | cause any problems during execution. """ % (ipythondir,sys.exc_info()[1]) |
|
|||
1232 | wait() |
|
|||
1233 | else: |
|
|||
1234 | for fname in glb('ipythonrc*'): |
|
|||
1235 | try: |
|
|||
1236 | native_line_ends(fname,backup=0) |
|
|||
1237 | except IOError: |
|
|||
1238 | pass |
|
|||
1239 |
|
||||
1240 | if mode == 'install': |
|
|||
1241 | print """ |
|
|||
1242 | Successful installation! |
|
|||
1243 |
|
||||
1244 | Please read the sections 'Initial Configuration' and 'Quick Tips' in the |
|
|||
1245 | IPython manual (there are both HTML and PDF versions supplied with the |
|
|||
1246 | distribution) to make sure that your system environment is properly configured |
|
|||
1247 | to take advantage of IPython's features. |
|
|||
1248 |
|
||||
1249 | Important note: the configuration system has changed! The old system is |
|
|||
1250 | still in place, but its setting may be partly overridden by the settings in |
|
|||
1251 | "~/.ipython/ipy_user_conf.py" config file. Please take a look at the file |
|
|||
1252 | if some of the new settings bother you. |
|
|||
1253 |
|
||||
1254 | """ |
|
1322 | """ | |
1255 | else: |
|
1323 | return user_setup(ipythondir,rc_suffix,mode) | |
1256 | print """ |
|
|||
1257 | Successful upgrade! |
|
|||
1258 |
|
||||
1259 | All files in your directory: |
|
|||
1260 | %(ipythondir)s |
|
|||
1261 | which would have been overwritten by the upgrade were backed up with a .old |
|
|||
1262 | extension. If you had made particular customizations in those files you may |
|
|||
1263 | want to merge them back into the new files.""" % locals() |
|
|||
1264 | wait() |
|
|||
1265 | os.chdir(cwd) |
|
|||
1266 | # end user_setup() |
|
|||
1267 |
|
1324 | |||
1268 | def atexit_operations(self): |
|
1325 | def atexit_operations(self): | |
1269 | """This will be executed at the time of exit. |
|
1326 | """This will be executed at the time of exit. | |
@@ -1441,35 +1498,53 b' want to merge them back into the new files.""" % locals()' | |||||
1441 | return True |
|
1498 | return True | |
1442 | return ask_yes_no(prompt,default) |
|
1499 | return ask_yes_no(prompt,default) | |
1443 |
|
1500 | |||
1444 |
def |
|
1501 | def new_main_mod(self,ns=None): | |
1445 | """Cache a main module. |
|
1502 | """Return a new 'main' module object for user code execution. | |
|
1503 | """ | |||
|
1504 | main_mod = self._user_main_module | |||
|
1505 | init_fakemod_dict(main_mod,ns) | |||
|
1506 | return main_mod | |||
|
1507 | ||||
|
1508 | def cache_main_mod(self,ns,fname): | |||
|
1509 | """Cache a main module's namespace. | |||
1446 |
|
1510 | |||
1447 |
When scripts are executed via %run, we must keep a reference to the |
|
1511 | When scripts are executed via %run, we must keep a reference to the | |
1448 |
__main__ module (a FakeModule instance) around so |
|
1512 | namespace of their __main__ module (a FakeModule instance) around so | |
1449 |
clear it, rendering objects defined therein |
|
1513 | that Python doesn't clear it, rendering objects defined therein | |
|
1514 | useless. | |||
1450 |
|
1515 | |||
1451 | This method keeps said reference in a private dict, keyed by the |
|
1516 | This method keeps said reference in a private dict, keyed by the | |
1452 | absolute path of the module object (which corresponds to the script |
|
1517 | absolute path of the module object (which corresponds to the script | |
1453 | path). This way, for multiple executions of the same script we only |
|
1518 | path). This way, for multiple executions of the same script we only | |
1454 |
keep one copy of |
|
1519 | keep one copy of the namespace (the last one), thus preventing memory | |
1455 |
from old references while allowing the objects from the last |
|
1520 | leaks from old references while allowing the objects from the last | |
1456 | to be accessible. |
|
1521 | execution to be accessible. | |
|
1522 | ||||
|
1523 | Note: we can not allow the actual FakeModule instances to be deleted, | |||
|
1524 | because of how Python tears down modules (it hard-sets all their | |||
|
1525 | references to None without regard for reference counts). This method | |||
|
1526 | must therefore make a *copy* of the given namespace, to allow the | |||
|
1527 | original module's __dict__ to be cleared and reused. | |||
|
1528 | ||||
1457 |
|
1529 | |||
1458 | Parameters |
|
1530 | Parameters | |
1459 | ---------- |
|
1531 | ---------- | |
1460 | mod : a module object |
|
1532 | ns : a namespace (a dict, typically) | |
|
1533 | ||||
|
1534 | fname : str | |||
|
1535 | Filename associated with the namespace. | |||
1461 |
|
1536 | |||
1462 | Examples |
|
1537 | Examples | |
1463 | -------- |
|
1538 | -------- | |
1464 |
|
1539 | |||
1465 | In [10]: import IPython |
|
1540 | In [10]: import IPython | |
1466 |
|
1541 | |||
1467 | In [11]: _ip.IP.cache_main_mod(IPython) |
|
1542 | In [11]: _ip.IP.cache_main_mod(IPython.__dict__,IPython.__file__) | |
1468 |
|
1543 | |||
1469 |
In [12]: IPython.__file__ in _ip.IP._ |
|
1544 | In [12]: IPython.__file__ in _ip.IP._main_ns_cache | |
1470 | Out[12]: True |
|
1545 | Out[12]: True | |
1471 | """ |
|
1546 | """ | |
1472 |
self. |
|
1547 | self._main_ns_cache[os.path.abspath(fname)] = ns.copy() | |
1473 |
|
1548 | |||
1474 | def clear_main_mod_cache(self): |
|
1549 | def clear_main_mod_cache(self): | |
1475 | """Clear the cache of main modules. |
|
1550 | """Clear the cache of main modules. | |
@@ -1481,17 +1556,17 b' want to merge them back into the new files.""" % locals()' | |||||
1481 |
|
1556 | |||
1482 | In [15]: import IPython |
|
1557 | In [15]: import IPython | |
1483 |
|
1558 | |||
1484 | In [16]: _ip.IP.cache_main_mod(IPython) |
|
1559 | In [16]: _ip.IP.cache_main_mod(IPython.__dict__,IPython.__file__) | |
1485 |
|
1560 | |||
1486 |
In [17]: len(_ip.IP._ |
|
1561 | In [17]: len(_ip.IP._main_ns_cache) > 0 | |
1487 | Out[17]: True |
|
1562 | Out[17]: True | |
1488 |
|
1563 | |||
1489 | In [18]: _ip.IP.clear_main_mod_cache() |
|
1564 | In [18]: _ip.IP.clear_main_mod_cache() | |
1490 |
|
1565 | |||
1491 |
In [19]: len(_ip.IP._ |
|
1566 | In [19]: len(_ip.IP._main_ns_cache) == 0 | |
1492 | Out[19]: True |
|
1567 | Out[19]: True | |
1493 | """ |
|
1568 | """ | |
1494 |
self. |
|
1569 | self._main_ns_cache.clear() | |
1495 |
|
1570 | |||
1496 | def _should_recompile(self,e): |
|
1571 | def _should_recompile(self,e): | |
1497 | """Utility routine for edit_syntax_error""" |
|
1572 | """Utility routine for edit_syntax_error""" |
@@ -61,7 +61,7 b' def main():' | |||||
61 | # plugin needs to be gone through with a fine |
|
61 | # plugin needs to be gone through with a fine | |
62 | # toothed comb to find what is causing the problem. |
|
62 | # toothed comb to find what is causing the problem. | |
63 | # '--with-ipdoctest', |
|
63 | # '--with-ipdoctest', | |
64 | '--doctest-tests','--doctest-extension=txt', |
|
64 | '--ipdoctest-tests','--ipdoctest-extension=txt', | |
65 | '--detailed-errors', |
|
65 | '--detailed-errors', | |
66 |
|
66 | |||
67 | # We add --exe because of setuptools' imbecility (it |
|
67 | # We add --exe because of setuptools' imbecility (it | |
@@ -81,11 +81,13 b' def main():' | |||||
81 | (':' in arg and '.py' in arg): |
|
81 | (':' in arg and '.py' in arg): | |
82 | has_tests = True |
|
82 | has_tests = True | |
83 | break |
|
83 | break | |
|
84 | ||||
84 | # If nothing was specifically requested, test full IPython |
|
85 | # If nothing was specifically requested, test full IPython | |
85 | if not has_tests: |
|
86 | if not has_tests: | |
86 | argv.append('IPython') |
|
87 | argv.append('IPython') | |
87 |
|
88 | |||
88 |
# Construct list of plugins, omitting the existing doctest plugin |
|
89 | # Construct list of plugins, omitting the existing doctest plugin, which | |
|
90 | # ours replaces (and extends). | |||
89 | plugins = [IPythonDoctest(EXCLUDE)] |
|
91 | plugins = [IPythonDoctest(EXCLUDE)] | |
90 | for p in nose.plugins.builtin.plugins: |
|
92 | for p in nose.plugins.builtin.plugins: | |
91 | plug = p() |
|
93 | plug = p() |
@@ -15,7 +15,6 b' Limitations:' | |||||
15 | won't even have these special _NN variables set at all. |
|
15 | won't even have these special _NN variables set at all. | |
16 | """ |
|
16 | """ | |
17 |
|
17 | |||
18 |
|
||||
19 | #----------------------------------------------------------------------------- |
|
18 | #----------------------------------------------------------------------------- | |
20 | # Module imports |
|
19 | # Module imports | |
21 |
|
20 | |||
@@ -123,6 +122,13 b' class ipnsdict(dict):' | |||||
123 | def start_ipython(): |
|
122 | def start_ipython(): | |
124 | """Start a global IPython shell, which we need for IPython-specific syntax. |
|
123 | """Start a global IPython shell, which we need for IPython-specific syntax. | |
125 | """ |
|
124 | """ | |
|
125 | ||||
|
126 | # This function should only ever run once! | |||
|
127 | if hasattr(start_ipython,'already_called'): | |||
|
128 | return | |||
|
129 | start_ipython.already_called = True | |||
|
130 | ||||
|
131 | # Ok, first time we're called, go ahead | |||
126 | import new |
|
132 | import new | |
127 |
|
133 | |||
128 | import IPython |
|
134 | import IPython | |
@@ -691,6 +697,7 b' class ExtensionDoctest(doctests.Doctest):' | |||||
691 | to exclude any filename which matches them from inclusion in the test |
|
697 | to exclude any filename which matches them from inclusion in the test | |
692 | suite (using pattern.search(), NOT pattern.match() ). |
|
698 | suite (using pattern.search(), NOT pattern.match() ). | |
693 | """ |
|
699 | """ | |
|
700 | ||||
694 | if exclude_patterns is None: |
|
701 | if exclude_patterns is None: | |
695 | exclude_patterns = [] |
|
702 | exclude_patterns = [] | |
696 | self.exclude_patterns = map(re.compile,exclude_patterns) |
|
703 | self.exclude_patterns = map(re.compile,exclude_patterns) | |
@@ -836,15 +843,33 b' class IPythonDoctest(ExtensionDoctest):' | |||||
836 | optionflags=optionflags, |
|
843 | optionflags=optionflags, | |
837 | checker=self.checker) |
|
844 | checker=self.checker) | |
838 |
|
845 | |||
839 | def configure(self, options, config): |
|
846 | def options(self, parser, env=os.environ): | |
|
847 | Plugin.options(self, parser, env) | |||
|
848 | parser.add_option('--ipdoctest-tests', action='store_true', | |||
|
849 | dest='ipdoctest_tests', | |||
|
850 | default=env.get('NOSE_IPDOCTEST_TESTS',True), | |||
|
851 | help="Also look for doctests in test modules. " | |||
|
852 | "Note that classes, methods and functions should " | |||
|
853 | "have either doctests or non-doctest tests, " | |||
|
854 | "not both. [NOSE_IPDOCTEST_TESTS]") | |||
|
855 | parser.add_option('--ipdoctest-extension', action="append", | |||
|
856 | dest="ipdoctest_extension", | |||
|
857 | help="Also look for doctests in files with " | |||
|
858 | "this extension [NOSE_IPDOCTEST_EXTENSION]") | |||
|
859 | # Set the default as a list, if given in env; otherwise | |||
|
860 | # an additional value set on the command line will cause | |||
|
861 | # an error. | |||
|
862 | env_setting = env.get('NOSE_IPDOCTEST_EXTENSION') | |||
|
863 | if env_setting is not None: | |||
|
864 | parser.set_defaults(ipdoctest_extension=tolist(env_setting)) | |||
840 |
|
865 | |||
|
866 | def configure(self, options, config): | |||
841 | Plugin.configure(self, options, config) |
|
867 | Plugin.configure(self, options, config) | |
842 | self.doctest_tests = options.doctest_tests |
|
868 | self.doctest_tests = options.ipdoctest_tests | |
843 |
self.extension = tolist(options.doctest |
|
869 | self.extension = tolist(options.ipdoctest_extension) | |
844 |
|
870 | |||
845 | self.parser = IPDocTestParser() |
|
871 | self.parser = IPDocTestParser() | |
846 | self.finder = DocTestFinder(parser=self.parser) |
|
872 | self.finder = DocTestFinder(parser=self.parser) | |
847 | self.checker = IPDoctestOutputChecker() |
|
873 | self.checker = IPDoctestOutputChecker() | |
848 | self.globs = None |
|
874 | self.globs = None | |
849 | self.extraglobs = None |
|
875 | self.extraglobs = None | |
850 |
|
@@ -7,7 +7,8 b' import gc' | |||||
7 |
|
7 | |||
8 | class C(object): |
|
8 | class C(object): | |
9 | def __del__(self): |
|
9 | def __del__(self): | |
10 | print 'deleting object...' |
|
10 | pass | |
|
11 | #print 'deleting object...' # dbg | |||
11 |
|
12 | |||
12 | c = C() |
|
13 | c = C() | |
13 |
|
14 |
@@ -39,13 +39,10 b' def doctest_ivars():' | |||||
39 | Out[6]: 1 |
|
39 | Out[6]: 1 | |
40 | """ |
|
40 | """ | |
41 |
|
41 | |||
42 | @dec.skip_doctest |
|
42 | #@dec.skip_doctest | |
43 | def doctest_refs(): |
|
43 | def doctest_refs(): | |
44 | """DocTest reference holding issues when running scripts. |
|
44 | """DocTest reference holding issues when running scripts. | |
45 |
|
45 | |||
46 | In [32]: run show_refs.py |
|
46 | In [32]: run show_refs.py | |
47 | c referrers: [<type 'dict'>] |
|
47 | c referrers: [<type 'dict'>] | |
48 |
|
||||
49 | In [33]: map(type,gc.get_referrers(c)) |
|
|||
50 | Out[33]: [<type 'dict'>] |
|
|||
51 | """ |
|
48 | """ |
@@ -26,7 +26,7 b' import sys' | |||||
26 |
|
26 | |||
27 | class A(object): |
|
27 | class A(object): | |
28 | def __del__(self): |
|
28 | def __del__(self): | |
29 | print 'object A deleted' |
|
29 | print 'obj_del.py: object A deleted' | |
30 |
|
30 | |||
31 | a = A() |
|
31 | a = A() | |
32 |
|
32 |
@@ -16,11 +16,12 b' class C(object):' | |||||
16 | self.name = name |
|
16 | self.name = name | |
17 |
|
17 | |||
18 | def __del__(self): |
|
18 | def __del__(self): | |
19 |
print ' |
|
19 | print 'tclass.py: deleting object:',self.name | |
20 |
|
20 | |||
21 | try: |
|
21 | try: | |
22 | name = sys.argv[1] |
|
22 | name = sys.argv[1] | |
23 | except IndexError: |
|
23 | except IndexError: | |
24 | pass |
|
24 | pass | |
25 | else: |
|
25 | else: | |
|
26 | if name.startswith('C'): | |||
26 | c = C(name) |
|
27 | c = C(name) |
@@ -1,17 +1,68 b'' | |||||
1 | """Tests for the key iplib module, where the main ipython class is defined. |
|
1 | """Tests for the key iplib module, where the main ipython class is defined. | |
2 | """ |
|
2 | """ | |
|
3 | #----------------------------------------------------------------------------- | |||
|
4 | # Module imports | |||
|
5 | #----------------------------------------------------------------------------- | |||
3 |
|
6 | |||
|
7 | # stdlib | |||
|
8 | import os | |||
|
9 | import shutil | |||
|
10 | import tempfile | |||
|
11 | ||||
|
12 | # third party | |||
4 | import nose.tools as nt |
|
13 | import nose.tools as nt | |
5 |
|
14 | |||
|
15 | # our own packages | |||
|
16 | from IPython import iplib | |||
|
17 | ||||
|
18 | #----------------------------------------------------------------------------- | |||
|
19 | # Globals | |||
|
20 | #----------------------------------------------------------------------------- | |||
|
21 | ||||
|
22 | # Useful global ipapi object and main IPython one. Unfortunately we have a | |||
|
23 | # long precedent of carrying the 'ipapi' global object which is injected into | |||
|
24 | # the system namespace as _ip, but that keeps a pointer to the actual IPython | |||
|
25 | # InteractiveShell instance, which is named IP. Since in testing we do need | |||
|
26 | # access to the real thing (we want to probe beyond what ipapi exposes), make | |||
|
27 | # here a global reference to each. In general, things that are exposed by the | |||
|
28 | # ipapi instance should be read from there, but we also will often need to use | |||
|
29 | # the actual IPython one. | |||
|
30 | ||||
|
31 | ip = _ip # This is the ipapi instance | |||
|
32 | IP = ip.IP # This is the actual IPython shell 'raw' object. | |||
|
33 | ||||
|
34 | #----------------------------------------------------------------------------- | |||
|
35 | # Test functions | |||
|
36 | #----------------------------------------------------------------------------- | |||
6 |
|
37 | |||
7 | def test_reset(): |
|
38 | def test_reset(): | |
8 | """reset must clear most namespaces.""" |
|
39 | """reset must clear most namespaces.""" | |
9 | ip = _ip.IP |
|
40 | IP.reset() # first, it should run without error | |
10 | ip.reset() # first, it should run without error |
|
|||
11 | # Then, check that most namespaces end up empty |
|
41 | # Then, check that most namespaces end up empty | |
12 |
for ns in |
|
42 | for ns in IP.ns_refs_table: | |
13 |
if ns is |
|
43 | if ns is IP.user_ns: | |
14 | # The user namespace is reset with some data, so we can't check for |
|
44 | # The user namespace is reset with some data, so we can't check for | |
15 | # it being empty |
|
45 | # it being empty | |
16 | continue |
|
46 | continue | |
17 | nt.assert_equals(len(ns),0) |
|
47 | nt.assert_equals(len(ns),0) | |
|
48 | ||||
|
49 | ||||
|
50 | # make sure that user_setup can be run re-entrantly in 'install' mode. | |||
|
51 | def test_user_setup(): | |||
|
52 | # use a lambda to pass kwargs to the generator | |||
|
53 | user_setup = lambda a,k: iplib.user_setup(*a,**k) | |||
|
54 | kw = dict(mode='install', interactive=False) | |||
|
55 | ||||
|
56 | # Call the user setup and verify that the directory exists | |||
|
57 | yield user_setup, (ip.options.ipythondir,''), kw | |||
|
58 | yield os.path.isdir, ip.options.ipythondir | |||
|
59 | ||||
|
60 | # Now repeat the operation with a non-existent directory. Check both that | |||
|
61 | # the call succeeds and that the directory is created. | |||
|
62 | tmpdir = tempfile.mktemp(prefix='ipython-test-') | |||
|
63 | try: | |||
|
64 | yield user_setup, (tmpdir,''), kw | |||
|
65 | yield os.path.isdir, tmpdir | |||
|
66 | finally: | |||
|
67 | # In this case, clean up the temp dir once done | |||
|
68 | shutil.rmtree(tmpdir) |
@@ -37,7 +37,7 b' def test_rehashx():' | |||||
37 | def doctest_run_ns(): |
|
37 | def doctest_run_ns(): | |
38 | """Classes declared %run scripts must be instantiable afterwards. |
|
38 | """Classes declared %run scripts must be instantiable afterwards. | |
39 |
|
39 | |||
40 | In [11]: run tclass |
|
40 | In [11]: run tclass foo | |
41 |
|
41 | |||
42 | In [12]: isinstance(f(),foo) |
|
42 | In [12]: isinstance(f(),foo) | |
43 | Out[12]: True |
|
43 | Out[12]: True | |
@@ -47,12 +47,10 b' def doctest_run_ns():' | |||||
47 | def doctest_run_ns2(): |
|
47 | def doctest_run_ns2(): | |
48 | """Classes declared %run scripts must be instantiable afterwards. |
|
48 | """Classes declared %run scripts must be instantiable afterwards. | |
49 |
|
49 | |||
50 |
In [ |
|
50 | In [4]: run tclass C-first_pass | |
51 |
|
51 | |||
52 |
In [ |
|
52 | In [5]: run tclass C-second_pass | |
53 |
|
53 | tclass.py: deleting object: C-first_pass | ||
54 | In [5]: run tclass second_pass |
|
|||
55 | Deleting object: first_pass |
|
|||
56 | """ |
|
54 | """ | |
57 |
|
55 | |||
58 |
|
56 | |||
@@ -85,7 +83,7 b' def test_obj_del():' | |||||
85 | test_dir = os.path.dirname(__file__) |
|
83 | test_dir = os.path.dirname(__file__) | |
86 | del_file = os.path.join(test_dir,'obj_del.py') |
|
84 | del_file = os.path.join(test_dir,'obj_del.py') | |
87 | out = _ip.IP.getoutput('ipython %s' % del_file) |
|
85 | out = _ip.IP.getoutput('ipython %s' % del_file) | |
88 | nt.assert_equals(out,'object A deleted') |
|
86 | nt.assert_equals(out,'obj_del.py: object A deleted') | |
89 |
|
87 | |||
90 |
|
88 | |||
91 | def test_shist(): |
|
89 | def test_shist(): | |
@@ -133,3 +131,21 b' def test_fail_dec2(*a,**k):' | |||||
133 | def test_fail_dec3(*a,**k): |
|
131 | def test_fail_dec3(*a,**k): | |
134 | yield nt.assert_true, False |
|
132 | yield nt.assert_true, False | |
135 |
|
133 | |||
|
134 | ||||
|
135 | def doctest_refbug(): | |||
|
136 | """Very nasty problem with references held by multiple runs of a script. | |||
|
137 | See: https://bugs.launchpad.net/ipython/+bug/269966 | |||
|
138 | ||||
|
139 | In [1]: _ip.IP.clear_main_mod_cache() | |||
|
140 | ||||
|
141 | In [2]: run refbug | |||
|
142 | ||||
|
143 | In [3]: call_f() | |||
|
144 | lowercased: hello | |||
|
145 | ||||
|
146 | In [4]: run refbug | |||
|
147 | ||||
|
148 | In [5]: call_f() | |||
|
149 | lowercased: hello | |||
|
150 | lowercased: hello | |||
|
151 | """ |
@@ -345,6 +345,37 b' nosetests option. For example, you can use ``--pdb`` or ``--pdb-failures`` to' | |||||
345 | automatically activate the interactive Pdb debugger on errors or failures. See |
|
345 | automatically activate the interactive Pdb debugger on errors or failures. See | |
346 | the nosetests documentation for further details. |
|
346 | the nosetests documentation for further details. | |
347 |
|
347 | |||
|
348 | .. warning:: | |||
|
349 | ||||
|
350 | Note that right now we have a nasty interaction between ipdoctest and | |||
|
351 | twisted. Until we figure this out, please use the following instructions to | |||
|
352 | ensure that at least you run all the tests. | |||
|
353 | ||||
|
354 | Right now, if you now run:: | |||
|
355 | ||||
|
356 | $ iptest [any options] [any submodules] | |||
|
357 | ||||
|
358 | it will NOT load ipdoctest but won't cause any Twisted problems. | |||
|
359 | ||||
|
360 | Once you're happy that you didn't break Twisted, run:: | |||
|
361 | ||||
|
362 | $ iptest --with-ipdoctest [any options] [any submodules] | |||
|
363 | ||||
|
364 | This MAY give a Twisted AlreadyCalledError exception at the end, but it will | |||
|
365 | also correctly load up all of the ipython-specific tests and doctests. | |||
|
366 | ||||
|
367 | The above can be made easier with a trivial shell alias:: | |||
|
368 | ||||
|
369 | $ alias iptest2='iptest --with-ipdoctest' | |||
|
370 | ||||
|
371 | So that you can run:: | |||
|
372 | ||||
|
373 | $ iptest ... | |||
|
374 | # Twisted happy | |||
|
375 | # iptest2 ... | |||
|
376 | # ignore possible Twisted error, this checks all the rest. | |||
|
377 | ||||
|
378 | ||||
348 | A few tips for writing tests |
|
379 | A few tips for writing tests | |
349 | ---------------------------- |
|
380 | ---------------------------- | |
350 |
|
381 |
General Comments 0
You need to be logged in to leave comments.
Login now