Show More
@@ -0,0 +1,1 | |||||
|
1 | IPython/.git_commit_info.ini export-subst |
@@ -0,0 +1,9 | |||||
|
1 | # This is an ini file that may contain information about the code state | |||
|
2 | [commit hash] | |||
|
3 | ||||
|
4 | # The line below may contain a valid hash if it has been substituted during | |||
|
5 | # 'git archive' | |||
|
6 | archive_subst_hash=$Format:%h$ | |||
|
7 | ||||
|
8 | # This line may be modified by the install process | |||
|
9 | install_hash= |
@@ -1,8 +1,10 | |||||
|
1 | build | |||
|
2 | ./dist | |||
1 | docs/dist |
|
3 | docs/dist | |
2 | docs/build/* |
|
4 | docs/build/* | |
3 | docs/source/api/generated |
|
5 | docs/source/api/generated | |
4 | *.pyc |
|
6 | *.py[co] | |
5 | build |
|
7 | build | |
6 | *.egg-info |
|
8 | *.egg-info | |
7 | *.py~ |
|
9 | *~ | |
8 | *.bak |
|
10 | *.bak |
@@ -1,77 +1,85 | |||||
1 | IPython is licensed under the terms of the new or revised BSD license, as follows: |
|
1 | ============================= | |
|
2 | The IPython licensing terms | |||
|
3 | ============================= | |||
2 |
|
4 | |||
3 | Copyright (c) 2008, IPython Development Team |
|
5 | IPython is licensed under the terms of the Modified BSD License (also known as | |
|
6 | New or Revised BSD), as follows: | |||
|
7 | ||||
|
8 | Copyright (c) 2008-2010, IPython Development Team | |||
|
9 | Copyright (c) 2001-2007, Fernando Perez. <fernando.perez@colorado.edu> | |||
|
10 | Copyright (c) 2001, Janko Hauser <jhauser@zscout.de> | |||
|
11 | Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu> | |||
4 |
|
12 | |||
5 | All rights reserved. |
|
13 | All rights reserved. | |
6 |
|
14 | |||
7 |
Redistribution and use in source and binary forms, with or without |
|
15 | Redistribution and use in source and binary forms, with or without | |
8 | are permitted provided that the following conditions are met: |
|
16 | modification, are permitted provided that the following conditions are met: | |
9 |
|
17 | |||
10 |
Redistributions of source code must retain the above copyright notice, this |
|
18 | Redistributions of source code must retain the above copyright notice, this | |
11 | conditions and the following disclaimer. |
|
19 | list of conditions and the following disclaimer. | |
12 |
|
20 | |||
13 |
Redistributions in binary form must reproduce the above copyright notice, this |
|
21 | Redistributions in binary form must reproduce the above copyright notice, this | |
14 |
of conditions and the following disclaimer in the documentation and/or |
|
22 | list of conditions and the following disclaimer in the documentation and/or | |
15 | materials provided with the distribution. |
|
23 | other materials provided with the distribution. | |
16 |
|
24 | |||
17 |
Neither the name of the IPython Development Team nor the names of its |
|
25 | Neither the name of the IPython Development Team nor the names of its | |
18 |
may be used to endorse or promote products derived from this |
|
26 | contributors may be used to endorse or promote products derived from this | |
19 | specific prior written permission. |
|
27 | software without specific prior written permission. | |
20 |
|
28 | |||
21 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
30 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
23 |
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
31 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
24 |
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
|
32 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE | |
25 |
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
33 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 |
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
34 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|
35 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
28 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
36 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
29 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
37 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
30 | POSSIBILITY OF SUCH DAMAGE. |
|
38 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 |
|
39 | |||
32 | About the IPython Development Team |
|
40 | About the IPython Development Team | |
33 | ---------------------------------- |
|
41 | ---------------------------------- | |
34 |
|
42 | |||
35 |
Fernando Perez began IPython in 2001 based on code from Janko Hauser |
|
43 | Fernando Perez began IPython in 2001 based on code from Janko Hauser | |
36 |
and Nathaniel Gray <n8gray@caltech.edu>. Fernando is still |
|
44 | <jhauser@zscout.de> and Nathaniel Gray <n8gray@caltech.edu>. Fernando is still | |
|
45 | the project lead. | |||
|
46 | ||||
|
47 | The IPython Development Team is the set of all contributors to the IPython | |||
|
48 | project. This includes all of the IPython subprojects. A full list with | |||
|
49 | details is kept in the documentation directory, in the file | |||
|
50 | ``about/credits.txt``. | |||
37 |
|
51 | |||
38 | The IPython Development Team is the set of all contributors to the IPython project. |
|
52 | The core team that coordinates development on GitHub can be found here: | |
39 | This includes all of the IPython subprojects. Here is a list of the currently active contributors: |
|
53 | http://github.com/ipython. As of late 2010, it consists of: | |
40 |
|
54 | |||
41 | * Matthieu Brucher |
|
55 | * Brian E. Granger | |
42 | * Ondrej Certik |
|
56 | * Jonathan March | |
43 | * Laurent Dufrechou |
|
57 | * Evan Patterson | |
44 | * Robert Kern |
|
58 | * Fernando Perez | |
45 | * Brian E. Granger |
|
59 | * Min Ragan-Kelley | |
46 | * Fernando Perez (project leader) |
|
60 | * Robert Kern | |
47 | * Benjamin Ragan-Kelley |
|
|||
48 | * Ville M. Vainio |
|
|||
49 | * Gael Varoququx |
|
|||
50 | * Stefan van der Walt |
|
|||
51 | * Tech-X Corporation |
|
|||
52 | * Barry Wark |
|
|||
53 |
|
61 | |||
54 | If your name is missing, please add it. |
|
|||
55 |
|
62 | |||
56 | Our Copyright Policy |
|
63 | Our Copyright Policy | |
57 | -------------------- |
|
64 | -------------------- | |
58 |
|
65 | |||
59 |
IPython uses a shared copyright model. Each contributor maintains copyright |
|
66 | IPython uses a shared copyright model. Each contributor maintains copyright | |
60 | their contributions to IPython. But, it is important to note that these |
|
67 | over their contributions to IPython. But, it is important to note that these | |
61 | contributions are typically only changes to the repositories. Thus, the IPython |
|
68 | contributions are typically only changes to the repositories. Thus, the IPython | |
62 | source code, in its entirety is not the copyright of any single person or |
|
69 | source code, in its entirety is not the copyright of any single person or | |
63 | institution. Instead, it is the collective copyright of the entire IPython |
|
70 | institution. Instead, it is the collective copyright of the entire IPython | |
64 | Development Team. If individual contributors want to maintain a record of what |
|
71 | Development Team. If individual contributors want to maintain a record of what | |
65 |
changes/contributions they have specific copyright on, they should indicate |
|
72 | changes/contributions they have specific copyright on, they should indicate | |
66 |
copyright in the commit message of the change, when they commit the |
|
73 | their copyright in the commit message of the change, when they commit the | |
67 | one of the IPython repositories. |
|
74 | change to one of the IPython repositories. | |
68 |
|
75 | |||
69 |
With this in mind, the following banner should be used in any source code file |
|
76 | With this in mind, the following banner should be used in any source code file | |
70 | indicate the copyright and license terms: |
|
77 | to indicate the copyright and license terms: | |
71 |
|
78 | |||
72 |
#----------------------------------------------------------------------------- |
|
79 | #----------------------------------------------------------------------------- | |
73 |
# |
|
80 | # Copyright (c) 2010, IPython Development Team. | |
|
81 | # | |||
|
82 | # Distributed under the terms of the Modified BSD License. | |||
74 | # |
|
83 | # | |
75 | # Distributed under the terms of the BSD License. The full license is in |
|
84 | # The full license is in the file COPYING.txt, distributed with this software. | |
76 | # the file COPYING, distributed as part of this software. |
|
85 | #----------------------------------------------------------------------------- | |
77 | #------------------------------------------------------------------------------- No newline at end of file |
|
@@ -1,55 +1,59 | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | """ |
|
3 | """ | |
4 | IPython. |
|
4 | IPython. | |
5 |
|
5 | |||
6 | IPython is a set of tools for interactive and exploratory computing in Python. |
|
6 | IPython is a set of tools for interactive and exploratory computing in Python. | |
7 | """ |
|
7 | """ | |
8 | #----------------------------------------------------------------------------- |
|
8 | #----------------------------------------------------------------------------- | |
9 |
# Copyright ( |
|
9 | # Copyright (c) 2008-2010, IPython Development Team. | |
|
10 | # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu> | |||
|
11 | # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de> | |||
|
12 | # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu> | |||
10 | # |
|
13 | # | |
11 |
# Distributed under the terms of the BSD License. |
|
14 | # Distributed under the terms of the Modified BSD License. | |
12 | # the file COPYING, distributed as part of this software. |
|
15 | # | |
|
16 | # The full license is in the file COPYING.txt, distributed with this software. | |||
13 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
14 |
|
18 | |||
15 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
16 | # Imports |
|
20 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
21 | #----------------------------------------------------------------------------- | |
18 | from __future__ import absolute_import |
|
22 | from __future__ import absolute_import | |
19 |
|
23 | |||
20 | import os |
|
24 | import os | |
21 | import sys |
|
25 | import sys | |
22 |
|
26 | |||
23 | #----------------------------------------------------------------------------- |
|
27 | #----------------------------------------------------------------------------- | |
24 | # Setup everything |
|
28 | # Setup everything | |
25 | #----------------------------------------------------------------------------- |
|
29 | #----------------------------------------------------------------------------- | |
26 |
|
30 | |||
27 | # Don't forget to also update setup.py when this changes! |
|
31 | # Don't forget to also update setup.py when this changes! | |
28 | if sys.version[0:3] < '2.6': |
|
32 | if sys.version[0:3] < '2.6': | |
29 | raise ImportError('Python Version 2.6 or above is required for IPython.') |
|
33 | raise ImportError('Python Version 2.6 or above is required for IPython.') | |
30 |
|
34 | |||
31 |
|
35 | |||
32 | # Make it easy to import extensions - they are always directly on pythonpath. |
|
36 | # Make it easy to import extensions - they are always directly on pythonpath. | |
33 | # Therefore, non-IPython modules can be added to extensions directory. |
|
37 | # Therefore, non-IPython modules can be added to extensions directory. | |
34 | # This should probably be in ipapp.py. |
|
38 | # This should probably be in ipapp.py. | |
35 | sys.path.append(os.path.join(os.path.dirname(__file__), "extensions")) |
|
39 | sys.path.append(os.path.join(os.path.dirname(__file__), "extensions")) | |
36 |
|
40 | |||
37 | #----------------------------------------------------------------------------- |
|
41 | #----------------------------------------------------------------------------- | |
38 | # Setup the top level names |
|
42 | # Setup the top level names | |
39 | #----------------------------------------------------------------------------- |
|
43 | #----------------------------------------------------------------------------- | |
40 |
|
44 | |||
41 | from .config.loader import Config |
|
45 | from .config.loader import Config | |
42 | from .core import release |
|
46 | from .core import release | |
43 | from .core.application import Application |
|
47 | from .core.application import Application | |
44 | from .frontend.terminal.embed import embed |
|
48 | from .frontend.terminal.embed import embed | |
45 | from .core.error import TryNext |
|
49 | from .core.error import TryNext | |
46 | from .core.interactiveshell import InteractiveShell |
|
50 | from .core.interactiveshell import InteractiveShell | |
47 | from .testing import test |
|
51 | from .testing import test | |
|
52 | from .utils.sysinfo import sys_info | |||
48 |
|
53 | |||
49 | # Release data |
|
54 | # Release data | |
50 | __author__ = '' |
|
55 | __author__ = '' | |
51 | for author, email in release.authors.itervalues(): |
|
56 | for author, email in release.authors.itervalues(): | |
52 | __author__ += author + ' <' + email + '>\n' |
|
57 | __author__ += author + ' <' + email + '>\n' | |
53 | __license__ = release.license |
|
58 | __license__ = release.license | |
54 | __version__ = release.version |
|
59 | __version__ = release.version | |
55 | __revision__ = release.revision |
|
@@ -1,122 +1,123 | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Release data for the IPython project.""" |
|
2 | """Release data for the IPython project.""" | |
3 |
|
3 | |||
4 | #***************************************************************************** |
|
4 | #----------------------------------------------------------------------------- | |
5 |
# |
|
5 | # Copyright (c) 2008-2010, IPython Development Team. | |
6 |
# |
|
6 | # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu> | |
7 |
# |
|
7 | # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de> | |
8 | # <n8gray@caltech.edu> |
|
8 | # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu> | |
9 | # |
|
9 | # | |
10 |
# Distributed under the terms of the BSD License. |
|
10 | # Distributed under the terms of the Modified BSD License. | |
11 | # the file COPYING, distributed as part of this software. |
|
11 | # | |
12 | #***************************************************************************** |
|
12 | # The full license is in the file COPYING.txt, distributed with this software. | |
|
13 | #----------------------------------------------------------------------------- | |||
13 |
|
14 | |||
14 | # Name of the package for release purposes. This is the name which labels |
|
15 | # Name of the package for release purposes. This is the name which labels | |
15 | # the tarballs and RPMs made by distutils, so it's best to lowercase it. |
|
16 | # the tarballs and RPMs made by distutils, so it's best to lowercase it. | |
16 | name = 'ipython' |
|
17 | name = 'ipython' | |
17 |
|
18 | |||
18 | # For versions with substrings (like 0.6.16.svn), use an extra . to separate |
|
19 | # IPython version information. An empty _version_extra corresponds to a full | |
19 | # the new substring. We have to avoid using either dashes or underscores, |
|
20 | # release. 'dev' as a _version_extra string means this is a development | |
20 | # because bdist_rpm does not accept dashes (an RPM) convention, and |
|
21 | # version | |
21 | # bdist_deb does not accept underscores (a Debian convention). |
|
22 | _version_major = 0 | |
|
23 | _version_minor = 11 | |||
|
24 | _version_micro = '' # use '' for first of series, number for 1 and above | |||
|
25 | _version_extra = 'dev' | |||
|
26 | #_version_extra = '' # Uncomment this for full releases | |||
22 |
|
27 | |||
23 | development = True # change this to False to do a release |
|
28 | # Construct full version string from these. | |
24 | version_base = '0.11.alpha1' |
|
29 | _ver = [_version_major, _version_minor] | |
25 | branch = 'ipython' |
|
30 | if _version_micro: | |
26 | # This needs to be updated to something that is meaningful for git |
|
31 | _ver.append(_version_micro) | |
27 | revision = '0' |
|
32 | if _version_extra: | |
|
33 | _ver.append(_version_extra) | |||
28 |
|
34 | |||
29 | if development: |
|
35 | __version__ = '.'.join(map(str, _ver)) | |
30 | if branch == 'ipython': |
|
|||
31 | version = '%s.git' % (version_base) |
|
|||
32 | else: |
|
|||
33 | version = '%s.git.%s' % (version_base, branch) |
|
|||
34 | else: |
|
|||
35 | version = version_base |
|
|||
36 |
|
36 | |||
|
37 | version = __version__ # backwards compatibility name | |||
37 |
|
38 | |||
38 | description = "An interactive computing environment for Python" |
|
39 | description = "An interactive computing environment for Python" | |
39 |
|
40 | |||
40 | long_description = \ |
|
41 | long_description = \ | |
41 | """ |
|
42 | """ | |
42 | The goal of IPython is to create a comprehensive environment for |
|
43 | The goal of IPython is to create a comprehensive environment for | |
43 | interactive and exploratory computing. To support this goal, IPython |
|
44 | interactive and exploratory computing. To support this goal, IPython | |
44 | has two main components: |
|
45 | has two main components: | |
45 |
|
46 | |||
46 | * An enhanced interactive Python shell. |
|
47 | * An enhanced interactive Python shell. | |
47 |
|
48 | |||
48 | * An architecture for interactive parallel computing. |
|
49 | * An architecture for interactive parallel computing. | |
49 |
|
50 | |||
50 | The enhanced interactive Python shell has the following main features: |
|
51 | The enhanced interactive Python shell has the following main features: | |
51 |
|
52 | |||
52 | * Comprehensive object introspection. |
|
53 | * Comprehensive object introspection. | |
53 |
|
54 | |||
54 | * Input history, persistent across sessions. |
|
55 | * Input history, persistent across sessions. | |
55 |
|
56 | |||
56 | * Caching of output results during a session with automatically generated |
|
57 | * Caching of output results during a session with automatically generated | |
57 | references. |
|
58 | references. | |
58 |
|
59 | |||
59 | * Readline based name completion. |
|
60 | * Readline based name completion. | |
60 |
|
61 | |||
61 | * Extensible system of 'magic' commands for controlling the environment and |
|
62 | * Extensible system of 'magic' commands for controlling the environment and | |
62 | performing many tasks related either to IPython or the operating system. |
|
63 | performing many tasks related either to IPython or the operating system. | |
63 |
|
64 | |||
64 | * Configuration system with easy switching between different setups (simpler |
|
65 | * Configuration system with easy switching between different setups (simpler | |
65 | than changing $PYTHONSTARTUP environment variables every time). |
|
66 | than changing $PYTHONSTARTUP environment variables every time). | |
66 |
|
67 | |||
67 | * Session logging and reloading. |
|
68 | * Session logging and reloading. | |
68 |
|
69 | |||
69 | * Extensible syntax processing for special purpose situations. |
|
70 | * Extensible syntax processing for special purpose situations. | |
70 |
|
71 | |||
71 | * Access to the system shell with user-extensible alias system. |
|
72 | * Access to the system shell with user-extensible alias system. | |
72 |
|
73 | |||
73 | * Easily embeddable in other Python programs and wxPython GUIs. |
|
74 | * Easily embeddable in other Python programs and wxPython GUIs. | |
74 |
|
75 | |||
75 | * Integrated access to the pdb debugger and the Python profiler. |
|
76 | * Integrated access to the pdb debugger and the Python profiler. | |
76 |
|
77 | |||
77 | The parallel computing architecture has the following main features: |
|
78 | The parallel computing architecture has the following main features: | |
78 |
|
79 | |||
79 | * Quickly parallelize Python code from an interactive Python/IPython session. |
|
80 | * Quickly parallelize Python code from an interactive Python/IPython session. | |
80 |
|
81 | |||
81 | * A flexible and dynamic process model that be deployed on anything from |
|
82 | * A flexible and dynamic process model that be deployed on anything from | |
82 | multicore workstations to supercomputers. |
|
83 | multicore workstations to supercomputers. | |
83 |
|
84 | |||
84 | * An architecture that supports many different styles of parallelism, from |
|
85 | * An architecture that supports many different styles of parallelism, from | |
85 | message passing to task farming. |
|
86 | message passing to task farming. | |
86 |
|
87 | |||
87 | * Both blocking and fully asynchronous interfaces. |
|
88 | * Both blocking and fully asynchronous interfaces. | |
88 |
|
89 | |||
89 | * High level APIs that enable many things to be parallelized in a few lines |
|
90 | * High level APIs that enable many things to be parallelized in a few lines | |
90 | of code. |
|
91 | of code. | |
91 |
|
92 | |||
92 | * Share live parallel jobs with other users securely. |
|
93 | * Share live parallel jobs with other users securely. | |
93 |
|
94 | |||
94 | * Dynamically load balanced task farming system. |
|
95 | * Dynamically load balanced task farming system. | |
95 |
|
96 | |||
96 | * Robust error handling in parallel code. |
|
97 | * Robust error handling in parallel code. | |
97 |
|
98 | |||
98 |
The latest development version is always available from IPython's ` |
|
99 | The latest development version is always available from IPython's `GitHub | |
99 |
site <http:// |
|
100 | site <http://github.com/ipython>`_. | |
100 | """ |
|
101 | """ | |
101 |
|
102 | |||
102 | license = 'BSD' |
|
103 | license = 'BSD' | |
103 |
|
104 | |||
104 | authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'), |
|
105 | authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'), | |
105 | 'Janko' : ('Janko Hauser','jhauser@zscout.de'), |
|
106 | 'Janko' : ('Janko Hauser','jhauser@zscout.de'), | |
106 | 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'), |
|
107 | 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'), | |
107 | 'Ville' : ('Ville Vainio','vivainio@gmail.com'), |
|
108 | 'Ville' : ('Ville Vainio','vivainio@gmail.com'), | |
108 | 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'), |
|
109 | 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'), | |
109 | 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com') |
|
110 | 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com') | |
110 | } |
|
111 | } | |
111 |
|
112 | |||
112 | author = 'The IPython Development Team' |
|
113 | author = 'The IPython Development Team' | |
113 |
|
114 | |||
114 | author_email = 'ipython-dev@scipy.org' |
|
115 | author_email = 'ipython-dev@scipy.org' | |
115 |
|
116 | |||
116 | url = 'http://ipython.scipy.org' |
|
117 | url = 'http://ipython.scipy.org' | |
117 |
|
118 | |||
118 | download_url = 'http://ipython.scipy.org/dist' |
|
119 | download_url = 'http://ipython.scipy.org/dist' | |
119 |
|
120 | |||
120 | platforms = ['Linux','Mac OSX','Windows XP/2000/NT','Windows 95/98/ME'] |
|
121 | platforms = ['Linux','Mac OSX','Windows XP/2000/NT','Windows 95/98/ME'] | |
121 |
|
122 | |||
122 | keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed'] |
|
123 | keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed'] |
@@ -1,665 +1,664 | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | """ |
|
3 | """ | |
4 | The :class:`~IPython.core.application.Application` object for the command |
|
4 | The :class:`~IPython.core.application.Application` object for the command | |
5 | line :command:`ipython` program. |
|
5 | line :command:`ipython` program. | |
6 |
|
6 | |||
7 | Authors |
|
7 | Authors | |
8 | ------- |
|
8 | ------- | |
9 |
|
9 | |||
10 | * Brian Granger |
|
10 | * Brian Granger | |
11 | * Fernando Perez |
|
11 | * Fernando Perez | |
12 | """ |
|
12 | """ | |
13 |
|
13 | |||
14 | #----------------------------------------------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
15 | # Copyright (C) 2008-2010 The IPython Development Team |
|
15 | # Copyright (C) 2008-2010 The IPython Development Team | |
16 | # |
|
16 | # | |
17 | # Distributed under the terms of the BSD License. The full license is in |
|
17 | # Distributed under the terms of the BSD License. The full license is in | |
18 | # the file COPYING, distributed as part of this software. |
|
18 | # the file COPYING, distributed as part of this software. | |
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 |
|
20 | |||
21 | #----------------------------------------------------------------------------- |
|
21 | #----------------------------------------------------------------------------- | |
22 | # Imports |
|
22 | # Imports | |
23 | #----------------------------------------------------------------------------- |
|
23 | #----------------------------------------------------------------------------- | |
24 |
|
24 | |||
25 | from __future__ import absolute_import |
|
25 | from __future__ import absolute_import | |
26 |
|
26 | |||
27 | import logging |
|
27 | import logging | |
28 | import os |
|
28 | import os | |
29 | import sys |
|
29 | import sys | |
30 |
|
30 | |||
31 | from IPython.core import release |
|
31 | from IPython.core import release | |
32 | from IPython.core.crashhandler import CrashHandler |
|
32 | from IPython.core.crashhandler import CrashHandler | |
33 | from IPython.core.application import Application, BaseAppConfigLoader |
|
33 | from IPython.core.application import Application, BaseAppConfigLoader | |
34 | from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell |
|
34 | from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell | |
35 | from IPython.config.loader import ( |
|
35 | from IPython.config.loader import ( | |
36 | Config, |
|
36 | Config, | |
37 | PyFileConfigLoader |
|
37 | PyFileConfigLoader | |
38 | ) |
|
38 | ) | |
39 | from IPython.lib import inputhook |
|
39 | from IPython.lib import inputhook | |
40 | from IPython.utils.path import filefind, get_ipython_dir |
|
40 | from IPython.utils.path import filefind, get_ipython_dir | |
41 | from IPython.core import usage |
|
41 | from IPython.core import usage | |
42 |
|
42 | |||
43 | #----------------------------------------------------------------------------- |
|
43 | #----------------------------------------------------------------------------- | |
44 | # Globals, utilities and helpers |
|
44 | # Globals, utilities and helpers | |
45 | #----------------------------------------------------------------------------- |
|
45 | #----------------------------------------------------------------------------- | |
46 |
|
46 | |||
47 | #: The default config file name for this application. |
|
47 | #: The default config file name for this application. | |
48 | default_config_file_name = u'ipython_config.py' |
|
48 | default_config_file_name = u'ipython_config.py' | |
49 |
|
49 | |||
50 |
|
50 | |||
51 | class IPAppConfigLoader(BaseAppConfigLoader): |
|
51 | class IPAppConfigLoader(BaseAppConfigLoader): | |
52 |
|
52 | |||
53 | def _add_arguments(self): |
|
53 | def _add_arguments(self): | |
54 | super(IPAppConfigLoader, self)._add_arguments() |
|
54 | super(IPAppConfigLoader, self)._add_arguments() | |
55 | paa = self.parser.add_argument |
|
55 | paa = self.parser.add_argument | |
56 | paa('-p', |
|
56 | paa('-p', | |
57 | '--profile', dest='Global.profile', type=unicode, |
|
57 | '--profile', dest='Global.profile', type=unicode, | |
58 | help= |
|
58 | help= | |
59 | """The string name of the ipython profile to be used. Assume that your |
|
59 | """The string name of the ipython profile to be used. Assume that your | |
60 | config file is ipython_config-<name>.py (looks in current dir first, |
|
60 | config file is ipython_config-<name>.py (looks in current dir first, | |
61 | then in IPYTHON_DIR). This is a quick way to keep and load multiple |
|
61 | then in IPYTHON_DIR). This is a quick way to keep and load multiple | |
62 | config files for different tasks, especially if include your basic one |
|
62 | config files for different tasks, especially if include your basic one | |
63 | in your more specialized ones. You can keep a basic |
|
63 | in your more specialized ones. You can keep a basic | |
64 | IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which |
|
64 | IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which | |
65 | include this one and load extra things for particular tasks.""", |
|
65 | include this one and load extra things for particular tasks.""", | |
66 | metavar='Global.profile') |
|
66 | metavar='Global.profile') | |
67 | paa('--config-file', |
|
67 | paa('--config-file', | |
68 | dest='Global.config_file', type=unicode, |
|
68 | dest='Global.config_file', type=unicode, | |
69 | help= |
|
69 | help= | |
70 | """Set the config file name to override default. Normally IPython |
|
70 | """Set the config file name to override default. Normally IPython | |
71 | loads ipython_config.py (from current directory) or |
|
71 | loads ipython_config.py (from current directory) or | |
72 | IPYTHON_DIR/ipython_config.py. If the loading of your config file |
|
72 | IPYTHON_DIR/ipython_config.py. If the loading of your config file | |
73 | fails, IPython starts with a bare bones configuration (no modules |
|
73 | fails, IPython starts with a bare bones configuration (no modules | |
74 | loaded at all).""", |
|
74 | loaded at all).""", | |
75 | metavar='Global.config_file') |
|
75 | metavar='Global.config_file') | |
76 | paa('--autocall', |
|
76 | paa('--autocall', | |
77 | dest='InteractiveShell.autocall', type=int, |
|
77 | dest='InteractiveShell.autocall', type=int, | |
78 | help= |
|
78 | help= | |
79 | """Make IPython automatically call any callable object even if you |
|
79 | """Make IPython automatically call any callable object even if you | |
80 | didn't type explicit parentheses. For example, 'str 43' becomes |
|
80 | didn't type explicit parentheses. For example, 'str 43' becomes | |
81 | 'str(43)' automatically. The value can be '0' to disable the feature, |
|
81 | 'str(43)' automatically. The value can be '0' to disable the feature, | |
82 | '1' for 'smart' autocall, where it is not applied if there are no more |
|
82 | '1' for 'smart' autocall, where it is not applied if there are no more | |
83 | arguments on the line, and '2' for 'full' autocall, where all callable |
|
83 | arguments on the line, and '2' for 'full' autocall, where all callable | |
84 | objects are automatically called (even if no arguments are present). |
|
84 | objects are automatically called (even if no arguments are present). | |
85 | The default is '1'.""", |
|
85 | The default is '1'.""", | |
86 | metavar='InteractiveShell.autocall') |
|
86 | metavar='InteractiveShell.autocall') | |
87 | paa('--autoindent', |
|
87 | paa('--autoindent', | |
88 | action='store_true', dest='InteractiveShell.autoindent', |
|
88 | action='store_true', dest='InteractiveShell.autoindent', | |
89 | help='Turn on autoindenting.') |
|
89 | help='Turn on autoindenting.') | |
90 | paa('--no-autoindent', |
|
90 | paa('--no-autoindent', | |
91 | action='store_false', dest='InteractiveShell.autoindent', |
|
91 | action='store_false', dest='InteractiveShell.autoindent', | |
92 | help='Turn off autoindenting.') |
|
92 | help='Turn off autoindenting.') | |
93 | paa('--automagic', |
|
93 | paa('--automagic', | |
94 | action='store_true', dest='InteractiveShell.automagic', |
|
94 | action='store_true', dest='InteractiveShell.automagic', | |
95 | help= |
|
95 | help= | |
96 | """Turn on the auto calling of magic commands. Type %%magic at the |
|
96 | """Turn on the auto calling of magic commands. Type %%magic at the | |
97 | IPython prompt for more information.""") |
|
97 | IPython prompt for more information.""") | |
98 | paa('--no-automagic', |
|
98 | paa('--no-automagic', | |
99 | action='store_false', dest='InteractiveShell.automagic', |
|
99 | action='store_false', dest='InteractiveShell.automagic', | |
100 | help='Turn off the auto calling of magic commands.') |
|
100 | help='Turn off the auto calling of magic commands.') | |
101 | paa('--autoedit-syntax', |
|
101 | paa('--autoedit-syntax', | |
102 | action='store_true', dest='TerminalInteractiveShell.autoedit_syntax', |
|
102 | action='store_true', dest='TerminalInteractiveShell.autoedit_syntax', | |
103 | help='Turn on auto editing of files with syntax errors.') |
|
103 | help='Turn on auto editing of files with syntax errors.') | |
104 | paa('--no-autoedit-syntax', |
|
104 | paa('--no-autoedit-syntax', | |
105 | action='store_false', dest='TerminalInteractiveShell.autoedit_syntax', |
|
105 | action='store_false', dest='TerminalInteractiveShell.autoedit_syntax', | |
106 | help='Turn off auto editing of files with syntax errors.') |
|
106 | help='Turn off auto editing of files with syntax errors.') | |
107 | paa('--banner', |
|
107 | paa('--banner', | |
108 | action='store_true', dest='Global.display_banner', |
|
108 | action='store_true', dest='Global.display_banner', | |
109 | help='Display a banner upon starting IPython.') |
|
109 | help='Display a banner upon starting IPython.') | |
110 | paa('--no-banner', |
|
110 | paa('--no-banner', | |
111 | action='store_false', dest='Global.display_banner', |
|
111 | action='store_false', dest='Global.display_banner', | |
112 | help="Don't display a banner upon starting IPython.") |
|
112 | help="Don't display a banner upon starting IPython.") | |
113 | paa('--cache-size', |
|
113 | paa('--cache-size', | |
114 | type=int, dest='InteractiveShell.cache_size', |
|
114 | type=int, dest='InteractiveShell.cache_size', | |
115 | help= |
|
115 | help= | |
116 | """Set the size of the output cache. The default is 1000, you can |
|
116 | """Set the size of the output cache. The default is 1000, you can | |
117 | change it permanently in your config file. Setting it to 0 completely |
|
117 | change it permanently in your config file. Setting it to 0 completely | |
118 | disables the caching system, and the minimum value accepted is 20 (if |
|
118 | disables the caching system, and the minimum value accepted is 20 (if | |
119 | you provide a value less than 20, it is reset to 0 and a warning is |
|
119 | you provide a value less than 20, it is reset to 0 and a warning is | |
120 | issued). This limit is defined because otherwise you'll spend more |
|
120 | issued). This limit is defined because otherwise you'll spend more | |
121 | time re-flushing a too small cache than working""", |
|
121 | time re-flushing a too small cache than working""", | |
122 | metavar='InteractiveShell.cache_size') |
|
122 | metavar='InteractiveShell.cache_size') | |
123 | paa('--classic', |
|
123 | paa('--classic', | |
124 | action='store_true', dest='Global.classic', |
|
124 | action='store_true', dest='Global.classic', | |
125 | help="Gives IPython a similar feel to the classic Python prompt.") |
|
125 | help="Gives IPython a similar feel to the classic Python prompt.") | |
126 | paa('--colors', |
|
126 | paa('--colors', | |
127 | type=str, dest='InteractiveShell.colors', |
|
127 | type=str, dest='InteractiveShell.colors', | |
128 | help="Set the color scheme (NoColor, Linux, and LightBG).", |
|
128 | help="Set the color scheme (NoColor, Linux, and LightBG).", | |
129 | metavar='InteractiveShell.colors') |
|
129 | metavar='InteractiveShell.colors') | |
130 | paa('--color-info', |
|
130 | paa('--color-info', | |
131 | action='store_true', dest='InteractiveShell.color_info', |
|
131 | action='store_true', dest='InteractiveShell.color_info', | |
132 | help= |
|
132 | help= | |
133 | """IPython can display information about objects via a set of func- |
|
133 | """IPython can display information about objects via a set of func- | |
134 | tions, and optionally can use colors for this, syntax highlighting |
|
134 | tions, and optionally can use colors for this, syntax highlighting | |
135 | source code and various other elements. However, because this |
|
135 | source code and various other elements. However, because this | |
136 | information is passed through a pager (like 'less') and many pagers get |
|
136 | information is passed through a pager (like 'less') and many pagers get | |
137 | confused with color codes, this option is off by default. You can test |
|
137 | confused with color codes, this option is off by default. You can test | |
138 | it and turn it on permanently in your ipython_config.py file if it |
|
138 | it and turn it on permanently in your ipython_config.py file if it | |
139 | works for you. Test it and turn it on permanently if it works with |
|
139 | works for you. Test it and turn it on permanently if it works with | |
140 | your system. The magic function %%color_info allows you to toggle this |
|
140 | your system. The magic function %%color_info allows you to toggle this | |
141 | inter- actively for testing.""") |
|
141 | inter- actively for testing.""") | |
142 | paa('--no-color-info', |
|
142 | paa('--no-color-info', | |
143 | action='store_false', dest='InteractiveShell.color_info', |
|
143 | action='store_false', dest='InteractiveShell.color_info', | |
144 | help="Disable using colors for info related things.") |
|
144 | help="Disable using colors for info related things.") | |
145 | paa('--confirm-exit', |
|
145 | paa('--confirm-exit', | |
146 | action='store_true', dest='TerminalInteractiveShell.confirm_exit', |
|
146 | action='store_true', dest='TerminalInteractiveShell.confirm_exit', | |
147 | help= |
|
147 | help= | |
148 | """Set to confirm when you try to exit IPython with an EOF (Control-D |
|
148 | """Set to confirm when you try to exit IPython with an EOF (Control-D | |
149 | in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or |
|
149 | in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or | |
150 | '%%Exit', you can force a direct exit without any confirmation.""") |
|
150 | '%%Exit', you can force a direct exit without any confirmation.""") | |
151 | paa('--no-confirm-exit', |
|
151 | paa('--no-confirm-exit', | |
152 | action='store_false', dest='TerminalInteractiveShell.confirm_exit', |
|
152 | action='store_false', dest='TerminalInteractiveShell.confirm_exit', | |
153 | help="Don't prompt the user when exiting.") |
|
153 | help="Don't prompt the user when exiting.") | |
154 | paa('--deep-reload', |
|
154 | paa('--deep-reload', | |
155 | action='store_true', dest='InteractiveShell.deep_reload', |
|
155 | action='store_true', dest='InteractiveShell.deep_reload', | |
156 | help= |
|
156 | help= | |
157 | """Enable deep (recursive) reloading by default. IPython can use the |
|
157 | """Enable deep (recursive) reloading by default. IPython can use the | |
158 | deep_reload module which reloads changes in modules recursively (it |
|
158 | deep_reload module which reloads changes in modules recursively (it | |
159 | replaces the reload() function, so you don't need to change anything to |
|
159 | replaces the reload() function, so you don't need to change anything to | |
160 | use it). deep_reload() forces a full reload of modules whose code may |
|
160 | use it). deep_reload() forces a full reload of modules whose code may | |
161 | have changed, which the default reload() function does not. When |
|
161 | have changed, which the default reload() function does not. When | |
162 | deep_reload is off, IPython will use the normal reload(), but |
|
162 | deep_reload is off, IPython will use the normal reload(), but | |
163 | deep_reload will still be available as dreload(). This fea- ture is off |
|
163 | deep_reload will still be available as dreload(). This fea- ture is off | |
164 | by default [which means that you have both normal reload() and |
|
164 | by default [which means that you have both normal reload() and | |
165 | dreload()].""") |
|
165 | dreload()].""") | |
166 | paa('--no-deep-reload', |
|
166 | paa('--no-deep-reload', | |
167 | action='store_false', dest='InteractiveShell.deep_reload', |
|
167 | action='store_false', dest='InteractiveShell.deep_reload', | |
168 | help="Disable deep (recursive) reloading by default.") |
|
168 | help="Disable deep (recursive) reloading by default.") | |
169 | paa('--editor', |
|
169 | paa('--editor', | |
170 | type=str, dest='TerminalInteractiveShell.editor', |
|
170 | type=str, dest='TerminalInteractiveShell.editor', | |
171 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad).", |
|
171 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad).", | |
172 | metavar='TerminalInteractiveShell.editor') |
|
172 | metavar='TerminalInteractiveShell.editor') | |
173 | paa('--log','-l', |
|
173 | paa('--log','-l', | |
174 | action='store_true', dest='InteractiveShell.logstart', |
|
174 | action='store_true', dest='InteractiveShell.logstart', | |
175 | help="Start logging to the default log file (./ipython_log.py).") |
|
175 | help="Start logging to the default log file (./ipython_log.py).") | |
176 | paa('--logfile','-lf', |
|
176 | paa('--logfile','-lf', | |
177 | type=unicode, dest='InteractiveShell.logfile', |
|
177 | type=unicode, dest='InteractiveShell.logfile', | |
178 | help="Start logging to logfile with this name.", |
|
178 | help="Start logging to logfile with this name.", | |
179 | metavar='InteractiveShell.logfile') |
|
179 | metavar='InteractiveShell.logfile') | |
180 | paa('--log-append','-la', |
|
180 | paa('--log-append','-la', | |
181 | type=unicode, dest='InteractiveShell.logappend', |
|
181 | type=unicode, dest='InteractiveShell.logappend', | |
182 | help="Start logging to the given file in append mode.", |
|
182 | help="Start logging to the given file in append mode.", | |
183 | metavar='InteractiveShell.logfile') |
|
183 | metavar='InteractiveShell.logfile') | |
184 | paa('--pdb', |
|
184 | paa('--pdb', | |
185 | action='store_true', dest='InteractiveShell.pdb', |
|
185 | action='store_true', dest='InteractiveShell.pdb', | |
186 | help="Enable auto calling the pdb debugger after every exception.") |
|
186 | help="Enable auto calling the pdb debugger after every exception.") | |
187 | paa('--no-pdb', |
|
187 | paa('--no-pdb', | |
188 | action='store_false', dest='InteractiveShell.pdb', |
|
188 | action='store_false', dest='InteractiveShell.pdb', | |
189 | help="Disable auto calling the pdb debugger after every exception.") |
|
189 | help="Disable auto calling the pdb debugger after every exception.") | |
190 | paa('--pprint', |
|
190 | paa('--pprint', | |
191 | action='store_true', dest='InteractiveShell.pprint', |
|
191 | action='store_true', dest='InteractiveShell.pprint', | |
192 | help="Enable auto pretty printing of results.") |
|
192 | help="Enable auto pretty printing of results.") | |
193 | paa('--no-pprint', |
|
193 | paa('--no-pprint', | |
194 | action='store_false', dest='InteractiveShell.pprint', |
|
194 | action='store_false', dest='InteractiveShell.pprint', | |
195 | help="Disable auto auto pretty printing of results.") |
|
195 | help="Disable auto auto pretty printing of results.") | |
196 | paa('--prompt-in1','-pi1', |
|
196 | paa('--prompt-in1','-pi1', | |
197 | type=str, dest='InteractiveShell.prompt_in1', |
|
197 | type=str, dest='InteractiveShell.prompt_in1', | |
198 | help= |
|
198 | help= | |
199 | """Set the main input prompt ('In [\#]: '). Note that if you are using |
|
199 | """Set the main input prompt ('In [\#]: '). Note that if you are using | |
200 | numbered prompts, the number is represented with a '\#' in the string. |
|
200 | numbered prompts, the number is represented with a '\#' in the string. | |
201 | Don't forget to quote strings with spaces embedded in them. Most |
|
201 | Don't forget to quote strings with spaces embedded in them. Most | |
202 | bash-like escapes can be used to customize IPython's prompts, as well |
|
202 | bash-like escapes can be used to customize IPython's prompts, as well | |
203 | as a few additional ones which are IPython-spe- cific. All valid |
|
203 | as a few additional ones which are IPython-spe- cific. All valid | |
204 | prompt escapes are described in detail in the Customization section of |
|
204 | prompt escapes are described in detail in the Customization section of | |
205 | the IPython manual.""", |
|
205 | the IPython manual.""", | |
206 | metavar='InteractiveShell.prompt_in1') |
|
206 | metavar='InteractiveShell.prompt_in1') | |
207 | paa('--prompt-in2','-pi2', |
|
207 | paa('--prompt-in2','-pi2', | |
208 | type=str, dest='InteractiveShell.prompt_in2', |
|
208 | type=str, dest='InteractiveShell.prompt_in2', | |
209 | help= |
|
209 | help= | |
210 | """Set the secondary input prompt (' .\D.: '). Similar to the previous |
|
210 | """Set the secondary input prompt (' .\D.: '). Similar to the previous | |
211 | option, but used for the continuation prompts. The special sequence |
|
211 | option, but used for the continuation prompts. The special sequence | |
212 | '\D' is similar to '\#', but with all digits replaced by dots (so you |
|
212 | '\D' is similar to '\#', but with all digits replaced by dots (so you | |
213 | can have your continuation prompt aligned with your input prompt). |
|
213 | can have your continuation prompt aligned with your input prompt). | |
214 | Default: ' .\D.: ' (note three spaces at the start for alignment with |
|
214 | Default: ' .\D.: ' (note three spaces at the start for alignment with | |
215 | 'In [\#]')""", |
|
215 | 'In [\#]')""", | |
216 | metavar='InteractiveShell.prompt_in2') |
|
216 | metavar='InteractiveShell.prompt_in2') | |
217 | paa('--prompt-out','-po', |
|
217 | paa('--prompt-out','-po', | |
218 | type=str, dest='InteractiveShell.prompt_out', |
|
218 | type=str, dest='InteractiveShell.prompt_out', | |
219 | help="Set the output prompt ('Out[\#]:')", |
|
219 | help="Set the output prompt ('Out[\#]:')", | |
220 | metavar='InteractiveShell.prompt_out') |
|
220 | metavar='InteractiveShell.prompt_out') | |
221 | paa('--quick', |
|
221 | paa('--quick', | |
222 | action='store_true', dest='Global.quick', |
|
222 | action='store_true', dest='Global.quick', | |
223 | help="Enable quick startup with no config files.") |
|
223 | help="Enable quick startup with no config files.") | |
224 | paa('--readline', |
|
224 | paa('--readline', | |
225 | action='store_true', dest='InteractiveShell.readline_use', |
|
225 | action='store_true', dest='InteractiveShell.readline_use', | |
226 | help="Enable readline for command line usage.") |
|
226 | help="Enable readline for command line usage.") | |
227 | paa('--no-readline', |
|
227 | paa('--no-readline', | |
228 | action='store_false', dest='InteractiveShell.readline_use', |
|
228 | action='store_false', dest='InteractiveShell.readline_use', | |
229 | help="Disable readline for command line usage.") |
|
229 | help="Disable readline for command line usage.") | |
230 | paa('--screen-length','-sl', |
|
230 | paa('--screen-length','-sl', | |
231 | type=int, dest='TerminalInteractiveShell.screen_length', |
|
231 | type=int, dest='TerminalInteractiveShell.screen_length', | |
232 | help= |
|
232 | help= | |
233 | """Number of lines of your screen, used to control printing of very |
|
233 | """Number of lines of your screen, used to control printing of very | |
234 | long strings. Strings longer than this number of lines will be sent |
|
234 | long strings. Strings longer than this number of lines will be sent | |
235 | through a pager instead of directly printed. The default value for |
|
235 | through a pager instead of directly printed. The default value for | |
236 | this is 0, which means IPython will auto-detect your screen size every |
|
236 | this is 0, which means IPython will auto-detect your screen size every | |
237 | time it needs to print certain potentially long strings (this doesn't |
|
237 | time it needs to print certain potentially long strings (this doesn't | |
238 | change the behavior of the 'print' keyword, it's only triggered |
|
238 | change the behavior of the 'print' keyword, it's only triggered | |
239 | internally). If for some reason this isn't working well (it needs |
|
239 | internally). If for some reason this isn't working well (it needs | |
240 | curses support), specify it yourself. Otherwise don't change the |
|
240 | curses support), specify it yourself. Otherwise don't change the | |
241 | default.""", |
|
241 | default.""", | |
242 | metavar='TerminalInteractiveShell.screen_length') |
|
242 | metavar='TerminalInteractiveShell.screen_length') | |
243 | paa('--separate-in','-si', |
|
243 | paa('--separate-in','-si', | |
244 | type=str, dest='InteractiveShell.separate_in', |
|
244 | type=str, dest='InteractiveShell.separate_in', | |
245 | help="Separator before input prompts. Default '\\n'.", |
|
245 | help="Separator before input prompts. Default '\\n'.", | |
246 | metavar='InteractiveShell.separate_in') |
|
246 | metavar='InteractiveShell.separate_in') | |
247 | paa('--separate-out','-so', |
|
247 | paa('--separate-out','-so', | |
248 | type=str, dest='InteractiveShell.separate_out', |
|
248 | type=str, dest='InteractiveShell.separate_out', | |
249 | help="Separator before output prompts. Default 0 (nothing).", |
|
249 | help="Separator before output prompts. Default 0 (nothing).", | |
250 | metavar='InteractiveShell.separate_out') |
|
250 | metavar='InteractiveShell.separate_out') | |
251 | paa('--separate-out2','-so2', |
|
251 | paa('--separate-out2','-so2', | |
252 | type=str, dest='InteractiveShell.separate_out2', |
|
252 | type=str, dest='InteractiveShell.separate_out2', | |
253 | help="Separator after output prompts. Default 0 (nonight).", |
|
253 | help="Separator after output prompts. Default 0 (nonight).", | |
254 | metavar='InteractiveShell.separate_out2') |
|
254 | metavar='InteractiveShell.separate_out2') | |
255 | paa('--no-sep', |
|
255 | paa('--no-sep', | |
256 | action='store_true', dest='Global.nosep', |
|
256 | action='store_true', dest='Global.nosep', | |
257 | help="Eliminate all spacing between prompts.") |
|
257 | help="Eliminate all spacing between prompts.") | |
258 | paa('--term-title', |
|
258 | paa('--term-title', | |
259 | action='store_true', dest='TerminalInteractiveShell.term_title', |
|
259 | action='store_true', dest='TerminalInteractiveShell.term_title', | |
260 | help="Enable auto setting the terminal title.") |
|
260 | help="Enable auto setting the terminal title.") | |
261 | paa('--no-term-title', |
|
261 | paa('--no-term-title', | |
262 | action='store_false', dest='TerminalInteractiveShell.term_title', |
|
262 | action='store_false', dest='TerminalInteractiveShell.term_title', | |
263 | help="Disable auto setting the terminal title.") |
|
263 | help="Disable auto setting the terminal title.") | |
264 | paa('--xmode', |
|
264 | paa('--xmode', | |
265 | type=str, dest='InteractiveShell.xmode', |
|
265 | type=str, dest='InteractiveShell.xmode', | |
266 | help= |
|
266 | help= | |
267 | """Exception reporting mode ('Plain','Context','Verbose'). Plain: |
|
267 | """Exception reporting mode ('Plain','Context','Verbose'). Plain: | |
268 | similar to python's normal traceback printing. Context: prints 5 lines |
|
268 | similar to python's normal traceback printing. Context: prints 5 lines | |
269 | of context source code around each line in the traceback. Verbose: |
|
269 | of context source code around each line in the traceback. Verbose: | |
270 | similar to Context, but additionally prints the variables currently |
|
270 | similar to Context, but additionally prints the variables currently | |
271 | visible where the exception happened (shortening their strings if too |
|
271 | visible where the exception happened (shortening their strings if too | |
272 | long). This can potentially be very slow, if you happen to have a huge |
|
272 | long). This can potentially be very slow, if you happen to have a huge | |
273 | data structure whose string representation is complex to compute. |
|
273 | data structure whose string representation is complex to compute. | |
274 | Your computer may appear to freeze for a while with cpu usage at 100%%. |
|
274 | Your computer may appear to freeze for a while with cpu usage at 100%%. | |
275 | If this occurs, you can cancel the traceback with Ctrl-C (maybe hitting |
|
275 | If this occurs, you can cancel the traceback with Ctrl-C (maybe hitting | |
276 | it more than once). |
|
276 | it more than once). | |
277 | """, |
|
277 | """, | |
278 | metavar='InteractiveShell.xmode') |
|
278 | metavar='InteractiveShell.xmode') | |
279 | paa('--ext', |
|
279 | paa('--ext', | |
280 | type=str, dest='Global.extra_extension', |
|
280 | type=str, dest='Global.extra_extension', | |
281 | help="The dotted module name of an IPython extension to load.", |
|
281 | help="The dotted module name of an IPython extension to load.", | |
282 | metavar='Global.extra_extension') |
|
282 | metavar='Global.extra_extension') | |
283 | paa('-c', |
|
283 | paa('-c', | |
284 | type=str, dest='Global.code_to_run', |
|
284 | type=str, dest='Global.code_to_run', | |
285 | help="Execute the given command string.", |
|
285 | help="Execute the given command string.", | |
286 | metavar='Global.code_to_run') |
|
286 | metavar='Global.code_to_run') | |
287 | paa('-i', |
|
287 | paa('-i', | |
288 | action='store_true', dest='Global.force_interact', |
|
288 | action='store_true', dest='Global.force_interact', | |
289 | help= |
|
289 | help= | |
290 | "If running code from the command line, become interactive afterwards.") |
|
290 | "If running code from the command line, become interactive afterwards.") | |
291 |
|
291 | |||
292 | # Options to start with GUI control enabled from the beginning |
|
292 | # Options to start with GUI control enabled from the beginning | |
293 | paa('--gui', |
|
293 | paa('--gui', | |
294 | type=str, dest='Global.gui', |
|
294 | type=str, dest='Global.gui', | |
295 | help="Enable GUI event loop integration ('qt', 'wx', 'gtk').", |
|
295 | help="Enable GUI event loop integration ('qt', 'wx', 'gtk').", | |
296 | metavar='gui-mode') |
|
296 | metavar='gui-mode') | |
297 | paa('--pylab','-pylab', |
|
297 | paa('--pylab','-pylab', | |
298 | type=str, dest='Global.pylab', |
|
298 | type=str, dest='Global.pylab', | |
299 | nargs='?', const='auto', metavar='gui-mode', |
|
299 | nargs='?', const='auto', metavar='gui-mode', | |
300 | help="Pre-load matplotlib and numpy for interactive use. "+ |
|
300 | help="Pre-load matplotlib and numpy for interactive use. "+ | |
301 | "If no value is given, the gui backend is matplotlib's, else use "+ |
|
301 | "If no value is given, the gui backend is matplotlib's, else use "+ | |
302 | "one of: ['tk', 'qt', 'wx', 'gtk'].") |
|
302 | "one of: ['tk', 'qt', 'wx', 'gtk'].") | |
303 |
|
303 | |||
304 | # Legacy GUI options. Leave them in for backwards compatibility, but the |
|
304 | # Legacy GUI options. Leave them in for backwards compatibility, but the | |
305 | # 'thread' names are really a misnomer now. |
|
305 | # 'thread' names are really a misnomer now. | |
306 | paa('--wthread', '-wthread', |
|
306 | paa('--wthread', '-wthread', | |
307 | action='store_true', dest='Global.wthread', |
|
307 | action='store_true', dest='Global.wthread', | |
308 | help= |
|
308 | help= | |
309 | """Enable wxPython event loop integration. (DEPRECATED, use --gui wx)""") |
|
309 | """Enable wxPython event loop integration. (DEPRECATED, use --gui wx)""") | |
310 | paa('--q4thread', '--qthread', '-q4thread', '-qthread', |
|
310 | paa('--q4thread', '--qthread', '-q4thread', '-qthread', | |
311 | action='store_true', dest='Global.q4thread', |
|
311 | action='store_true', dest='Global.q4thread', | |
312 | help= |
|
312 | help= | |
313 | """Enable Qt4 event loop integration. Qt3 is no longer supported. |
|
313 | """Enable Qt4 event loop integration. Qt3 is no longer supported. | |
314 | (DEPRECATED, use --gui qt)""") |
|
314 | (DEPRECATED, use --gui qt)""") | |
315 | paa('--gthread', '-gthread', |
|
315 | paa('--gthread', '-gthread', | |
316 | action='store_true', dest='Global.gthread', |
|
316 | action='store_true', dest='Global.gthread', | |
317 | help= |
|
317 | help= | |
318 | """Enable GTK event loop integration. (DEPRECATED, use --gui gtk)""") |
|
318 | """Enable GTK event loop integration. (DEPRECATED, use --gui gtk)""") | |
319 |
|
319 | |||
320 |
|
320 | |||
321 | #----------------------------------------------------------------------------- |
|
321 | #----------------------------------------------------------------------------- | |
322 | # Crash handler for this application |
|
322 | # Crash handler for this application | |
323 | #----------------------------------------------------------------------------- |
|
323 | #----------------------------------------------------------------------------- | |
324 |
|
324 | |||
325 |
|
||||
326 | _message_template = """\ |
|
325 | _message_template = """\ | |
327 | Oops, $self.app_name crashed. We do our best to make it stable, but... |
|
326 | Oops, $self.app_name crashed. We do our best to make it stable, but... | |
328 |
|
327 | |||
329 | A crash report was automatically generated with the following information: |
|
328 | A crash report was automatically generated with the following information: | |
330 | - A verbatim copy of the crash traceback. |
|
329 | - A verbatim copy of the crash traceback. | |
331 | - A copy of your input history during this session. |
|
330 | - A copy of your input history during this session. | |
332 | - Data on your current $self.app_name configuration. |
|
331 | - Data on your current $self.app_name configuration. | |
333 |
|
332 | |||
334 | It was left in the file named: |
|
333 | It was left in the file named: | |
335 | \t'$self.crash_report_fname' |
|
334 | \t'$self.crash_report_fname' | |
336 | If you can email this file to the developers, the information in it will help |
|
335 | If you can email this file to the developers, the information in it will help | |
337 | them in understanding and correcting the problem. |
|
336 | them in understanding and correcting the problem. | |
338 |
|
337 | |||
339 | You can mail it to: $self.contact_name at $self.contact_email |
|
338 | You can mail it to: $self.contact_name at $self.contact_email | |
340 | with the subject '$self.app_name Crash Report'. |
|
339 | with the subject '$self.app_name Crash Report'. | |
341 |
|
340 | |||
342 | If you want to do it now, the following command will work (under Unix): |
|
341 | If you want to do it now, the following command will work (under Unix): | |
343 | mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname |
|
342 | mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname | |
344 |
|
343 | |||
345 | To ensure accurate tracking of this issue, please file a report about it at: |
|
344 | To ensure accurate tracking of this issue, please file a report about it at: | |
346 | $self.bug_tracker |
|
345 | $self.bug_tracker | |
347 | """ |
|
346 | """ | |
348 |
|
347 | |||
349 | class IPAppCrashHandler(CrashHandler): |
|
348 | class IPAppCrashHandler(CrashHandler): | |
350 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" |
|
349 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" | |
351 |
|
350 | |||
352 | message_template = _message_template |
|
351 | message_template = _message_template | |
353 |
|
352 | |||
354 | def __init__(self, app): |
|
353 | def __init__(self, app): | |
355 | contact_name = release.authors['Fernando'][0] |
|
354 | contact_name = release.authors['Fernando'][0] | |
356 | contact_email = release.authors['Fernando'][1] |
|
355 | contact_email = release.authors['Fernando'][1] | |
357 |
bug_tracker = 'http |
|
356 | bug_tracker = 'http://github.com/ipython/ipython/issues' | |
358 | super(IPAppCrashHandler,self).__init__( |
|
357 | super(IPAppCrashHandler,self).__init__( | |
359 | app, contact_name, contact_email, bug_tracker |
|
358 | app, contact_name, contact_email, bug_tracker | |
360 | ) |
|
359 | ) | |
361 |
|
360 | |||
362 | def make_report(self,traceback): |
|
361 | def make_report(self,traceback): | |
363 | """Return a string containing a crash report.""" |
|
362 | """Return a string containing a crash report.""" | |
364 |
|
363 | |||
365 | sec_sep = self.section_sep |
|
364 | sec_sep = self.section_sep | |
366 | # Start with parent report |
|
365 | # Start with parent report | |
367 | report = [super(IPAppCrashHandler, self).make_report(traceback)] |
|
366 | report = [super(IPAppCrashHandler, self).make_report(traceback)] | |
368 | # Add interactive-specific info we may have |
|
367 | # Add interactive-specific info we may have | |
369 | rpt_add = report.append |
|
368 | rpt_add = report.append | |
370 | try: |
|
369 | try: | |
371 | rpt_add(sec_sep+"History of session input:") |
|
370 | rpt_add(sec_sep+"History of session input:") | |
372 | for line in self.app.shell.user_ns['_ih']: |
|
371 | for line in self.app.shell.user_ns['_ih']: | |
373 | rpt_add(line) |
|
372 | rpt_add(line) | |
374 | rpt_add('\n*** Last line of input (may not be in above history):\n') |
|
373 | rpt_add('\n*** Last line of input (may not be in above history):\n') | |
375 | rpt_add(self.app.shell._last_input_line+'\n') |
|
374 | rpt_add(self.app.shell._last_input_line+'\n') | |
376 | except: |
|
375 | except: | |
377 | pass |
|
376 | pass | |
378 |
|
377 | |||
379 | return ''.join(report) |
|
378 | return ''.join(report) | |
380 |
|
379 | |||
381 |
|
380 | |||
382 | #----------------------------------------------------------------------------- |
|
381 | #----------------------------------------------------------------------------- | |
383 | # Main classes and functions |
|
382 | # Main classes and functions | |
384 | #----------------------------------------------------------------------------- |
|
383 | #----------------------------------------------------------------------------- | |
385 |
|
384 | |||
386 | class IPythonApp(Application): |
|
385 | class IPythonApp(Application): | |
387 | name = u'ipython' |
|
386 | name = u'ipython' | |
388 | #: argparse formats better the 'usage' than the 'description' field |
|
387 | #: argparse formats better the 'usage' than the 'description' field | |
389 | description = None |
|
388 | description = None | |
390 | usage = usage.cl_usage |
|
389 | usage = usage.cl_usage | |
391 | command_line_loader = IPAppConfigLoader |
|
390 | command_line_loader = IPAppConfigLoader | |
392 | default_config_file_name = default_config_file_name |
|
391 | default_config_file_name = default_config_file_name | |
393 | crash_handler_class = IPAppCrashHandler |
|
392 | crash_handler_class = IPAppCrashHandler | |
394 |
|
393 | |||
395 | def create_default_config(self): |
|
394 | def create_default_config(self): | |
396 | super(IPythonApp, self).create_default_config() |
|
395 | super(IPythonApp, self).create_default_config() | |
397 | # Eliminate multiple lookups |
|
396 | # Eliminate multiple lookups | |
398 | Global = self.default_config.Global |
|
397 | Global = self.default_config.Global | |
399 |
|
398 | |||
400 | # Set all default values |
|
399 | # Set all default values | |
401 | Global.display_banner = True |
|
400 | Global.display_banner = True | |
402 |
|
401 | |||
403 | # If the -c flag is given or a file is given to run at the cmd line |
|
402 | # If the -c flag is given or a file is given to run at the cmd line | |
404 | # like "ipython foo.py", normally we exit without starting the main |
|
403 | # like "ipython foo.py", normally we exit without starting the main | |
405 | # loop. The force_interact config variable allows a user to override |
|
404 | # loop. The force_interact config variable allows a user to override | |
406 | # this and interact. It is also set by the -i cmd line flag, just |
|
405 | # this and interact. It is also set by the -i cmd line flag, just | |
407 | # like Python. |
|
406 | # like Python. | |
408 | Global.force_interact = False |
|
407 | Global.force_interact = False | |
409 |
|
408 | |||
410 | # By default always interact by starting the IPython mainloop. |
|
409 | # By default always interact by starting the IPython mainloop. | |
411 | Global.interact = True |
|
410 | Global.interact = True | |
412 |
|
411 | |||
413 | # No GUI integration by default |
|
412 | # No GUI integration by default | |
414 | Global.gui = False |
|
413 | Global.gui = False | |
415 | # Pylab off by default |
|
414 | # Pylab off by default | |
416 | Global.pylab = False |
|
415 | Global.pylab = False | |
417 |
|
416 | |||
418 | # Deprecated versions of gui support that used threading, we support |
|
417 | # Deprecated versions of gui support that used threading, we support | |
419 | # them just for bacwards compatibility as an alternate spelling for |
|
418 | # them just for bacwards compatibility as an alternate spelling for | |
420 | # '--gui X' |
|
419 | # '--gui X' | |
421 | Global.qthread = False |
|
420 | Global.qthread = False | |
422 | Global.q4thread = False |
|
421 | Global.q4thread = False | |
423 | Global.wthread = False |
|
422 | Global.wthread = False | |
424 | Global.gthread = False |
|
423 | Global.gthread = False | |
425 |
|
424 | |||
426 | def load_file_config(self): |
|
425 | def load_file_config(self): | |
427 | if hasattr(self.command_line_config.Global, 'quick'): |
|
426 | if hasattr(self.command_line_config.Global, 'quick'): | |
428 | if self.command_line_config.Global.quick: |
|
427 | if self.command_line_config.Global.quick: | |
429 | self.file_config = Config() |
|
428 | self.file_config = Config() | |
430 | return |
|
429 | return | |
431 | super(IPythonApp, self).load_file_config() |
|
430 | super(IPythonApp, self).load_file_config() | |
432 |
|
431 | |||
433 | def post_load_file_config(self): |
|
432 | def post_load_file_config(self): | |
434 | if hasattr(self.command_line_config.Global, 'extra_extension'): |
|
433 | if hasattr(self.command_line_config.Global, 'extra_extension'): | |
435 | if not hasattr(self.file_config.Global, 'extensions'): |
|
434 | if not hasattr(self.file_config.Global, 'extensions'): | |
436 | self.file_config.Global.extensions = [] |
|
435 | self.file_config.Global.extensions = [] | |
437 | self.file_config.Global.extensions.append( |
|
436 | self.file_config.Global.extensions.append( | |
438 | self.command_line_config.Global.extra_extension) |
|
437 | self.command_line_config.Global.extra_extension) | |
439 | del self.command_line_config.Global.extra_extension |
|
438 | del self.command_line_config.Global.extra_extension | |
440 |
|
439 | |||
441 | def pre_construct(self): |
|
440 | def pre_construct(self): | |
442 | config = self.master_config |
|
441 | config = self.master_config | |
443 |
|
442 | |||
444 | if hasattr(config.Global, 'classic'): |
|
443 | if hasattr(config.Global, 'classic'): | |
445 | if config.Global.classic: |
|
444 | if config.Global.classic: | |
446 | config.InteractiveShell.cache_size = 0 |
|
445 | config.InteractiveShell.cache_size = 0 | |
447 | config.InteractiveShell.pprint = 0 |
|
446 | config.InteractiveShell.pprint = 0 | |
448 | config.InteractiveShell.prompt_in1 = '>>> ' |
|
447 | config.InteractiveShell.prompt_in1 = '>>> ' | |
449 | config.InteractiveShell.prompt_in2 = '... ' |
|
448 | config.InteractiveShell.prompt_in2 = '... ' | |
450 | config.InteractiveShell.prompt_out = '' |
|
449 | config.InteractiveShell.prompt_out = '' | |
451 | config.InteractiveShell.separate_in = \ |
|
450 | config.InteractiveShell.separate_in = \ | |
452 | config.InteractiveShell.separate_out = \ |
|
451 | config.InteractiveShell.separate_out = \ | |
453 | config.InteractiveShell.separate_out2 = '' |
|
452 | config.InteractiveShell.separate_out2 = '' | |
454 | config.InteractiveShell.colors = 'NoColor' |
|
453 | config.InteractiveShell.colors = 'NoColor' | |
455 | config.InteractiveShell.xmode = 'Plain' |
|
454 | config.InteractiveShell.xmode = 'Plain' | |
456 |
|
455 | |||
457 | if hasattr(config.Global, 'nosep'): |
|
456 | if hasattr(config.Global, 'nosep'): | |
458 | if config.Global.nosep: |
|
457 | if config.Global.nosep: | |
459 | config.InteractiveShell.separate_in = \ |
|
458 | config.InteractiveShell.separate_in = \ | |
460 | config.InteractiveShell.separate_out = \ |
|
459 | config.InteractiveShell.separate_out = \ | |
461 | config.InteractiveShell.separate_out2 = '' |
|
460 | config.InteractiveShell.separate_out2 = '' | |
462 |
|
461 | |||
463 | # if there is code of files to run from the cmd line, don't interact |
|
462 | # if there is code of files to run from the cmd line, don't interact | |
464 | # unless the -i flag (Global.force_interact) is true. |
|
463 | # unless the -i flag (Global.force_interact) is true. | |
465 | code_to_run = config.Global.get('code_to_run','') |
|
464 | code_to_run = config.Global.get('code_to_run','') | |
466 | file_to_run = False |
|
465 | file_to_run = False | |
467 | if self.extra_args and self.extra_args[0]: |
|
466 | if self.extra_args and self.extra_args[0]: | |
468 | file_to_run = True |
|
467 | file_to_run = True | |
469 | if file_to_run or code_to_run: |
|
468 | if file_to_run or code_to_run: | |
470 | if not config.Global.force_interact: |
|
469 | if not config.Global.force_interact: | |
471 | config.Global.interact = False |
|
470 | config.Global.interact = False | |
472 |
|
471 | |||
473 | def construct(self): |
|
472 | def construct(self): | |
474 | # I am a little hesitant to put these into InteractiveShell itself. |
|
473 | # I am a little hesitant to put these into InteractiveShell itself. | |
475 | # But that might be the place for them |
|
474 | # But that might be the place for them | |
476 | sys.path.insert(0, '') |
|
475 | sys.path.insert(0, '') | |
477 |
|
476 | |||
478 | # Create an InteractiveShell instance. |
|
477 | # Create an InteractiveShell instance. | |
479 | self.shell = TerminalInteractiveShell.instance(config=self.master_config) |
|
478 | self.shell = TerminalInteractiveShell.instance(config=self.master_config) | |
480 |
|
479 | |||
481 | def post_construct(self): |
|
480 | def post_construct(self): | |
482 | """Do actions after construct, but before starting the app.""" |
|
481 | """Do actions after construct, but before starting the app.""" | |
483 | config = self.master_config |
|
482 | config = self.master_config | |
484 |
|
483 | |||
485 | # shell.display_banner should always be False for the terminal |
|
484 | # shell.display_banner should always be False for the terminal | |
486 | # based app, because we call shell.show_banner() by hand below |
|
485 | # based app, because we call shell.show_banner() by hand below | |
487 | # so the banner shows *before* all extension loading stuff. |
|
486 | # so the banner shows *before* all extension loading stuff. | |
488 | self.shell.display_banner = False |
|
487 | self.shell.display_banner = False | |
489 | if config.Global.display_banner and \ |
|
488 | if config.Global.display_banner and \ | |
490 | config.Global.interact: |
|
489 | config.Global.interact: | |
491 | self.shell.show_banner() |
|
490 | self.shell.show_banner() | |
492 |
|
491 | |||
493 | # Make sure there is a space below the banner. |
|
492 | # Make sure there is a space below the banner. | |
494 | if self.log_level <= logging.INFO: print |
|
493 | if self.log_level <= logging.INFO: print | |
495 |
|
494 | |||
496 | # Now a variety of things that happen after the banner is printed. |
|
495 | # Now a variety of things that happen after the banner is printed. | |
497 | self._enable_gui_pylab() |
|
496 | self._enable_gui_pylab() | |
498 | self._load_extensions() |
|
497 | self._load_extensions() | |
499 | self._run_exec_lines() |
|
498 | self._run_exec_lines() | |
500 | self._run_exec_files() |
|
499 | self._run_exec_files() | |
501 | self._run_cmd_line_code() |
|
500 | self._run_cmd_line_code() | |
502 |
|
501 | |||
503 | def _enable_gui_pylab(self): |
|
502 | def _enable_gui_pylab(self): | |
504 | """Enable GUI event loop integration, taking pylab into account.""" |
|
503 | """Enable GUI event loop integration, taking pylab into account.""" | |
505 | Global = self.master_config.Global |
|
504 | Global = self.master_config.Global | |
506 |
|
505 | |||
507 | # Select which gui to use |
|
506 | # Select which gui to use | |
508 | if Global.gui: |
|
507 | if Global.gui: | |
509 | gui = Global.gui |
|
508 | gui = Global.gui | |
510 | # The following are deprecated, but there's likely to be a lot of use |
|
509 | # The following are deprecated, but there's likely to be a lot of use | |
511 | # of this form out there, so we might as well support it for now. But |
|
510 | # of this form out there, so we might as well support it for now. But | |
512 | # the --gui option above takes precedence. |
|
511 | # the --gui option above takes precedence. | |
513 | elif Global.wthread: |
|
512 | elif Global.wthread: | |
514 | gui = inputhook.GUI_WX |
|
513 | gui = inputhook.GUI_WX | |
515 | elif Global.qthread: |
|
514 | elif Global.qthread: | |
516 | gui = inputhook.GUI_QT |
|
515 | gui = inputhook.GUI_QT | |
517 | elif Global.gthread: |
|
516 | elif Global.gthread: | |
518 | gui = inputhook.GUI_GTK |
|
517 | gui = inputhook.GUI_GTK | |
519 | else: |
|
518 | else: | |
520 | gui = None |
|
519 | gui = None | |
521 |
|
520 | |||
522 | # Using --pylab will also require gui activation, though which toolkit |
|
521 | # Using --pylab will also require gui activation, though which toolkit | |
523 | # to use may be chosen automatically based on mpl configuration. |
|
522 | # to use may be chosen automatically based on mpl configuration. | |
524 | if Global.pylab: |
|
523 | if Global.pylab: | |
525 | activate = self.shell.enable_pylab |
|
524 | activate = self.shell.enable_pylab | |
526 | if Global.pylab == 'auto': |
|
525 | if Global.pylab == 'auto': | |
527 | gui = None |
|
526 | gui = None | |
528 | else: |
|
527 | else: | |
529 | gui = Global.pylab |
|
528 | gui = Global.pylab | |
530 | else: |
|
529 | else: | |
531 | # Enable only GUI integration, no pylab |
|
530 | # Enable only GUI integration, no pylab | |
532 | activate = inputhook.enable_gui |
|
531 | activate = inputhook.enable_gui | |
533 |
|
532 | |||
534 | if gui or Global.pylab: |
|
533 | if gui or Global.pylab: | |
535 | try: |
|
534 | try: | |
536 | self.log.info("Enabling GUI event loop integration, " |
|
535 | self.log.info("Enabling GUI event loop integration, " | |
537 | "toolkit=%s, pylab=%s" % (gui, Global.pylab) ) |
|
536 | "toolkit=%s, pylab=%s" % (gui, Global.pylab) ) | |
538 | activate(gui) |
|
537 | activate(gui) | |
539 | except: |
|
538 | except: | |
540 | self.log.warn("Error in enabling GUI event loop integration:") |
|
539 | self.log.warn("Error in enabling GUI event loop integration:") | |
541 | self.shell.showtraceback() |
|
540 | self.shell.showtraceback() | |
542 |
|
541 | |||
543 | def _load_extensions(self): |
|
542 | def _load_extensions(self): | |
544 | """Load all IPython extensions in Global.extensions. |
|
543 | """Load all IPython extensions in Global.extensions. | |
545 |
|
544 | |||
546 | This uses the :meth:`ExtensionManager.load_extensions` to load all |
|
545 | This uses the :meth:`ExtensionManager.load_extensions` to load all | |
547 | the extensions listed in ``self.master_config.Global.extensions``. |
|
546 | the extensions listed in ``self.master_config.Global.extensions``. | |
548 | """ |
|
547 | """ | |
549 | try: |
|
548 | try: | |
550 | if hasattr(self.master_config.Global, 'extensions'): |
|
549 | if hasattr(self.master_config.Global, 'extensions'): | |
551 | self.log.debug("Loading IPython extensions...") |
|
550 | self.log.debug("Loading IPython extensions...") | |
552 | extensions = self.master_config.Global.extensions |
|
551 | extensions = self.master_config.Global.extensions | |
553 | for ext in extensions: |
|
552 | for ext in extensions: | |
554 | try: |
|
553 | try: | |
555 | self.log.info("Loading IPython extension: %s" % ext) |
|
554 | self.log.info("Loading IPython extension: %s" % ext) | |
556 | self.shell.extension_manager.load_extension(ext) |
|
555 | self.shell.extension_manager.load_extension(ext) | |
557 | except: |
|
556 | except: | |
558 | self.log.warn("Error in loading extension: %s" % ext) |
|
557 | self.log.warn("Error in loading extension: %s" % ext) | |
559 | self.shell.showtraceback() |
|
558 | self.shell.showtraceback() | |
560 | except: |
|
559 | except: | |
561 | self.log.warn("Unknown error in loading extensions:") |
|
560 | self.log.warn("Unknown error in loading extensions:") | |
562 | self.shell.showtraceback() |
|
561 | self.shell.showtraceback() | |
563 |
|
562 | |||
564 | def _run_exec_lines(self): |
|
563 | def _run_exec_lines(self): | |
565 | """Run lines of code in Global.exec_lines in the user's namespace.""" |
|
564 | """Run lines of code in Global.exec_lines in the user's namespace.""" | |
566 | try: |
|
565 | try: | |
567 | if hasattr(self.master_config.Global, 'exec_lines'): |
|
566 | if hasattr(self.master_config.Global, 'exec_lines'): | |
568 | self.log.debug("Running code from Global.exec_lines...") |
|
567 | self.log.debug("Running code from Global.exec_lines...") | |
569 | exec_lines = self.master_config.Global.exec_lines |
|
568 | exec_lines = self.master_config.Global.exec_lines | |
570 | for line in exec_lines: |
|
569 | for line in exec_lines: | |
571 | try: |
|
570 | try: | |
572 | self.log.info("Running code in user namespace: %s" % |
|
571 | self.log.info("Running code in user namespace: %s" % | |
573 | line) |
|
572 | line) | |
574 | self.shell.run_cell(line) |
|
573 | self.shell.run_cell(line) | |
575 | except: |
|
574 | except: | |
576 | self.log.warn("Error in executing line in user " |
|
575 | self.log.warn("Error in executing line in user " | |
577 | "namespace: %s" % line) |
|
576 | "namespace: %s" % line) | |
578 | self.shell.showtraceback() |
|
577 | self.shell.showtraceback() | |
579 | except: |
|
578 | except: | |
580 | self.log.warn("Unknown error in handling Global.exec_lines:") |
|
579 | self.log.warn("Unknown error in handling Global.exec_lines:") | |
581 | self.shell.showtraceback() |
|
580 | self.shell.showtraceback() | |
582 |
|
581 | |||
583 | def _exec_file(self, fname): |
|
582 | def _exec_file(self, fname): | |
584 | full_filename = filefind(fname, [u'.', self.ipython_dir]) |
|
583 | full_filename = filefind(fname, [u'.', self.ipython_dir]) | |
585 | if os.path.isfile(full_filename): |
|
584 | if os.path.isfile(full_filename): | |
586 | if full_filename.endswith(u'.py'): |
|
585 | if full_filename.endswith(u'.py'): | |
587 | self.log.info("Running file in user namespace: %s" % |
|
586 | self.log.info("Running file in user namespace: %s" % | |
588 | full_filename) |
|
587 | full_filename) | |
589 | # Ensure that __file__ is always defined to match Python behavior |
|
588 | # Ensure that __file__ is always defined to match Python behavior | |
590 | self.shell.user_ns['__file__'] = fname |
|
589 | self.shell.user_ns['__file__'] = fname | |
591 | try: |
|
590 | try: | |
592 | self.shell.safe_execfile(full_filename, self.shell.user_ns) |
|
591 | self.shell.safe_execfile(full_filename, self.shell.user_ns) | |
593 | finally: |
|
592 | finally: | |
594 | del self.shell.user_ns['__file__'] |
|
593 | del self.shell.user_ns['__file__'] | |
595 | elif full_filename.endswith('.ipy'): |
|
594 | elif full_filename.endswith('.ipy'): | |
596 | self.log.info("Running file in user namespace: %s" % |
|
595 | self.log.info("Running file in user namespace: %s" % | |
597 | full_filename) |
|
596 | full_filename) | |
598 | self.shell.safe_execfile_ipy(full_filename) |
|
597 | self.shell.safe_execfile_ipy(full_filename) | |
599 | else: |
|
598 | else: | |
600 | self.log.warn("File does not have a .py or .ipy extension: <%s>" |
|
599 | self.log.warn("File does not have a .py or .ipy extension: <%s>" | |
601 | % full_filename) |
|
600 | % full_filename) | |
602 | def _run_exec_files(self): |
|
601 | def _run_exec_files(self): | |
603 | try: |
|
602 | try: | |
604 | if hasattr(self.master_config.Global, 'exec_files'): |
|
603 | if hasattr(self.master_config.Global, 'exec_files'): | |
605 | self.log.debug("Running files in Global.exec_files...") |
|
604 | self.log.debug("Running files in Global.exec_files...") | |
606 | exec_files = self.master_config.Global.exec_files |
|
605 | exec_files = self.master_config.Global.exec_files | |
607 | for fname in exec_files: |
|
606 | for fname in exec_files: | |
608 | self._exec_file(fname) |
|
607 | self._exec_file(fname) | |
609 | except: |
|
608 | except: | |
610 | self.log.warn("Unknown error in handling Global.exec_files:") |
|
609 | self.log.warn("Unknown error in handling Global.exec_files:") | |
611 | self.shell.showtraceback() |
|
610 | self.shell.showtraceback() | |
612 |
|
611 | |||
613 | def _run_cmd_line_code(self): |
|
612 | def _run_cmd_line_code(self): | |
614 | if hasattr(self.master_config.Global, 'code_to_run'): |
|
613 | if hasattr(self.master_config.Global, 'code_to_run'): | |
615 | line = self.master_config.Global.code_to_run |
|
614 | line = self.master_config.Global.code_to_run | |
616 | try: |
|
615 | try: | |
617 | self.log.info("Running code given at command line (-c): %s" % |
|
616 | self.log.info("Running code given at command line (-c): %s" % | |
618 | line) |
|
617 | line) | |
619 | self.shell.run_cell(line) |
|
618 | self.shell.run_cell(line) | |
620 | except: |
|
619 | except: | |
621 | self.log.warn("Error in executing line in user namespace: %s" % |
|
620 | self.log.warn("Error in executing line in user namespace: %s" % | |
622 | line) |
|
621 | line) | |
623 | self.shell.showtraceback() |
|
622 | self.shell.showtraceback() | |
624 | return |
|
623 | return | |
625 | # Like Python itself, ignore the second if the first of these is present |
|
624 | # Like Python itself, ignore the second if the first of these is present | |
626 | try: |
|
625 | try: | |
627 | fname = self.extra_args[0] |
|
626 | fname = self.extra_args[0] | |
628 | except: |
|
627 | except: | |
629 | pass |
|
628 | pass | |
630 | else: |
|
629 | else: | |
631 | try: |
|
630 | try: | |
632 | self._exec_file(fname) |
|
631 | self._exec_file(fname) | |
633 | except: |
|
632 | except: | |
634 | self.log.warn("Error in executing file in user namespace: %s" % |
|
633 | self.log.warn("Error in executing file in user namespace: %s" % | |
635 | fname) |
|
634 | fname) | |
636 | self.shell.showtraceback() |
|
635 | self.shell.showtraceback() | |
637 |
|
636 | |||
638 | def start_app(self): |
|
637 | def start_app(self): | |
639 | if self.master_config.Global.interact: |
|
638 | if self.master_config.Global.interact: | |
640 | self.log.debug("Starting IPython's mainloop...") |
|
639 | self.log.debug("Starting IPython's mainloop...") | |
641 | self.shell.mainloop() |
|
640 | self.shell.mainloop() | |
642 | else: |
|
641 | else: | |
643 | self.log.debug("IPython not interactive, start_app is no-op...") |
|
642 | self.log.debug("IPython not interactive, start_app is no-op...") | |
644 |
|
643 | |||
645 |
|
644 | |||
646 | def load_default_config(ipython_dir=None): |
|
645 | def load_default_config(ipython_dir=None): | |
647 | """Load the default config file from the default ipython_dir. |
|
646 | """Load the default config file from the default ipython_dir. | |
648 |
|
647 | |||
649 | This is useful for embedded shells. |
|
648 | This is useful for embedded shells. | |
650 | """ |
|
649 | """ | |
651 | if ipython_dir is None: |
|
650 | if ipython_dir is None: | |
652 | ipython_dir = get_ipython_dir() |
|
651 | ipython_dir = get_ipython_dir() | |
653 | cl = PyFileConfigLoader(default_config_file_name, ipython_dir) |
|
652 | cl = PyFileConfigLoader(default_config_file_name, ipython_dir) | |
654 | config = cl.load_config() |
|
653 | config = cl.load_config() | |
655 | return config |
|
654 | return config | |
656 |
|
655 | |||
657 |
|
656 | |||
658 | def launch_new_instance(): |
|
657 | def launch_new_instance(): | |
659 | """Create and run a full blown IPython instance""" |
|
658 | """Create and run a full blown IPython instance""" | |
660 | app = IPythonApp() |
|
659 | app = IPythonApp() | |
661 | app.start() |
|
660 | app.start() | |
662 |
|
661 | |||
663 |
|
662 | |||
664 | if __name__ == '__main__': |
|
663 | if __name__ == '__main__': | |
665 | launch_new_instance() |
|
664 | launch_new_instance() |
@@ -1,538 +1,538 | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | """ |
|
3 | """ | |
4 | The IPython cluster directory |
|
4 | The IPython cluster directory | |
5 | """ |
|
5 | """ | |
6 |
|
6 | |||
7 | #----------------------------------------------------------------------------- |
|
7 | #----------------------------------------------------------------------------- | |
8 | # Copyright (C) 2008-2009 The IPython Development Team |
|
8 | # Copyright (C) 2008-2009 The IPython Development Team | |
9 | # |
|
9 | # | |
10 | # Distributed under the terms of the BSD License. The full license is in |
|
10 | # Distributed under the terms of the BSD License. The full license is in | |
11 | # the file COPYING, distributed as part of this software. |
|
11 | # the file COPYING, distributed as part of this software. | |
12 | #----------------------------------------------------------------------------- |
|
12 | #----------------------------------------------------------------------------- | |
13 |
|
13 | |||
14 | #----------------------------------------------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
15 | # Imports |
|
15 | # Imports | |
16 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
17 |
|
17 | |||
18 | from __future__ import with_statement |
|
18 | from __future__ import with_statement | |
19 |
|
19 | |||
20 | import os |
|
20 | import os | |
21 | import shutil |
|
21 | import shutil | |
22 | import sys |
|
22 | import sys | |
23 | import warnings |
|
23 | import warnings | |
24 |
|
24 | |||
25 | from twisted.python import log |
|
25 | from twisted.python import log | |
26 |
|
26 | |||
27 | from IPython.config.loader import PyFileConfigLoader |
|
27 | from IPython.config.loader import PyFileConfigLoader | |
28 | from IPython.core.application import Application, BaseAppConfigLoader |
|
28 | from IPython.core.application import Application, BaseAppConfigLoader | |
29 | from IPython.config.configurable import Configurable |
|
29 | from IPython.config.configurable import Configurable | |
30 | from IPython.core.crashhandler import CrashHandler |
|
30 | from IPython.core.crashhandler import CrashHandler | |
31 | from IPython.core import release |
|
31 | from IPython.core import release | |
32 | from IPython.utils.path import ( |
|
32 | from IPython.utils.path import ( | |
33 | get_ipython_package_dir, |
|
33 | get_ipython_package_dir, | |
34 | expand_path |
|
34 | expand_path | |
35 | ) |
|
35 | ) | |
36 | from IPython.utils.traitlets import Unicode |
|
36 | from IPython.utils.traitlets import Unicode | |
37 |
|
37 | |||
38 | #----------------------------------------------------------------------------- |
|
38 | #----------------------------------------------------------------------------- | |
39 | # Warnings control |
|
39 | # Warnings control | |
40 | #----------------------------------------------------------------------------- |
|
40 | #----------------------------------------------------------------------------- | |
41 | # Twisted generates annoying warnings with Python 2.6, as will do other code |
|
41 | # Twisted generates annoying warnings with Python 2.6, as will do other code | |
42 | # that imports 'sets' as of today |
|
42 | # that imports 'sets' as of today | |
43 | warnings.filterwarnings('ignore', 'the sets module is deprecated', |
|
43 | warnings.filterwarnings('ignore', 'the sets module is deprecated', | |
44 | DeprecationWarning ) |
|
44 | DeprecationWarning ) | |
45 |
|
45 | |||
46 | # This one also comes from Twisted |
|
46 | # This one also comes from Twisted | |
47 | warnings.filterwarnings('ignore', 'the sha module is deprecated', |
|
47 | warnings.filterwarnings('ignore', 'the sha module is deprecated', | |
48 | DeprecationWarning) |
|
48 | DeprecationWarning) | |
49 |
|
49 | |||
50 | #----------------------------------------------------------------------------- |
|
50 | #----------------------------------------------------------------------------- | |
51 | # Module errors |
|
51 | # Module errors | |
52 | #----------------------------------------------------------------------------- |
|
52 | #----------------------------------------------------------------------------- | |
53 |
|
53 | |||
54 | class ClusterDirError(Exception): |
|
54 | class ClusterDirError(Exception): | |
55 | pass |
|
55 | pass | |
56 |
|
56 | |||
57 |
|
57 | |||
58 | class PIDFileError(Exception): |
|
58 | class PIDFileError(Exception): | |
59 | pass |
|
59 | pass | |
60 |
|
60 | |||
61 |
|
61 | |||
62 | #----------------------------------------------------------------------------- |
|
62 | #----------------------------------------------------------------------------- | |
63 | # Class for managing cluster directories |
|
63 | # Class for managing cluster directories | |
64 | #----------------------------------------------------------------------------- |
|
64 | #----------------------------------------------------------------------------- | |
65 |
|
65 | |||
66 | class ClusterDir(Configurable): |
|
66 | class ClusterDir(Configurable): | |
67 | """An object to manage the cluster directory and its resources. |
|
67 | """An object to manage the cluster directory and its resources. | |
68 |
|
68 | |||
69 | The cluster directory is used by :command:`ipcontroller`, |
|
69 | The cluster directory is used by :command:`ipcontroller`, | |
70 | :command:`ipcontroller` and :command:`ipcontroller` to manage the |
|
70 | :command:`ipcontroller` and :command:`ipcontroller` to manage the | |
71 | configuration, logging and security of these applications. |
|
71 | configuration, logging and security of these applications. | |
72 |
|
72 | |||
73 | This object knows how to find, create and manage these directories. This |
|
73 | This object knows how to find, create and manage these directories. This | |
74 | should be used by any code that want's to handle cluster directories. |
|
74 | should be used by any code that want's to handle cluster directories. | |
75 | """ |
|
75 | """ | |
76 |
|
76 | |||
77 | security_dir_name = Unicode('security') |
|
77 | security_dir_name = Unicode('security') | |
78 | log_dir_name = Unicode('log') |
|
78 | log_dir_name = Unicode('log') | |
79 | pid_dir_name = Unicode('pid') |
|
79 | pid_dir_name = Unicode('pid') | |
80 | security_dir = Unicode(u'') |
|
80 | security_dir = Unicode(u'') | |
81 | log_dir = Unicode(u'') |
|
81 | log_dir = Unicode(u'') | |
82 | pid_dir = Unicode(u'') |
|
82 | pid_dir = Unicode(u'') | |
83 | location = Unicode(u'') |
|
83 | location = Unicode(u'') | |
84 |
|
84 | |||
85 | def __init__(self, location=u''): |
|
85 | def __init__(self, location=u''): | |
86 | super(ClusterDir, self).__init__(location=location) |
|
86 | super(ClusterDir, self).__init__(location=location) | |
87 |
|
87 | |||
88 | def _location_changed(self, name, old, new): |
|
88 | def _location_changed(self, name, old, new): | |
89 | if not os.path.isdir(new): |
|
89 | if not os.path.isdir(new): | |
90 | os.makedirs(new) |
|
90 | os.makedirs(new) | |
91 | self.security_dir = os.path.join(new, self.security_dir_name) |
|
91 | self.security_dir = os.path.join(new, self.security_dir_name) | |
92 | self.log_dir = os.path.join(new, self.log_dir_name) |
|
92 | self.log_dir = os.path.join(new, self.log_dir_name) | |
93 | self.pid_dir = os.path.join(new, self.pid_dir_name) |
|
93 | self.pid_dir = os.path.join(new, self.pid_dir_name) | |
94 | self.check_dirs() |
|
94 | self.check_dirs() | |
95 |
|
95 | |||
96 | def _log_dir_changed(self, name, old, new): |
|
96 | def _log_dir_changed(self, name, old, new): | |
97 | self.check_log_dir() |
|
97 | self.check_log_dir() | |
98 |
|
98 | |||
99 | def check_log_dir(self): |
|
99 | def check_log_dir(self): | |
100 | if not os.path.isdir(self.log_dir): |
|
100 | if not os.path.isdir(self.log_dir): | |
101 | os.mkdir(self.log_dir) |
|
101 | os.mkdir(self.log_dir) | |
102 |
|
102 | |||
103 | def _security_dir_changed(self, name, old, new): |
|
103 | def _security_dir_changed(self, name, old, new): | |
104 | self.check_security_dir() |
|
104 | self.check_security_dir() | |
105 |
|
105 | |||
106 | def check_security_dir(self): |
|
106 | def check_security_dir(self): | |
107 | if not os.path.isdir(self.security_dir): |
|
107 | if not os.path.isdir(self.security_dir): | |
108 | os.mkdir(self.security_dir, 0700) |
|
108 | os.mkdir(self.security_dir, 0700) | |
109 | os.chmod(self.security_dir, 0700) |
|
109 | os.chmod(self.security_dir, 0700) | |
110 |
|
110 | |||
111 | def _pid_dir_changed(self, name, old, new): |
|
111 | def _pid_dir_changed(self, name, old, new): | |
112 | self.check_pid_dir() |
|
112 | self.check_pid_dir() | |
113 |
|
113 | |||
114 | def check_pid_dir(self): |
|
114 | def check_pid_dir(self): | |
115 | if not os.path.isdir(self.pid_dir): |
|
115 | if not os.path.isdir(self.pid_dir): | |
116 | os.mkdir(self.pid_dir, 0700) |
|
116 | os.mkdir(self.pid_dir, 0700) | |
117 | os.chmod(self.pid_dir, 0700) |
|
117 | os.chmod(self.pid_dir, 0700) | |
118 |
|
118 | |||
119 | def check_dirs(self): |
|
119 | def check_dirs(self): | |
120 | self.check_security_dir() |
|
120 | self.check_security_dir() | |
121 | self.check_log_dir() |
|
121 | self.check_log_dir() | |
122 | self.check_pid_dir() |
|
122 | self.check_pid_dir() | |
123 |
|
123 | |||
124 | def load_config_file(self, filename): |
|
124 | def load_config_file(self, filename): | |
125 | """Load a config file from the top level of the cluster dir. |
|
125 | """Load a config file from the top level of the cluster dir. | |
126 |
|
126 | |||
127 | Parameters |
|
127 | Parameters | |
128 | ---------- |
|
128 | ---------- | |
129 | filename : unicode or str |
|
129 | filename : unicode or str | |
130 | The filename only of the config file that must be located in |
|
130 | The filename only of the config file that must be located in | |
131 | the top-level of the cluster directory. |
|
131 | the top-level of the cluster directory. | |
132 | """ |
|
132 | """ | |
133 | loader = PyFileConfigLoader(filename, self.location) |
|
133 | loader = PyFileConfigLoader(filename, self.location) | |
134 | return loader.load_config() |
|
134 | return loader.load_config() | |
135 |
|
135 | |||
136 | def copy_config_file(self, config_file, path=None, overwrite=False): |
|
136 | def copy_config_file(self, config_file, path=None, overwrite=False): | |
137 | """Copy a default config file into the active cluster directory. |
|
137 | """Copy a default config file into the active cluster directory. | |
138 |
|
138 | |||
139 | Default configuration files are kept in :mod:`IPython.config.default`. |
|
139 | Default configuration files are kept in :mod:`IPython.config.default`. | |
140 | This function moves these from that location to the working cluster |
|
140 | This function moves these from that location to the working cluster | |
141 | directory. |
|
141 | directory. | |
142 | """ |
|
142 | """ | |
143 | if path is None: |
|
143 | if path is None: | |
144 | import IPython.config.default |
|
144 | import IPython.config.default | |
145 | path = IPython.config.default.__file__.split(os.path.sep)[:-1] |
|
145 | path = IPython.config.default.__file__.split(os.path.sep)[:-1] | |
146 | path = os.path.sep.join(path) |
|
146 | path = os.path.sep.join(path) | |
147 | src = os.path.join(path, config_file) |
|
147 | src = os.path.join(path, config_file) | |
148 | dst = os.path.join(self.location, config_file) |
|
148 | dst = os.path.join(self.location, config_file) | |
149 | if not os.path.isfile(dst) or overwrite: |
|
149 | if not os.path.isfile(dst) or overwrite: | |
150 | shutil.copy(src, dst) |
|
150 | shutil.copy(src, dst) | |
151 |
|
151 | |||
152 | def copy_all_config_files(self, path=None, overwrite=False): |
|
152 | def copy_all_config_files(self, path=None, overwrite=False): | |
153 | """Copy all config files into the active cluster directory.""" |
|
153 | """Copy all config files into the active cluster directory.""" | |
154 | for f in [u'ipcontroller_config.py', u'ipengine_config.py', |
|
154 | for f in [u'ipcontroller_config.py', u'ipengine_config.py', | |
155 | u'ipcluster_config.py']: |
|
155 | u'ipcluster_config.py']: | |
156 | self.copy_config_file(f, path=path, overwrite=overwrite) |
|
156 | self.copy_config_file(f, path=path, overwrite=overwrite) | |
157 |
|
157 | |||
158 | @classmethod |
|
158 | @classmethod | |
159 | def create_cluster_dir(csl, cluster_dir): |
|
159 | def create_cluster_dir(csl, cluster_dir): | |
160 | """Create a new cluster directory given a full path. |
|
160 | """Create a new cluster directory given a full path. | |
161 |
|
161 | |||
162 | Parameters |
|
162 | Parameters | |
163 | ---------- |
|
163 | ---------- | |
164 | cluster_dir : str |
|
164 | cluster_dir : str | |
165 | The full path to the cluster directory. If it does exist, it will |
|
165 | The full path to the cluster directory. If it does exist, it will | |
166 | be used. If not, it will be created. |
|
166 | be used. If not, it will be created. | |
167 | """ |
|
167 | """ | |
168 | return ClusterDir(location=cluster_dir) |
|
168 | return ClusterDir(location=cluster_dir) | |
169 |
|
169 | |||
170 | @classmethod |
|
170 | @classmethod | |
171 | def create_cluster_dir_by_profile(cls, path, profile=u'default'): |
|
171 | def create_cluster_dir_by_profile(cls, path, profile=u'default'): | |
172 | """Create a cluster dir by profile name and path. |
|
172 | """Create a cluster dir by profile name and path. | |
173 |
|
173 | |||
174 | Parameters |
|
174 | Parameters | |
175 | ---------- |
|
175 | ---------- | |
176 | path : str |
|
176 | path : str | |
177 | The path (directory) to put the cluster directory in. |
|
177 | The path (directory) to put the cluster directory in. | |
178 | profile : str |
|
178 | profile : str | |
179 | The name of the profile. The name of the cluster directory will |
|
179 | The name of the profile. The name of the cluster directory will | |
180 | be "cluster_<profile>". |
|
180 | be "cluster_<profile>". | |
181 | """ |
|
181 | """ | |
182 | if not os.path.isdir(path): |
|
182 | if not os.path.isdir(path): | |
183 | raise ClusterDirError('Directory not found: %s' % path) |
|
183 | raise ClusterDirError('Directory not found: %s' % path) | |
184 | cluster_dir = os.path.join(path, u'cluster_' + profile) |
|
184 | cluster_dir = os.path.join(path, u'cluster_' + profile) | |
185 | return ClusterDir(location=cluster_dir) |
|
185 | return ClusterDir(location=cluster_dir) | |
186 |
|
186 | |||
187 | @classmethod |
|
187 | @classmethod | |
188 | def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'): |
|
188 | def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'): | |
189 | """Find an existing cluster dir by profile name, return its ClusterDir. |
|
189 | """Find an existing cluster dir by profile name, return its ClusterDir. | |
190 |
|
190 | |||
191 | This searches through a sequence of paths for a cluster dir. If it |
|
191 | This searches through a sequence of paths for a cluster dir. If it | |
192 | is not found, a :class:`ClusterDirError` exception will be raised. |
|
192 | is not found, a :class:`ClusterDirError` exception will be raised. | |
193 |
|
193 | |||
194 | The search path algorithm is: |
|
194 | The search path algorithm is: | |
195 | 1. ``os.getcwd()`` |
|
195 | 1. ``os.getcwd()`` | |
196 | 2. ``ipython_dir`` |
|
196 | 2. ``ipython_dir`` | |
197 | 3. The directories found in the ":" separated |
|
197 | 3. The directories found in the ":" separated | |
198 | :env:`IPCLUSTER_DIR_PATH` environment variable. |
|
198 | :env:`IPCLUSTER_DIR_PATH` environment variable. | |
199 |
|
199 | |||
200 | Parameters |
|
200 | Parameters | |
201 | ---------- |
|
201 | ---------- | |
202 | ipython_dir : unicode or str |
|
202 | ipython_dir : unicode or str | |
203 | The IPython directory to use. |
|
203 | The IPython directory to use. | |
204 | profile : unicode or str |
|
204 | profile : unicode or str | |
205 | The name of the profile. The name of the cluster directory |
|
205 | The name of the profile. The name of the cluster directory | |
206 | will be "cluster_<profile>". |
|
206 | will be "cluster_<profile>". | |
207 | """ |
|
207 | """ | |
208 | dirname = u'cluster_' + profile |
|
208 | dirname = u'cluster_' + profile | |
209 | cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','') |
|
209 | cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','') | |
210 | if cluster_dir_paths: |
|
210 | if cluster_dir_paths: | |
211 | cluster_dir_paths = cluster_dir_paths.split(':') |
|
211 | cluster_dir_paths = cluster_dir_paths.split(':') | |
212 | else: |
|
212 | else: | |
213 | cluster_dir_paths = [] |
|
213 | cluster_dir_paths = [] | |
214 | paths = [os.getcwd(), ipython_dir] + cluster_dir_paths |
|
214 | paths = [os.getcwd(), ipython_dir] + cluster_dir_paths | |
215 | for p in paths: |
|
215 | for p in paths: | |
216 | cluster_dir = os.path.join(p, dirname) |
|
216 | cluster_dir = os.path.join(p, dirname) | |
217 | if os.path.isdir(cluster_dir): |
|
217 | if os.path.isdir(cluster_dir): | |
218 | return ClusterDir(location=cluster_dir) |
|
218 | return ClusterDir(location=cluster_dir) | |
219 | else: |
|
219 | else: | |
220 | raise ClusterDirError('Cluster directory not found in paths: %s' % dirname) |
|
220 | raise ClusterDirError('Cluster directory not found in paths: %s' % dirname) | |
221 |
|
221 | |||
222 | @classmethod |
|
222 | @classmethod | |
223 | def find_cluster_dir(cls, cluster_dir): |
|
223 | def find_cluster_dir(cls, cluster_dir): | |
224 | """Find/create a cluster dir and return its ClusterDir. |
|
224 | """Find/create a cluster dir and return its ClusterDir. | |
225 |
|
225 | |||
226 | This will create the cluster directory if it doesn't exist. |
|
226 | This will create the cluster directory if it doesn't exist. | |
227 |
|
227 | |||
228 | Parameters |
|
228 | Parameters | |
229 | ---------- |
|
229 | ---------- | |
230 | cluster_dir : unicode or str |
|
230 | cluster_dir : unicode or str | |
231 | The path of the cluster directory. This is expanded using |
|
231 | The path of the cluster directory. This is expanded using | |
232 | :func:`IPython.utils.genutils.expand_path`. |
|
232 | :func:`IPython.utils.genutils.expand_path`. | |
233 | """ |
|
233 | """ | |
234 | cluster_dir = expand_path(cluster_dir) |
|
234 | cluster_dir = expand_path(cluster_dir) | |
235 | if not os.path.isdir(cluster_dir): |
|
235 | if not os.path.isdir(cluster_dir): | |
236 | raise ClusterDirError('Cluster directory not found: %s' % cluster_dir) |
|
236 | raise ClusterDirError('Cluster directory not found: %s' % cluster_dir) | |
237 | return ClusterDir(location=cluster_dir) |
|
237 | return ClusterDir(location=cluster_dir) | |
238 |
|
238 | |||
239 |
|
239 | |||
240 | #----------------------------------------------------------------------------- |
|
240 | #----------------------------------------------------------------------------- | |
241 | # Command line options |
|
241 | # Command line options | |
242 | #----------------------------------------------------------------------------- |
|
242 | #----------------------------------------------------------------------------- | |
243 |
|
243 | |||
244 | class ClusterDirConfigLoader(BaseAppConfigLoader): |
|
244 | class ClusterDirConfigLoader(BaseAppConfigLoader): | |
245 |
|
245 | |||
246 | def _add_cluster_profile(self, parser): |
|
246 | def _add_cluster_profile(self, parser): | |
247 | paa = parser.add_argument |
|
247 | paa = parser.add_argument | |
248 | paa('-p', '--profile', |
|
248 | paa('-p', '--profile', | |
249 | dest='Global.profile',type=unicode, |
|
249 | dest='Global.profile',type=unicode, | |
250 | help= |
|
250 | help= | |
251 | """The string name of the profile to be used. This determines the name |
|
251 | """The string name of the profile to be used. This determines the name | |
252 | of the cluster dir as: cluster_<profile>. The default profile is named |
|
252 | of the cluster dir as: cluster_<profile>. The default profile is named | |
253 | 'default'. The cluster directory is resolve this way if the |
|
253 | 'default'. The cluster directory is resolve this way if the | |
254 | --cluster-dir option is not used.""", |
|
254 | --cluster-dir option is not used.""", | |
255 | metavar='Global.profile') |
|
255 | metavar='Global.profile') | |
256 |
|
256 | |||
257 | def _add_cluster_dir(self, parser): |
|
257 | def _add_cluster_dir(self, parser): | |
258 | paa = parser.add_argument |
|
258 | paa = parser.add_argument | |
259 | paa('--cluster-dir', |
|
259 | paa('--cluster-dir', | |
260 | dest='Global.cluster_dir',type=unicode, |
|
260 | dest='Global.cluster_dir',type=unicode, | |
261 | help="""Set the cluster dir. This overrides the logic used by the |
|
261 | help="""Set the cluster dir. This overrides the logic used by the | |
262 | --profile option.""", |
|
262 | --profile option.""", | |
263 | metavar='Global.cluster_dir') |
|
263 | metavar='Global.cluster_dir') | |
264 |
|
264 | |||
265 | def _add_work_dir(self, parser): |
|
265 | def _add_work_dir(self, parser): | |
266 | paa = parser.add_argument |
|
266 | paa = parser.add_argument | |
267 | paa('--work-dir', |
|
267 | paa('--work-dir', | |
268 | dest='Global.work_dir',type=unicode, |
|
268 | dest='Global.work_dir',type=unicode, | |
269 | help='Set the working dir for the process.', |
|
269 | help='Set the working dir for the process.', | |
270 | metavar='Global.work_dir') |
|
270 | metavar='Global.work_dir') | |
271 |
|
271 | |||
272 | def _add_clean_logs(self, parser): |
|
272 | def _add_clean_logs(self, parser): | |
273 | paa = parser.add_argument |
|
273 | paa = parser.add_argument | |
274 | paa('--clean-logs', |
|
274 | paa('--clean-logs', | |
275 | dest='Global.clean_logs', action='store_true', |
|
275 | dest='Global.clean_logs', action='store_true', | |
276 | help='Delete old log flies before starting.') |
|
276 | help='Delete old log flies before starting.') | |
277 |
|
277 | |||
278 | def _add_no_clean_logs(self, parser): |
|
278 | def _add_no_clean_logs(self, parser): | |
279 | paa = parser.add_argument |
|
279 | paa = parser.add_argument | |
280 | paa('--no-clean-logs', |
|
280 | paa('--no-clean-logs', | |
281 | dest='Global.clean_logs', action='store_false', |
|
281 | dest='Global.clean_logs', action='store_false', | |
282 | help="Don't Delete old log flies before starting.") |
|
282 | help="Don't Delete old log flies before starting.") | |
283 |
|
283 | |||
284 | def _add_arguments(self): |
|
284 | def _add_arguments(self): | |
285 | super(ClusterDirConfigLoader, self)._add_arguments() |
|
285 | super(ClusterDirConfigLoader, self)._add_arguments() | |
286 | self._add_cluster_profile(self.parser) |
|
286 | self._add_cluster_profile(self.parser) | |
287 | self._add_cluster_dir(self.parser) |
|
287 | self._add_cluster_dir(self.parser) | |
288 | self._add_work_dir(self.parser) |
|
288 | self._add_work_dir(self.parser) | |
289 | self._add_clean_logs(self.parser) |
|
289 | self._add_clean_logs(self.parser) | |
290 | self._add_no_clean_logs(self.parser) |
|
290 | self._add_no_clean_logs(self.parser) | |
291 |
|
291 | |||
292 |
|
292 | |||
293 | #----------------------------------------------------------------------------- |
|
293 | #----------------------------------------------------------------------------- | |
294 | # Crash handler for this application |
|
294 | # Crash handler for this application | |
295 | #----------------------------------------------------------------------------- |
|
295 | #----------------------------------------------------------------------------- | |
296 |
|
296 | |||
297 |
|
297 | |||
298 | _message_template = """\ |
|
298 | _message_template = """\ | |
299 | Oops, $self.app_name crashed. We do our best to make it stable, but... |
|
299 | Oops, $self.app_name crashed. We do our best to make it stable, but... | |
300 |
|
300 | |||
301 | A crash report was automatically generated with the following information: |
|
301 | A crash report was automatically generated with the following information: | |
302 | - A verbatim copy of the crash traceback. |
|
302 | - A verbatim copy of the crash traceback. | |
303 | - Data on your current $self.app_name configuration. |
|
303 | - Data on your current $self.app_name configuration. | |
304 |
|
304 | |||
305 | It was left in the file named: |
|
305 | It was left in the file named: | |
306 | \t'$self.crash_report_fname' |
|
306 | \t'$self.crash_report_fname' | |
307 | If you can email this file to the developers, the information in it will help |
|
307 | If you can email this file to the developers, the information in it will help | |
308 | them in understanding and correcting the problem. |
|
308 | them in understanding and correcting the problem. | |
309 |
|
309 | |||
310 | You can mail it to: $self.contact_name at $self.contact_email |
|
310 | You can mail it to: $self.contact_name at $self.contact_email | |
311 | with the subject '$self.app_name Crash Report'. |
|
311 | with the subject '$self.app_name Crash Report'. | |
312 |
|
312 | |||
313 | If you want to do it now, the following command will work (under Unix): |
|
313 | If you want to do it now, the following command will work (under Unix): | |
314 | mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname |
|
314 | mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname | |
315 |
|
315 | |||
316 | To ensure accurate tracking of this issue, please file a report about it at: |
|
316 | To ensure accurate tracking of this issue, please file a report about it at: | |
317 | $self.bug_tracker |
|
317 | $self.bug_tracker | |
318 | """ |
|
318 | """ | |
319 |
|
319 | |||
320 | class ClusterDirCrashHandler(CrashHandler): |
|
320 | class ClusterDirCrashHandler(CrashHandler): | |
321 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" |
|
321 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" | |
322 |
|
322 | |||
323 | message_template = _message_template |
|
323 | message_template = _message_template | |
324 |
|
324 | |||
325 | def __init__(self, app): |
|
325 | def __init__(self, app): | |
326 | contact_name = release.authors['Brian'][0] |
|
326 | contact_name = release.authors['Brian'][0] | |
327 | contact_email = release.authors['Brian'][1] |
|
327 | contact_email = release.authors['Brian'][1] | |
328 |
bug_tracker = 'http |
|
328 | bug_tracker = 'http://github.com/ipython/ipython/issues' | |
329 | super(ClusterDirCrashHandler,self).__init__( |
|
329 | super(ClusterDirCrashHandler,self).__init__( | |
330 | app, contact_name, contact_email, bug_tracker |
|
330 | app, contact_name, contact_email, bug_tracker | |
331 | ) |
|
331 | ) | |
332 |
|
332 | |||
333 |
|
333 | |||
334 | #----------------------------------------------------------------------------- |
|
334 | #----------------------------------------------------------------------------- | |
335 | # Main application |
|
335 | # Main application | |
336 | #----------------------------------------------------------------------------- |
|
336 | #----------------------------------------------------------------------------- | |
337 |
|
337 | |||
338 | class ApplicationWithClusterDir(Application): |
|
338 | class ApplicationWithClusterDir(Application): | |
339 | """An application that puts everything into a cluster directory. |
|
339 | """An application that puts everything into a cluster directory. | |
340 |
|
340 | |||
341 | Instead of looking for things in the ipython_dir, this type of application |
|
341 | Instead of looking for things in the ipython_dir, this type of application | |
342 | will use its own private directory called the "cluster directory" |
|
342 | will use its own private directory called the "cluster directory" | |
343 | for things like config files, log files, etc. |
|
343 | for things like config files, log files, etc. | |
344 |
|
344 | |||
345 | The cluster directory is resolved as follows: |
|
345 | The cluster directory is resolved as follows: | |
346 |
|
346 | |||
347 | * If the ``--cluster-dir`` option is given, it is used. |
|
347 | * If the ``--cluster-dir`` option is given, it is used. | |
348 | * If ``--cluster-dir`` is not given, the application directory is |
|
348 | * If ``--cluster-dir`` is not given, the application directory is | |
349 | resolve using the profile name as ``cluster_<profile>``. The search |
|
349 | resolve using the profile name as ``cluster_<profile>``. The search | |
350 | path for this directory is then i) cwd if it is found there |
|
350 | path for this directory is then i) cwd if it is found there | |
351 | and ii) in ipython_dir otherwise. |
|
351 | and ii) in ipython_dir otherwise. | |
352 |
|
352 | |||
353 | The config file for the application is to be put in the cluster |
|
353 | The config file for the application is to be put in the cluster | |
354 | dir and named the value of the ``config_file_name`` class attribute. |
|
354 | dir and named the value of the ``config_file_name`` class attribute. | |
355 | """ |
|
355 | """ | |
356 |
|
356 | |||
357 | command_line_loader = ClusterDirConfigLoader |
|
357 | command_line_loader = ClusterDirConfigLoader | |
358 | crash_handler_class = ClusterDirCrashHandler |
|
358 | crash_handler_class = ClusterDirCrashHandler | |
359 | auto_create_cluster_dir = True |
|
359 | auto_create_cluster_dir = True | |
360 |
|
360 | |||
361 | def create_default_config(self): |
|
361 | def create_default_config(self): | |
362 | super(ApplicationWithClusterDir, self).create_default_config() |
|
362 | super(ApplicationWithClusterDir, self).create_default_config() | |
363 | self.default_config.Global.profile = u'default' |
|
363 | self.default_config.Global.profile = u'default' | |
364 | self.default_config.Global.cluster_dir = u'' |
|
364 | self.default_config.Global.cluster_dir = u'' | |
365 | self.default_config.Global.work_dir = os.getcwd() |
|
365 | self.default_config.Global.work_dir = os.getcwd() | |
366 | self.default_config.Global.log_to_file = False |
|
366 | self.default_config.Global.log_to_file = False | |
367 | self.default_config.Global.clean_logs = False |
|
367 | self.default_config.Global.clean_logs = False | |
368 |
|
368 | |||
369 | def find_resources(self): |
|
369 | def find_resources(self): | |
370 | """This resolves the cluster directory. |
|
370 | """This resolves the cluster directory. | |
371 |
|
371 | |||
372 | This tries to find the cluster directory and if successful, it will |
|
372 | This tries to find the cluster directory and if successful, it will | |
373 | have done: |
|
373 | have done: | |
374 | * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for |
|
374 | * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for | |
375 | the application. |
|
375 | the application. | |
376 | * Sets ``self.cluster_dir`` attribute of the application and config |
|
376 | * Sets ``self.cluster_dir`` attribute of the application and config | |
377 | objects. |
|
377 | objects. | |
378 |
|
378 | |||
379 | The algorithm used for this is as follows: |
|
379 | The algorithm used for this is as follows: | |
380 | 1. Try ``Global.cluster_dir``. |
|
380 | 1. Try ``Global.cluster_dir``. | |
381 | 2. Try using ``Global.profile``. |
|
381 | 2. Try using ``Global.profile``. | |
382 | 3. If both of these fail and ``self.auto_create_cluster_dir`` is |
|
382 | 3. If both of these fail and ``self.auto_create_cluster_dir`` is | |
383 | ``True``, then create the new cluster dir in the IPython directory. |
|
383 | ``True``, then create the new cluster dir in the IPython directory. | |
384 | 4. If all fails, then raise :class:`ClusterDirError`. |
|
384 | 4. If all fails, then raise :class:`ClusterDirError`. | |
385 | """ |
|
385 | """ | |
386 |
|
386 | |||
387 | try: |
|
387 | try: | |
388 | cluster_dir = self.command_line_config.Global.cluster_dir |
|
388 | cluster_dir = self.command_line_config.Global.cluster_dir | |
389 | except AttributeError: |
|
389 | except AttributeError: | |
390 | cluster_dir = self.default_config.Global.cluster_dir |
|
390 | cluster_dir = self.default_config.Global.cluster_dir | |
391 | cluster_dir = expand_path(cluster_dir) |
|
391 | cluster_dir = expand_path(cluster_dir) | |
392 | try: |
|
392 | try: | |
393 | self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir) |
|
393 | self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir) | |
394 | except ClusterDirError: |
|
394 | except ClusterDirError: | |
395 | pass |
|
395 | pass | |
396 | else: |
|
396 | else: | |
397 | self.log.info('Using existing cluster dir: %s' % \ |
|
397 | self.log.info('Using existing cluster dir: %s' % \ | |
398 | self.cluster_dir_obj.location |
|
398 | self.cluster_dir_obj.location | |
399 | ) |
|
399 | ) | |
400 | self.finish_cluster_dir() |
|
400 | self.finish_cluster_dir() | |
401 | return |
|
401 | return | |
402 |
|
402 | |||
403 | try: |
|
403 | try: | |
404 | self.profile = self.command_line_config.Global.profile |
|
404 | self.profile = self.command_line_config.Global.profile | |
405 | except AttributeError: |
|
405 | except AttributeError: | |
406 | self.profile = self.default_config.Global.profile |
|
406 | self.profile = self.default_config.Global.profile | |
407 | try: |
|
407 | try: | |
408 | self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile( |
|
408 | self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile( | |
409 | self.ipython_dir, self.profile) |
|
409 | self.ipython_dir, self.profile) | |
410 | except ClusterDirError: |
|
410 | except ClusterDirError: | |
411 | pass |
|
411 | pass | |
412 | else: |
|
412 | else: | |
413 | self.log.info('Using existing cluster dir: %s' % \ |
|
413 | self.log.info('Using existing cluster dir: %s' % \ | |
414 | self.cluster_dir_obj.location |
|
414 | self.cluster_dir_obj.location | |
415 | ) |
|
415 | ) | |
416 | self.finish_cluster_dir() |
|
416 | self.finish_cluster_dir() | |
417 | return |
|
417 | return | |
418 |
|
418 | |||
419 | if self.auto_create_cluster_dir: |
|
419 | if self.auto_create_cluster_dir: | |
420 | self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile( |
|
420 | self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile( | |
421 | self.ipython_dir, self.profile |
|
421 | self.ipython_dir, self.profile | |
422 | ) |
|
422 | ) | |
423 | self.log.info('Creating new cluster dir: %s' % \ |
|
423 | self.log.info('Creating new cluster dir: %s' % \ | |
424 | self.cluster_dir_obj.location |
|
424 | self.cluster_dir_obj.location | |
425 | ) |
|
425 | ) | |
426 | self.finish_cluster_dir() |
|
426 | self.finish_cluster_dir() | |
427 | else: |
|
427 | else: | |
428 | raise ClusterDirError('Could not find a valid cluster directory.') |
|
428 | raise ClusterDirError('Could not find a valid cluster directory.') | |
429 |
|
429 | |||
430 | def finish_cluster_dir(self): |
|
430 | def finish_cluster_dir(self): | |
431 | # Set the cluster directory |
|
431 | # Set the cluster directory | |
432 | self.cluster_dir = self.cluster_dir_obj.location |
|
432 | self.cluster_dir = self.cluster_dir_obj.location | |
433 |
|
433 | |||
434 | # These have to be set because they could be different from the one |
|
434 | # These have to be set because they could be different from the one | |
435 | # that we just computed. Because command line has the highest |
|
435 | # that we just computed. Because command line has the highest | |
436 | # priority, this will always end up in the master_config. |
|
436 | # priority, this will always end up in the master_config. | |
437 | self.default_config.Global.cluster_dir = self.cluster_dir |
|
437 | self.default_config.Global.cluster_dir = self.cluster_dir | |
438 | self.command_line_config.Global.cluster_dir = self.cluster_dir |
|
438 | self.command_line_config.Global.cluster_dir = self.cluster_dir | |
439 |
|
439 | |||
440 | def find_config_file_name(self): |
|
440 | def find_config_file_name(self): | |
441 | """Find the config file name for this application.""" |
|
441 | """Find the config file name for this application.""" | |
442 | # For this type of Application it should be set as a class attribute. |
|
442 | # For this type of Application it should be set as a class attribute. | |
443 | if not hasattr(self, 'default_config_file_name'): |
|
443 | if not hasattr(self, 'default_config_file_name'): | |
444 | self.log.critical("No config filename found") |
|
444 | self.log.critical("No config filename found") | |
445 | else: |
|
445 | else: | |
446 | self.config_file_name = self.default_config_file_name |
|
446 | self.config_file_name = self.default_config_file_name | |
447 |
|
447 | |||
448 | def find_config_file_paths(self): |
|
448 | def find_config_file_paths(self): | |
449 | # Set the search path to to the cluster directory. We should NOT |
|
449 | # Set the search path to to the cluster directory. We should NOT | |
450 | # include IPython.config.default here as the default config files |
|
450 | # include IPython.config.default here as the default config files | |
451 | # are ALWAYS automatically moved to the cluster directory. |
|
451 | # are ALWAYS automatically moved to the cluster directory. | |
452 | conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default') |
|
452 | conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default') | |
453 | self.config_file_paths = (self.cluster_dir,) |
|
453 | self.config_file_paths = (self.cluster_dir,) | |
454 |
|
454 | |||
455 | def pre_construct(self): |
|
455 | def pre_construct(self): | |
456 | # The log and security dirs were set earlier, but here we put them |
|
456 | # The log and security dirs were set earlier, but here we put them | |
457 | # into the config and log them. |
|
457 | # into the config and log them. | |
458 | config = self.master_config |
|
458 | config = self.master_config | |
459 | sdir = self.cluster_dir_obj.security_dir |
|
459 | sdir = self.cluster_dir_obj.security_dir | |
460 | self.security_dir = config.Global.security_dir = sdir |
|
460 | self.security_dir = config.Global.security_dir = sdir | |
461 | ldir = self.cluster_dir_obj.log_dir |
|
461 | ldir = self.cluster_dir_obj.log_dir | |
462 | self.log_dir = config.Global.log_dir = ldir |
|
462 | self.log_dir = config.Global.log_dir = ldir | |
463 | pdir = self.cluster_dir_obj.pid_dir |
|
463 | pdir = self.cluster_dir_obj.pid_dir | |
464 | self.pid_dir = config.Global.pid_dir = pdir |
|
464 | self.pid_dir = config.Global.pid_dir = pdir | |
465 | self.log.info("Cluster directory set to: %s" % self.cluster_dir) |
|
465 | self.log.info("Cluster directory set to: %s" % self.cluster_dir) | |
466 | config.Global.work_dir = unicode(expand_path(config.Global.work_dir)) |
|
466 | config.Global.work_dir = unicode(expand_path(config.Global.work_dir)) | |
467 | # Change to the working directory. We do this just before construct |
|
467 | # Change to the working directory. We do this just before construct | |
468 | # is called so all the components there have the right working dir. |
|
468 | # is called so all the components there have the right working dir. | |
469 | self.to_work_dir() |
|
469 | self.to_work_dir() | |
470 |
|
470 | |||
471 | def to_work_dir(self): |
|
471 | def to_work_dir(self): | |
472 | wd = self.master_config.Global.work_dir |
|
472 | wd = self.master_config.Global.work_dir | |
473 | if unicode(wd) != unicode(os.getcwd()): |
|
473 | if unicode(wd) != unicode(os.getcwd()): | |
474 | os.chdir(wd) |
|
474 | os.chdir(wd) | |
475 | self.log.info("Changing to working dir: %s" % wd) |
|
475 | self.log.info("Changing to working dir: %s" % wd) | |
476 |
|
476 | |||
477 | def start_logging(self): |
|
477 | def start_logging(self): | |
478 | # Remove old log files |
|
478 | # Remove old log files | |
479 | if self.master_config.Global.clean_logs: |
|
479 | if self.master_config.Global.clean_logs: | |
480 | log_dir = self.master_config.Global.log_dir |
|
480 | log_dir = self.master_config.Global.log_dir | |
481 | for f in os.listdir(log_dir): |
|
481 | for f in os.listdir(log_dir): | |
482 | if f.startswith(self.name + u'-') and f.endswith('.log'): |
|
482 | if f.startswith(self.name + u'-') and f.endswith('.log'): | |
483 | os.remove(os.path.join(log_dir, f)) |
|
483 | os.remove(os.path.join(log_dir, f)) | |
484 | # Start logging to the new log file |
|
484 | # Start logging to the new log file | |
485 | if self.master_config.Global.log_to_file: |
|
485 | if self.master_config.Global.log_to_file: | |
486 | log_filename = self.name + u'-' + str(os.getpid()) + u'.log' |
|
486 | log_filename = self.name + u'-' + str(os.getpid()) + u'.log' | |
487 | logfile = os.path.join(self.log_dir, log_filename) |
|
487 | logfile = os.path.join(self.log_dir, log_filename) | |
488 | open_log_file = open(logfile, 'w') |
|
488 | open_log_file = open(logfile, 'w') | |
489 | else: |
|
489 | else: | |
490 | open_log_file = sys.stdout |
|
490 | open_log_file = sys.stdout | |
491 | log.startLogging(open_log_file) |
|
491 | log.startLogging(open_log_file) | |
492 |
|
492 | |||
493 | def write_pid_file(self, overwrite=False): |
|
493 | def write_pid_file(self, overwrite=False): | |
494 | """Create a .pid file in the pid_dir with my pid. |
|
494 | """Create a .pid file in the pid_dir with my pid. | |
495 |
|
495 | |||
496 | This must be called after pre_construct, which sets `self.pid_dir`. |
|
496 | This must be called after pre_construct, which sets `self.pid_dir`. | |
497 | This raises :exc:`PIDFileError` if the pid file exists already. |
|
497 | This raises :exc:`PIDFileError` if the pid file exists already. | |
498 | """ |
|
498 | """ | |
499 | pid_file = os.path.join(self.pid_dir, self.name + u'.pid') |
|
499 | pid_file = os.path.join(self.pid_dir, self.name + u'.pid') | |
500 | if os.path.isfile(pid_file): |
|
500 | if os.path.isfile(pid_file): | |
501 | pid = self.get_pid_from_file() |
|
501 | pid = self.get_pid_from_file() | |
502 | if not overwrite: |
|
502 | if not overwrite: | |
503 | raise PIDFileError( |
|
503 | raise PIDFileError( | |
504 | 'The pid file [%s] already exists. \nThis could mean that this ' |
|
504 | 'The pid file [%s] already exists. \nThis could mean that this ' | |
505 | 'server is already running with [pid=%s].' % (pid_file, pid) |
|
505 | 'server is already running with [pid=%s].' % (pid_file, pid) | |
506 | ) |
|
506 | ) | |
507 | with open(pid_file, 'w') as f: |
|
507 | with open(pid_file, 'w') as f: | |
508 | self.log.info("Creating pid file: %s" % pid_file) |
|
508 | self.log.info("Creating pid file: %s" % pid_file) | |
509 | f.write(repr(os.getpid())+'\n') |
|
509 | f.write(repr(os.getpid())+'\n') | |
510 |
|
510 | |||
511 | def remove_pid_file(self): |
|
511 | def remove_pid_file(self): | |
512 | """Remove the pid file. |
|
512 | """Remove the pid file. | |
513 |
|
513 | |||
514 | This should be called at shutdown by registering a callback with |
|
514 | This should be called at shutdown by registering a callback with | |
515 | :func:`reactor.addSystemEventTrigger`. This needs to return |
|
515 | :func:`reactor.addSystemEventTrigger`. This needs to return | |
516 | ``None``. |
|
516 | ``None``. | |
517 | """ |
|
517 | """ | |
518 | pid_file = os.path.join(self.pid_dir, self.name + u'.pid') |
|
518 | pid_file = os.path.join(self.pid_dir, self.name + u'.pid') | |
519 | if os.path.isfile(pid_file): |
|
519 | if os.path.isfile(pid_file): | |
520 | try: |
|
520 | try: | |
521 | self.log.info("Removing pid file: %s" % pid_file) |
|
521 | self.log.info("Removing pid file: %s" % pid_file) | |
522 | os.remove(pid_file) |
|
522 | os.remove(pid_file) | |
523 | except: |
|
523 | except: | |
524 | self.log.warn("Error removing the pid file: %s" % pid_file) |
|
524 | self.log.warn("Error removing the pid file: %s" % pid_file) | |
525 |
|
525 | |||
526 | def get_pid_from_file(self): |
|
526 | def get_pid_from_file(self): | |
527 | """Get the pid from the pid file. |
|
527 | """Get the pid from the pid file. | |
528 |
|
528 | |||
529 | If the pid file doesn't exist a :exc:`PIDFileError` is raised. |
|
529 | If the pid file doesn't exist a :exc:`PIDFileError` is raised. | |
530 | """ |
|
530 | """ | |
531 | pid_file = os.path.join(self.pid_dir, self.name + u'.pid') |
|
531 | pid_file = os.path.join(self.pid_dir, self.name + u'.pid') | |
532 | if os.path.isfile(pid_file): |
|
532 | if os.path.isfile(pid_file): | |
533 | with open(pid_file, 'r') as f: |
|
533 | with open(pid_file, 'r') as f: | |
534 | pid = int(f.read().strip()) |
|
534 | pid = int(f.read().strip()) | |
535 | return pid |
|
535 | return pid | |
536 | else: |
|
536 | else: | |
537 | raise PIDFileError('pid file not found: %s' % pid_file) |
|
537 | raise PIDFileError('pid file not found: %s' % pid_file) | |
538 |
|
538 |
@@ -1,442 +1,442 | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """IPython Test Suite Runner. |
|
2 | """IPython Test Suite Runner. | |
3 |
|
3 | |||
4 | This module provides a main entry point to a user script to test IPython |
|
4 | This module provides a main entry point to a user script to test IPython | |
5 | itself from the command line. There are two ways of running this script: |
|
5 | itself from the command line. There are two ways of running this script: | |
6 |
|
6 | |||
7 | 1. With the syntax `iptest all`. This runs our entire test suite by |
|
7 | 1. With the syntax `iptest all`. This runs our entire test suite by | |
8 | calling this script (with different arguments) or trial recursively. This |
|
8 | calling this script (with different arguments) or trial recursively. This | |
9 | causes modules and package to be tested in different processes, using nose |
|
9 | causes modules and package to be tested in different processes, using nose | |
10 | or trial where appropriate. |
|
10 | or trial where appropriate. | |
11 | 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form |
|
11 | 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form | |
12 | the script simply calls nose, but with special command line flags and |
|
12 | the script simply calls nose, but with special command line flags and | |
13 | plugins loaded. |
|
13 | plugins loaded. | |
14 |
|
14 | |||
15 | For now, this script requires that both nose and twisted are installed. This |
|
15 | For now, this script requires that both nose and twisted are installed. This | |
16 | will change in the future. |
|
16 | will change in the future. | |
17 | """ |
|
17 | """ | |
18 |
|
18 | |||
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 | # Copyright (C) 2009 The IPython Development Team |
|
20 | # Copyright (C) 2009 The IPython Development Team | |
21 | # |
|
21 | # | |
22 | # Distributed under the terms of the BSD License. The full license is in |
|
22 | # Distributed under the terms of the BSD License. The full license is in | |
23 | # the file COPYING, distributed as part of this software. |
|
23 | # the file COPYING, distributed as part of this software. | |
24 | #----------------------------------------------------------------------------- |
|
24 | #----------------------------------------------------------------------------- | |
25 |
|
25 | |||
26 | #----------------------------------------------------------------------------- |
|
26 | #----------------------------------------------------------------------------- | |
27 | # Imports |
|
27 | # Imports | |
28 | #----------------------------------------------------------------------------- |
|
28 | #----------------------------------------------------------------------------- | |
29 |
|
29 | |||
30 | # Stdlib |
|
30 | # Stdlib | |
31 | import os |
|
31 | import os | |
32 | import os.path as path |
|
32 | import os.path as path | |
33 | import signal |
|
33 | import signal | |
34 | import sys |
|
34 | import sys | |
35 | import subprocess |
|
35 | import subprocess | |
36 | import tempfile |
|
36 | import tempfile | |
37 | import time |
|
37 | import time | |
38 | import warnings |
|
38 | import warnings | |
39 |
|
39 | |||
40 | # Note: monkeypatch! |
|
40 | # Note: monkeypatch! | |
41 | # We need to monkeypatch a small problem in nose itself first, before importing |
|
41 | # We need to monkeypatch a small problem in nose itself first, before importing | |
42 | # it for actual use. This should get into nose upstream, but its release cycle |
|
42 | # it for actual use. This should get into nose upstream, but its release cycle | |
43 | # is slow and we need it for our parametric tests to work correctly. |
|
43 | # is slow and we need it for our parametric tests to work correctly. | |
44 | from IPython.testing import nosepatch |
|
44 | from IPython.testing import nosepatch | |
45 | # Now, proceed to import nose itself |
|
45 | # Now, proceed to import nose itself | |
46 | import nose.plugins.builtin |
|
46 | import nose.plugins.builtin | |
47 | from nose.core import TestProgram |
|
47 | from nose.core import TestProgram | |
48 |
|
48 | |||
49 | # Our own imports |
|
49 | # Our own imports | |
50 | from IPython.utils.path import get_ipython_module_path |
|
50 | from IPython.utils.path import get_ipython_module_path | |
51 | from IPython.utils.process import find_cmd, pycmd2argv |
|
51 | from IPython.utils.process import find_cmd, pycmd2argv | |
52 | from IPython.utils.sysinfo import sys_info |
|
52 | from IPython.utils.sysinfo import sys_info | |
53 |
|
53 | |||
54 | from IPython.testing import globalipapp |
|
54 | from IPython.testing import globalipapp | |
55 | from IPython.testing.plugin.ipdoctest import IPythonDoctest |
|
55 | from IPython.testing.plugin.ipdoctest import IPythonDoctest | |
56 |
|
56 | |||
57 | pjoin = path.join |
|
57 | pjoin = path.join | |
58 |
|
58 | |||
59 |
|
59 | |||
60 | #----------------------------------------------------------------------------- |
|
60 | #----------------------------------------------------------------------------- | |
61 | # Globals |
|
61 | # Globals | |
62 | #----------------------------------------------------------------------------- |
|
62 | #----------------------------------------------------------------------------- | |
63 |
|
63 | |||
64 |
|
64 | |||
65 | #----------------------------------------------------------------------------- |
|
65 | #----------------------------------------------------------------------------- | |
66 | # Warnings control |
|
66 | # Warnings control | |
67 | #----------------------------------------------------------------------------- |
|
67 | #----------------------------------------------------------------------------- | |
68 |
|
68 | |||
69 | # Twisted generates annoying warnings with Python 2.6, as will do other code |
|
69 | # Twisted generates annoying warnings with Python 2.6, as will do other code | |
70 | # that imports 'sets' as of today |
|
70 | # that imports 'sets' as of today | |
71 | warnings.filterwarnings('ignore', 'the sets module is deprecated', |
|
71 | warnings.filterwarnings('ignore', 'the sets module is deprecated', | |
72 | DeprecationWarning ) |
|
72 | DeprecationWarning ) | |
73 |
|
73 | |||
74 | # This one also comes from Twisted |
|
74 | # This one also comes from Twisted | |
75 | warnings.filterwarnings('ignore', 'the sha module is deprecated', |
|
75 | warnings.filterwarnings('ignore', 'the sha module is deprecated', | |
76 | DeprecationWarning) |
|
76 | DeprecationWarning) | |
77 |
|
77 | |||
78 | # Wx on Fedora11 spits these out |
|
78 | # Wx on Fedora11 spits these out | |
79 | warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch', |
|
79 | warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch', | |
80 | UserWarning) |
|
80 | UserWarning) | |
81 |
|
81 | |||
82 | #----------------------------------------------------------------------------- |
|
82 | #----------------------------------------------------------------------------- | |
83 | # Logic for skipping doctests |
|
83 | # Logic for skipping doctests | |
84 | #----------------------------------------------------------------------------- |
|
84 | #----------------------------------------------------------------------------- | |
85 |
|
85 | |||
86 | def test_for(mod): |
|
86 | def test_for(mod): | |
87 | """Test to see if mod is importable.""" |
|
87 | """Test to see if mod is importable.""" | |
88 | try: |
|
88 | try: | |
89 | __import__(mod) |
|
89 | __import__(mod) | |
90 | except (ImportError, RuntimeError): |
|
90 | except (ImportError, RuntimeError): | |
91 | # GTK reports Runtime error if it can't be initialized even if it's |
|
91 | # GTK reports Runtime error if it can't be initialized even if it's | |
92 | # importable. |
|
92 | # importable. | |
93 | return False |
|
93 | return False | |
94 | else: |
|
94 | else: | |
95 | return True |
|
95 | return True | |
96 |
|
96 | |||
97 | # Global dict where we can store information on what we have and what we don't |
|
97 | # Global dict where we can store information on what we have and what we don't | |
98 | # have available at test run time |
|
98 | # have available at test run time | |
99 | have = {} |
|
99 | have = {} | |
100 |
|
100 | |||
101 | have['curses'] = test_for('_curses') |
|
101 | have['curses'] = test_for('_curses') | |
102 | have['wx'] = test_for('wx') |
|
102 | have['wx'] = test_for('wx') | |
103 | have['wx.aui'] = test_for('wx.aui') |
|
103 | have['wx.aui'] = test_for('wx.aui') | |
104 | have['zope.interface'] = test_for('zope.interface') |
|
104 | have['zope.interface'] = test_for('zope.interface') | |
105 | have['twisted'] = test_for('twisted') |
|
105 | have['twisted'] = test_for('twisted') | |
106 | have['foolscap'] = test_for('foolscap') |
|
106 | have['foolscap'] = test_for('foolscap') | |
107 | have['pexpect'] = test_for('pexpect') |
|
107 | have['pexpect'] = test_for('pexpect') | |
108 | have['gtk'] = test_for('gtk') |
|
108 | have['gtk'] = test_for('gtk') | |
109 | have['gobject'] = test_for('gobject') |
|
109 | have['gobject'] = test_for('gobject') | |
110 |
|
110 | |||
111 | #----------------------------------------------------------------------------- |
|
111 | #----------------------------------------------------------------------------- | |
112 | # Functions and classes |
|
112 | # Functions and classes | |
113 | #----------------------------------------------------------------------------- |
|
113 | #----------------------------------------------------------------------------- | |
114 |
|
114 | |||
115 | def report(): |
|
115 | def report(): | |
116 | """Return a string with a summary report of test-related variables.""" |
|
116 | """Return a string with a summary report of test-related variables.""" | |
117 |
|
117 | |||
118 | out = [ sys_info() ] |
|
118 | out = [ sys_info(), '\n'] | |
119 |
|
119 | |||
120 | avail = [] |
|
120 | avail = [] | |
121 | not_avail = [] |
|
121 | not_avail = [] | |
122 |
|
122 | |||
123 | for k, is_avail in have.items(): |
|
123 | for k, is_avail in have.items(): | |
124 | if is_avail: |
|
124 | if is_avail: | |
125 | avail.append(k) |
|
125 | avail.append(k) | |
126 | else: |
|
126 | else: | |
127 | not_avail.append(k) |
|
127 | not_avail.append(k) | |
128 |
|
128 | |||
129 | if avail: |
|
129 | if avail: | |
130 | out.append('\nTools and libraries available at test time:\n') |
|
130 | out.append('\nTools and libraries available at test time:\n') | |
131 | avail.sort() |
|
131 | avail.sort() | |
132 | out.append(' ' + ' '.join(avail)+'\n') |
|
132 | out.append(' ' + ' '.join(avail)+'\n') | |
133 |
|
133 | |||
134 | if not_avail: |
|
134 | if not_avail: | |
135 | out.append('\nTools and libraries NOT available at test time:\n') |
|
135 | out.append('\nTools and libraries NOT available at test time:\n') | |
136 | not_avail.sort() |
|
136 | not_avail.sort() | |
137 | out.append(' ' + ' '.join(not_avail)+'\n') |
|
137 | out.append(' ' + ' '.join(not_avail)+'\n') | |
138 |
|
138 | |||
139 | return ''.join(out) |
|
139 | return ''.join(out) | |
140 |
|
140 | |||
141 |
|
141 | |||
142 | def make_exclude(): |
|
142 | def make_exclude(): | |
143 | """Make patterns of modules and packages to exclude from testing. |
|
143 | """Make patterns of modules and packages to exclude from testing. | |
144 |
|
144 | |||
145 | For the IPythonDoctest plugin, we need to exclude certain patterns that |
|
145 | For the IPythonDoctest plugin, we need to exclude certain patterns that | |
146 | cause testing problems. We should strive to minimize the number of |
|
146 | cause testing problems. We should strive to minimize the number of | |
147 | skipped modules, since this means untested code. |
|
147 | skipped modules, since this means untested code. | |
148 |
|
148 | |||
149 | These modules and packages will NOT get scanned by nose at all for tests. |
|
149 | These modules and packages will NOT get scanned by nose at all for tests. | |
150 | """ |
|
150 | """ | |
151 | # Simple utility to make IPython paths more readably, we need a lot of |
|
151 | # Simple utility to make IPython paths more readably, we need a lot of | |
152 | # these below |
|
152 | # these below | |
153 | ipjoin = lambda *paths: pjoin('IPython', *paths) |
|
153 | ipjoin = lambda *paths: pjoin('IPython', *paths) | |
154 |
|
154 | |||
155 | exclusions = [ipjoin('external'), |
|
155 | exclusions = [ipjoin('external'), | |
156 | pjoin('IPython_doctest_plugin'), |
|
156 | pjoin('IPython_doctest_plugin'), | |
157 | ipjoin('quarantine'), |
|
157 | ipjoin('quarantine'), | |
158 | ipjoin('deathrow'), |
|
158 | ipjoin('deathrow'), | |
159 | ipjoin('testing', 'attic'), |
|
159 | ipjoin('testing', 'attic'), | |
160 | # This guy is probably attic material |
|
160 | # This guy is probably attic material | |
161 | ipjoin('testing', 'mkdoctests'), |
|
161 | ipjoin('testing', 'mkdoctests'), | |
162 | # Testing inputhook will need a lot of thought, to figure out |
|
162 | # Testing inputhook will need a lot of thought, to figure out | |
163 | # how to have tests that don't lock up with the gui event |
|
163 | # how to have tests that don't lock up with the gui event | |
164 | # loops in the picture |
|
164 | # loops in the picture | |
165 | ipjoin('lib', 'inputhook'), |
|
165 | ipjoin('lib', 'inputhook'), | |
166 | # Config files aren't really importable stand-alone |
|
166 | # Config files aren't really importable stand-alone | |
167 | ipjoin('config', 'default'), |
|
167 | ipjoin('config', 'default'), | |
168 | ipjoin('config', 'profile'), |
|
168 | ipjoin('config', 'profile'), | |
169 | ] |
|
169 | ] | |
170 |
|
170 | |||
171 | if not have['wx']: |
|
171 | if not have['wx']: | |
172 | exclusions.append(ipjoin('lib', 'inputhookwx')) |
|
172 | exclusions.append(ipjoin('lib', 'inputhookwx')) | |
173 |
|
173 | |||
174 | if not have['gtk'] or not have['gobject']: |
|
174 | if not have['gtk'] or not have['gobject']: | |
175 | exclusions.append(ipjoin('lib', 'inputhookgtk')) |
|
175 | exclusions.append(ipjoin('lib', 'inputhookgtk')) | |
176 |
|
176 | |||
177 | # These have to be skipped on win32 because the use echo, rm, cd, etc. |
|
177 | # These have to be skipped on win32 because the use echo, rm, cd, etc. | |
178 | # See ticket https://bugs.launchpad.net/bugs/366982 |
|
178 | # See ticket https://bugs.launchpad.net/bugs/366982 | |
179 | if sys.platform == 'win32': |
|
179 | if sys.platform == 'win32': | |
180 | exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip')) |
|
180 | exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip')) | |
181 | exclusions.append(ipjoin('testing', 'plugin', 'dtexample')) |
|
181 | exclusions.append(ipjoin('testing', 'plugin', 'dtexample')) | |
182 |
|
182 | |||
183 | if not have['pexpect']: |
|
183 | if not have['pexpect']: | |
184 | exclusions.extend([ipjoin('scripts', 'irunner'), |
|
184 | exclusions.extend([ipjoin('scripts', 'irunner'), | |
185 | ipjoin('lib', 'irunner')]) |
|
185 | ipjoin('lib', 'irunner')]) | |
186 |
|
186 | |||
187 | # This is scary. We still have things in frontend and testing that |
|
187 | # This is scary. We still have things in frontend and testing that | |
188 | # are being tested by nose that use twisted. We need to rethink |
|
188 | # are being tested by nose that use twisted. We need to rethink | |
189 | # how we are isolating dependencies in testing. |
|
189 | # how we are isolating dependencies in testing. | |
190 | if not (have['twisted'] and have['zope.interface'] and have['foolscap']): |
|
190 | if not (have['twisted'] and have['zope.interface'] and have['foolscap']): | |
191 | exclusions.extend( |
|
191 | exclusions.extend( | |
192 | [ipjoin('testing', 'parametric'), |
|
192 | [ipjoin('testing', 'parametric'), | |
193 | ipjoin('testing', 'util'), |
|
193 | ipjoin('testing', 'util'), | |
194 | ipjoin('testing', 'tests', 'test_decorators_trial'), |
|
194 | ipjoin('testing', 'tests', 'test_decorators_trial'), | |
195 | ] ) |
|
195 | ] ) | |
196 |
|
196 | |||
197 | # This is needed for the reg-exp to match on win32 in the ipdoctest plugin. |
|
197 | # This is needed for the reg-exp to match on win32 in the ipdoctest plugin. | |
198 | if sys.platform == 'win32': |
|
198 | if sys.platform == 'win32': | |
199 | exclusions = [s.replace('\\','\\\\') for s in exclusions] |
|
199 | exclusions = [s.replace('\\','\\\\') for s in exclusions] | |
200 |
|
200 | |||
201 | return exclusions |
|
201 | return exclusions | |
202 |
|
202 | |||
203 |
|
203 | |||
204 | class IPTester(object): |
|
204 | class IPTester(object): | |
205 | """Call that calls iptest or trial in a subprocess. |
|
205 | """Call that calls iptest or trial in a subprocess. | |
206 | """ |
|
206 | """ | |
207 | #: string, name of test runner that will be called |
|
207 | #: string, name of test runner that will be called | |
208 | runner = None |
|
208 | runner = None | |
209 | #: list, parameters for test runner |
|
209 | #: list, parameters for test runner | |
210 | params = None |
|
210 | params = None | |
211 | #: list, arguments of system call to be made to call test runner |
|
211 | #: list, arguments of system call to be made to call test runner | |
212 | call_args = None |
|
212 | call_args = None | |
213 | #: list, process ids of subprocesses we start (for cleanup) |
|
213 | #: list, process ids of subprocesses we start (for cleanup) | |
214 | pids = None |
|
214 | pids = None | |
215 |
|
215 | |||
216 | def __init__(self, runner='iptest', params=None): |
|
216 | def __init__(self, runner='iptest', params=None): | |
217 | """Create new test runner.""" |
|
217 | """Create new test runner.""" | |
218 | p = os.path |
|
218 | p = os.path | |
219 | if runner == 'iptest': |
|
219 | if runner == 'iptest': | |
220 | iptest_app = get_ipython_module_path('IPython.testing.iptest') |
|
220 | iptest_app = get_ipython_module_path('IPython.testing.iptest') | |
221 | self.runner = pycmd2argv(iptest_app) + sys.argv[1:] |
|
221 | self.runner = pycmd2argv(iptest_app) + sys.argv[1:] | |
222 | elif runner == 'trial': |
|
222 | elif runner == 'trial': | |
223 | # For trial, it needs to be installed system-wide |
|
223 | # For trial, it needs to be installed system-wide | |
224 | self.runner = pycmd2argv(p.abspath(find_cmd('trial'))) |
|
224 | self.runner = pycmd2argv(p.abspath(find_cmd('trial'))) | |
225 | else: |
|
225 | else: | |
226 | raise Exception('Not a valid test runner: %s' % repr(runner)) |
|
226 | raise Exception('Not a valid test runner: %s' % repr(runner)) | |
227 | if params is None: |
|
227 | if params is None: | |
228 | params = [] |
|
228 | params = [] | |
229 | if isinstance(params, str): |
|
229 | if isinstance(params, str): | |
230 | params = [params] |
|
230 | params = [params] | |
231 | self.params = params |
|
231 | self.params = params | |
232 |
|
232 | |||
233 | # Assemble call |
|
233 | # Assemble call | |
234 | self.call_args = self.runner+self.params |
|
234 | self.call_args = self.runner+self.params | |
235 |
|
235 | |||
236 | # Store pids of anything we start to clean up on deletion, if possible |
|
236 | # Store pids of anything we start to clean up on deletion, if possible | |
237 | # (on posix only, since win32 has no os.kill) |
|
237 | # (on posix only, since win32 has no os.kill) | |
238 | self.pids = [] |
|
238 | self.pids = [] | |
239 |
|
239 | |||
240 | if sys.platform == 'win32': |
|
240 | if sys.platform == 'win32': | |
241 | def _run_cmd(self): |
|
241 | def _run_cmd(self): | |
242 | # On Windows, use os.system instead of subprocess.call, because I |
|
242 | # On Windows, use os.system instead of subprocess.call, because I | |
243 | # was having problems with subprocess and I just don't know enough |
|
243 | # was having problems with subprocess and I just don't know enough | |
244 | # about win32 to debug this reliably. Os.system may be the 'old |
|
244 | # about win32 to debug this reliably. Os.system may be the 'old | |
245 | # fashioned' way to do it, but it works just fine. If someone |
|
245 | # fashioned' way to do it, but it works just fine. If someone | |
246 | # later can clean this up that's fine, as long as the tests run |
|
246 | # later can clean this up that's fine, as long as the tests run | |
247 | # reliably in win32. |
|
247 | # reliably in win32. | |
248 | # What types of problems are you having. They may be related to |
|
248 | # What types of problems are you having. They may be related to | |
249 | # running Python in unboffered mode. BG. |
|
249 | # running Python in unboffered mode. BG. | |
250 | return os.system(' '.join(self.call_args)) |
|
250 | return os.system(' '.join(self.call_args)) | |
251 | else: |
|
251 | else: | |
252 | def _run_cmd(self): |
|
252 | def _run_cmd(self): | |
253 | # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg |
|
253 | # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg | |
254 | subp = subprocess.Popen(self.call_args) |
|
254 | subp = subprocess.Popen(self.call_args) | |
255 | self.pids.append(subp.pid) |
|
255 | self.pids.append(subp.pid) | |
256 | # If this fails, the pid will be left in self.pids and cleaned up |
|
256 | # If this fails, the pid will be left in self.pids and cleaned up | |
257 | # later, but if the wait call succeeds, then we can clear the |
|
257 | # later, but if the wait call succeeds, then we can clear the | |
258 | # stored pid. |
|
258 | # stored pid. | |
259 | retcode = subp.wait() |
|
259 | retcode = subp.wait() | |
260 | self.pids.pop() |
|
260 | self.pids.pop() | |
261 | return retcode |
|
261 | return retcode | |
262 |
|
262 | |||
263 | def run(self): |
|
263 | def run(self): | |
264 | """Run the stored commands""" |
|
264 | """Run the stored commands""" | |
265 | try: |
|
265 | try: | |
266 | return self._run_cmd() |
|
266 | return self._run_cmd() | |
267 | except: |
|
267 | except: | |
268 | import traceback |
|
268 | import traceback | |
269 | traceback.print_exc() |
|
269 | traceback.print_exc() | |
270 | return 1 # signal failure |
|
270 | return 1 # signal failure | |
271 |
|
271 | |||
272 | def __del__(self): |
|
272 | def __del__(self): | |
273 | """Cleanup on exit by killing any leftover processes.""" |
|
273 | """Cleanup on exit by killing any leftover processes.""" | |
274 |
|
274 | |||
275 | if not hasattr(os, 'kill'): |
|
275 | if not hasattr(os, 'kill'): | |
276 | return |
|
276 | return | |
277 |
|
277 | |||
278 | for pid in self.pids: |
|
278 | for pid in self.pids: | |
279 | try: |
|
279 | try: | |
280 | print 'Cleaning stale PID:', pid |
|
280 | print 'Cleaning stale PID:', pid | |
281 | os.kill(pid, signal.SIGKILL) |
|
281 | os.kill(pid, signal.SIGKILL) | |
282 | except OSError: |
|
282 | except OSError: | |
283 | # This is just a best effort, if we fail or the process was |
|
283 | # This is just a best effort, if we fail or the process was | |
284 | # really gone, ignore it. |
|
284 | # really gone, ignore it. | |
285 | pass |
|
285 | pass | |
286 |
|
286 | |||
287 |
|
287 | |||
288 | def make_runners(): |
|
288 | def make_runners(): | |
289 | """Define the top-level packages that need to be tested. |
|
289 | """Define the top-level packages that need to be tested. | |
290 | """ |
|
290 | """ | |
291 |
|
291 | |||
292 | # Packages to be tested via nose, that only depend on the stdlib |
|
292 | # Packages to be tested via nose, that only depend on the stdlib | |
293 | nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib', |
|
293 | nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib', | |
294 | 'scripts', 'testing', 'utils' ] |
|
294 | 'scripts', 'testing', 'utils' ] | |
295 | # The machinery in kernel needs twisted for real testing |
|
295 | # The machinery in kernel needs twisted for real testing | |
296 | trial_pkg_names = [] |
|
296 | trial_pkg_names = [] | |
297 |
|
297 | |||
298 | # And add twisted ones if conditions are met |
|
298 | # And add twisted ones if conditions are met | |
299 | if have['zope.interface'] and have['twisted'] and have['foolscap']: |
|
299 | if have['zope.interface'] and have['twisted'] and have['foolscap']: | |
300 | # We only list IPython.kernel for testing using twisted.trial as |
|
300 | # We only list IPython.kernel for testing using twisted.trial as | |
301 | # nose and twisted.trial have conflicts that make the testing system |
|
301 | # nose and twisted.trial have conflicts that make the testing system | |
302 | # unstable. |
|
302 | # unstable. | |
303 | trial_pkg_names.append('kernel') |
|
303 | trial_pkg_names.append('kernel') | |
304 |
|
304 | |||
305 | # For debugging this code, only load quick stuff |
|
305 | # For debugging this code, only load quick stuff | |
306 | #nose_pkg_names = ['core', 'extensions'] # dbg |
|
306 | #nose_pkg_names = ['core', 'extensions'] # dbg | |
307 | #trial_pkg_names = [] # dbg |
|
307 | #trial_pkg_names = [] # dbg | |
308 |
|
308 | |||
309 | # Make fully qualified package names prepending 'IPython.' to our name lists |
|
309 | # Make fully qualified package names prepending 'IPython.' to our name lists | |
310 | nose_packages = ['IPython.%s' % m for m in nose_pkg_names ] |
|
310 | nose_packages = ['IPython.%s' % m for m in nose_pkg_names ] | |
311 | trial_packages = ['IPython.%s' % m for m in trial_pkg_names ] |
|
311 | trial_packages = ['IPython.%s' % m for m in trial_pkg_names ] | |
312 |
|
312 | |||
313 | # Make runners |
|
313 | # Make runners | |
314 | runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ] |
|
314 | runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ] | |
315 | runners.extend([ (v, IPTester('trial', params=v)) for v in trial_packages ]) |
|
315 | runners.extend([ (v, IPTester('trial', params=v)) for v in trial_packages ]) | |
316 |
|
316 | |||
317 | return runners |
|
317 | return runners | |
318 |
|
318 | |||
319 |
|
319 | |||
320 | def run_iptest(): |
|
320 | def run_iptest(): | |
321 | """Run the IPython test suite using nose. |
|
321 | """Run the IPython test suite using nose. | |
322 |
|
322 | |||
323 | This function is called when this script is **not** called with the form |
|
323 | This function is called when this script is **not** called with the form | |
324 | `iptest all`. It simply calls nose with appropriate command line flags |
|
324 | `iptest all`. It simply calls nose with appropriate command line flags | |
325 | and accepts all of the standard nose arguments. |
|
325 | and accepts all of the standard nose arguments. | |
326 | """ |
|
326 | """ | |
327 |
|
327 | |||
328 | warnings.filterwarnings('ignore', |
|
328 | warnings.filterwarnings('ignore', | |
329 | 'This will be removed soon. Use IPython.testing.util instead') |
|
329 | 'This will be removed soon. Use IPython.testing.util instead') | |
330 |
|
330 | |||
331 | argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks |
|
331 | argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks | |
332 |
|
332 | |||
333 | # Loading ipdoctest causes problems with Twisted, but |
|
333 | # Loading ipdoctest causes problems with Twisted, but | |
334 | # our test suite runner now separates things and runs |
|
334 | # our test suite runner now separates things and runs | |
335 | # all Twisted tests with trial. |
|
335 | # all Twisted tests with trial. | |
336 | '--with-ipdoctest', |
|
336 | '--with-ipdoctest', | |
337 | '--ipdoctest-tests','--ipdoctest-extension=txt', |
|
337 | '--ipdoctest-tests','--ipdoctest-extension=txt', | |
338 |
|
338 | |||
339 | # We add --exe because of setuptools' imbecility (it |
|
339 | # We add --exe because of setuptools' imbecility (it | |
340 | # blindly does chmod +x on ALL files). Nose does the |
|
340 | # blindly does chmod +x on ALL files). Nose does the | |
341 | # right thing and it tries to avoid executables, |
|
341 | # right thing and it tries to avoid executables, | |
342 | # setuptools unfortunately forces our hand here. This |
|
342 | # setuptools unfortunately forces our hand here. This | |
343 | # has been discussed on the distutils list and the |
|
343 | # has been discussed on the distutils list and the | |
344 | # setuptools devs refuse to fix this problem! |
|
344 | # setuptools devs refuse to fix this problem! | |
345 | '--exe', |
|
345 | '--exe', | |
346 | ] |
|
346 | ] | |
347 |
|
347 | |||
348 | if nose.__version__ >= '0.11': |
|
348 | if nose.__version__ >= '0.11': | |
349 | # I don't fully understand why we need this one, but depending on what |
|
349 | # I don't fully understand why we need this one, but depending on what | |
350 | # directory the test suite is run from, if we don't give it, 0 tests |
|
350 | # directory the test suite is run from, if we don't give it, 0 tests | |
351 | # get run. Specifically, if the test suite is run from the source dir |
|
351 | # get run. Specifically, if the test suite is run from the source dir | |
352 | # with an argument (like 'iptest.py IPython.core', 0 tests are run, |
|
352 | # with an argument (like 'iptest.py IPython.core', 0 tests are run, | |
353 | # even if the same call done in this directory works fine). It appears |
|
353 | # even if the same call done in this directory works fine). It appears | |
354 | # that if the requested package is in the current dir, nose bails early |
|
354 | # that if the requested package is in the current dir, nose bails early | |
355 | # by default. Since it's otherwise harmless, leave it in by default |
|
355 | # by default. Since it's otherwise harmless, leave it in by default | |
356 | # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it. |
|
356 | # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it. | |
357 | argv.append('--traverse-namespace') |
|
357 | argv.append('--traverse-namespace') | |
358 |
|
358 | |||
359 | # Construct list of plugins, omitting the existing doctest plugin, which |
|
359 | # Construct list of plugins, omitting the existing doctest plugin, which | |
360 | # ours replaces (and extends). |
|
360 | # ours replaces (and extends). | |
361 | plugins = [IPythonDoctest(make_exclude())] |
|
361 | plugins = [IPythonDoctest(make_exclude())] | |
362 | for p in nose.plugins.builtin.plugins: |
|
362 | for p in nose.plugins.builtin.plugins: | |
363 | plug = p() |
|
363 | plug = p() | |
364 | if plug.name == 'doctest': |
|
364 | if plug.name == 'doctest': | |
365 | continue |
|
365 | continue | |
366 | plugins.append(plug) |
|
366 | plugins.append(plug) | |
367 |
|
367 | |||
368 | # We need a global ipython running in this process |
|
368 | # We need a global ipython running in this process | |
369 | globalipapp.start_ipython() |
|
369 | globalipapp.start_ipython() | |
370 | # Now nose can run |
|
370 | # Now nose can run | |
371 | TestProgram(argv=argv, plugins=plugins) |
|
371 | TestProgram(argv=argv, plugins=plugins) | |
372 |
|
372 | |||
373 |
|
373 | |||
374 | def run_iptestall(): |
|
374 | def run_iptestall(): | |
375 | """Run the entire IPython test suite by calling nose and trial. |
|
375 | """Run the entire IPython test suite by calling nose and trial. | |
376 |
|
376 | |||
377 | This function constructs :class:`IPTester` instances for all IPython |
|
377 | This function constructs :class:`IPTester` instances for all IPython | |
378 | modules and package and then runs each of them. This causes the modules |
|
378 | modules and package and then runs each of them. This causes the modules | |
379 | and packages of IPython to be tested each in their own subprocess using |
|
379 | and packages of IPython to be tested each in their own subprocess using | |
380 | nose or twisted.trial appropriately. |
|
380 | nose or twisted.trial appropriately. | |
381 | """ |
|
381 | """ | |
382 |
|
382 | |||
383 | runners = make_runners() |
|
383 | runners = make_runners() | |
384 |
|
384 | |||
385 | # Run the test runners in a temporary dir so we can nuke it when finished |
|
385 | # Run the test runners in a temporary dir so we can nuke it when finished | |
386 | # to clean up any junk files left over by accident. This also makes it |
|
386 | # to clean up any junk files left over by accident. This also makes it | |
387 | # robust against being run in non-writeable directories by mistake, as the |
|
387 | # robust against being run in non-writeable directories by mistake, as the | |
388 | # temp dir will always be user-writeable. |
|
388 | # temp dir will always be user-writeable. | |
389 | curdir = os.getcwd() |
|
389 | curdir = os.getcwd() | |
390 | testdir = tempfile.gettempdir() |
|
390 | testdir = tempfile.gettempdir() | |
391 | os.chdir(testdir) |
|
391 | os.chdir(testdir) | |
392 |
|
392 | |||
393 | # Run all test runners, tracking execution time |
|
393 | # Run all test runners, tracking execution time | |
394 | failed = [] |
|
394 | failed = [] | |
395 | t_start = time.time() |
|
395 | t_start = time.time() | |
396 | try: |
|
396 | try: | |
397 | for (name, runner) in runners: |
|
397 | for (name, runner) in runners: | |
398 | print '*'*70 |
|
398 | print '*'*70 | |
399 | print 'IPython test group:',name |
|
399 | print 'IPython test group:',name | |
400 | res = runner.run() |
|
400 | res = runner.run() | |
401 | if res: |
|
401 | if res: | |
402 | failed.append( (name, runner) ) |
|
402 | failed.append( (name, runner) ) | |
403 | finally: |
|
403 | finally: | |
404 | os.chdir(curdir) |
|
404 | os.chdir(curdir) | |
405 | t_end = time.time() |
|
405 | t_end = time.time() | |
406 | t_tests = t_end - t_start |
|
406 | t_tests = t_end - t_start | |
407 | nrunners = len(runners) |
|
407 | nrunners = len(runners) | |
408 | nfail = len(failed) |
|
408 | nfail = len(failed) | |
409 | # summarize results |
|
409 | # summarize results | |
410 |
|
410 | |||
411 | print '*'*70 |
|
411 | print '*'*70 | |
412 | print 'Test suite completed for system with the following information:' |
|
412 | print 'Test suite completed for system with the following information:' | |
413 | print report() |
|
413 | print report() | |
414 | print 'Ran %s test groups in %.3fs' % (nrunners, t_tests) |
|
414 | print 'Ran %s test groups in %.3fs' % (nrunners, t_tests) | |
415 |
|
415 | |||
416 | print 'Status:' |
|
416 | print 'Status:' | |
417 | if not failed: |
|
417 | if not failed: | |
418 | print 'OK' |
|
418 | print 'OK' | |
419 | else: |
|
419 | else: | |
420 | # If anything went wrong, point out what command to rerun manually to |
|
420 | # If anything went wrong, point out what command to rerun manually to | |
421 | # see the actual errors and individual summary |
|
421 | # see the actual errors and individual summary | |
422 | print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners) |
|
422 | print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners) | |
423 | for name, failed_runner in failed: |
|
423 | for name, failed_runner in failed: | |
424 | print '-'*40 |
|
424 | print '-'*40 | |
425 | print 'Runner failed:',name |
|
425 | print 'Runner failed:',name | |
426 | print 'You may wish to rerun this one individually, with:' |
|
426 | print 'You may wish to rerun this one individually, with:' | |
427 | print ' '.join(failed_runner.call_args) |
|
427 | print ' '.join(failed_runner.call_args) | |
428 |
|
428 | |||
429 |
|
429 | |||
430 |
|
430 | |||
431 | def main(): |
|
431 | def main(): | |
432 | for arg in sys.argv[1:]: |
|
432 | for arg in sys.argv[1:]: | |
433 | if arg.startswith('IPython'): |
|
433 | if arg.startswith('IPython'): | |
434 | # This is in-process |
|
434 | # This is in-process | |
435 | run_iptest() |
|
435 | run_iptest() | |
436 | else: |
|
436 | else: | |
437 | # This starts subprocesses |
|
437 | # This starts subprocesses | |
438 | run_iptestall() |
|
438 | run_iptestall() | |
439 |
|
439 | |||
440 |
|
440 | |||
441 | if __name__ == '__main__': |
|
441 | if __name__ == '__main__': | |
442 | main() |
|
442 | main() |
@@ -1,100 +1,185 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """ |
|
2 | """ | |
3 | Utilities for getting information about a system. |
|
3 | Utilities for getting information about IPython and the system it's running in. | |
4 | """ |
|
4 | """ | |
5 |
|
5 | |||
6 | #----------------------------------------------------------------------------- |
|
6 | #----------------------------------------------------------------------------- | |
7 | # Copyright (C) 2008-2009 The IPython Development Team |
|
7 | # Copyright (C) 2008-2009 The IPython Development Team | |
8 | # |
|
8 | # | |
9 | # Distributed under the terms of the BSD License. The full license is in |
|
9 | # Distributed under the terms of the BSD License. The full license is in | |
10 | # the file COPYING, distributed as part of this software. |
|
10 | # the file COPYING, distributed as part of this software. | |
11 | #----------------------------------------------------------------------------- |
|
11 | #----------------------------------------------------------------------------- | |
12 |
|
12 | |||
13 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
14 | # Imports |
|
14 | # Imports | |
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 |
|
16 | |||
17 | import os |
|
17 | import os | |
18 | import platform |
|
18 | import platform | |
|
19 | import pprint | |||
19 | import sys |
|
20 | import sys | |
20 | import subprocess |
|
21 | import subprocess | |
21 |
|
22 | |||
|
23 | from ConfigParser import ConfigParser | |||
|
24 | ||||
22 | from IPython.core import release |
|
25 | from IPython.core import release | |
23 |
|
26 | |||
24 | #----------------------------------------------------------------------------- |
|
27 | #----------------------------------------------------------------------------- | |
|
28 | # Globals | |||
|
29 | #----------------------------------------------------------------------------- | |||
|
30 | COMMIT_INFO_FNAME = '.git_commit_info.ini' | |||
|
31 | ||||
|
32 | #----------------------------------------------------------------------------- | |||
25 | # Code |
|
33 | # Code | |
26 | #----------------------------------------------------------------------------- |
|
34 | #----------------------------------------------------------------------------- | |
27 |
|
35 | |||
|
36 | def pkg_commit_hash(pkg_path): | |||
|
37 | """Get short form of commit hash given directory `pkg_path` | |||
|
38 | ||||
|
39 | There should be a file called 'COMMIT_INFO.txt' in `pkg_path`. This is a | |||
|
40 | file in INI file format, with at least one section: ``commit hash``, and two | |||
|
41 | variables ``archive_subst_hash`` and ``install_hash``. The first has a | |||
|
42 | substitution pattern in it which may have been filled by the execution of | |||
|
43 | ``git archive`` if this is an archive generated that way. The second is | |||
|
44 | filled in by the installation, if the installation is from a git archive. | |||
|
45 | ||||
|
46 | We get the commit hash from (in order of preference): | |||
|
47 | ||||
|
48 | * A substituted value in ``archive_subst_hash`` | |||
|
49 | * A written commit hash value in ``install_hash` | |||
|
50 | * git output, if we are in a git repository | |||
|
51 | ||||
|
52 | If all these fail, we return a not-found placeholder tuple | |||
|
53 | ||||
|
54 | Parameters | |||
|
55 | ---------- | |||
|
56 | pkg_path : str | |||
|
57 | directory containing package | |||
|
58 | ||||
|
59 | Returns | |||
|
60 | ------- | |||
|
61 | hash_from : str | |||
|
62 | Where we got the hash from - description | |||
|
63 | hash_str : str | |||
|
64 | short form of hash | |||
|
65 | """ | |||
|
66 | # Try and get commit from written commit text file | |||
|
67 | pth = os.path.join(pkg_path, COMMIT_INFO_FNAME) | |||
|
68 | if not os.path.isfile(pth): | |||
|
69 | raise IOError('Missing commit info file %s' % pth) | |||
|
70 | cfg_parser = ConfigParser() | |||
|
71 | cfg_parser.read(pth) | |||
|
72 | archive_subst = cfg_parser.get('commit hash', 'archive_subst_hash') | |||
|
73 | if not archive_subst.startswith('$Format'): # it has been substituted | |||
|
74 | return 'archive substitution', archive_subst | |||
|
75 | install_subst = cfg_parser.get('commit hash', 'install_hash') | |||
|
76 | if install_subst != '': | |||
|
77 | return 'installation', install_subst | |||
|
78 | # maybe we are in a repository | |||
|
79 | proc = subprocess.Popen('git rev-parse --short HEAD', | |||
|
80 | stdout=subprocess.PIPE, | |||
|
81 | stderr=subprocess.PIPE, | |||
|
82 | cwd=pkg_path, shell=True) | |||
|
83 | repo_commit, _ = proc.communicate() | |||
|
84 | if repo_commit: | |||
|
85 | return 'repository', repo_commit.strip() | |||
|
86 | return '(none found)', '<not found>' | |||
|
87 | ||||
|
88 | ||||
|
89 | def pkg_info(pkg_path): | |||
|
90 | """Return dict describing the context of this package | |||
|
91 | ||||
|
92 | Parameters | |||
|
93 | ---------- | |||
|
94 | pkg_path : str | |||
|
95 | path containing __init__.py for package | |||
|
96 | ||||
|
97 | Returns | |||
|
98 | ------- | |||
|
99 | context : dict | |||
|
100 | with named parameters of interest | |||
|
101 | """ | |||
|
102 | src, hsh = pkg_commit_hash(pkg_path) | |||
|
103 | return dict( | |||
|
104 | ipython_version=release.version, | |||
|
105 | ipython_path=pkg_path, | |||
|
106 | commit_source=src, | |||
|
107 | commit_hash=hsh, | |||
|
108 | sys_version=sys.version, | |||
|
109 | sys_executable=sys.executable, | |||
|
110 | sys_platform=sys.platform, | |||
|
111 | platform=platform.platform(), | |||
|
112 | os_name=os.name, | |||
|
113 | ) | |||
|
114 | ||||
|
115 | ||||
28 | def sys_info(): |
|
116 | def sys_info(): | |
29 | """Return useful information about IPython and the system, as a string. |
|
117 | """Return useful information about IPython and the system, as a string. | |
30 |
|
118 | |||
31 |
Example |
|
119 | Example | |
32 |
------- |
|
120 | ------- | |
33 |
In [ |
|
121 | In [2]: print sys_info() | |
34 | IPython version: 0.11.bzr.r1340 # random |
|
122 | {'commit_hash': '144fdae', # random | |
35 | BZR revision : 1340 |
|
123 | 'commit_source': 'repository', | |
36 | Platform info : os.name -> posix, sys.platform -> linux2 |
|
124 | 'ipython_path': '/home/fperez/usr/lib/python2.6/site-packages/IPython', | |
37 | : Linux-2.6.31-17-generic-i686-with-Ubuntu-9.10-karmic |
|
125 | 'ipython_version': '0.11.dev', | |
38 | Python info : 2.6.4 (r264:75706, Dec 7 2009, 18:45:15) |
|
126 | 'os_name': 'posix', | |
39 | [GCC 4.4.1] |
|
127 | 'platform': 'Linux-2.6.35-22-generic-i686-with-Ubuntu-10.10-maverick', | |
40 | """ |
|
128 | 'sys_executable': '/usr/bin/python', | |
41 | out = [] |
|
129 | 'sys_platform': 'linux2', | |
42 | out.append('IPython version: %s' % release.version) |
|
130 | 'sys_version': '2.6.6 (r266:84292, Sep 15 2010, 15:52:39) \\n[GCC 4.4.5]'} | |
43 | out.append('BZR revision : %s' % release.revision) |
|
131 | """ | |
44 | out.append('Platform info : os.name -> %s, sys.platform -> %s' % |
|
132 | p = os.path | |
45 | (os.name,sys.platform) ) |
|
133 | path = p.dirname(p.abspath(p.join(__file__, '..'))) | |
46 | out.append(' : %s' % platform.platform()) |
|
134 | return pprint.pformat(pkg_info(path)) | |
47 | out.append('Python info : %s' % sys.version) |
|
|||
48 | out.append('') # ensure closing newline |
|
|||
49 | return '\n'.join(out) |
|
|||
50 |
|
135 | |||
51 |
|
136 | |||
52 | def _num_cpus_unix(): |
|
137 | def _num_cpus_unix(): | |
53 | """Return the number of active CPUs on a Unix system.""" |
|
138 | """Return the number of active CPUs on a Unix system.""" | |
54 | return os.sysconf("SC_NPROCESSORS_ONLN") |
|
139 | return os.sysconf("SC_NPROCESSORS_ONLN") | |
55 |
|
140 | |||
56 |
|
141 | |||
57 | def _num_cpus_darwin(): |
|
142 | def _num_cpus_darwin(): | |
58 | """Return the number of active CPUs on a Darwin system.""" |
|
143 | """Return the number of active CPUs on a Darwin system.""" | |
59 | p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE) |
|
144 | p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE) | |
60 | return p.stdout.read() |
|
145 | return p.stdout.read() | |
61 |
|
146 | |||
62 |
|
147 | |||
63 | def _num_cpus_windows(): |
|
148 | def _num_cpus_windows(): | |
64 | """Return the number of active CPUs on a Windows system.""" |
|
149 | """Return the number of active CPUs on a Windows system.""" | |
65 | return os.environ.get("NUMBER_OF_PROCESSORS") |
|
150 | return os.environ.get("NUMBER_OF_PROCESSORS") | |
66 |
|
151 | |||
67 |
|
152 | |||
68 | def num_cpus(): |
|
153 | def num_cpus(): | |
69 | """Return the effective number of CPUs in the system as an integer. |
|
154 | """Return the effective number of CPUs in the system as an integer. | |
70 |
|
155 | |||
71 | This cross-platform function makes an attempt at finding the total number of |
|
156 | This cross-platform function makes an attempt at finding the total number of | |
72 | available CPUs in the system, as returned by various underlying system and |
|
157 | available CPUs in the system, as returned by various underlying system and | |
73 | python calls. |
|
158 | python calls. | |
74 |
|
159 | |||
75 | If it can't find a sensible answer, it returns 1 (though an error *may* make |
|
160 | If it can't find a sensible answer, it returns 1 (though an error *may* make | |
76 | it return a large positive number that's actually incorrect). |
|
161 | it return a large positive number that's actually incorrect). | |
77 | """ |
|
162 | """ | |
78 |
|
163 | |||
79 | # Many thanks to the Parallel Python project (http://www.parallelpython.com) |
|
164 | # Many thanks to the Parallel Python project (http://www.parallelpython.com) | |
80 | # for the names of the keys we needed to look up for this function. This |
|
165 | # for the names of the keys we needed to look up for this function. This | |
81 | # code was inspired by their equivalent function. |
|
166 | # code was inspired by their equivalent function. | |
82 |
|
167 | |||
83 | ncpufuncs = {'Linux':_num_cpus_unix, |
|
168 | ncpufuncs = {'Linux':_num_cpus_unix, | |
84 | 'Darwin':_num_cpus_darwin, |
|
169 | 'Darwin':_num_cpus_darwin, | |
85 | 'Windows':_num_cpus_windows, |
|
170 | 'Windows':_num_cpus_windows, | |
86 | # On Vista, python < 2.5.2 has a bug and returns 'Microsoft' |
|
171 | # On Vista, python < 2.5.2 has a bug and returns 'Microsoft' | |
87 | # See http://bugs.python.org/issue1082 for details. |
|
172 | # See http://bugs.python.org/issue1082 for details. | |
88 | 'Microsoft':_num_cpus_windows, |
|
173 | 'Microsoft':_num_cpus_windows, | |
89 | } |
|
174 | } | |
90 |
|
175 | |||
91 | ncpufunc = ncpufuncs.get(platform.system(), |
|
176 | ncpufunc = ncpufuncs.get(platform.system(), | |
92 | # default to unix version (Solaris, AIX, etc) |
|
177 | # default to unix version (Solaris, AIX, etc) | |
93 | _num_cpus_unix) |
|
178 | _num_cpus_unix) | |
94 |
|
179 | |||
95 | try: |
|
180 | try: | |
96 | ncpus = max(1,int(ncpufunc())) |
|
181 | ncpus = max(1,int(ncpufunc())) | |
97 | except: |
|
182 | except: | |
98 | ncpus = 1 |
|
183 | ncpus = 1 | |
99 | return ncpus |
|
184 | return ncpus | |
100 |
|
185 |
@@ -1,36 +1,37 | |||||
1 | include ipython.py |
|
1 | include ipython.py | |
2 | include setupbase.py |
|
2 | include setupbase.py | |
3 | include setupegg.py |
|
3 | include setupegg.py | |
4 |
|
4 | |||
5 | graft setupext |
|
5 | graft setupext | |
6 |
|
6 | |||
7 | graft scripts |
|
7 | graft scripts | |
8 | graft IPython/kernel |
|
8 | graft IPython/kernel | |
9 | graft IPython/config |
|
9 | graft IPython/config | |
10 | graft IPython/core |
|
10 | graft IPython/core | |
11 | # graft IPython/deathrow |
|
11 | # graft IPython/deathrow | |
12 | graft IPython/external |
|
12 | graft IPython/external | |
13 | graft IPython/frontend |
|
13 | graft IPython/frontend | |
14 | graft IPython/gui |
|
14 | graft IPython/gui | |
15 | graft IPython/lib |
|
15 | graft IPython/lib | |
16 | graft IPython/quarantine |
|
16 | graft IPython/quarantine | |
17 | graft IPython/scripts |
|
17 | graft IPython/scripts | |
18 | graft IPython/testing |
|
18 | graft IPython/testing | |
19 | graft IPython/utils |
|
19 | graft IPython/utils | |
20 |
|
20 | |||
|
21 | include IPython/.git_commit_info.ini | |||
21 |
|
22 | |||
22 | graft docs |
|
23 | graft docs | |
23 | exclude docs/\#* |
|
24 | exclude docs/\#* | |
24 | exclude docs/man/*.1 |
|
25 | exclude docs/man/*.1 | |
25 |
|
26 | |||
26 | # docs subdirs we want to skip |
|
27 | # docs subdirs we want to skip | |
27 | prune docs/attic |
|
28 | prune docs/attic | |
28 | prune docs/build |
|
29 | prune docs/build | |
29 |
|
30 | |||
30 | global-exclude *~ |
|
31 | global-exclude *~ | |
31 | global-exclude *.flc |
|
32 | global-exclude *.flc | |
32 | global-exclude *.pyc |
|
33 | global-exclude *.pyc | |
33 | global-exclude .dircopy.log |
|
34 | global-exclude .dircopy.log | |
34 | global-exclude .svn |
|
35 | global-exclude .svn | |
35 | global-exclude .bzr |
|
36 | global-exclude .bzr | |
36 | global-exclude .hgignore |
|
37 | global-exclude .hgignore |
@@ -1,40 +1,35 | |||||
1 | ============== |
|
1 | ============== | |
2 | IPython README |
|
2 | IPython README | |
3 | ============== |
|
3 | ============== | |
4 |
|
4 | |||
5 | Overview |
|
5 | Overview | |
6 | ======== |
|
6 | ======== | |
7 |
|
7 | |||
8 | Welcome to IPython. Our full documentation can be found in the ``docs/dist`` |
|
8 | Welcome to IPython. Our full documentation can be found in the ``docs/dist`` | |
9 | subdirectory in ``.html`` and ``.pdf`` formats, also available online at our |
|
9 | subdirectory in ``.html`` and ``.pdf`` formats, also available online at our | |
10 | `website <http://ipython.scipy.org>`_. The ``docs/source`` directory contains |
|
10 | `website <http://ipython.scipy.org>`_. The ``docs/source`` directory contains | |
11 | the plaintext version of these manuals. |
|
11 | the plaintext version of these manuals. | |
12 |
|
12 | |||
13 |
|
13 | |||
14 | Dependencies and supported Python versions |
|
14 | Dependencies and supported Python versions | |
15 | ========================================== |
|
15 | ========================================== | |
16 |
|
16 | |||
17 | For full details, see the installation section of the manual. The basic parts |
|
17 | For full details, see the installation section of the manual. The basic parts | |
18 | of IPython only need the Python standard library, but much of its more advanced |
|
18 | of IPython only need the Python standard library, but much of its more advanced | |
19 | functionality requires extra packages. |
|
19 | functionality requires extra packages. | |
20 |
|
20 | |||
21 |
Officially, IPython requires Python version 2. |
|
21 | Officially, IPython requires Python version 2.6 or 2.7. An experimental port | |
22 | started to port IPython to Python 3.0. |
|
22 | of IPython to Python 3.x has been sarted, and is available at | |
23 |
|
23 | http://github.com/takowl/ipython/tree/ipython-py3k. | ||
24 | .. warning:: |
|
|||
25 |
|
||||
26 | IPython 0.10 has only been well tested with Python 2.5 and 2.6. Parts of |
|
|||
27 | it may work with Python 2.4, but we do not officially support Python 2.4 |
|
|||
28 | anymore. If you need to use 2.4, you can still run IPython 0.9. |
|
|||
29 |
|
24 | |||
30 |
|
25 | |||
31 | Instant running |
|
26 | Instant running | |
32 | =============== |
|
27 | =============== | |
33 |
|
28 | |||
34 | You can run IPython from this directory without even installing it system-wide |
|
29 | You can run IPython from this directory without even installing it system-wide | |
35 | by typing at the terminal: |
|
30 | by typing at the terminal: | |
36 |
|
31 | |||
37 | .. code-block:: bash |
|
32 | .. code-block:: bash | |
38 |
|
33 | |||
39 | python ipython.py |
|
34 | python ipython.py | |
40 |
|
35 |
@@ -1,388 +1,391 | |||||
1 | .. _testing: |
|
1 | .. _testing: | |
2 |
|
2 | |||
3 | ========================================== |
|
3 | ========================================== | |
4 | Testing IPython for users and developers |
|
4 | Testing IPython for users and developers | |
5 | ========================================== |
|
5 | ========================================== | |
6 |
|
6 | |||
7 | Overview |
|
7 | Overview | |
8 | ======== |
|
8 | ======== | |
9 |
|
9 | |||
10 | It is extremely important that all code contributed to IPython has tests. |
|
10 | It is extremely important that all code contributed to IPython has tests. | |
11 | Tests should be written as unittests, doctests or other entities that the |
|
11 | Tests should be written as unittests, doctests or other entities that the | |
12 | IPython test system can detect. See below for more details on this. |
|
12 | IPython test system can detect. See below for more details on this. | |
13 |
|
13 | |||
14 | Each subpackage in IPython should have its own :file:`tests` directory that |
|
14 | Each subpackage in IPython should have its own :file:`tests` directory that | |
15 | contains all of the tests for that subpackage. All of the files in the |
|
15 | contains all of the tests for that subpackage. All of the files in the | |
16 | :file:`tests` directory should have the word "tests" in them to enable |
|
16 | :file:`tests` directory should have the word "tests" in them to enable | |
17 | the testing framework to find them. |
|
17 | the testing framework to find them. | |
18 |
|
18 | |||
19 | In docstrings, examples (either using IPython prompts like ``In [1]:`` or |
|
19 | In docstrings, examples (either using IPython prompts like ``In [1]:`` or | |
20 | 'classic' python ``>>>`` ones) can and should be included. The testing system |
|
20 | 'classic' python ``>>>`` ones) can and should be included. The testing system | |
21 | will detect them as doctests and will run them; it offers control to skip parts |
|
21 | will detect them as doctests and will run them; it offers control to skip parts | |
22 | or all of a specific doctest if the example is meant to be informative but |
|
22 | or all of a specific doctest if the example is meant to be informative but | |
23 | shows non-reproducible information (like filesystem data). |
|
23 | shows non-reproducible information (like filesystem data). | |
24 |
|
24 | |||
25 | If a subpackage has any dependencies beyond the Python standard library, the |
|
25 | If a subpackage has any dependencies beyond the Python standard library, the | |
26 | tests for that subpackage should be skipped if the dependencies are not found. |
|
26 | tests for that subpackage should be skipped if the dependencies are not found. | |
27 | This is very important so users don't get tests failing simply because they |
|
27 | This is very important so users don't get tests failing simply because they | |
28 | don't have dependencies. |
|
28 | don't have dependencies. | |
29 |
|
29 | |||
30 | The testing system we use is a hybrid of nose_ and Twisted's trial_ test runner. |
|
30 | The testing system we use is a hybrid of nose_ and Twisted's trial_ test runner. | |
31 | We use both because nose detects more things than Twisted and allows for more |
|
31 | We use both because nose detects more things than Twisted and allows for more | |
32 | flexible (and lighter-weight) ways of writing tests; in particular we've |
|
32 | flexible (and lighter-weight) ways of writing tests; in particular we've | |
33 | developed a nose plugin that allows us to paste verbatim IPython sessions and |
|
33 | developed a nose plugin that allows us to paste verbatim IPython sessions and | |
34 | test them as doctests, which is extremely important for us. But the parts of |
|
34 | test them as doctests, which is extremely important for us. But the parts of | |
35 | IPython that depend on Twisted must be tested using trial, because only trial |
|
35 | IPython that depend on Twisted must be tested using trial, because only trial | |
36 | manages the Twisted reactor correctly. |
|
36 | manages the Twisted reactor correctly. | |
37 |
|
37 | |||
38 | .. _nose: http://code.google.com/p/python-nose |
|
38 | .. _nose: http://code.google.com/p/python-nose | |
39 | .. _trial: http://twistedmatrix.com/trac/wiki/TwistedTrial |
|
39 | .. _trial: http://twistedmatrix.com/trac/wiki/TwistedTrial | |
40 |
|
40 | |||
41 |
|
41 | |||
42 | For the impatient: running the tests |
|
42 | For the impatient: running the tests | |
43 | ==================================== |
|
43 | ==================================== | |
44 |
|
44 | |||
45 | You can run IPython from the source download directory without even installing |
|
45 | You can run IPython from the source download directory without even installing | |
46 | it system-wide or having configure anything, by typing at the terminal: |
|
46 | it system-wide or having configure anything, by typing at the terminal: | |
47 |
|
47 | |||
48 | .. code-block:: bash |
|
48 | .. code-block:: bash | |
49 |
|
49 | |||
50 | python ipython.py |
|
50 | python ipython.py | |
51 |
|
51 | |||
52 | and similarly, you can execute the built-in test suite with: |
|
52 | In order to run the test suite, you must at least be able to import IPython, | |
53 |
|
53 | even if you haven't fully installed the user-facing scripts yet (common in a | ||
54 | .. code-block:: bash |
|
54 | development environment). You can then run the tests with: | |
55 |
|
||||
56 | python iptest.py |
|
|||
57 |
|
55 | |||
|
56 | .. code-block:: bash | |||
58 |
|
57 | |||
59 | This script manages intelligently both nose and trial, choosing the correct |
|
58 | python -c "import IPython; IPython.test()" | |
60 | test system for each of IPython's components. |
|
|||
61 |
|
59 | |||
62 | Once you have either installed it or at least configured your system to be |
|
60 | Once you have installed IPython either via a full install or using: | |
63 | able to import IPython, you can run the tests with: |
|
|||
64 |
|
61 | |||
65 | .. code-block:: bash |
|
62 | .. code-block:: bash | |
66 |
|
63 | |||
67 | python -c "import IPython; IPython.test()" |
|
64 | python setup.py develop | |
68 |
|
65 | |||
69 | This should work as long as IPython can be imported, even if you haven't fully |
|
66 | you will have available a system-wide script called :file:`iptest` that runs | |
70 | installed the user-facing scripts yet (common in a development environment). |
|
67 | the full test suite. You can then run the suite with: | |
71 | Once you have installed IPython, you will have available system-wide a script |
|
|||
72 | called :file:`iptest` that does the exact same as the :file:`iptest.py` script |
|
|||
73 | in the source directory, so you can then test simply with: |
|
|||
74 |
|
68 | |||
75 | .. code-block:: bash |
|
69 | .. code-block:: bash | |
76 |
|
70 | |||
77 | iptest [args] |
|
71 | iptest [args] | |
78 |
|
72 | |||
79 |
|
73 | |||
80 | Regardless of how you run things, you should eventually see something like: |
|
74 | Regardless of how you run things, you should eventually see something like: | |
81 |
|
75 | |||
82 | .. code-block:: bash |
|
76 | .. code-block:: bash | |
83 |
|
77 | |||
84 | ********************************************************************** |
|
78 | ********************************************************************** | |
85 | Test suite completed for system with the following information: |
|
79 | Test suite completed for system with the following information: | |
86 | IPython version: 0.11.bzr.r1340 |
|
80 | {'commit_hash': '144fdae', | |
87 | BZR revision : 1340 |
|
81 | 'commit_source': 'repository', | |
88 | Platform info : os.name -> posix, sys.platform -> linux2 |
|
82 | 'ipython_path': '/home/fperez/usr/lib/python2.6/site-packages/IPython', | |
89 | : Linux-2.6.31-17-generic-i686-with-Ubuntu-9.10-karmic |
|
83 | 'ipython_version': '0.11.dev', | |
90 | Python info : 2.6.4 (r264:75706, Dec 7 2009, 18:45:15) |
|
84 | 'os_name': 'posix', | |
91 | [GCC 4.4.1] |
|
85 | 'platform': 'Linux-2.6.35-22-generic-i686-with-Ubuntu-10.10-maverick', | |
92 |
|
86 | 'sys_executable': '/usr/bin/python', | ||
93 | Running from an installed IPython: True |
|
87 | 'sys_platform': 'linux2', | |
|
88 | 'sys_version': '2.6.6 (r266:84292, Sep 15 2010, 15:52:39) \n[GCC 4.4.5]'} | |||
94 |
|
89 | |||
95 | Tools and libraries available at test time: |
|
90 | Tools and libraries available at test time: | |
96 | curses foolscap gobject gtk pexpect twisted wx wx.aui zope.interface |
|
91 | curses foolscap gobject gtk pexpect twisted wx wx.aui zope.interface | |
97 |
|
92 | |||
98 | Tools and libraries NOT available at test time: |
|
93 | Ran 9 test groups in 67.213s | |
99 | objc |
|
|||
100 |
|
||||
101 | Ran 11 test groups in 36.244s |
|
|||
102 |
|
94 | |||
103 | Status: |
|
95 | Status: | |
104 | OK |
|
96 | OK | |
105 |
|
97 | |||
|
98 | ||||
106 | If not, there will be a message indicating which test group failed and how to |
|
99 | If not, there will be a message indicating which test group failed and how to | |
107 | rerun that group individually. For example, this tests the |
|
100 | rerun that group individually. For example, this tests the | |
108 | :mod:`IPython.utils` subpackage, the :option:`-v` option shows progress |
|
101 | :mod:`IPython.utils` subpackage, the :option:`-v` option shows progress | |
109 | indicators: |
|
102 | indicators: | |
110 |
|
103 | |||
111 | .. code-block:: bash |
|
104 | .. code-block:: bash | |
112 |
|
105 | |||
113 |
$ |
|
106 | $ iptest -v IPython.utils | |
114 | ..........................SS..SSS............................S.S... |
|
107 | ..........................SS..SSS............................S.S... | |
115 | ......................................................... |
|
108 | ......................................................... | |
116 | ---------------------------------------------------------------------- |
|
109 | ---------------------------------------------------------------------- | |
117 | Ran 125 tests in 0.119s |
|
110 | Ran 125 tests in 0.119s | |
118 |
|
111 | |||
119 | OK (SKIP=7) |
|
112 | OK (SKIP=7) | |
120 |
|
113 | |||
121 |
|
114 | |||
122 | Because the IPython test machinery is based on nose, you can use all nose |
|
115 | Because the IPython test machinery is based on nose, you can use all nose | |
123 | options and syntax, typing ``iptest -h`` shows all available options. For |
|
116 | options and syntax, typing ``iptest -h`` shows all available options. For | |
124 | example, this lets you run the specific test :func:`test_rehashx` inside the |
|
117 | example, this lets you run the specific test :func:`test_rehashx` inside the | |
125 | :mod:`test_magic` module: |
|
118 | :mod:`test_magic` module: | |
126 |
|
119 | |||
127 | .. code-block:: bash |
|
120 | .. code-block:: bash | |
128 |
|
121 | |||
129 |
$ |
|
122 | $ iptest -vv IPython.core.tests.test_magic:test_rehashx | |
130 | IPython.core.tests.test_magic.test_rehashx(True,) ... ok |
|
123 | IPython.core.tests.test_magic.test_rehashx(True,) ... ok | |
131 | IPython.core.tests.test_magic.test_rehashx(True,) ... ok |
|
124 | IPython.core.tests.test_magic.test_rehashx(True,) ... ok | |
132 |
|
125 | |||
133 | ---------------------------------------------------------------------- |
|
126 | ---------------------------------------------------------------------- | |
134 | Ran 2 tests in 0.100s |
|
127 | Ran 2 tests in 0.100s | |
135 |
|
128 | |||
136 | OK |
|
129 | OK | |
137 |
|
130 | |||
138 | When developing, the :option:`--pdb` and :option:`--pdb-failures` of nose are |
|
131 | When developing, the :option:`--pdb` and :option:`--pdb-failures` of nose are | |
139 | particularly useful, these drop you into an interactive pdb session at the |
|
132 | particularly useful, these drop you into an interactive pdb session at the | |
140 | point of the error or failure respectively. |
|
133 | point of the error or failure respectively. | |
141 |
|
134 | |||
142 | To run Twisted-using tests, use the :command:`trial` command on a per file or |
|
135 | To run Twisted-using tests, use the :command:`trial` command on a per file or | |
143 | package basis: |
|
136 | package basis: | |
144 |
|
137 | |||
145 | .. code-block:: bash |
|
138 | .. code-block:: bash | |
146 |
|
139 | |||
147 | trial IPython.kernel |
|
140 | trial IPython.kernel | |
148 |
|
141 | |||
|
142 | .. note:: | |||
|
143 | ||||
|
144 | The system information summary printed above is accessible from the top | |||
|
145 | level package. If you encounter a problem with IPython, it's useful to | |||
|
146 | include this information when reporting on the mailing list; use:: | |||
|
147 | ||||
|
148 | from IPython import sys_info | |||
|
149 | print sys_info() | |||
|
150 | ||||
|
151 | and include the resulting information in your query. | |||
149 |
|
152 | |||
150 | For developers: writing tests |
|
153 | For developers: writing tests | |
151 | ============================= |
|
154 | ============================= | |
152 |
|
155 | |||
153 | By now IPython has a reasonable test suite, so the best way to see what's |
|
156 | By now IPython has a reasonable test suite, so the best way to see what's | |
154 | available is to look at the :file:`tests` directory in most subpackages. But |
|
157 | available is to look at the :file:`tests` directory in most subpackages. But | |
155 | here are a few pointers to make the process easier. |
|
158 | here are a few pointers to make the process easier. | |
156 |
|
159 | |||
157 |
|
160 | |||
158 | Main tools: :mod:`IPython.testing` |
|
161 | Main tools: :mod:`IPython.testing` | |
159 | ---------------------------------- |
|
162 | ---------------------------------- | |
160 |
|
163 | |||
161 | The :mod:`IPython.testing` package is where all of the machinery to test |
|
164 | The :mod:`IPython.testing` package is where all of the machinery to test | |
162 | IPython (rather than the tests for its various parts) lives. In particular, |
|
165 | IPython (rather than the tests for its various parts) lives. In particular, | |
163 | the :mod:`iptest` module in there has all the smarts to control the test |
|
166 | the :mod:`iptest` module in there has all the smarts to control the test | |
164 | process. In there, the :func:`make_exclude` function is used to build a |
|
167 | process. In there, the :func:`make_exclude` function is used to build a | |
165 | blacklist of exclusions, these are modules that do not get even imported for |
|
168 | blacklist of exclusions, these are modules that do not get even imported for | |
166 | tests. This is important so that things that would fail to even import because |
|
169 | tests. This is important so that things that would fail to even import because | |
167 | of missing dependencies don't give errors to end users, as we stated above. |
|
170 | of missing dependencies don't give errors to end users, as we stated above. | |
168 |
|
171 | |||
169 | The :mod:`decorators` module contains a lot of useful decorators, especially |
|
172 | The :mod:`decorators` module contains a lot of useful decorators, especially | |
170 | useful to mark individual tests that should be skipped under certain conditions |
|
173 | useful to mark individual tests that should be skipped under certain conditions | |
171 | (rather than blacklisting the package altogether because of a missing major |
|
174 | (rather than blacklisting the package altogether because of a missing major | |
172 | dependency). |
|
175 | dependency). | |
173 |
|
176 | |||
174 | Our nose plugin for doctests |
|
177 | Our nose plugin for doctests | |
175 | ---------------------------- |
|
178 | ---------------------------- | |
176 |
|
179 | |||
177 | The :mod:`plugin` subpackage in testing contains a nose plugin called |
|
180 | The :mod:`plugin` subpackage in testing contains a nose plugin called | |
178 | :mod:`ipdoctest` that teaches nose about IPython syntax, so you can write |
|
181 | :mod:`ipdoctest` that teaches nose about IPython syntax, so you can write | |
179 | doctests with IPython prompts. You can also mark doctest output with ``# |
|
182 | doctests with IPython prompts. You can also mark doctest output with ``# | |
180 | random`` for the output corresponding to a single input to be ignored (stronger |
|
183 | random`` for the output corresponding to a single input to be ignored (stronger | |
181 | than using ellipsis and useful to keep it as an example). If you want the |
|
184 | than using ellipsis and useful to keep it as an example). If you want the | |
182 | entire docstring to be executed but none of the output from any input to be |
|
185 | entire docstring to be executed but none of the output from any input to be | |
183 | checked, you can use the ``# all-random`` marker. The |
|
186 | checked, you can use the ``# all-random`` marker. The | |
184 | :mod:`IPython.testing.plugin.dtexample` module contains examples of how to use |
|
187 | :mod:`IPython.testing.plugin.dtexample` module contains examples of how to use | |
185 | these; for reference here is how to use ``# random``:: |
|
188 | these; for reference here is how to use ``# random``:: | |
186 |
|
189 | |||
187 | def ranfunc(): |
|
190 | def ranfunc(): | |
188 | """A function with some random output. |
|
191 | """A function with some random output. | |
189 |
|
192 | |||
190 | Normal examples are verified as usual: |
|
193 | Normal examples are verified as usual: | |
191 | >>> 1+3 |
|
194 | >>> 1+3 | |
192 | 4 |
|
195 | 4 | |
193 |
|
196 | |||
194 | But if you put '# random' in the output, it is ignored: |
|
197 | But if you put '# random' in the output, it is ignored: | |
195 | >>> 1+3 |
|
198 | >>> 1+3 | |
196 | junk goes here... # random |
|
199 | junk goes here... # random | |
197 |
|
200 | |||
198 | >>> 1+2 |
|
201 | >>> 1+2 | |
199 | again, anything goes #random |
|
202 | again, anything goes #random | |
200 | if multiline, the random mark is only needed once. |
|
203 | if multiline, the random mark is only needed once. | |
201 |
|
204 | |||
202 | >>> 1+2 |
|
205 | >>> 1+2 | |
203 | You can also put the random marker at the end: |
|
206 | You can also put the random marker at the end: | |
204 | # random |
|
207 | # random | |
205 |
|
208 | |||
206 | >>> 1+2 |
|
209 | >>> 1+2 | |
207 | # random |
|
210 | # random | |
208 | .. or at the beginning. |
|
211 | .. or at the beginning. | |
209 |
|
212 | |||
210 | More correct input is properly verified: |
|
213 | More correct input is properly verified: | |
211 | >>> ranfunc() |
|
214 | >>> ranfunc() | |
212 | 'ranfunc' |
|
215 | 'ranfunc' | |
213 | """ |
|
216 | """ | |
214 | return 'ranfunc' |
|
217 | return 'ranfunc' | |
215 |
|
218 | |||
216 | and an example of ``# all-random``:: |
|
219 | and an example of ``# all-random``:: | |
217 |
|
220 | |||
218 | def random_all(): |
|
221 | def random_all(): | |
219 | """A function where we ignore the output of ALL examples. |
|
222 | """A function where we ignore the output of ALL examples. | |
220 |
|
223 | |||
221 | Examples: |
|
224 | Examples: | |
222 |
|
225 | |||
223 | # all-random |
|
226 | # all-random | |
224 |
|
227 | |||
225 | This mark tells the testing machinery that all subsequent examples |
|
228 | This mark tells the testing machinery that all subsequent examples | |
226 | should be treated as random (ignoring their output). They are still |
|
229 | should be treated as random (ignoring their output). They are still | |
227 | executed, so if a they raise an error, it will be detected as such, |
|
230 | executed, so if a they raise an error, it will be detected as such, | |
228 | but their output is completely ignored. |
|
231 | but their output is completely ignored. | |
229 |
|
232 | |||
230 | >>> 1+3 |
|
233 | >>> 1+3 | |
231 | junk goes here... |
|
234 | junk goes here... | |
232 |
|
235 | |||
233 | >>> 1+3 |
|
236 | >>> 1+3 | |
234 | klasdfj; |
|
237 | klasdfj; | |
235 |
|
238 | |||
236 | In [8]: print 'hello' |
|
239 | In [8]: print 'hello' | |
237 | world # random |
|
240 | world # random | |
238 |
|
241 | |||
239 | In [9]: iprand() |
|
242 | In [9]: iprand() | |
240 | Out[9]: 'iprand' |
|
243 | Out[9]: 'iprand' | |
241 | """ |
|
244 | """ | |
242 | return 'iprand' |
|
245 | return 'iprand' | |
243 |
|
246 | |||
244 |
|
247 | |||
245 | When writing docstrings, you can use the ``@skip_doctest`` decorator to |
|
248 | When writing docstrings, you can use the ``@skip_doctest`` decorator to | |
246 | indicate that a docstring should *not* be treated as a doctest at all. The |
|
249 | indicate that a docstring should *not* be treated as a doctest at all. The | |
247 | difference between ``# all-random`` and ``@skip_doctest`` is that the former |
|
250 | difference between ``# all-random`` and ``@skip_doctest`` is that the former | |
248 | executes the example but ignores output, while the latter doesn't execute any |
|
251 | executes the example but ignores output, while the latter doesn't execute any | |
249 | code. ``@skip_doctest`` should be used for docstrings whose examples are |
|
252 | code. ``@skip_doctest`` should be used for docstrings whose examples are | |
250 | purely informational. |
|
253 | purely informational. | |
251 |
|
254 | |||
252 | If a given docstring fails under certain conditions but otherwise is a good |
|
255 | If a given docstring fails under certain conditions but otherwise is a good | |
253 | doctest, you can use code like the following, that relies on the 'null' |
|
256 | doctest, you can use code like the following, that relies on the 'null' | |
254 | decorator to leave the docstring intact where it works as a test:: |
|
257 | decorator to leave the docstring intact where it works as a test:: | |
255 |
|
258 | |||
256 | # The docstring for full_path doctests differently on win32 (different path |
|
259 | # The docstring for full_path doctests differently on win32 (different path | |
257 | # separator) so just skip the doctest there, and use a null decorator |
|
260 | # separator) so just skip the doctest there, and use a null decorator | |
258 | # elsewhere: |
|
261 | # elsewhere: | |
259 |
|
262 | |||
260 | doctest_deco = dec.skip_doctest if sys.platform == 'win32' else dec.null_deco |
|
263 | doctest_deco = dec.skip_doctest if sys.platform == 'win32' else dec.null_deco | |
261 |
|
264 | |||
262 | @doctest_deco |
|
265 | @doctest_deco | |
263 | def full_path(startPath,files): |
|
266 | def full_path(startPath,files): | |
264 | """Make full paths for all the listed files, based on startPath...""" |
|
267 | """Make full paths for all the listed files, based on startPath...""" | |
265 |
|
268 | |||
266 | # function body follows... |
|
269 | # function body follows... | |
267 |
|
270 | |||
268 | With our nose plugin that understands IPython syntax, an extremely effective |
|
271 | With our nose plugin that understands IPython syntax, an extremely effective | |
269 | way to write tests is to simply copy and paste an interactive session into a |
|
272 | way to write tests is to simply copy and paste an interactive session into a | |
270 | docstring. You can writing this type of test, where your docstring is meant |
|
273 | docstring. You can writing this type of test, where your docstring is meant | |
271 | *only* as a test, by prefixing the function name with ``doctest_`` and leaving |
|
274 | *only* as a test, by prefixing the function name with ``doctest_`` and leaving | |
272 | its body *absolutely empty* other than the docstring. In |
|
275 | its body *absolutely empty* other than the docstring. In | |
273 | :mod:`IPython.core.tests.test_magic` you can find several examples of this, but |
|
276 | :mod:`IPython.core.tests.test_magic` you can find several examples of this, but | |
274 | for completeness sake, your code should look like this (a simple case):: |
|
277 | for completeness sake, your code should look like this (a simple case):: | |
275 |
|
278 | |||
276 | def doctest_time(): |
|
279 | def doctest_time(): | |
277 | """ |
|
280 | """ | |
278 | In [10]: %time None |
|
281 | In [10]: %time None | |
279 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
282 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
280 | Wall time: 0.00 s |
|
283 | Wall time: 0.00 s | |
281 | """ |
|
284 | """ | |
282 |
|
285 | |||
283 | This function is only analyzed for its docstring but it is not considered a |
|
286 | This function is only analyzed for its docstring but it is not considered a | |
284 | separate test, which is why its body should be empty. |
|
287 | separate test, which is why its body should be empty. | |
285 |
|
288 | |||
286 |
|
289 | |||
287 | Parametric tests done right |
|
290 | Parametric tests done right | |
288 | --------------------------- |
|
291 | --------------------------- | |
289 |
|
292 | |||
290 | If you need to run multiple tests inside the same standalone function or method |
|
293 | If you need to run multiple tests inside the same standalone function or method | |
291 | of a :class:`unittest.TestCase` subclass, IPython provides the ``parametric`` |
|
294 | of a :class:`unittest.TestCase` subclass, IPython provides the ``parametric`` | |
292 | decorator for this purpose. This is superior to how test generators work in |
|
295 | decorator for this purpose. This is superior to how test generators work in | |
293 | nose, because IPython's keeps intact your stack, which makes debugging vastly |
|
296 | nose, because IPython's keeps intact your stack, which makes debugging vastly | |
294 | easier. For example, these are some parametric tests both in class form and as |
|
297 | easier. For example, these are some parametric tests both in class form and as | |
295 | a standalone function (choose in each situation the style that best fits the |
|
298 | a standalone function (choose in each situation the style that best fits the | |
296 | problem at hand, since both work):: |
|
299 | problem at hand, since both work):: | |
297 |
|
300 | |||
298 | from IPython.testing import decorators as dec |
|
301 | from IPython.testing import decorators as dec | |
299 |
|
302 | |||
300 | def is_smaller(i,j): |
|
303 | def is_smaller(i,j): | |
301 | assert i<j,"%s !< %s" % (i,j) |
|
304 | assert i<j,"%s !< %s" % (i,j) | |
302 |
|
305 | |||
303 | class Tester(ParametricTestCase): |
|
306 | class Tester(ParametricTestCase): | |
304 |
|
307 | |||
305 | def test_parametric(self): |
|
308 | def test_parametric(self): | |
306 | yield is_smaller(3, 4) |
|
309 | yield is_smaller(3, 4) | |
307 | x, y = 1, 2 |
|
310 | x, y = 1, 2 | |
308 | yield is_smaller(x, y) |
|
311 | yield is_smaller(x, y) | |
309 |
|
312 | |||
310 | @dec.parametric |
|
313 | @dec.parametric | |
311 | def test_par_standalone(): |
|
314 | def test_par_standalone(): | |
312 | yield is_smaller(3, 4) |
|
315 | yield is_smaller(3, 4) | |
313 | x, y = 1, 2 |
|
316 | x, y = 1, 2 | |
314 | yield is_smaller(x, y) |
|
317 | yield is_smaller(x, y) | |
315 |
|
318 | |||
316 |
|
319 | |||
317 | Writing tests for Twisted-using code |
|
320 | Writing tests for Twisted-using code | |
318 | ------------------------------------ |
|
321 | ------------------------------------ | |
319 |
|
322 | |||
320 | Tests of Twisted [Twisted]_ using code should be written by subclassing the |
|
323 | Tests of Twisted [Twisted]_ using code should be written by subclassing the | |
321 | ``TestCase`` class that comes with ``twisted.trial.unittest``. Furthermore, all |
|
324 | ``TestCase`` class that comes with ``twisted.trial.unittest``. Furthermore, all | |
322 | :class:`Deferred` instances that are created in the test must be properly |
|
325 | :class:`Deferred` instances that are created in the test must be properly | |
323 | chained and the final one *must* be the return value of the test method. |
|
326 | chained and the final one *must* be the return value of the test method. | |
324 |
|
327 | |||
325 | .. note:: |
|
328 | .. note:: | |
326 |
|
329 | |||
327 | The best place to see how to use the testing tools, are the tests for these |
|
330 | The best place to see how to use the testing tools, are the tests for these | |
328 | tools themselves, which live in :mod:`IPython.testing.tests`. |
|
331 | tools themselves, which live in :mod:`IPython.testing.tests`. | |
329 |
|
332 | |||
330 |
|
333 | |||
331 | Design requirements |
|
334 | Design requirements | |
332 | =================== |
|
335 | =================== | |
333 |
|
336 | |||
334 | This section is a set of notes on the key points of the IPython testing needs, |
|
337 | This section is a set of notes on the key points of the IPython testing needs, | |
335 | that were used when writing the system and should be kept for reference as it |
|
338 | that were used when writing the system and should be kept for reference as it | |
336 | eveolves. |
|
339 | eveolves. | |
337 |
|
340 | |||
338 | Testing IPython in full requires modifications to the default behavior of nose |
|
341 | Testing IPython in full requires modifications to the default behavior of nose | |
339 | and doctest, because the IPython prompt is not recognized to determine Python |
|
342 | and doctest, because the IPython prompt is not recognized to determine Python | |
340 | input, and because IPython admits user input that is not valid Python (things |
|
343 | input, and because IPython admits user input that is not valid Python (things | |
341 | like ``%magics`` and ``!system commands``. |
|
344 | like ``%magics`` and ``!system commands``. | |
342 |
|
345 | |||
343 | We basically need to be able to test the following types of code: |
|
346 | We basically need to be able to test the following types of code: | |
344 |
|
347 | |||
345 | 1. Pure Python files containing normal tests. These are not a problem, since |
|
348 | 1. Pure Python files containing normal tests. These are not a problem, since | |
346 | Nose will pick them up as long as they conform to the (flexible) conventions |
|
349 | Nose will pick them up as long as they conform to the (flexible) conventions | |
347 | used by nose to recognize tests. |
|
350 | used by nose to recognize tests. | |
348 |
|
351 | |||
349 | 2. Python files containing doctests. Here, we have two possibilities: |
|
352 | 2. Python files containing doctests. Here, we have two possibilities: | |
350 | - The prompts are the usual ``>>>`` and the input is pure Python. |
|
353 | - The prompts are the usual ``>>>`` and the input is pure Python. | |
351 | - The prompts are of the form ``In [1]:`` and the input can contain extended |
|
354 | - The prompts are of the form ``In [1]:`` and the input can contain extended | |
352 | IPython expressions. |
|
355 | IPython expressions. | |
353 |
|
356 | |||
354 | In the first case, Nose will recognize the doctests as long as it is called |
|
357 | In the first case, Nose will recognize the doctests as long as it is called | |
355 | with the ``--with-doctest`` flag. But the second case will likely require |
|
358 | with the ``--with-doctest`` flag. But the second case will likely require | |
356 | modifications or the writing of a new doctest plugin for Nose that is |
|
359 | modifications or the writing of a new doctest plugin for Nose that is | |
357 | IPython-aware. |
|
360 | IPython-aware. | |
358 |
|
361 | |||
359 | 3. ReStructuredText files that contain code blocks. For this type of file, we |
|
362 | 3. ReStructuredText files that contain code blocks. For this type of file, we | |
360 | have three distinct possibilities for the code blocks: |
|
363 | have three distinct possibilities for the code blocks: | |
361 | - They use ``>>>`` prompts. |
|
364 | - They use ``>>>`` prompts. | |
362 | - They use ``In [1]:`` prompts. |
|
365 | - They use ``In [1]:`` prompts. | |
363 | - They are standalone blocks of pure Python code without any prompts. |
|
366 | - They are standalone blocks of pure Python code without any prompts. | |
364 |
|
367 | |||
365 | The first two cases are similar to the situation #2 above, except that in |
|
368 | The first two cases are similar to the situation #2 above, except that in | |
366 | this case the doctests must be extracted from input code blocks using |
|
369 | this case the doctests must be extracted from input code blocks using | |
367 | docutils instead of from the Python docstrings. |
|
370 | docutils instead of from the Python docstrings. | |
368 |
|
371 | |||
369 | In the third case, we must have a convention for distinguishing code blocks |
|
372 | In the third case, we must have a convention for distinguishing code blocks | |
370 | that are meant for execution from others that may be snippets of shell code |
|
373 | that are meant for execution from others that may be snippets of shell code | |
371 | or other examples not meant to be run. One possibility is to assume that |
|
374 | or other examples not meant to be run. One possibility is to assume that | |
372 | all indented code blocks are meant for execution, but to have a special |
|
375 | all indented code blocks are meant for execution, but to have a special | |
373 | docutils directive for input that should not be executed. |
|
376 | docutils directive for input that should not be executed. | |
374 |
|
377 | |||
375 | For those code blocks that we will execute, the convention used will simply |
|
378 | For those code blocks that we will execute, the convention used will simply | |
376 | be that they get called and are considered successful if they run to |
|
379 | be that they get called and are considered successful if they run to | |
377 | completion without raising errors. This is similar to what Nose does for |
|
380 | completion without raising errors. This is similar to what Nose does for | |
378 | standalone test functions, and by putting asserts or other forms of |
|
381 | standalone test functions, and by putting asserts or other forms of | |
379 | exception-raising statements it becomes possible to have literate examples |
|
382 | exception-raising statements it becomes possible to have literate examples | |
380 | that double as lightweight tests. |
|
383 | that double as lightweight tests. | |
381 |
|
384 | |||
382 | 4. Extension modules with doctests in function and method docstrings. |
|
385 | 4. Extension modules with doctests in function and method docstrings. | |
383 | Currently Nose simply can't find these docstrings correctly, because the |
|
386 | Currently Nose simply can't find these docstrings correctly, because the | |
384 | underlying doctest DocTestFinder object fails there. Similarly to #2 above, |
|
387 | underlying doctest DocTestFinder object fails there. Similarly to #2 above, | |
385 | the docstrings could have either pure python or IPython prompts. |
|
388 | the docstrings could have either pure python or IPython prompts. | |
386 |
|
389 | |||
387 | Of these, only 3-c (reST with standalone code blocks) is not implemented at |
|
390 | Of these, only 3-c (reST with standalone code blocks) is not implemented at | |
388 | this point. |
|
391 | this point. |
@@ -1,251 +1,257 | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # -*- coding: utf-8 -*- |
|
2 | # -*- coding: utf-8 -*- | |
3 | """Setup script for IPython. |
|
3 | """Setup script for IPython. | |
4 |
|
4 | |||
5 | Under Posix environments it works like a typical setup.py script. |
|
5 | Under Posix environments it works like a typical setup.py script. | |
6 | Under Windows, the command sdist is not supported, since IPython |
|
6 | Under Windows, the command sdist is not supported, since IPython | |
7 | requires utilities which are not available under Windows.""" |
|
7 | requires utilities which are not available under Windows.""" | |
8 |
|
8 | |||
9 |
#----------------------------------------------------------------------------- |
|
9 | #----------------------------------------------------------------------------- | |
10 |
# Copyright ( |
|
10 | # Copyright (c) 2008-2010, IPython Development Team. | |
|
11 | # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu> | |||
|
12 | # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de> | |||
|
13 | # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu> | |||
11 | # |
|
14 | # | |
12 |
# Distributed under the terms of the BSD License. |
|
15 | # Distributed under the terms of the Modified BSD License. | |
13 | # the file COPYING, distributed as part of this software. |
|
16 | # | |
14 | #------------------------------------------------------------------------------- |
|
17 | # The full license is in the file COPYING.txt, distributed with this software. | |
|
18 | #----------------------------------------------------------------------------- | |||
15 |
|
19 | |||
16 | #----------------------------------------------------------------------------- |
|
20 | #----------------------------------------------------------------------------- | |
17 | # Minimal Python version sanity check |
|
21 | # Minimal Python version sanity check | |
18 | #----------------------------------------------------------------------------- |
|
22 | #----------------------------------------------------------------------------- | |
19 |
|
23 | |||
20 | import sys |
|
24 | import sys | |
21 |
|
25 | |||
22 | # This check is also made in IPython/__init__, don't forget to update both when |
|
26 | # This check is also made in IPython/__init__, don't forget to update both when | |
23 | # changing Python version requirements. |
|
27 | # changing Python version requirements. | |
24 | if sys.version[0:3] < '2.6': |
|
28 | if sys.version[0:3] < '2.6': | |
25 | error = """\ |
|
29 | error = """\ | |
26 | ERROR: 'IPython requires Python Version 2.6 or above.' |
|
30 | ERROR: 'IPython requires Python Version 2.6 or above.' | |
27 | Exiting.""" |
|
31 | Exiting.""" | |
28 | print >> sys.stderr, error |
|
32 | print >> sys.stderr, error | |
29 | sys.exit(1) |
|
33 | sys.exit(1) | |
30 |
|
34 | |||
31 | # At least we're on the python version we need, move on. |
|
35 | # At least we're on the python version we need, move on. | |
32 |
|
36 | |||
33 | #------------------------------------------------------------------------------- |
|
37 | #------------------------------------------------------------------------------- | |
34 | # Imports |
|
38 | # Imports | |
35 | #------------------------------------------------------------------------------- |
|
39 | #------------------------------------------------------------------------------- | |
36 |
|
40 | |||
37 | # Stdlib imports |
|
41 | # Stdlib imports | |
38 | import os |
|
42 | import os | |
39 | import shutil |
|
43 | import shutil | |
40 |
|
44 | |||
41 | from glob import glob |
|
45 | from glob import glob | |
42 |
|
46 | |||
43 | # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly |
|
47 | # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly | |
44 | # update it when the contents of directories change. |
|
48 | # update it when the contents of directories change. | |
45 | if os.path.exists('MANIFEST'): os.remove('MANIFEST') |
|
49 | if os.path.exists('MANIFEST'): os.remove('MANIFEST') | |
46 |
|
50 | |||
47 | from distutils.core import setup |
|
51 | from distutils.core import setup | |
48 |
|
52 | |||
49 | # Our own imports |
|
53 | # Our own imports | |
50 | from IPython.utils.path import target_update |
|
54 | from IPython.utils.path import target_update | |
51 |
|
55 | |||
52 | from setupbase import ( |
|
56 | from setupbase import ( | |
53 | setup_args, |
|
57 | setup_args, | |
54 | find_packages, |
|
58 | find_packages, | |
55 | find_package_data, |
|
59 | find_package_data, | |
56 | find_scripts, |
|
60 | find_scripts, | |
57 | find_data_files, |
|
61 | find_data_files, | |
58 | check_for_dependencies |
|
62 | check_for_dependencies, | |
|
63 | record_commit_info, | |||
59 | ) |
|
64 | ) | |
60 |
|
65 | |||
61 | isfile = os.path.isfile |
|
66 | isfile = os.path.isfile | |
62 | pjoin = os.path.join |
|
67 | pjoin = os.path.join | |
63 |
|
68 | |||
64 | #----------------------------------------------------------------------------- |
|
69 | #----------------------------------------------------------------------------- | |
65 | # Function definitions |
|
70 | # Function definitions | |
66 | #----------------------------------------------------------------------------- |
|
71 | #----------------------------------------------------------------------------- | |
67 |
|
72 | |||
68 | def cleanup(): |
|
73 | def cleanup(): | |
69 | """Clean up the junk left around by the build process""" |
|
74 | """Clean up the junk left around by the build process""" | |
70 | if "develop" not in sys.argv: |
|
75 | if "develop" not in sys.argv: | |
71 | try: |
|
76 | try: | |
72 | shutil.rmtree('ipython.egg-info') |
|
77 | shutil.rmtree('ipython.egg-info') | |
73 | except: |
|
78 | except: | |
74 | try: |
|
79 | try: | |
75 | os.unlink('ipython.egg-info') |
|
80 | os.unlink('ipython.egg-info') | |
76 | except: |
|
81 | except: | |
77 | pass |
|
82 | pass | |
78 |
|
83 | |||
79 | #------------------------------------------------------------------------------- |
|
84 | #------------------------------------------------------------------------------- | |
80 | # Handle OS specific things |
|
85 | # Handle OS specific things | |
81 | #------------------------------------------------------------------------------- |
|
86 | #------------------------------------------------------------------------------- | |
82 |
|
87 | |||
83 | if os.name == 'posix': |
|
88 | if os.name == 'posix': | |
84 | os_name = 'posix' |
|
89 | os_name = 'posix' | |
85 | elif os.name in ['nt','dos']: |
|
90 | elif os.name in ['nt','dos']: | |
86 | os_name = 'windows' |
|
91 | os_name = 'windows' | |
87 | else: |
|
92 | else: | |
88 | print 'Unsupported operating system:',os.name |
|
93 | print 'Unsupported operating system:',os.name | |
89 | sys.exit(1) |
|
94 | sys.exit(1) | |
90 |
|
95 | |||
91 | # Under Windows, 'sdist' has not been supported. Now that the docs build with |
|
96 | # Under Windows, 'sdist' has not been supported. Now that the docs build with | |
92 | # Sphinx it might work, but let's not turn it on until someone confirms that it |
|
97 | # Sphinx it might work, but let's not turn it on until someone confirms that it | |
93 | # actually works. |
|
98 | # actually works. | |
94 | if os_name == 'windows' and 'sdist' in sys.argv: |
|
99 | if os_name == 'windows' and 'sdist' in sys.argv: | |
95 | print 'The sdist command is not available under Windows. Exiting.' |
|
100 | print 'The sdist command is not available under Windows. Exiting.' | |
96 | sys.exit(1) |
|
101 | sys.exit(1) | |
97 |
|
102 | |||
98 | #------------------------------------------------------------------------------- |
|
103 | #------------------------------------------------------------------------------- | |
99 | # Things related to the IPython documentation |
|
104 | # Things related to the IPython documentation | |
100 | #------------------------------------------------------------------------------- |
|
105 | #------------------------------------------------------------------------------- | |
101 |
|
106 | |||
102 | # update the manuals when building a source dist |
|
107 | # update the manuals when building a source dist | |
103 | if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'): |
|
108 | if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'): | |
104 | import textwrap |
|
109 | import textwrap | |
105 |
|
110 | |||
106 | # List of things to be updated. Each entry is a triplet of args for |
|
111 | # List of things to be updated. Each entry is a triplet of args for | |
107 | # target_update() |
|
112 | # target_update() | |
108 | to_update = [ |
|
113 | to_update = [ | |
109 | # FIXME - Disabled for now: we need to redo an automatic way |
|
114 | # FIXME - Disabled for now: we need to redo an automatic way | |
110 | # of generating the magic info inside the rst. |
|
115 | # of generating the magic info inside the rst. | |
111 | #('docs/magic.tex', |
|
116 | #('docs/magic.tex', | |
112 | #['IPython/Magic.py'], |
|
117 | #['IPython/Magic.py'], | |
113 | #"cd doc && ./update_magic.sh" ), |
|
118 | #"cd doc && ./update_magic.sh" ), | |
114 |
|
119 | |||
115 | ('docs/man/ipcluster.1.gz', |
|
120 | ('docs/man/ipcluster.1.gz', | |
116 | ['docs/man/ipcluster.1'], |
|
121 | ['docs/man/ipcluster.1'], | |
117 | 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'), |
|
122 | 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'), | |
118 |
|
123 | |||
119 | ('docs/man/ipcontroller.1.gz', |
|
124 | ('docs/man/ipcontroller.1.gz', | |
120 | ['docs/man/ipcontroller.1'], |
|
125 | ['docs/man/ipcontroller.1'], | |
121 | 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'), |
|
126 | 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'), | |
122 |
|
127 | |||
123 | ('docs/man/ipengine.1.gz', |
|
128 | ('docs/man/ipengine.1.gz', | |
124 | ['docs/man/ipengine.1'], |
|
129 | ['docs/man/ipengine.1'], | |
125 | 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'), |
|
130 | 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'), | |
126 |
|
131 | |||
127 | ('docs/man/ipython.1.gz', |
|
132 | ('docs/man/ipython.1.gz', | |
128 | ['docs/man/ipython.1'], |
|
133 | ['docs/man/ipython.1'], | |
129 | 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'), |
|
134 | 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'), | |
130 |
|
135 | |||
131 | ('docs/man/ipython-wx.1.gz', |
|
136 | ('docs/man/ipython-wx.1.gz', | |
132 | ['docs/man/ipython-wx.1'], |
|
137 | ['docs/man/ipython-wx.1'], | |
133 | 'cd docs/man && gzip -9c ipython-wx.1 > ipython-wx.1.gz'), |
|
138 | 'cd docs/man && gzip -9c ipython-wx.1 > ipython-wx.1.gz'), | |
134 |
|
139 | |||
135 | ('docs/man/ipythonx.1.gz', |
|
140 | ('docs/man/ipythonx.1.gz', | |
136 | ['docs/man/ipythonx.1'], |
|
141 | ['docs/man/ipythonx.1'], | |
137 | 'cd docs/man && gzip -9c ipythonx.1 > ipythonx.1.gz'), |
|
142 | 'cd docs/man && gzip -9c ipythonx.1 > ipythonx.1.gz'), | |
138 |
|
143 | |||
139 | ('docs/man/irunner.1.gz', |
|
144 | ('docs/man/irunner.1.gz', | |
140 | ['docs/man/irunner.1'], |
|
145 | ['docs/man/irunner.1'], | |
141 | 'cd docs/man && gzip -9c irunner.1 > irunner.1.gz'), |
|
146 | 'cd docs/man && gzip -9c irunner.1 > irunner.1.gz'), | |
142 |
|
147 | |||
143 | ('docs/man/pycolor.1.gz', |
|
148 | ('docs/man/pycolor.1.gz', | |
144 | ['docs/man/pycolor.1'], |
|
149 | ['docs/man/pycolor.1'], | |
145 | 'cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz'), |
|
150 | 'cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz'), | |
146 | ] |
|
151 | ] | |
147 |
|
152 | |||
148 | # Only build the docs if sphinx is present |
|
153 | # Only build the docs if sphinx is present | |
149 | try: |
|
154 | try: | |
150 | import sphinx |
|
155 | import sphinx | |
151 | except ImportError: |
|
156 | except ImportError: | |
152 | pass |
|
157 | pass | |
153 | else: |
|
158 | else: | |
154 | # The Makefile calls the do_sphinx scripts to build html and pdf, so |
|
159 | # The Makefile calls the do_sphinx scripts to build html and pdf, so | |
155 | # just one target is enough to cover all manual generation |
|
160 | # just one target is enough to cover all manual generation | |
156 |
|
161 | |||
157 | # First, compute all the dependencies that can force us to rebuild the |
|
162 | # First, compute all the dependencies that can force us to rebuild the | |
158 | # docs. Start with the main release file that contains metadata |
|
163 | # docs. Start with the main release file that contains metadata | |
159 | docdeps = ['IPython/core/release.py'] |
|
164 | docdeps = ['IPython/core/release.py'] | |
160 | # Inculde all the reST sources |
|
165 | # Inculde all the reST sources | |
161 | pjoin = os.path.join |
|
166 | pjoin = os.path.join | |
162 | for dirpath,dirnames,filenames in os.walk('docs/source'): |
|
167 | for dirpath,dirnames,filenames in os.walk('docs/source'): | |
163 | if dirpath in ['_static','_templates']: |
|
168 | if dirpath in ['_static','_templates']: | |
164 | continue |
|
169 | continue | |
165 | docdeps += [ pjoin(dirpath,f) for f in filenames |
|
170 | docdeps += [ pjoin(dirpath,f) for f in filenames | |
166 | if f.endswith('.txt') ] |
|
171 | if f.endswith('.txt') ] | |
167 | # and the examples |
|
172 | # and the examples | |
168 | for dirpath,dirnames,filenames in os.walk('docs/example'): |
|
173 | for dirpath,dirnames,filenames in os.walk('docs/example'): | |
169 | docdeps += [ pjoin(dirpath,f) for f in filenames |
|
174 | docdeps += [ pjoin(dirpath,f) for f in filenames | |
170 | if not f.endswith('~') ] |
|
175 | if not f.endswith('~') ] | |
171 | # then, make them all dependencies for the main PDF (the html will get |
|
176 | # then, make them all dependencies for the main PDF (the html will get | |
172 | # auto-generated as well). |
|
177 | # auto-generated as well). | |
173 | to_update.append( |
|
178 | to_update.append( | |
174 | ('docs/dist/ipython.pdf', |
|
179 | ('docs/dist/ipython.pdf', | |
175 | docdeps, |
|
180 | docdeps, | |
176 | "cd docs && make dist") |
|
181 | "cd docs && make dist") | |
177 | ) |
|
182 | ) | |
178 |
|
183 | |||
179 | [ target_update(*t) for t in to_update ] |
|
184 | [ target_update(*t) for t in to_update ] | |
180 |
|
185 | |||
181 | #--------------------------------------------------------------------------- |
|
186 | #--------------------------------------------------------------------------- | |
182 | # Find all the packages, package data, scripts and data_files |
|
187 | # Find all the packages, package data, scripts and data_files | |
183 | #--------------------------------------------------------------------------- |
|
188 | #--------------------------------------------------------------------------- | |
184 |
|
189 | |||
185 | packages = find_packages() |
|
190 | packages = find_packages() | |
186 | package_data = find_package_data() |
|
191 | package_data = find_package_data() | |
187 | scripts = find_scripts() |
|
192 | scripts = find_scripts() | |
188 | data_files = find_data_files() |
|
193 | data_files = find_data_files() | |
189 |
|
194 | |||
190 | #--------------------------------------------------------------------------- |
|
195 | #--------------------------------------------------------------------------- | |
191 | # Handle dependencies and setuptools specific things |
|
196 | # Handle dependencies and setuptools specific things | |
192 | #--------------------------------------------------------------------------- |
|
197 | #--------------------------------------------------------------------------- | |
193 |
|
198 | |||
194 | # For some commands, use setuptools. Note that we do NOT list install here! |
|
199 | # For some commands, use setuptools. Note that we do NOT list install here! | |
195 | # If you want a setuptools-enhanced install, just run 'setupegg.py install' |
|
200 | # If you want a setuptools-enhanced install, just run 'setupegg.py install' | |
196 | if len(set(('develop', 'sdist', 'release', 'bdist_egg', 'bdist_rpm', |
|
201 | if len(set(('develop', 'sdist', 'release', 'bdist_egg', 'bdist_rpm', | |
197 | 'bdist', 'bdist_dumb', 'bdist_wininst', 'install_egg_info', |
|
202 | 'bdist', 'bdist_dumb', 'bdist_wininst', 'install_egg_info', | |
198 | 'build_sphinx', 'egg_info', 'easy_install', 'upload', |
|
203 | 'build_sphinx', 'egg_info', 'easy_install', 'upload', | |
199 | )).intersection(sys.argv)) > 0: |
|
204 | )).intersection(sys.argv)) > 0: | |
200 | import setuptools |
|
205 | import setuptools | |
201 |
|
206 | |||
202 | # This dict is used for passing extra arguments that are setuptools |
|
207 | # This dict is used for passing extra arguments that are setuptools | |
203 | # specific to setup |
|
208 | # specific to setup | |
204 | setuptools_extra_args = {} |
|
209 | setuptools_extra_args = {} | |
205 |
|
210 | |||
206 | if 'setuptools' in sys.modules: |
|
211 | if 'setuptools' in sys.modules: | |
207 | setuptools_extra_args['zip_safe'] = False |
|
212 | setuptools_extra_args['zip_safe'] = False | |
208 | setuptools_extra_args['entry_points'] = { |
|
213 | setuptools_extra_args['entry_points'] = { | |
209 | 'console_scripts': [ |
|
214 | 'console_scripts': [ | |
210 | 'ipython = IPython.frontend.terminal.ipapp:launch_new_instance', |
|
215 | 'ipython = IPython.frontend.terminal.ipapp:launch_new_instance', | |
211 | 'ipython-qtconsole = IPython.frontend.qt.console.ipythonqt:main', |
|
216 | 'ipython-qtconsole = IPython.frontend.qt.console.ipythonqt:main', | |
212 | 'pycolor = IPython.utils.PyColorize:main', |
|
217 | 'pycolor = IPython.utils.PyColorize:main', | |
213 | 'ipcontroller = IPython.kernel.ipcontrollerapp:launch_new_instance', |
|
218 | 'ipcontroller = IPython.kernel.ipcontrollerapp:launch_new_instance', | |
214 | 'ipengine = IPython.kernel.ipengineapp:launch_new_instance', |
|
219 | 'ipengine = IPython.kernel.ipengineapp:launch_new_instance', | |
215 | 'ipcluster = IPython.kernel.ipclusterapp:launch_new_instance', |
|
220 | 'ipcluster = IPython.kernel.ipclusterapp:launch_new_instance', | |
216 | 'iptest = IPython.testing.iptest:main', |
|
221 | 'iptest = IPython.testing.iptest:main', | |
217 | 'irunner = IPython.lib.irunner:main' |
|
222 | 'irunner = IPython.lib.irunner:main' | |
218 | ] |
|
223 | ] | |
219 | } |
|
224 | } | |
220 | setup_args['extras_require'] = dict( |
|
225 | setup_args['extras_require'] = dict( | |
221 | kernel = [ |
|
226 | kernel = [ | |
222 | 'zope.interface>=3.4.1', |
|
227 | 'zope.interface>=3.4.1', | |
223 | 'Twisted>=8.0.1', |
|
228 | 'Twisted>=8.0.1', | |
224 | 'foolscap>=0.2.6' |
|
229 | 'foolscap>=0.2.6' | |
225 | ], |
|
230 | ], | |
226 | doc='Sphinx>=0.3', |
|
231 | doc='Sphinx>=0.3', | |
227 | test='nose>=0.10.1', |
|
232 | test='nose>=0.10.1', | |
228 | security='pyOpenSSL>=0.6' |
|
233 | security='pyOpenSSL>=0.6' | |
229 | ) |
|
234 | ) | |
230 | # Allow setuptools to handle the scripts |
|
235 | # Allow setuptools to handle the scripts | |
231 | scripts = [] |
|
236 | scripts = [] | |
232 | else: |
|
237 | else: | |
233 | # If we are running without setuptools, call this function which will |
|
238 | # If we are running without setuptools, call this function which will | |
234 | # check for dependencies an inform the user what is needed. This is |
|
239 | # check for dependencies an inform the user what is needed. This is | |
235 | # just to make life easy for users. |
|
240 | # just to make life easy for users. | |
236 | check_for_dependencies() |
|
241 | check_for_dependencies() | |
237 |
|
242 | |||
238 | #--------------------------------------------------------------------------- |
|
243 | #--------------------------------------------------------------------------- | |
239 | # Do the actual setup now |
|
244 | # Do the actual setup now | |
240 | #--------------------------------------------------------------------------- |
|
245 | #--------------------------------------------------------------------------- | |
241 |
|
246 | |||
|
247 | setup_args['cmdclass'] = {'build_py': record_commit_info('IPython')} | |||
242 | setup_args['packages'] = packages |
|
248 | setup_args['packages'] = packages | |
243 | setup_args['package_data'] = package_data |
|
249 | setup_args['package_data'] = package_data | |
244 | setup_args['scripts'] = scripts |
|
250 | setup_args['scripts'] = scripts | |
245 | setup_args['data_files'] = data_files |
|
251 | setup_args['data_files'] = data_files | |
246 | setup_args.update(setuptools_extra_args) |
|
252 | setup_args.update(setuptools_extra_args) | |
247 |
|
253 | |||
248 |
|
254 | |||
249 | if __name__ == '__main__': |
|
255 | if __name__ == '__main__': | |
250 | setup(**setup_args) |
|
256 | setup(**setup_args) | |
251 | cleanup() |
|
257 | cleanup() |
@@ -1,314 +1,375 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 |
|
||||
3 | """ |
|
2 | """ | |
4 | This module defines the things that are used in setup.py for building IPython |
|
3 | This module defines the things that are used in setup.py for building IPython | |
5 |
|
4 | |||
6 | This includes: |
|
5 | This includes: | |
7 |
|
6 | |||
8 | * The basic arguments to setup |
|
7 | * The basic arguments to setup | |
9 | * Functions for finding things like packages, package data, etc. |
|
8 | * Functions for finding things like packages, package data, etc. | |
10 | * A function for checking dependencies. |
|
9 | * A function for checking dependencies. | |
11 | """ |
|
10 | """ | |
12 |
|
11 | from __future__ import print_function | ||
13 | __docformat__ = "restructuredtext en" |
|
|||
14 |
|
12 | |||
15 | #------------------------------------------------------------------------------- |
|
13 | #------------------------------------------------------------------------------- | |
16 | # Copyright (C) 2008 The IPython Development Team |
|
14 | # Copyright (C) 2008 The IPython Development Team | |
17 | # |
|
15 | # | |
18 | # Distributed under the terms of the BSD License. The full license is in |
|
16 | # Distributed under the terms of the BSD License. The full license is in | |
19 | # the file COPYING, distributed as part of this software. |
|
17 | # the file COPYING, distributed as part of this software. | |
20 | #------------------------------------------------------------------------------- |
|
18 | #------------------------------------------------------------------------------- | |
21 |
|
19 | |||
22 | #------------------------------------------------------------------------------- |
|
20 | #------------------------------------------------------------------------------- | |
23 | # Imports |
|
21 | # Imports | |
24 | #------------------------------------------------------------------------------- |
|
22 | #------------------------------------------------------------------------------- | |
|
23 | import os | |||
|
24 | import sys | |||
25 |
|
25 | |||
26 | import os, sys |
|
26 | from ConfigParser import ConfigParser | |
27 |
|
27 | from distutils.command.build_py import build_py | ||
28 | from glob import glob |
|
28 | from glob import glob | |
29 |
|
29 | |||
30 | from setupext import install_data_ext |
|
30 | from setupext import install_data_ext | |
31 |
|
31 | |||
32 | #------------------------------------------------------------------------------- |
|
32 | #------------------------------------------------------------------------------- | |
33 | # Useful globals and utility functions |
|
33 | # Useful globals and utility functions | |
34 | #------------------------------------------------------------------------------- |
|
34 | #------------------------------------------------------------------------------- | |
35 |
|
35 | |||
36 | # A few handy globals |
|
36 | # A few handy globals | |
37 | isfile = os.path.isfile |
|
37 | isfile = os.path.isfile | |
38 | pjoin = os.path.join |
|
38 | pjoin = os.path.join | |
39 |
|
39 | |||
40 | def oscmd(s): |
|
40 | def oscmd(s): | |
41 |
print |
|
41 | print(">", s) | |
42 | os.system(s) |
|
42 | os.system(s) | |
43 |
|
43 | |||
44 | # A little utility we'll need below, since glob() does NOT allow you to do |
|
44 | # A little utility we'll need below, since glob() does NOT allow you to do | |
45 | # exclusion on multiple endings! |
|
45 | # exclusion on multiple endings! | |
46 | def file_doesnt_endwith(test,endings): |
|
46 | def file_doesnt_endwith(test,endings): | |
47 | """Return true if test is a file and its name does NOT end with any |
|
47 | """Return true if test is a file and its name does NOT end with any | |
48 | of the strings listed in endings.""" |
|
48 | of the strings listed in endings.""" | |
49 | if not isfile(test): |
|
49 | if not isfile(test): | |
50 | return False |
|
50 | return False | |
51 | for e in endings: |
|
51 | for e in endings: | |
52 | if test.endswith(e): |
|
52 | if test.endswith(e): | |
53 | return False |
|
53 | return False | |
54 | return True |
|
54 | return True | |
55 |
|
55 | |||
56 | #--------------------------------------------------------------------------- |
|
56 | #--------------------------------------------------------------------------- | |
57 | # Basic project information |
|
57 | # Basic project information | |
58 | #--------------------------------------------------------------------------- |
|
58 | #--------------------------------------------------------------------------- | |
59 |
|
59 | |||
60 | # release.py contains version, authors, license, url, keywords, etc. |
|
60 | # release.py contains version, authors, license, url, keywords, etc. | |
61 | execfile(pjoin('IPython','core','release.py')) |
|
61 | execfile(pjoin('IPython','core','release.py')) | |
62 |
|
62 | |||
63 | # Create a dict with the basic information |
|
63 | # Create a dict with the basic information | |
64 | # This dict is eventually passed to setup after additional keys are added. |
|
64 | # This dict is eventually passed to setup after additional keys are added. | |
65 | setup_args = dict( |
|
65 | setup_args = dict( | |
66 | name = name, |
|
66 | name = name, | |
67 | version = version, |
|
67 | version = version, | |
68 | description = description, |
|
68 | description = description, | |
69 | long_description = long_description, |
|
69 | long_description = long_description, | |
70 | author = author, |
|
70 | author = author, | |
71 | author_email = author_email, |
|
71 | author_email = author_email, | |
72 | url = url, |
|
72 | url = url, | |
73 | download_url = download_url, |
|
73 | download_url = download_url, | |
74 | license = license, |
|
74 | license = license, | |
75 | platforms = platforms, |
|
75 | platforms = platforms, | |
76 | keywords = keywords, |
|
76 | keywords = keywords, | |
77 | cmdclass = {'install_data': install_data_ext}, |
|
77 | cmdclass = {'install_data': install_data_ext}, | |
78 | ) |
|
78 | ) | |
79 |
|
79 | |||
80 |
|
80 | |||
81 | #--------------------------------------------------------------------------- |
|
81 | #--------------------------------------------------------------------------- | |
82 | # Find packages |
|
82 | # Find packages | |
83 | #--------------------------------------------------------------------------- |
|
83 | #--------------------------------------------------------------------------- | |
84 |
|
84 | |||
85 | def add_package(packages,pname,config=False,tests=False,scripts=False, |
|
85 | def add_package(packages,pname,config=False,tests=False,scripts=False, | |
86 | others=None): |
|
86 | others=None): | |
87 | """ |
|
87 | """ | |
88 | Add a package to the list of packages, including certain subpackages. |
|
88 | Add a package to the list of packages, including certain subpackages. | |
89 | """ |
|
89 | """ | |
90 | packages.append('.'.join(['IPython',pname])) |
|
90 | packages.append('.'.join(['IPython',pname])) | |
91 | if config: |
|
91 | if config: | |
92 | packages.append('.'.join(['IPython',pname,'config'])) |
|
92 | packages.append('.'.join(['IPython',pname,'config'])) | |
93 | if tests: |
|
93 | if tests: | |
94 | packages.append('.'.join(['IPython',pname,'tests'])) |
|
94 | packages.append('.'.join(['IPython',pname,'tests'])) | |
95 | if scripts: |
|
95 | if scripts: | |
96 | packages.append('.'.join(['IPython',pname,'scripts'])) |
|
96 | packages.append('.'.join(['IPython',pname,'scripts'])) | |
97 | if others is not None: |
|
97 | if others is not None: | |
98 | for o in others: |
|
98 | for o in others: | |
99 | packages.append('.'.join(['IPython',pname,o])) |
|
99 | packages.append('.'.join(['IPython',pname,o])) | |
100 |
|
100 | |||
101 | def find_packages(): |
|
101 | def find_packages(): | |
102 | """ |
|
102 | """ | |
103 | Find all of IPython's packages. |
|
103 | Find all of IPython's packages. | |
104 | """ |
|
104 | """ | |
105 | packages = ['IPython'] |
|
105 | packages = ['IPython'] | |
106 | add_package(packages, 'config', tests=True, others=['default','profile']) |
|
106 | add_package(packages, 'config', tests=True, others=['default','profile']) | |
107 | add_package(packages, 'core', tests=True) |
|
107 | add_package(packages, 'core', tests=True) | |
108 | add_package(packages, 'deathrow', tests=True) |
|
108 | add_package(packages, 'deathrow', tests=True) | |
109 | add_package(packages, 'extensions') |
|
109 | add_package(packages, 'extensions') | |
110 | add_package(packages, 'external') |
|
110 | add_package(packages, 'external') | |
111 | add_package(packages, 'frontend') |
|
111 | add_package(packages, 'frontend') | |
112 | add_package(packages, 'frontend.qt') |
|
112 | add_package(packages, 'frontend.qt') | |
113 | add_package(packages, 'frontend.qt.console', tests=True) |
|
113 | add_package(packages, 'frontend.qt.console', tests=True) | |
114 | add_package(packages, 'frontend.terminal', tests=True) |
|
114 | add_package(packages, 'frontend.terminal', tests=True) | |
115 | add_package(packages, 'kernel', config=False, tests=True, scripts=True) |
|
115 | add_package(packages, 'kernel', config=False, tests=True, scripts=True) | |
116 | add_package(packages, 'kernel.core', config=False, tests=True) |
|
116 | add_package(packages, 'kernel.core', config=False, tests=True) | |
117 | add_package(packages, 'lib', tests=True) |
|
117 | add_package(packages, 'lib', tests=True) | |
118 | add_package(packages, 'quarantine', tests=True) |
|
118 | add_package(packages, 'quarantine', tests=True) | |
119 | add_package(packages, 'scripts') |
|
119 | add_package(packages, 'scripts') | |
120 | add_package(packages, 'testing', tests=True) |
|
120 | add_package(packages, 'testing', tests=True) | |
121 | add_package(packages, 'testing.plugin', tests=False) |
|
121 | add_package(packages, 'testing.plugin', tests=False) | |
122 | add_package(packages, 'utils', tests=True) |
|
122 | add_package(packages, 'utils', tests=True) | |
123 | add_package(packages, 'zmq') |
|
123 | add_package(packages, 'zmq') | |
124 | add_package(packages, 'zmq.pylab') |
|
124 | add_package(packages, 'zmq.pylab') | |
125 | return packages |
|
125 | return packages | |
126 |
|
126 | |||
127 | #--------------------------------------------------------------------------- |
|
127 | #--------------------------------------------------------------------------- | |
128 | # Find package data |
|
128 | # Find package data | |
129 | #--------------------------------------------------------------------------- |
|
129 | #--------------------------------------------------------------------------- | |
130 |
|
130 | |||
131 | def find_package_data(): |
|
131 | def find_package_data(): | |
132 | """ |
|
132 | """ | |
133 | Find IPython's package_data. |
|
133 | Find IPython's package_data. | |
134 | """ |
|
134 | """ | |
135 | # This is not enough for these things to appear in an sdist. |
|
135 | # This is not enough for these things to appear in an sdist. | |
136 | # We need to muck with the MANIFEST to get this to work |
|
136 | # We need to muck with the MANIFEST to get this to work | |
137 | package_data = { |
|
137 | package_data = { | |
138 | 'IPython.config.userconfig' : ['*'], |
|
138 | 'IPython.config.userconfig' : ['*'], | |
139 | 'IPython.testing' : ['*.txt'] |
|
139 | 'IPython.testing' : ['*.txt'] | |
140 | } |
|
140 | } | |
141 | return package_data |
|
141 | return package_data | |
142 |
|
142 | |||
143 |
|
143 | |||
144 | #--------------------------------------------------------------------------- |
|
144 | #--------------------------------------------------------------------------- | |
145 | # Find data files |
|
145 | # Find data files | |
146 | #--------------------------------------------------------------------------- |
|
146 | #--------------------------------------------------------------------------- | |
147 |
|
147 | |||
148 | def make_dir_struct(tag,base,out_base): |
|
148 | def make_dir_struct(tag,base,out_base): | |
149 | """Make the directory structure of all files below a starting dir. |
|
149 | """Make the directory structure of all files below a starting dir. | |
150 |
|
150 | |||
151 | This is just a convenience routine to help build a nested directory |
|
151 | This is just a convenience routine to help build a nested directory | |
152 | hierarchy because distutils is too stupid to do this by itself. |
|
152 | hierarchy because distutils is too stupid to do this by itself. | |
153 |
|
153 | |||
154 | XXX - this needs a proper docstring! |
|
154 | XXX - this needs a proper docstring! | |
155 | """ |
|
155 | """ | |
156 |
|
156 | |||
157 | # we'll use these a lot below |
|
157 | # we'll use these a lot below | |
158 | lbase = len(base) |
|
158 | lbase = len(base) | |
159 | pathsep = os.path.sep |
|
159 | pathsep = os.path.sep | |
160 | lpathsep = len(pathsep) |
|
160 | lpathsep = len(pathsep) | |
161 |
|
161 | |||
162 | out = [] |
|
162 | out = [] | |
163 | for (dirpath,dirnames,filenames) in os.walk(base): |
|
163 | for (dirpath,dirnames,filenames) in os.walk(base): | |
164 | # we need to strip out the dirpath from the base to map it to the |
|
164 | # we need to strip out the dirpath from the base to map it to the | |
165 | # output (installation) path. This requires possibly stripping the |
|
165 | # output (installation) path. This requires possibly stripping the | |
166 | # path separator, because otherwise pjoin will not work correctly |
|
166 | # path separator, because otherwise pjoin will not work correctly | |
167 | # (pjoin('foo/','/bar') returns '/bar'). |
|
167 | # (pjoin('foo/','/bar') returns '/bar'). | |
168 |
|
168 | |||
169 | dp_eff = dirpath[lbase:] |
|
169 | dp_eff = dirpath[lbase:] | |
170 | if dp_eff.startswith(pathsep): |
|
170 | if dp_eff.startswith(pathsep): | |
171 | dp_eff = dp_eff[lpathsep:] |
|
171 | dp_eff = dp_eff[lpathsep:] | |
172 | # The output path must be anchored at the out_base marker |
|
172 | # The output path must be anchored at the out_base marker | |
173 | out_path = pjoin(out_base,dp_eff) |
|
173 | out_path = pjoin(out_base,dp_eff) | |
174 | # Now we can generate the final filenames. Since os.walk only produces |
|
174 | # Now we can generate the final filenames. Since os.walk only produces | |
175 | # filenames, we must join back with the dirpath to get full valid file |
|
175 | # filenames, we must join back with the dirpath to get full valid file | |
176 | # paths: |
|
176 | # paths: | |
177 | pfiles = [pjoin(dirpath,f) for f in filenames] |
|
177 | pfiles = [pjoin(dirpath,f) for f in filenames] | |
178 |
# Finally, generate the entry we need, which is a |
|
178 | # Finally, generate the entry we need, which is a pari of (output | |
179 | # path, files) for use as a data_files parameter in install_data. |
|
179 | # path, files) for use as a data_files parameter in install_data. | |
180 |
out.append(( |
|
180 | out.append((out_path, pfiles)) | |
181 |
|
181 | |||
182 | return out |
|
182 | return out | |
183 |
|
183 | |||
184 |
|
184 | |||
185 | def find_data_files(): |
|
185 | def find_data_files(): | |
186 | """ |
|
186 | """ | |
187 | Find IPython's data_files. |
|
187 | Find IPython's data_files. | |
188 |
|
188 | |||
189 | Most of these are docs. |
|
189 | Most of these are docs. | |
190 | """ |
|
190 | """ | |
191 |
|
191 | |||
192 | docdirbase = pjoin('share', 'doc', 'ipython') |
|
192 | docdirbase = pjoin('share', 'doc', 'ipython') | |
193 | manpagebase = pjoin('share', 'man', 'man1') |
|
193 | manpagebase = pjoin('share', 'man', 'man1') | |
194 |
|
194 | |||
195 | # Simple file lists can be made by hand |
|
195 | # Simple file lists can be made by hand | |
196 | manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz'))) |
|
196 | manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz'))) | |
197 | igridhelpfiles = filter(isfile, glob(pjoin('IPython','extensions','igrid_help.*'))) |
|
197 | igridhelpfiles = filter(isfile, | |
|
198 | glob(pjoin('IPython','extensions','igrid_help.*'))) | |||
198 |
|
199 | |||
199 | # For nested structures, use the utility above |
|
200 | # For nested structures, use the utility above | |
200 | example_files = make_dir_struct( |
|
201 | example_files = make_dir_struct( | |
201 | 'data', |
|
202 | 'data', | |
202 | pjoin('docs','examples'), |
|
203 | pjoin('docs','examples'), | |
203 | pjoin(docdirbase,'examples') |
|
204 | pjoin(docdirbase,'examples') | |
204 | ) |
|
205 | ) | |
205 | manual_files = make_dir_struct( |
|
206 | manual_files = make_dir_struct( | |
206 | 'data', |
|
207 | 'data', | |
207 | pjoin('docs','dist'), |
|
208 | pjoin('docs','dist'), | |
208 | pjoin(docdirbase,'manual') |
|
209 | pjoin(docdirbase,'manual') | |
209 | ) |
|
210 | ) | |
210 |
|
211 | |||
211 | # And assemble the entire output list |
|
212 | # And assemble the entire output list | |
212 |
data_files = [ ( |
|
213 | data_files = [ (manpagebase, manpages), | |
213 |
( |
|
214 | (pjoin(docdirbase, 'extensions'), igridhelpfiles), | |
214 | ] + manual_files + example_files |
|
215 | ] + manual_files + example_files | |
215 |
|
216 | |||
216 | ## import pprint # dbg |
|
217 | ## import pprint # dbg | |
217 |
## print |
|
218 | ## print('*'*80) | |
218 |
## print |
|
219 | ## print('data files') | |
219 | ## pprint.pprint(data_files) |
|
220 | ## pprint.pprint(data_files) | |
220 |
## print |
|
221 | ## print('*'*80) | |
221 |
|
222 | |||
222 | return data_files |
|
223 | return data_files | |
223 |
|
224 | |||
224 |
|
225 | |||
225 | def make_man_update_target(manpage): |
|
226 | def make_man_update_target(manpage): | |
226 | """Return a target_update-compliant tuple for the given manpage. |
|
227 | """Return a target_update-compliant tuple for the given manpage. | |
227 |
|
228 | |||
228 | Parameters |
|
229 | Parameters | |
229 | ---------- |
|
230 | ---------- | |
230 | manpage : string |
|
231 | manpage : string | |
231 | Name of the manpage, must include the section number (trailing number). |
|
232 | Name of the manpage, must include the section number (trailing number). | |
232 |
|
233 | |||
233 | Example |
|
234 | Example | |
234 | ------- |
|
235 | ------- | |
235 |
|
236 | |||
236 | >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE |
|
237 | >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE | |
237 | ('docs/man/ipython.1.gz', |
|
238 | ('docs/man/ipython.1.gz', | |
238 | ['docs/man/ipython.1'], |
|
239 | ['docs/man/ipython.1'], | |
239 | 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz') |
|
240 | 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz') | |
240 | """ |
|
241 | """ | |
241 | man_dir = pjoin('docs', 'man') |
|
242 | man_dir = pjoin('docs', 'man') | |
242 | manpage_gz = manpage + '.gz' |
|
243 | manpage_gz = manpage + '.gz' | |
243 | manpath = pjoin(man_dir, manpage) |
|
244 | manpath = pjoin(man_dir, manpage) | |
244 | manpath_gz = pjoin(man_dir, manpage_gz) |
|
245 | manpath_gz = pjoin(man_dir, manpage_gz) | |
245 | gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" % |
|
246 | gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" % | |
246 | locals() ) |
|
247 | locals() ) | |
247 | return (manpath_gz, [manpath], gz_cmd) |
|
248 | return (manpath_gz, [manpath], gz_cmd) | |
248 |
|
249 | |||
249 | #--------------------------------------------------------------------------- |
|
250 | #--------------------------------------------------------------------------- | |
250 | # Find scripts |
|
251 | # Find scripts | |
251 | #--------------------------------------------------------------------------- |
|
252 | #--------------------------------------------------------------------------- | |
252 |
|
253 | |||
253 | def find_scripts(): |
|
254 | def find_scripts(): | |
254 | """ |
|
255 | """ | |
255 | Find IPython's scripts. |
|
256 | Find IPython's scripts. | |
256 | """ |
|
257 | """ | |
257 | kernel_scripts = pjoin('IPython','kernel','scripts') |
|
258 | kernel_scripts = pjoin('IPython','kernel','scripts') | |
258 | main_scripts = pjoin('IPython','scripts') |
|
259 | main_scripts = pjoin('IPython','scripts') | |
259 | scripts = [pjoin(kernel_scripts, 'ipengine'), |
|
260 | scripts = [pjoin(kernel_scripts, 'ipengine'), | |
260 | pjoin(kernel_scripts, 'ipcontroller'), |
|
261 | pjoin(kernel_scripts, 'ipcontroller'), | |
261 | pjoin(kernel_scripts, 'ipcluster'), |
|
262 | pjoin(kernel_scripts, 'ipcluster'), | |
262 | pjoin(main_scripts, 'ipython'), |
|
263 | pjoin(main_scripts, 'ipython'), | |
263 | pjoin(main_scripts, 'ipython-qtconsole'), |
|
264 | pjoin(main_scripts, 'ipython-qtconsole'), | |
264 | pjoin(main_scripts, 'pycolor'), |
|
265 | pjoin(main_scripts, 'pycolor'), | |
265 | pjoin(main_scripts, 'irunner'), |
|
266 | pjoin(main_scripts, 'irunner'), | |
266 | pjoin(main_scripts, 'iptest') |
|
267 | pjoin(main_scripts, 'iptest') | |
267 | ] |
|
268 | ] | |
268 |
|
269 | |||
269 | # Script to be run by the windows binary installer after the default setup |
|
270 | # Script to be run by the windows binary installer after the default setup | |
270 | # routine, to add shortcuts and similar windows-only things. Windows |
|
271 | # routine, to add shortcuts and similar windows-only things. Windows | |
271 | # post-install scripts MUST reside in the scripts/ dir, otherwise distutils |
|
272 | # post-install scripts MUST reside in the scripts/ dir, otherwise distutils | |
272 | # doesn't find them. |
|
273 | # doesn't find them. | |
273 | if 'bdist_wininst' in sys.argv: |
|
274 | if 'bdist_wininst' in sys.argv: | |
274 | if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv): |
|
275 | if len(sys.argv) > 2 and \ | |
275 | print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting." |
|
276 | ('sdist' in sys.argv or 'bdist_rpm' in sys.argv): | |
|
277 | print("ERROR: bdist_wininst must be run alone. Exiting.", | |||
|
278 | file=sys.stderr) | |||
276 | sys.exit(1) |
|
279 | sys.exit(1) | |
277 | scripts.append(pjoin('scripts','ipython_win_post_install.py')) |
|
280 | scripts.append(pjoin('scripts','ipython_win_post_install.py')) | |
278 |
|
281 | |||
279 | return scripts |
|
282 | return scripts | |
280 |
|
283 | |||
281 | #--------------------------------------------------------------------------- |
|
284 | #--------------------------------------------------------------------------- | |
282 | # Verify all dependencies |
|
285 | # Verify all dependencies | |
283 | #--------------------------------------------------------------------------- |
|
286 | #--------------------------------------------------------------------------- | |
284 |
|
287 | |||
285 | def check_for_dependencies(): |
|
288 | def check_for_dependencies(): | |
286 | """Check for IPython's dependencies. |
|
289 | """Check for IPython's dependencies. | |
287 |
|
290 | |||
288 | This function should NOT be called if running under setuptools! |
|
291 | This function should NOT be called if running under setuptools! | |
289 | """ |
|
292 | """ | |
290 | from setupext.setupext import ( |
|
293 | from setupext.setupext import ( | |
291 |
print_line, print_raw, print_status, |
|
294 | print_line, print_raw, print_status, | |
292 | check_for_zopeinterface, check_for_twisted, |
|
295 | check_for_zopeinterface, check_for_twisted, | |
293 | check_for_foolscap, check_for_pyopenssl, |
|
296 | check_for_foolscap, check_for_pyopenssl, | |
294 | check_for_sphinx, check_for_pygments, |
|
297 | check_for_sphinx, check_for_pygments, | |
295 | check_for_nose, check_for_pexpect |
|
298 | check_for_nose, check_for_pexpect | |
296 | ) |
|
299 | ) | |
297 | print_line() |
|
300 | print_line() | |
298 | print_raw("BUILDING IPYTHON") |
|
301 | print_raw("BUILDING IPYTHON") | |
299 | print_status('python', sys.version) |
|
302 | print_status('python', sys.version) | |
300 | print_status('platform', sys.platform) |
|
303 | print_status('platform', sys.platform) | |
301 | if sys.platform == 'win32': |
|
304 | if sys.platform == 'win32': | |
302 | print_status('Windows version', sys.getwindowsversion()) |
|
305 | print_status('Windows version', sys.getwindowsversion()) | |
303 |
|
306 | |||
304 | print_raw("") |
|
307 | print_raw("") | |
305 | print_raw("OPTIONAL DEPENDENCIES") |
|
308 | print_raw("OPTIONAL DEPENDENCIES") | |
306 |
|
309 | |||
307 | check_for_zopeinterface() |
|
310 | check_for_zopeinterface() | |
308 | check_for_twisted() |
|
311 | check_for_twisted() | |
309 | check_for_foolscap() |
|
312 | check_for_foolscap() | |
310 | check_for_pyopenssl() |
|
313 | check_for_pyopenssl() | |
311 | check_for_sphinx() |
|
314 | check_for_sphinx() | |
312 | check_for_pygments() |
|
315 | check_for_pygments() | |
313 | check_for_nose() |
|
316 | check_for_nose() | |
314 | check_for_pexpect() |
|
317 | check_for_pexpect() | |
|
318 | ||||
|
319 | ||||
|
320 | def record_commit_info(pkg_dir, build_cmd=build_py): | |||
|
321 | """ Return extended build command class for recording commit | |||
|
322 | ||||
|
323 | The extended command tries to run git to find the current commit, getting | |||
|
324 | the empty string if it fails. It then writes the commit hash into a file | |||
|
325 | in the `pkg_dir` path, named ``.git_commit_info.ini``. | |||
|
326 | ||||
|
327 | In due course this information can be used by the package after it is | |||
|
328 | installed, to tell you what commit it was installed from if known. | |||
|
329 | ||||
|
330 | To make use of this system, you need a package with a .git_commit_info.ini | |||
|
331 | file - e.g. ``myproject/.git_commit_info.ini`` - that might well look like | |||
|
332 | this:: | |||
|
333 | ||||
|
334 | # This is an ini file that may contain information about the code state | |||
|
335 | [commit hash] | |||
|
336 | # The line below may contain a valid hash if it has been substituted | |||
|
337 | # during 'git archive' | |||
|
338 | archive_subst_hash=$Format:%h$ | |||
|
339 | # This line may be modified by the install process | |||
|
340 | install_hash= | |||
|
341 | ||||
|
342 | The .git_commit_info file above is also designed to be used with git | |||
|
343 | substitution - so you probably also want a ``.gitattributes`` file in the | |||
|
344 | root directory of your working tree that contains something like this:: | |||
|
345 | ||||
|
346 | myproject/.git_commit_info.ini export-subst | |||
|
347 | ||||
|
348 | That will cause the ``.git_commit_info.ini`` file to get filled in by ``git | |||
|
349 | archive`` - useful in case someone makes such an archive - for example with | |||
|
350 | via the github 'download source' button. | |||
|
351 | ||||
|
352 | Although all the above will work as is, you might consider having something | |||
|
353 | like a ``get_info()`` function in your package to display the commit | |||
|
354 | information at the terminal. See the ``pkg_info.py`` module in the nipy | |||
|
355 | package for an example. | |||
|
356 | """ | |||
|
357 | class MyBuildPy(build_cmd): | |||
|
358 | ''' Subclass to write commit data into installation tree ''' | |||
|
359 | def run(self): | |||
|
360 | build_py.run(self) | |||
|
361 | import subprocess | |||
|
362 | proc = subprocess.Popen('git rev-parse --short HEAD', | |||
|
363 | stdout=subprocess.PIPE, | |||
|
364 | stderr=subprocess.PIPE, | |||
|
365 | shell=True) | |||
|
366 | repo_commit, _ = proc.communicate() | |||
|
367 | # We write the installation commit even if it's empty | |||
|
368 | cfg_parser = ConfigParser() | |||
|
369 | cfg_parser.read(pjoin(pkg_dir, '.git_commit_info.ini')) | |||
|
370 | cfg_parser.set('commit hash', 'install_hash', repo_commit) | |||
|
371 | out_pth = pjoin(self.build_lib, pkg_dir, '.git_commit_info.ini') | |||
|
372 | out_file = open(out_pth, 'wt') | |||
|
373 | cfg_parser.write(out_file) | |||
|
374 | out_file.close() | |||
|
375 | return MyBuildPy |
@@ -1,35 +1,35 | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | """IPython release build script. |
|
2 | """IPython release build script. | |
3 | """ |
|
3 | """ | |
4 | from toollib import * |
|
4 | from toollib import * | |
5 |
|
5 | |||
6 | # Get main ipython dir, this will raise if it doesn't pass some checks |
|
6 | # Get main ipython dir, this will raise if it doesn't pass some checks | |
7 | ipdir = get_ipdir() |
|
7 | ipdir = get_ipdir() | |
8 | cd(ipdir) |
|
8 | cd(ipdir) | |
9 |
|
9 | |||
10 | # Load release info |
|
10 | # Load release info | |
11 | execfile(pjoin('IPython','core','release.py')) |
|
11 | execfile(pjoin('IPython','core','release.py')) | |
12 |
|
12 | |||
13 | # Check that everything compiles |
|
13 | # Check that everything compiles | |
14 | compile_tree() |
|
14 | compile_tree() | |
15 |
|
15 | |||
16 | # Cleanup |
|
16 | # Cleanup | |
17 | for d in ['build','dist',pjoin('docs','build'),pjoin('docs','dist'), |
|
17 | for d in ['build','dist',pjoin('docs','build'),pjoin('docs','dist'), | |
18 | pjoin('docs','source','api','generated')]: |
|
18 | pjoin('docs','source','api','generated')]: | |
19 | if os.path.isdir(d): |
|
19 | if os.path.isdir(d): | |
20 | remove_tree(d) |
|
20 | remove_tree(d) | |
21 |
|
21 | |||
22 | # Build source and binary distros |
|
22 | # Build source and binary distros | |
23 |
|
|
23 | sh('./setup.py sdist --formats=gztar,zip') | |
24 |
|
24 | |||
25 | # Build eggs |
|
25 | # Build eggs | |
26 |
|
|
26 | sh('python2.6 ./setupegg.py bdist_egg') | |
27 |
|
27 | |||
28 | # Call the windows build separately, so that the extra Windows scripts don't |
|
28 | # Call the windows build separately, so that the extra Windows scripts don't | |
29 | # get pulled into Unix builds (setup.py has code which checks for |
|
29 | # get pulled into Unix builds (setup.py has code which checks for | |
30 | # bdist_wininst) |
|
30 | # bdist_wininst) | |
31 |
|
|
31 | sh("python setup.py bdist_wininst --install-script=ipython_win_post_install.py") | |
32 |
|
32 | |||
33 | # Change name so retarded Vista runs the installer correctly |
|
33 | # Change name so retarded Vista runs the installer correctly | |
34 |
|
|
34 | sh("rename 's/linux-i686/win32-setup/' dist/*.exe") | |
35 |
|
|
35 | sh("rename 's/linux-x86_64/win32-setup/' dist/*.exe") |
@@ -1,25 +1,25 | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | """Simple script to create a tarball with proper git info. |
|
2 | """Simple script to create a tarball with proper git info. | |
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 | import commands |
|
5 | import commands | |
6 | import os |
|
6 | import os | |
7 | import sys |
|
7 | import sys | |
8 | import shutil |
|
8 | import shutil | |
9 |
|
9 | |||
10 | from toollib import * |
|
10 | from toollib import * | |
11 |
|
11 | |||
12 | tag = commands.getoutput('git describe') |
|
12 | tag = commands.getoutput('git describe --tags') | |
13 | base_name = 'ipython-%s' % tag |
|
13 | base_name = 'ipython-%s' % tag | |
14 | tar_name = '%s.tgz' % base_name |
|
14 | tar_name = '%s.tgz' % base_name | |
15 |
|
15 | |||
16 | # git archive is weird: Even if I give it a specific path, it still won't |
|
16 | # git archive is weird: Even if I give it a specific path, it still won't | |
17 | # archive the whole tree. It seems the only way to get the whole tree is to cd |
|
17 | # archive the whole tree. It seems the only way to get the whole tree is to cd | |
18 | # to the top of the tree. There are long threads (since 2007) on the git list |
|
18 | # to the top of the tree. There are long threads (since 2007) on the git list | |
19 | # about this and it still doesn't work in a sensible way... |
|
19 | # about this and it still doesn't work in a sensible way... | |
20 |
|
20 | |||
21 | start_dir = os.getcwd() |
|
21 | start_dir = os.getcwd() | |
22 | cd('..') |
|
22 | cd('..') | |
23 | git_tpl = 'git archive --format=tar --prefix={0}/ HEAD | gzip > {1}' |
|
23 | git_tpl = 'git archive --format=tar --prefix={0}/ HEAD | gzip > {1}' | |
24 |
|
|
24 | sh(git_tpl.format(base_name, tar_name)) | |
25 |
|
|
25 | sh('mv {0} tools/'.format(tar_name)) |
@@ -1,55 +1,48 | |||||
1 | """Various utilities common to IPython release and maintenance tools. |
|
1 | """Various utilities common to IPython release and maintenance tools. | |
2 | """ |
|
2 | """ | |
3 | # Library imports |
|
3 | # Library imports | |
4 | import os |
|
4 | import os | |
5 | import sys |
|
5 | import sys | |
6 |
|
6 | |||
7 | from distutils.dir_util import remove_tree |
|
7 | from distutils.dir_util import remove_tree | |
8 |
|
8 | |||
9 | # Useful shorthands |
|
9 | # Useful shorthands | |
10 | pjoin = os.path.join |
|
10 | pjoin = os.path.join | |
11 | cd = os.chdir |
|
11 | cd = os.chdir | |
12 |
|
12 | |||
13 | # Utility functions |
|
13 | # Utility functions | |
14 |
def |
|
14 | def sh(cmd): | |
15 | """Run system command, raise SystemExit if it returns an error.""" |
|
15 | """Run system command in shell, raise SystemExit if it returns an error.""" | |
16 | print "$",cmd |
|
16 | print "$",cmd | |
17 | stat = os.system(cmd) |
|
17 | stat = os.system(cmd) | |
18 | #stat = 0 # Uncomment this and comment previous to run in debug mode |
|
18 | #stat = 0 # Uncomment this and comment previous to run in debug mode | |
19 | if stat: |
|
19 | if stat: | |
20 | raise SystemExit("Command %s failed with code: %s" % (cmd, stat)) |
|
20 | raise SystemExit("Command %s failed with code: %s" % (cmd, stat)) | |
21 |
|
21 | |||
|
22 | # Backwards compatibility | |||
|
23 | c = sh | |||
22 |
|
24 | |||
23 | def get_ipdir(): |
|
25 | def get_ipdir(): | |
24 | """Get IPython directory from command line, or assume it's the one above.""" |
|
26 | """Get IPython directory from command line, or assume it's the one above.""" | |
25 |
|
27 | |||
26 | # Initialize arguments and check location |
|
28 | # Initialize arguments and check location | |
27 | try: |
|
29 | try: | |
28 | ipdir = sys.argv[1] |
|
30 | ipdir = sys.argv[1] | |
29 | except IndexError: |
|
31 | except IndexError: | |
30 | ipdir = '..' |
|
32 | ipdir = '..' | |
31 |
|
33 | |||
32 | ipdir = os.path.abspath(ipdir) |
|
34 | ipdir = os.path.abspath(ipdir) | |
33 |
|
35 | |||
34 | cd(ipdir) |
|
36 | cd(ipdir) | |
35 | if not os.path.isdir('IPython') and os.path.isfile('setup.py'): |
|
37 | if not os.path.isdir('IPython') and os.path.isfile('setup.py'): | |
36 | raise SystemExit('Invalid ipython directory: %s' % ipdir) |
|
38 | raise SystemExit('Invalid ipython directory: %s' % ipdir) | |
37 | return ipdir |
|
39 | return ipdir | |
38 |
|
40 | |||
39 |
|
41 | |||
40 | def compile_tree(): |
|
42 | def compile_tree(): | |
41 | """Compile all Python files below current directory.""" |
|
43 | """Compile all Python files below current directory.""" | |
42 | vstr = '.'.join(map(str,sys.version_info[:2])) |
|
44 | stat = os.system('python -m compileall .') | |
43 | stat = os.system('python %s/lib/python%s/compileall.py .' % |
|
|||
44 | (sys.prefix,vstr)) |
|
|||
45 | if stat: |
|
45 | if stat: | |
46 | msg = '*** ERROR: Some Python files in tree do NOT compile! ***\n' |
|
46 | msg = '*** ERROR: Some Python files in tree do NOT compile! ***\n' | |
47 | msg += 'See messages above for the actual file that produced it.\n' |
|
47 | msg += 'See messages above for the actual file that produced it.\n' | |
48 | raise SystemExit(msg) |
|
48 | raise SystemExit(msg) | |
49 |
|
||||
50 |
|
||||
51 | def version_info(): |
|
|||
52 | """Return bzr version info as a dict.""" |
|
|||
53 | out = os.popen('bzr version-info') |
|
|||
54 | pairs = (l.split(':',1) for l in out) |
|
|||
55 | return dict(((k,v.strip()) for (k,v) in pairs)) |
|
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now