##// END OF EJS Templates
Update package metadata
Matthias Bussonnier -
Show More
@@ -1,125 +1,123 b''
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 # Copyright (c) 2008, IPython Development Team.
5 # Copyright (c) 2008, IPython Development Team.
6 # Copyright (c) 2001, Fernando Perez <fernando.perez@colorado.edu>
6 # Copyright (c) 2001, Fernando Perez <fernando.perez@colorado.edu>
7 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
7 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
8 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
8 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
9 #
9 #
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11 #
11 #
12 # The full license is in the file COPYING.txt, distributed with this software.
12 # The full license is in the file COPYING.txt, distributed with this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # 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
16 # 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.
17 name = 'ipython'
17 name = 'ipython'
18
18
19 # IPython version information. An empty _version_extra corresponds to a full
19 # IPython version information. An empty _version_extra corresponds to a full
20 # release. 'dev' as a _version_extra string means this is a development
20 # release. 'dev' as a _version_extra string means this is a development
21 # version
21 # version
22 _version_major = 4
22 _version_major = 4
23 _version_minor = 1
23 _version_minor = 1
24 _version_patch = 0
24 _version_patch = 0
25 _version_extra = '.dev'
25 _version_extra = '.dev'
26 #_version_extra = 'rc1'
26 #_version_extra = 'rc1'
27 # _version_extra = '' # Uncomment this for full releases
27 # _version_extra = '' # Uncomment this for full releases
28
28
29 # release.codename is deprecated in 2.0, will be removed in 3.0
29 # release.codename is deprecated in 2.0, will be removed in 3.0
30 codename = ''
30 codename = ''
31
31
32 # Construct full version string from these.
32 # Construct full version string from these.
33 _ver = [_version_major, _version_minor, _version_patch]
33 _ver = [_version_major, _version_minor, _version_patch]
34
34
35 __version__ = '.'.join(map(str, _ver))
35 __version__ = '.'.join(map(str, _ver))
36 if _version_extra:
36 if _version_extra:
37 __version__ = __version__ + _version_extra
37 __version__ = __version__ + _version_extra
38
38
39 version = __version__ # backwards compatibility name
39 version = __version__ # backwards compatibility name
40 version_info = (_version_major, _version_minor, _version_patch, _version_extra)
40 version_info = (_version_major, _version_minor, _version_patch, _version_extra)
41
41
42 # Change this when incrementing the kernel protocol version
42 # Change this when incrementing the kernel protocol version
43 kernel_protocol_version_info = (5, 0)
43 kernel_protocol_version_info = (5, 0)
44 kernel_protocol_version = "%i.%i" % kernel_protocol_version_info
44 kernel_protocol_version = "%i.%i" % kernel_protocol_version_info
45
45
46 description = "IPython: Productive Interactive Computing"
46 description = "IPython: Productive Interactive Computing"
47
47
48 long_description = \
48 long_description = \
49 """
49 """
50 IPython provides a rich toolkit to help you make the most out of using Python
50 IPython provides a rich toolkit to help you make the most out of using Python
51 interactively. Its main components are:
51 interactively. Its main components are:
52
52
53 * A powerful interactive Python shell
53 * A powerful interactive Python shell
54 * A `Jupyter <http://jupyter.org/>`_ kernel to work with Python code in Jupyter
54 * A `Jupyter <http://jupyter.org/>`_ kernel to work with Python code in Jupyter
55 notebooks and other interactive frontends.
55 notebooks and other interactive frontends.
56
56
57 The enhanced interactive Python shells have the following main features:
57 The enhanced interactive Python shells have the following main features:
58
58
59 * Comprehensive object introspection.
59 * Comprehensive object introspection.
60
60
61 * Input history, persistent across sessions.
61 * Input history, persistent across sessions.
62
62
63 * Caching of output results during a session with automatically generated
63 * Caching of output results during a session with automatically generated
64 references.
64 references.
65
65
66 * Extensible tab completion, with support by default for completion of python
66 * Extensible tab completion, with support by default for completion of python
67 variables and keywords, filenames and function keywords.
67 variables and keywords, filenames and function keywords.
68
68
69 * Extensible system of 'magic' commands for controlling the environment and
69 * Extensible system of 'magic' commands for controlling the environment and
70 performing many tasks related either to IPython or the operating system.
70 performing many tasks related either to IPython or the operating system.
71
71
72 * A rich configuration system with easy switching between different setups
72 * A rich configuration system with easy switching between different setups
73 (simpler than changing $PYTHONSTARTUP environment variables every time).
73 (simpler than changing $PYTHONSTARTUP environment variables every time).
74
74
75 * Session logging and reloading.
75 * Session logging and reloading.
76
76
77 * Extensible syntax processing for special purpose situations.
77 * Extensible syntax processing for special purpose situations.
78
78
79 * Access to the system shell with user-extensible alias system.
79 * Access to the system shell with user-extensible alias system.
80
80
81 * Easily embeddable in other Python programs and GUIs.
81 * Easily embeddable in other Python programs and GUIs.
82
82
83 * Integrated access to the pdb debugger and the Python profiler.
83 * Integrated access to the pdb debugger and the Python profiler.
84
84
85 The latest development version is always available from IPython's `GitHub
85 The latest development version is always available from IPython's `GitHub
86 site <http://github.com/ipython>`_.
86 site <http://github.com/ipython>`_.
87 """
87 """
88
88
89 license = 'BSD'
89 license = 'BSD'
90
90
91 authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'),
91 authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'),
92 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
92 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
93 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
93 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
94 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
94 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
95 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
95 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
96 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com'),
96 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com'),
97 'Thomas' : ('Thomas A. Kluyver', 'takowl@gmail.com'),
97 'Thomas' : ('Thomas A. Kluyver', 'takowl@gmail.com'),
98 'Jorgen' : ('Jorgen Stenarson', 'jorgen.stenarson@bostream.nu'),
98 'Jorgen' : ('Jorgen Stenarson', 'jorgen.stenarson@bostream.nu'),
99 'Matthias' : ('Matthias Bussonnier', 'bussonniermatthias@gmail.com'),
99 'Matthias' : ('Matthias Bussonnier', 'bussonniermatthias@gmail.com'),
100 }
100 }
101
101
102 author = 'The IPython Development Team'
102 author = 'The IPython Development Team'
103
103
104 author_email = 'ipython-dev@scipy.org'
104 author_email = 'ipython-dev@scipy.org'
105
105
106 url = 'http://ipython.org'
106 url = 'http://ipython.org'
107
107
108 download_url = 'https://github.com/ipython/ipython/downloads'
109
108
110 platforms = ['Linux','Mac OSX','Windows XP/Vista/7/8']
109 platforms = ['Linux','Mac OSX','Windows']
111
110
112 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed',
111 keywords = ['Interactive','Interpreter','Shell', 'Embedding']
113 'Web-based computing', 'Qt console', 'Embedding']
114
112
115 classifiers = [
113 classifiers = [
116 'Framework :: IPython',
114 'Framework :: IPython',
117 'Intended Audience :: Developers',
115 'Intended Audience :: Developers',
118 'Intended Audience :: Science/Research',
116 'Intended Audience :: Science/Research',
119 'License :: OSI Approved :: BSD License',
117 'License :: OSI Approved :: BSD License',
120 'Programming Language :: Python',
118 'Programming Language :: Python',
121 'Programming Language :: Python :: 2',
119 'Programming Language :: Python :: 2',
122 'Programming Language :: Python :: 2.7',
120 'Programming Language :: Python :: 2.7',
123 'Programming Language :: Python :: 3',
121 'Programming Language :: Python :: 3',
124 'Topic :: System :: Shells'
122 'Topic :: System :: Shells'
125 ]
123 ]
@@ -1,471 +1,470 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 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
4
4
5 This includes:
5 This includes:
6
6
7 * The basic arguments to setup
7 * The basic arguments to setup
8 * Functions for finding things like packages, package data, etc.
8 * Functions for finding things like packages, package data, etc.
9 * A function for checking dependencies.
9 * A function for checking dependencies.
10 """
10 """
11
11
12 # Copyright (c) IPython Development Team.
12 # Copyright (c) IPython Development Team.
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14
14
15 from __future__ import print_function
15 from __future__ import print_function
16
16
17 import re
17 import re
18 import os
18 import os
19 import sys
19 import sys
20
20
21 from distutils import log
21 from distutils import log
22 from distutils.command.build_py import build_py
22 from distutils.command.build_py import build_py
23 from distutils.command.build_scripts import build_scripts
23 from distutils.command.build_scripts import build_scripts
24 from distutils.command.install import install
24 from distutils.command.install import install
25 from distutils.command.install_scripts import install_scripts
25 from distutils.command.install_scripts import install_scripts
26 from distutils.cmd import Command
26 from distutils.cmd import Command
27 from glob import glob
27 from glob import glob
28
28
29 from setupext import install_data_ext
29 from setupext import install_data_ext
30
30
31 #-------------------------------------------------------------------------------
31 #-------------------------------------------------------------------------------
32 # Useful globals and utility functions
32 # Useful globals and utility functions
33 #-------------------------------------------------------------------------------
33 #-------------------------------------------------------------------------------
34
34
35 # A few handy globals
35 # A few handy globals
36 isfile = os.path.isfile
36 isfile = os.path.isfile
37 pjoin = os.path.join
37 pjoin = os.path.join
38 repo_root = os.path.dirname(os.path.abspath(__file__))
38 repo_root = os.path.dirname(os.path.abspath(__file__))
39
39
40 def oscmd(s):
40 def oscmd(s):
41 print(">", s)
41 print(">", s)
42 os.system(s)
42 os.system(s)
43
43
44 # Py3 compatibility hacks, without assuming IPython itself is installed with
44 # Py3 compatibility hacks, without assuming IPython itself is installed with
45 # the full py3compat machinery.
45 # the full py3compat machinery.
46
46
47 try:
47 try:
48 execfile
48 execfile
49 except NameError:
49 except NameError:
50 def execfile(fname, globs, locs=None):
50 def execfile(fname, globs, locs=None):
51 locs = locs or globs
51 locs = locs or globs
52 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
52 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
53
53
54 # A little utility we'll need below, since glob() does NOT allow you to do
54 # A little utility we'll need below, since glob() does NOT allow you to do
55 # exclusion on multiple endings!
55 # exclusion on multiple endings!
56 def file_doesnt_endwith(test,endings):
56 def file_doesnt_endwith(test,endings):
57 """Return true if test is a file and its name does NOT end with any
57 """Return true if test is a file and its name does NOT end with any
58 of the strings listed in endings."""
58 of the strings listed in endings."""
59 if not isfile(test):
59 if not isfile(test):
60 return False
60 return False
61 for e in endings:
61 for e in endings:
62 if test.endswith(e):
62 if test.endswith(e):
63 return False
63 return False
64 return True
64 return True
65
65
66 #---------------------------------------------------------------------------
66 #---------------------------------------------------------------------------
67 # Basic project information
67 # Basic project information
68 #---------------------------------------------------------------------------
68 #---------------------------------------------------------------------------
69
69
70 # release.py contains version, authors, license, url, keywords, etc.
70 # release.py contains version, authors, license, url, keywords, etc.
71 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
71 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
72
72
73 # Create a dict with the basic information
73 # Create a dict with the basic information
74 # This dict is eventually passed to setup after additional keys are added.
74 # This dict is eventually passed to setup after additional keys are added.
75 setup_args = dict(
75 setup_args = dict(
76 name = name,
76 name = name,
77 version = version,
77 version = version,
78 description = description,
78 description = description,
79 long_description = long_description,
79 long_description = long_description,
80 author = author,
80 author = author,
81 author_email = author_email,
81 author_email = author_email,
82 url = url,
82 url = url,
83 download_url = download_url,
84 license = license,
83 license = license,
85 platforms = platforms,
84 platforms = platforms,
86 keywords = keywords,
85 keywords = keywords,
87 classifiers = classifiers,
86 classifiers = classifiers,
88 cmdclass = {'install_data': install_data_ext},
87 cmdclass = {'install_data': install_data_ext},
89 )
88 )
90
89
91
90
92 #---------------------------------------------------------------------------
91 #---------------------------------------------------------------------------
93 # Find packages
92 # Find packages
94 #---------------------------------------------------------------------------
93 #---------------------------------------------------------------------------
95
94
96 def find_packages():
95 def find_packages():
97 """
96 """
98 Find all of IPython's packages.
97 Find all of IPython's packages.
99 """
98 """
100 excludes = ['deathrow', 'quarantine']
99 excludes = ['deathrow', 'quarantine']
101 packages = []
100 packages = []
102 for dir,subdirs,files in os.walk('IPython'):
101 for dir,subdirs,files in os.walk('IPython'):
103 package = dir.replace(os.path.sep, '.')
102 package = dir.replace(os.path.sep, '.')
104 if any(package.startswith('IPython.'+exc) for exc in excludes):
103 if any(package.startswith('IPython.'+exc) for exc in excludes):
105 # package is to be excluded (e.g. deathrow)
104 # package is to be excluded (e.g. deathrow)
106 continue
105 continue
107 if '__init__.py' not in files:
106 if '__init__.py' not in files:
108 # not a package
107 # not a package
109 continue
108 continue
110 packages.append(package)
109 packages.append(package)
111 return packages
110 return packages
112
111
113 #---------------------------------------------------------------------------
112 #---------------------------------------------------------------------------
114 # Find package data
113 # Find package data
115 #---------------------------------------------------------------------------
114 #---------------------------------------------------------------------------
116
115
117 def find_package_data():
116 def find_package_data():
118 """
117 """
119 Find IPython's package_data.
118 Find IPython's package_data.
120 """
119 """
121 # This is not enough for these things to appear in an sdist.
120 # This is not enough for these things to appear in an sdist.
122 # We need to muck with the MANIFEST to get this to work
121 # We need to muck with the MANIFEST to get this to work
123
122
124 package_data = {
123 package_data = {
125 'IPython.core' : ['profile/README*'],
124 'IPython.core' : ['profile/README*'],
126 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
125 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
127 'IPython.lib.tests' : ['*.wav'],
126 'IPython.lib.tests' : ['*.wav'],
128 'IPython.testing.plugin' : ['*.txt'],
127 'IPython.testing.plugin' : ['*.txt'],
129 }
128 }
130
129
131 return package_data
130 return package_data
132
131
133
132
134 def check_package_data(package_data):
133 def check_package_data(package_data):
135 """verify that package_data globs make sense"""
134 """verify that package_data globs make sense"""
136 print("checking package data")
135 print("checking package data")
137 for pkg, data in package_data.items():
136 for pkg, data in package_data.items():
138 pkg_root = pjoin(*pkg.split('.'))
137 pkg_root = pjoin(*pkg.split('.'))
139 for d in data:
138 for d in data:
140 path = pjoin(pkg_root, d)
139 path = pjoin(pkg_root, d)
141 if '*' in path:
140 if '*' in path:
142 assert len(glob(path)) > 0, "No files match pattern %s" % path
141 assert len(glob(path)) > 0, "No files match pattern %s" % path
143 else:
142 else:
144 assert os.path.exists(path), "Missing package data: %s" % path
143 assert os.path.exists(path), "Missing package data: %s" % path
145
144
146
145
147 def check_package_data_first(command):
146 def check_package_data_first(command):
148 """decorator for checking package_data before running a given command
147 """decorator for checking package_data before running a given command
149
148
150 Probably only needs to wrap build_py
149 Probably only needs to wrap build_py
151 """
150 """
152 class DecoratedCommand(command):
151 class DecoratedCommand(command):
153 def run(self):
152 def run(self):
154 check_package_data(self.package_data)
153 check_package_data(self.package_data)
155 command.run(self)
154 command.run(self)
156 return DecoratedCommand
155 return DecoratedCommand
157
156
158
157
159 #---------------------------------------------------------------------------
158 #---------------------------------------------------------------------------
160 # Find data files
159 # Find data files
161 #---------------------------------------------------------------------------
160 #---------------------------------------------------------------------------
162
161
163 def make_dir_struct(tag,base,out_base):
162 def make_dir_struct(tag,base,out_base):
164 """Make the directory structure of all files below a starting dir.
163 """Make the directory structure of all files below a starting dir.
165
164
166 This is just a convenience routine to help build a nested directory
165 This is just a convenience routine to help build a nested directory
167 hierarchy because distutils is too stupid to do this by itself.
166 hierarchy because distutils is too stupid to do this by itself.
168
167
169 XXX - this needs a proper docstring!
168 XXX - this needs a proper docstring!
170 """
169 """
171
170
172 # we'll use these a lot below
171 # we'll use these a lot below
173 lbase = len(base)
172 lbase = len(base)
174 pathsep = os.path.sep
173 pathsep = os.path.sep
175 lpathsep = len(pathsep)
174 lpathsep = len(pathsep)
176
175
177 out = []
176 out = []
178 for (dirpath,dirnames,filenames) in os.walk(base):
177 for (dirpath,dirnames,filenames) in os.walk(base):
179 # we need to strip out the dirpath from the base to map it to the
178 # we need to strip out the dirpath from the base to map it to the
180 # output (installation) path. This requires possibly stripping the
179 # output (installation) path. This requires possibly stripping the
181 # path separator, because otherwise pjoin will not work correctly
180 # path separator, because otherwise pjoin will not work correctly
182 # (pjoin('foo/','/bar') returns '/bar').
181 # (pjoin('foo/','/bar') returns '/bar').
183
182
184 dp_eff = dirpath[lbase:]
183 dp_eff = dirpath[lbase:]
185 if dp_eff.startswith(pathsep):
184 if dp_eff.startswith(pathsep):
186 dp_eff = dp_eff[lpathsep:]
185 dp_eff = dp_eff[lpathsep:]
187 # The output path must be anchored at the out_base marker
186 # The output path must be anchored at the out_base marker
188 out_path = pjoin(out_base,dp_eff)
187 out_path = pjoin(out_base,dp_eff)
189 # Now we can generate the final filenames. Since os.walk only produces
188 # Now we can generate the final filenames. Since os.walk only produces
190 # filenames, we must join back with the dirpath to get full valid file
189 # filenames, we must join back with the dirpath to get full valid file
191 # paths:
190 # paths:
192 pfiles = [pjoin(dirpath,f) for f in filenames]
191 pfiles = [pjoin(dirpath,f) for f in filenames]
193 # Finally, generate the entry we need, which is a pari of (output
192 # Finally, generate the entry we need, which is a pari of (output
194 # path, files) for use as a data_files parameter in install_data.
193 # path, files) for use as a data_files parameter in install_data.
195 out.append((out_path, pfiles))
194 out.append((out_path, pfiles))
196
195
197 return out
196 return out
198
197
199
198
200 def find_data_files():
199 def find_data_files():
201 """
200 """
202 Find IPython's data_files.
201 Find IPython's data_files.
203
202
204 Just man pages at this point.
203 Just man pages at this point.
205 """
204 """
206
205
207 manpagebase = pjoin('share', 'man', 'man1')
206 manpagebase = pjoin('share', 'man', 'man1')
208
207
209 # Simple file lists can be made by hand
208 # Simple file lists can be made by hand
210 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
209 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
211 if not manpages:
210 if not manpages:
212 # When running from a source tree, the manpages aren't gzipped
211 # When running from a source tree, the manpages aren't gzipped
213 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
212 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
214
213
215 # And assemble the entire output list
214 # And assemble the entire output list
216 data_files = [ (manpagebase, manpages) ]
215 data_files = [ (manpagebase, manpages) ]
217
216
218 return data_files
217 return data_files
219
218
220
219
221 def make_man_update_target(manpage):
220 def make_man_update_target(manpage):
222 """Return a target_update-compliant tuple for the given manpage.
221 """Return a target_update-compliant tuple for the given manpage.
223
222
224 Parameters
223 Parameters
225 ----------
224 ----------
226 manpage : string
225 manpage : string
227 Name of the manpage, must include the section number (trailing number).
226 Name of the manpage, must include the section number (trailing number).
228
227
229 Example
228 Example
230 -------
229 -------
231
230
232 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
231 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
233 ('docs/man/ipython.1.gz',
232 ('docs/man/ipython.1.gz',
234 ['docs/man/ipython.1'],
233 ['docs/man/ipython.1'],
235 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
234 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
236 """
235 """
237 man_dir = pjoin('docs', 'man')
236 man_dir = pjoin('docs', 'man')
238 manpage_gz = manpage + '.gz'
237 manpage_gz = manpage + '.gz'
239 manpath = pjoin(man_dir, manpage)
238 manpath = pjoin(man_dir, manpage)
240 manpath_gz = pjoin(man_dir, manpage_gz)
239 manpath_gz = pjoin(man_dir, manpage_gz)
241 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
240 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
242 locals() )
241 locals() )
243 return (manpath_gz, [manpath], gz_cmd)
242 return (manpath_gz, [manpath], gz_cmd)
244
243
245 # The two functions below are copied from IPython.utils.path, so we don't need
244 # The two functions below are copied from IPython.utils.path, so we don't need
246 # to import IPython during setup, which fails on Python 3.
245 # to import IPython during setup, which fails on Python 3.
247
246
248 def target_outdated(target,deps):
247 def target_outdated(target,deps):
249 """Determine whether a target is out of date.
248 """Determine whether a target is out of date.
250
249
251 target_outdated(target,deps) -> 1/0
250 target_outdated(target,deps) -> 1/0
252
251
253 deps: list of filenames which MUST exist.
252 deps: list of filenames which MUST exist.
254 target: single filename which may or may not exist.
253 target: single filename which may or may not exist.
255
254
256 If target doesn't exist or is older than any file listed in deps, return
255 If target doesn't exist or is older than any file listed in deps, return
257 true, otherwise return false.
256 true, otherwise return false.
258 """
257 """
259 try:
258 try:
260 target_time = os.path.getmtime(target)
259 target_time = os.path.getmtime(target)
261 except os.error:
260 except os.error:
262 return 1
261 return 1
263 for dep in deps:
262 for dep in deps:
264 dep_time = os.path.getmtime(dep)
263 dep_time = os.path.getmtime(dep)
265 if dep_time > target_time:
264 if dep_time > target_time:
266 #print "For target",target,"Dep failed:",dep # dbg
265 #print "For target",target,"Dep failed:",dep # dbg
267 #print "times (dep,tar):",dep_time,target_time # dbg
266 #print "times (dep,tar):",dep_time,target_time # dbg
268 return 1
267 return 1
269 return 0
268 return 0
270
269
271
270
272 def target_update(target,deps,cmd):
271 def target_update(target,deps,cmd):
273 """Update a target with a given command given a list of dependencies.
272 """Update a target with a given command given a list of dependencies.
274
273
275 target_update(target,deps,cmd) -> runs cmd if target is outdated.
274 target_update(target,deps,cmd) -> runs cmd if target is outdated.
276
275
277 This is just a wrapper around target_outdated() which calls the given
276 This is just a wrapper around target_outdated() which calls the given
278 command if target is outdated."""
277 command if target is outdated."""
279
278
280 if target_outdated(target,deps):
279 if target_outdated(target,deps):
281 os.system(cmd)
280 os.system(cmd)
282
281
283 #---------------------------------------------------------------------------
282 #---------------------------------------------------------------------------
284 # Find scripts
283 # Find scripts
285 #---------------------------------------------------------------------------
284 #---------------------------------------------------------------------------
286
285
287 def find_entry_points():
286 def find_entry_points():
288 """Defines the command line entry points for IPython
287 """Defines the command line entry points for IPython
289
288
290 This always uses setuptools-style entry points. When setuptools is not in
289 This always uses setuptools-style entry points. When setuptools is not in
291 use, our own build_scripts_entrypt class below parses these and builds
290 use, our own build_scripts_entrypt class below parses these and builds
292 command line scripts.
291 command line scripts.
293
292
294 Each of our entry points gets both a plain name, e.g. ipython, and one
293 Each of our entry points gets both a plain name, e.g. ipython, and one
295 suffixed with the Python major version number, e.g. ipython3.
294 suffixed with the Python major version number, e.g. ipython3.
296 """
295 """
297 ep = [
296 ep = [
298 'ipython%s = IPython:start_ipython',
297 'ipython%s = IPython:start_ipython',
299 'iptest%s = IPython.testing.iptestcontroller:main',
298 'iptest%s = IPython.testing.iptestcontroller:main',
300 ]
299 ]
301 suffix = str(sys.version_info[0])
300 suffix = str(sys.version_info[0])
302 return [e % '' for e in ep] + [e % suffix for e in ep]
301 return [e % '' for e in ep] + [e % suffix for e in ep]
303
302
304 script_src = """#!{executable}
303 script_src = """#!{executable}
305 # This script was automatically generated by setup.py
304 # This script was automatically generated by setup.py
306 if __name__ == '__main__':
305 if __name__ == '__main__':
307 from {mod} import {func}
306 from {mod} import {func}
308 {func}()
307 {func}()
309 """
308 """
310
309
311 class build_scripts_entrypt(build_scripts):
310 class build_scripts_entrypt(build_scripts):
312 """Build the command line scripts
311 """Build the command line scripts
313
312
314 Parse setuptools style entry points and write simple scripts to run the
313 Parse setuptools style entry points and write simple scripts to run the
315 target functions.
314 target functions.
316
315
317 On Windows, this also creates .cmd wrappers for the scripts so that you can
316 On Windows, this also creates .cmd wrappers for the scripts so that you can
318 easily launch them from a command line.
317 easily launch them from a command line.
319 """
318 """
320 def run(self):
319 def run(self):
321 self.mkpath(self.build_dir)
320 self.mkpath(self.build_dir)
322 outfiles = []
321 outfiles = []
323 for script in find_entry_points():
322 for script in find_entry_points():
324 name, entrypt = script.split('=')
323 name, entrypt = script.split('=')
325 name = name.strip()
324 name = name.strip()
326 entrypt = entrypt.strip()
325 entrypt = entrypt.strip()
327 outfile = os.path.join(self.build_dir, name)
326 outfile = os.path.join(self.build_dir, name)
328 outfiles.append(outfile)
327 outfiles.append(outfile)
329 print('Writing script to', outfile)
328 print('Writing script to', outfile)
330
329
331 mod, func = entrypt.split(':')
330 mod, func = entrypt.split(':')
332 with open(outfile, 'w') as f:
331 with open(outfile, 'w') as f:
333 f.write(script_src.format(executable=sys.executable,
332 f.write(script_src.format(executable=sys.executable,
334 mod=mod, func=func))
333 mod=mod, func=func))
335
334
336 if sys.platform == 'win32':
335 if sys.platform == 'win32':
337 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
336 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
338 # command line
337 # command line
339 cmd_file = os.path.join(self.build_dir, name + '.cmd')
338 cmd_file = os.path.join(self.build_dir, name + '.cmd')
340 cmd = '@"{python}" "%~dp0\{script}" %*\r\n'.format(
339 cmd = '@"{python}" "%~dp0\{script}" %*\r\n'.format(
341 python=sys.executable, script=name)
340 python=sys.executable, script=name)
342 log.info("Writing %s wrapper script" % cmd_file)
341 log.info("Writing %s wrapper script" % cmd_file)
343 with open(cmd_file, 'w') as f:
342 with open(cmd_file, 'w') as f:
344 f.write(cmd)
343 f.write(cmd)
345
344
346 return outfiles, outfiles
345 return outfiles, outfiles
347
346
348 class install_lib_symlink(Command):
347 class install_lib_symlink(Command):
349 user_options = [
348 user_options = [
350 ('install-dir=', 'd', "directory to install to"),
349 ('install-dir=', 'd', "directory to install to"),
351 ]
350 ]
352
351
353 def initialize_options(self):
352 def initialize_options(self):
354 self.install_dir = None
353 self.install_dir = None
355
354
356 def finalize_options(self):
355 def finalize_options(self):
357 self.set_undefined_options('symlink',
356 self.set_undefined_options('symlink',
358 ('install_lib', 'install_dir'),
357 ('install_lib', 'install_dir'),
359 )
358 )
360
359
361 def run(self):
360 def run(self):
362 if sys.platform == 'win32':
361 if sys.platform == 'win32':
363 raise Exception("This doesn't work on Windows.")
362 raise Exception("This doesn't work on Windows.")
364 pkg = os.path.join(os.getcwd(), 'IPython')
363 pkg = os.path.join(os.getcwd(), 'IPython')
365 dest = os.path.join(self.install_dir, 'IPython')
364 dest = os.path.join(self.install_dir, 'IPython')
366 if os.path.islink(dest):
365 if os.path.islink(dest):
367 print('removing existing symlink at %s' % dest)
366 print('removing existing symlink at %s' % dest)
368 os.unlink(dest)
367 os.unlink(dest)
369 print('symlinking %s -> %s' % (pkg, dest))
368 print('symlinking %s -> %s' % (pkg, dest))
370 os.symlink(pkg, dest)
369 os.symlink(pkg, dest)
371
370
372 class unsymlink(install):
371 class unsymlink(install):
373 def run(self):
372 def run(self):
374 dest = os.path.join(self.install_lib, 'IPython')
373 dest = os.path.join(self.install_lib, 'IPython')
375 if os.path.islink(dest):
374 if os.path.islink(dest):
376 print('removing symlink at %s' % dest)
375 print('removing symlink at %s' % dest)
377 os.unlink(dest)
376 os.unlink(dest)
378 else:
377 else:
379 print('No symlink exists at %s' % dest)
378 print('No symlink exists at %s' % dest)
380
379
381 class install_symlinked(install):
380 class install_symlinked(install):
382 def run(self):
381 def run(self):
383 if sys.platform == 'win32':
382 if sys.platform == 'win32':
384 raise Exception("This doesn't work on Windows.")
383 raise Exception("This doesn't work on Windows.")
385
384
386 # Run all sub-commands (at least those that need to be run)
385 # Run all sub-commands (at least those that need to be run)
387 for cmd_name in self.get_sub_commands():
386 for cmd_name in self.get_sub_commands():
388 self.run_command(cmd_name)
387 self.run_command(cmd_name)
389
388
390 # 'sub_commands': a list of commands this command might have to run to
389 # 'sub_commands': a list of commands this command might have to run to
391 # get its work done. See cmd.py for more info.
390 # get its work done. See cmd.py for more info.
392 sub_commands = [('install_lib_symlink', lambda self:True),
391 sub_commands = [('install_lib_symlink', lambda self:True),
393 ('install_scripts_sym', lambda self:True),
392 ('install_scripts_sym', lambda self:True),
394 ]
393 ]
395
394
396 class install_scripts_for_symlink(install_scripts):
395 class install_scripts_for_symlink(install_scripts):
397 """Redefined to get options from 'symlink' instead of 'install'.
396 """Redefined to get options from 'symlink' instead of 'install'.
398
397
399 I love distutils almost as much as I love setuptools.
398 I love distutils almost as much as I love setuptools.
400 """
399 """
401 def finalize_options(self):
400 def finalize_options(self):
402 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
401 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
403 self.set_undefined_options('symlink',
402 self.set_undefined_options('symlink',
404 ('install_scripts', 'install_dir'),
403 ('install_scripts', 'install_dir'),
405 ('force', 'force'),
404 ('force', 'force'),
406 ('skip_build', 'skip_build'),
405 ('skip_build', 'skip_build'),
407 )
406 )
408
407
409
408
410 #---------------------------------------------------------------------------
409 #---------------------------------------------------------------------------
411 # VCS related
410 # VCS related
412 #---------------------------------------------------------------------------
411 #---------------------------------------------------------------------------
413
412
414
413
415 def git_prebuild(pkg_dir, build_cmd=build_py):
414 def git_prebuild(pkg_dir, build_cmd=build_py):
416 """Return extended build or sdist command class for recording commit
415 """Return extended build or sdist command class for recording commit
417
416
418 records git commit in IPython.utils._sysinfo.commit
417 records git commit in IPython.utils._sysinfo.commit
419
418
420 for use in IPython.utils.sysinfo.sys_info() calls after installation.
419 for use in IPython.utils.sysinfo.sys_info() calls after installation.
421 """
420 """
422
421
423 class MyBuildPy(build_cmd):
422 class MyBuildPy(build_cmd):
424 ''' Subclass to write commit data into installation tree '''
423 ''' Subclass to write commit data into installation tree '''
425 def run(self):
424 def run(self):
426 # loose as `.dev` is suppose to be invalid
425 # loose as `.dev` is suppose to be invalid
427 print("check version number")
426 print("check version number")
428 loose_pep440re = re.compile('^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
427 loose_pep440re = re.compile('^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
429 import IPython.core.release as r
428 import IPython.core.release as r
430 if not loose_pep440re.match(r.version):
429 if not loose_pep440re.match(r.version):
431 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % r.version)
430 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % r.version)
432
431
433
432
434 build_cmd.run(self)
433 build_cmd.run(self)
435 # this one will only fire for build commands
434 # this one will only fire for build commands
436 if hasattr(self, 'build_lib'):
435 if hasattr(self, 'build_lib'):
437 self._record_commit(self.build_lib)
436 self._record_commit(self.build_lib)
438
437
439 def make_release_tree(self, base_dir, files):
438 def make_release_tree(self, base_dir, files):
440 # this one will fire for sdist
439 # this one will fire for sdist
441 build_cmd.make_release_tree(self, base_dir, files)
440 build_cmd.make_release_tree(self, base_dir, files)
442 self._record_commit(base_dir)
441 self._record_commit(base_dir)
443
442
444 def _record_commit(self, base_dir):
443 def _record_commit(self, base_dir):
445 import subprocess
444 import subprocess
446 proc = subprocess.Popen('git rev-parse --short HEAD',
445 proc = subprocess.Popen('git rev-parse --short HEAD',
447 stdout=subprocess.PIPE,
446 stdout=subprocess.PIPE,
448 stderr=subprocess.PIPE,
447 stderr=subprocess.PIPE,
449 shell=True)
448 shell=True)
450 repo_commit, _ = proc.communicate()
449 repo_commit, _ = proc.communicate()
451 repo_commit = repo_commit.strip().decode("ascii")
450 repo_commit = repo_commit.strip().decode("ascii")
452
451
453 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
452 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
454 if os.path.isfile(out_pth) and not repo_commit:
453 if os.path.isfile(out_pth) and not repo_commit:
455 # nothing to write, don't clobber
454 # nothing to write, don't clobber
456 return
455 return
457
456
458 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
457 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
459
458
460 # remove to avoid overwriting original via hard link
459 # remove to avoid overwriting original via hard link
461 try:
460 try:
462 os.remove(out_pth)
461 os.remove(out_pth)
463 except (IOError, OSError):
462 except (IOError, OSError):
464 pass
463 pass
465 with open(out_pth, 'w') as out_file:
464 with open(out_pth, 'w') as out_file:
466 out_file.writelines([
465 out_file.writelines([
467 '# GENERATED BY setup.py\n',
466 '# GENERATED BY setup.py\n',
468 'commit = u"%s"\n' % repo_commit,
467 'commit = u"%s"\n' % repo_commit,
469 ])
468 ])
470 return MyBuildPy
469 return MyBuildPy
471
470
General Comments 0
You need to be logged in to leave comments. Login now